T-sql GROUP BY с COUNT, а затем включить MAX из COUNT

Предположим, у вас была таблица "автомобилей" с сотнями тысяч строк, и вы хотели сделать группу по:

SELECT   CarID
         , CarName
         , COUNT(*) AS Total
FROM     dbo.tbl_Cars
GROUP BY CarID
         , CarName

группировка оставляет вас с результатом сродни:

CarID       CarName    Total
1872        Olds       202,121   
547841      BMW        175,298
9877        Ford        10,241

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

CarID       CarName    Total      Max Total
1872        Olds       202,121    202,121
547841      BMW        175,298    202,121
9877        Ford        10,241    202,121 

одним из подходов было бы поместить результат группы во временную таблицу, а затем получите MAX из таблицы temp в локальную переменную. Но мне интересно, как лучше всего это сделать.


обновление

общее табличное выражение кажется наиболее элегантным для записи, однако, как и @EBarr, мое ограниченное тестирование указывает на значительно более медленную производительность. Так что я не пойду с CTE.

как ссылка @EBarr имеет для COMPUTE опция указывает на функцию является устаревшим, это не кажется и лучший маршрут.

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

немного более подробно о моем случае использования: это, вероятно, может оказаться ряд других вопросов SO. Но достаточно сказать, что я загружаюсь большое подмножество данных во временную таблицу (поэтому подмножество tbl_Cars переход в #tbl_Cars, и даже #tbl_Cars могут быть дополнительно отфильтрованы и есть агрегации, выполняемые на нем), потому что я должен выполнить множественную фильтрацию и агрегации запросов в одной хранимой процедуре возвращает несколько результирующих наборов.


обновление 2

@EBarr использует оконную функцию хорошо и коротко. На заметку: если используется RIGHT JOIN к внешней справочной таблице,COUNT() функция должна выбрать столбец из tbl_Cars, а не '*'.

SELECT       M.MachineID
             , M.MachineType
             , COUNT(C.CarID) AS Total
             , MAX(COUNT(C.CarID)) OVER() as MaxTotal
FROM         dbo.tbl_Cars C
RIGHT JOIN   dbo.tbl_Machines M
      ON     C.CarID = M.CarID
GROUP BY     M.MachineID
             , M.MachineType

С точки зрения скорости, кажется хорошо, но в какой момент ты должен быть беспокоитесь о количестве чтений?

3 ответов


механически есть несколько способов сделать это. Вы можете использовать временные таблицы/переменную таблицы. Другой способ-с вложенными запросами и / или CTE, как показал @Aaron_Bertrand. Третий способ-использовать оконные функции, такие как...

SELECT    CarName,
          COUNT(*) as theCount,
          MAX(Count(*)) OVER(PARTITION BY 'foo') as MaxPerGroup
FROM      dbo.tbl_Cars
GROUP BY CarName

A непопулярный (read depricated) четвертый способ-использовать ключевое слово COMPUTE как таковое...

SELECT   CarID, CarName, Count(*)
FROM     dbo.tbl_Cars
GROUP BY CarID, CarName 
COMPUTE MAX(Count(*))   

на COMPUTE ключевое слово генерирует итоги, которые отображаются в виде дополнительных столбцов сводки в конце результирующего набора (посмотреть этот). В приведенном выше запросе вы фактически увидите два набора записей.

быстрый

теперь следующий вопрос-Что такое "лучший/быстрый/простой.- Я тут же думаю об Ан indexed view. Как напомнил мне @Aaron, индексированные представления имеют всевозможные ограничения. Однако вышеприведенная стратегия позволяет создать индексированное представление в SELECT...ОТ..ГРУППЫ. Затем, выбрав из индексированного представления применить функцию WINDOWED пункт.

не зная больше, однако, о вашем дизайне это будет трудно для кого-либо сказать вам, что лучше. Вы получите быстрые запросы lighting из индексированного представления. Но за это приходится платить. Цена расходы на техническое обслуживание. Если базовая таблица является целью большого количества операций вставки/обновления/удаления, обслуживание индексированного представления приведет к снижению производительности в других областях.

, если вы поделитесь немного больше об использовании шаблоны доступа к данным и данным люди смогут поделиться более глубоким пониманием.


МИКРО-ТЕСТ ПРОИЗВОДИТЕЛЬНОСТИ

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

генерация данных:

Create table Cars ( CarID int identity (1,1) primary key, 
                    CarName varchar(20), 
                    value int)
GO
insert into Cars (CarName, value)
values  ('Buick', 100),
        ('Ford', 10),
        ('Buick', 300),     
        ('Buick', 100),
        ('Pontiac', 300),       
        ('Bmw', 100),
        ('Mecedes', 300),       
        ('Chevy', 300),     
        ('Buick', 100),
        ('Ford', 200);
GO 1000

этот скрипт генерирует 10 000 строк. Я тогда выполните каждый из четырех следующих запросов несколько раз:

--just group by
select  CarName,COUNT(*) countThis
FROM    Cars
GROUP BY CarName        

--group by with compute (BAD BAD DEVELOPER!)
select  CarName,COUNT(*) countThis
FROM    Cars
GROUP BY CarName        
COMPUTE  MAX(Count(*));

-- windowed aggregates...
SELECT  CarName,
        COUNT(*) as theCount,
        MAX(Count(*)) OVER(PARTITION BY 'foo') as MaxInAnyGroup
FROM Cars
GROUP BY CarName        

--CTE version
;WITH x AS (
  SELECT   CarName,
           COUNT(*) AS Total
  FROM     Cars
  GROUP BY CarName
)
SELECT x.CarName, x.Total, x2.[Max Total]
FROM x CROSS JOIN (
  SELECT [Max Total] = MAX(Total) FROM x
) AS x2;

после выполнения вышеуказанных запросов я создал индексированное представление в запросе "just group by" выше. Затем я запустил запрос на индексированное представление, которое выполнило MAX(Count(*)) OVER(PARTITION BY 'foo'.

СРЕДНИЕ РЕЗУЛЬТАТЫ

Query                      CPU       Reads     Duration   
--------------------------------------------------------
Group By                   15        31        7 ms  
Group & Compute            15        31        7 ms
Windowed Functions         14        56        8 ms 
Common Table Exp.          16        62       15 ms
Windowed on Indexed View    0        24        0 ms

очевидно, что это микро-бенчмарк и только слегка поучительно, поэтому возьмите его за то, что он стоит.


вот так:

;WITH x AS
(
  SELECT   CarID
         , CarName
         , COUNT(*) AS Total
  FROM     dbo.tbl_Cars
  GROUP BY CarID, CarName
)
SELECT x.CarID, x.CarName, x.Total, x2.[Max Total]
FROM x CROSS JOIN
(
  SELECT [Max Total] = MAX(Total) FROM x
) AS x2;

SQL Server 2008 R2 и более новые версии, вы можете использовать:

GROUP BY CarID, CarName WITH ROLLUP