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