2012-02-09 24 views
11

からMAXはあなたが、行の数十万人との「カーズ」のテーブルを持っていた 、あなたは、GROUP BYをやってみたかったと仮定し、次のとおりですCOUNTとBY T-SQL GROUPにして、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の合計を取得するための最良の方法は、パフォーマンスと きれいなコーディングの面で、あるものですGROUPの結果をテンポラリテーブル に入れ、テンポラリテーブルからMAXをローカル変数に取得します。 しかし、私はこれを行うための最良の方法は何だろうと思っています。


UPDATE

共通テーブル式を書くのは最もエレガントなようで、@EBarrにまだ同様の 、私の限られたテストでは、大幅にパフォーマンスの低下を示しています。 だから私はCTEと一緒に行くつもりはありません。

リンク@EBarrのCOMPUTEオプションでは、機能 が推奨されていないことを示しているため、これは最適なルートではありません。

MAX値のローカル変数のオプションと テンポラリテーブルを使用すると、パフォーマンスの問題を認識していないので、私がダウンするルートになる可能性があります。

私の使用事例についてもう少し詳しく:おそらく シリーズの他の質問になる可能性があります。しかし、 のデータの大きなサブセットを一時テーブルにロードする(つまり、tbl_Carsのサブセットが#tbl_Carsに入る であり、さらに#tbl_Carsがさらにフィルタリングされて になる可能性があります)。複数の結果セットを返す単一のストアドプロシージャ 内で複数のフィルタリング と集約クエリを実行する必要があります。


UPDATE 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 

、それは細かいようだが、どの時点で、あなたは、読み込みの数に関する 心配する必要はありますか?

答えて

13

機械的にこれを行うにはいくつかの方法があります。あなたは一時テーブル/テーブル変数を使用することができます。別の方法は、@Aaron_Bertrandが示すネストされたクエリおよび/またはCTEです。 3番目の方法は、WINDOWED FUNCTIONSなどを使用することです。

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

不利(廃止予定読み取り)第四の方法は

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

COMPUTEキーワードはの終わりでのような追加のサマリー列に表示される合計を生成...などCOMPUTEキーワードを使用しています結果セット(see this)。上のクエリでは、実際には2つのレコードセットが表示されます。

最速

さて、次の問題は何である "最高/最速/最も簡単な。"私はすぐにindexed viewと思う。 @Aaronが穏やかに私に思い出させるように、インデックス付きビューにはあらゆる種類の制限があります。ただし、上記の戦略では、SELECT ... FROM ... GROUP BYで索引ビューを作成できます。インデックス付きビューから選択すると、WINDOWED FUNCTION句が適用されます。

しかし、あなたのデザインについては、何がベストかを誰にでも伝えるのは難しいでしょう。インデックス付きのビューから高速照会を照らすことができます。しかし、そのパフォーマンスは価格で提供されます。価格は維持費です。基になるテーブルが大量の挿入/更新/削除操作の対象である場合、インデックス付きビューのメンテナンスは他の領域のパフォーマンスを低下させます。

ユースケースとデータアクセスパターンについてもう少し分かち合うと、人々はより多くの洞察を分かち合うことができます。


MICRO性能試験

だから私は少しデータスクリプトを生成し、ウィンドウ関数対CTE性能のためのSQLプロファイラ番号を見ました。これはマイクロテストですのでの実数をの実荷重に試してみてください。

データ生成:

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行を生成します。私は、次の4つのクエリを複数回の各走っ:上記のクエリを実行した後

--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; 

を、私は上記のクエリ「でちょうどグループ」にインデックス付きビューを作成しました。次に、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 

明らかにこれは、マイクロベンチマークだけ穏やかに有益であるので、それは価値がある何のためにそれを取ります。

+0

あなたは、インデックス付きビューで 'MAX'を使用することはできません(私がしてきました5年間それを求める - http://connect.microsoft.com/SQLServer/feedback/details/267516/expand-aggregate-support-in-indexed-views-min-max)。また、 'theFieldBeingSearchedForMax'はテーブルにはありません、出力の一部です(最高のカウントです)。 –

+0

質問をもう一度読んでください。私はそれを誤って読んだ。 SQLの更新。 – EBarr

+0

- 申し訳ありませんが、最初のクエリでGROUP BYを追加できませんでした。私の悪い。 – mg1075

8

ここに1つの方法です:

;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; 
0

のSQL Server 2008 R2以降のバージョン、あなたが使用することができます。

GROUP BY CarID, CarName WITH ROLLUP 
関連する問題