2010-09-29 1 views
9

Я играл с наборами в SQL Server 2000 и имеют следующую структуру таблицы для одного из моих временных таблиц (#Periods):Выбор SUM значений TOP 2 в таблице с несколькими GROUP в SQL

 
    RestCTR  HoursCTR Duration Rest 
    ---------------------------------------- 
    1   337   2   0 
    2   337   46   1 
    3   337   2   0 
    4   337   46   1 
    5   338   1   0 
    6   338   46   1 
    7   338   2   0 
    8   338   46   1 
    9   338   1   0 
    10   339   46   1 
    ... 

Что я хотел бы сделать, так это рассчитать сумму двух наиболее длинных периодов отдыха для каждого HoursCTR, предпочтительно используя наборы и временные таблицы (а не курсоры или вложенные подзапросы).

Здесь нет запроса сон, который просто не будет работать в SQL (независимо от того, сколько раз я его запускаю):

Select HoursCTR, SUM (TOP 2 Duration) as LongestBreaks 
FROM #Periods 
WHERE Rest = 1 
Group By HoursCTR  

HoursCTR может иметь любое количество периодов отдыха (не включая ни одного).

Мое текущее решение не очень элегантно, и в основном включает в себя следующие этапы:

  1. Получить максимальную продолжительность отдыха, группы по HoursCTR
  2. Выберите первый (мин) RestCTR строку, которая возвращает эту максимальную продолжительность для каждого HoursCTR
  3. Повторите шаг 1 (за исключением строк уже собранных на стадии 2)
  4. Повторите шаг 2 (опять же, за исключением строк, собранных на стадии 2)
  5. Объединить в RestCTR строки (из шага 2 и 4) в одну таблицу
  6. Get сумма длительностей указала на строки на этапе 5, сгруппированной по HoursCTR

Если есть какая-либо функция множества, которые разрежут этот процесс вниз, они было бы очень желанным.

ответ

7

Лучший способ сделать это в SQL Server является с common table expression, нумерация строк в каждой группе с помощью функции кадрирования ROW_NUMBER():

WITH NumberedPeriods AS (
    SELECT HoursCTR, Duration, ROW_NUMBER() 
    OVER (PARTITION BY HoursCTR ORDER BY Duration DESC) AS RN 
    FROM #Periods 
    WHERE Rest = 1 
) 
SELECT HoursCTR, SUM(Duration) AS LongestBreaks 
FROM NumberedPeriods 
WHERE RN <= 2 
GROUP BY HoursCTR 

редактировать: Я добавил ORDER BY в предложении разделение, чтобы получить два самых больших остатка.


покаянием, я не заметил, что вам это нужно, чтобы работать в Microsoft SQL Server 2000. Эта версия не поддерживает CTE-х или функций окна. Я оставлю ответ выше, если он поможет кому-то другому.

В SQL Server 2000, общий совет использовать связанный подзапрос:

SELECT p1.HoursCTR, (SELECT SUM(t.Duration) FROM 
    (SELECT TOP 2 p2.Duration FROM #Periods AS p2 
    WHERE p2.HoursCTR = p1.HoursCTR 
    ORDER BY p2.Duration DESC) AS t) AS LongestBreaks 
FROM #Periods AS p1 
+0

как это выбрать два _longest_ перерывы Что мне не хватает? – Arkadiy

+2

OP указывает SQL Server 2000. ROW_NUMBER() и CTE недоступны. – bobs

+0

@bobs: Спасибо, я пропустил это. Я добавил другое решение. –

1

К сожалению для вас, Алекс, у вас есть правильное решение: коррелированные подзапросы, в зависимости от того, как они структурированы , в конечном итоге будет срабатывать несколько раз, что потенциально даст вам сотни отдельных запросов.

Поместите свое текущее решение в анализатор запросов, включите «Показать план выполнения» (Ctrl + K) и запустите его. У вас будет дополнительная вкладка внизу, которая покажет вам, как движок начал процесс сбора ваших результатов. Если вы сделаете то же самое с коррелированным подзапросом, вы увидите, что делает этот вариант.

Я считаю, что он может забивать таблицу #Periods примерно столько раз, сколько у вас есть отдельные строки в этой таблице.

Кроме того, мне кажется, что-то случилось с коррелированным подзапросом. Поскольку я избегаю их, как чуму, зная, что они злые, я не уверен, как это исправить.

+0

Спасибо, Дэвид, я надеялся, что кто-то может знать о хитрых трюках, которые я не обнаружил, но похоже, что этот шестиэтапный процесс может быть концом строки. –

+0

Ну ... вы говорите движку базы данных, что делать и как это делать, что не так уж и плохо. Двигатель не такой яркий, когда дело доходит до него. Как он должен знать, что вы не хотите, чтобы это было итеративным мусором, если вы сказали ему это сделать? –

2

SQL 2000 не имеет CTE, а не ROW_NUMBER().
Корреляционные подзапросы могут потребовать дополнительный шаг при использовании group by.

Это должно работать для вас:

SELECT 
    F.HoursCTR, 
    MAX (F.LongestBreaks) AS LongestBreaks -- Dummy max() so that groupby can be used. 
FROM 
    (
     SELECT 
      Pm.HoursCTR, 
      (
       SELECT 
        COALESCE (SUM (S.Duration), 0)  
       FROM 
        (
         SELECT TOP 2 T.Duration 
         FROM   #Periods AS T 
         WHERE   T.HoursCTR = Pm.HoursCTR 
         AND    T.Rest  = 1 
         ORDER BY  T.Duration DESC 
        ) AS S 
      ) AS LongestBreaks 
     FROM 
      #Periods AS Pm 
    ) AS F 
GROUP BY 
    F.HoursCTR