Что такое эффективный SQL-запрос для объединения с двойным максимумом в подзапросе? - PullRequest
0 голосов
/ 19 января 2011

Извините, что название дерьмо - я не мог придумать лучшего способа выразить это. Это моя структура данных:

Widget   WidgetTransition
------   ----------------
Id,      WidgetId,
...      TransitionTypeId,
         Cost,
         ...

Я хочу запрос, который даст мне список деталей для каждого виджета вместе с деталями перехода самый дорогой (max WidgetTransition.Cost). Если у виджета есть два или более переходов, которые «привязаны» для самого дорогого перехода, следует использовать детали самого последнего перехода (max WidgetTransition.WidgetId). Если виджет не имеет переходов, он не должен появляться в результатах. Это лучшее, что я мог придумать:

SELECT *
FROM   Widget
JOIN   WidgetTransition
ON     WidgetTransition.WidgetId = Widget.Id
AND    WidgetTransition.Cost = (
       SELECT Max(MostExpensiveTransition.Cost)
       FROM   WidgetTransition MostExpensiveTransition
       WHERE  MostExpensiveTransition.WidgetId = Widget.Id
       )

Это почти работает, но есть две проблемы.

  • Не справляется с связанными переходами должным образом. Если виджет имеет два или более связанных переходов, каждый переход будет отображаться в результатах, а не в самых последних.
  • В случае больших наборов данных запрос выполняется медленно. База данных Sybase, на которой я ее запускаю, выполнит два сканирования таблицы (WidgetTransition.Cost отсутствует в индексе) для WidgetTransition для каждого виджета. Предположительно один предназначен для объединения, а другой - для определения максимальной стоимости.

Есть ли лучший способ написать этот запрос, который решает связанную проблему и / или работает более эффективно? Я хочу избежать использования T-SQL или хранимой процедуры.

Ответы [ 3 ]

2 голосов
/ 19 января 2011

Если вы используете продукт базы данных, который поддерживает функции ранжирования и общие табличные выражения, такие как SQL Server 2005 и более поздние версии (или последние версии Sybase), вы можете сделать что-то вроде:

With Data As
    (
    Select WidgetId, TransitionTypeId
        , Row_Number() Over ( Partition By WidgetId 
                                Order By Cost Desc, WidgetId Desc ) As Rnk
    From WidgetTransition
    )
Select ...
From Widget As W
    Join Data As D
        On D.WidgetId = W.WidgetId
Where D.Rnk = 1
0 голосов
/ 19 января 2011

Если я вас правильно понимаю, это будет делать то, что вы хотите.Не знаю о производительности.Вы должны проверить это и попытаться выяснить, какие индексы полезны.

declare @Widget table (ID int)
declare @WidgetTransition table (WidgetID int, TransitionTypeID int, Cost int)

insert into @Widget values (1)
insert into @Widget values (2)
insert into @Widget values (3)

insert into @WidgetTransition values (1, 1, 1)
insert into @WidgetTransition values (1, 2, 2)
insert into @WidgetTransition values (2, 1, 2)
insert into @WidgetTransition values (2, 2, 12)
insert into @WidgetTransition values (2, 3, 12)

select *
from @Widget as Widget
  inner join @WidgetTransition as WidgetTransition
    on Widget.ID = WidgetTransition.WidgetID
  inner join
    ( select Widget.ID, max(WT.TransitionTypeID) as TrasitionTypeID
      from @Widget as Widget
        inner join
          ( select WidgetID, max(Cost) as Cost
            from @WidgetTransition
            group by WidgetID
          ) as MaxCost
          on Widget.ID = MaxCost.WidgetID
        inner join @WidgetTransition as WT
          on Widget.ID = WT.WidgetID and
             MaxCost.Cost = WT.Cost
        group by Widget.ID
    ) as MaxTransitionTypeID
    on Widget.ID = MaxTransitionTypeID.ID and
       WidgetTransition.TransitionTypeID = MaxTransitionTypeID.TrasitionTypeID
0 голосов
/ 19 января 2011

Вы пытались создать MaxValues, как показано ниже:

SELECT *
FROM   Widget
JOIN   WidgetTransition
ON     WidgetTransition.WidgetId = Widget.Id
JOIN   (SELECT MostExpensiveTransition.WidgetId, Max(MostExpensiveTransition.Cost) Cost
       FROM   WidgetTransition MostExpensiveTransition
       GROUP BY MostExpensiveTransition.WidgetId
       ) MaxValues
ON     MaxValues.WidgetId = Widget.Id
AND    WidgetTransition.Cost = MaxValues.Cost

Я собираюсь из памяти здесь, поскольку у меня нет базы данных sql, с которой можно играть в данный момент, так что извините, если онане совсем работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...