web-dev-qa-db-ja.com

SQL OVER()句 - いつそしてなぜそれが役に立つのか

    USE AdventureWorks2008R2;
GO
SELECT SalesOrderID, ProductID, OrderQty
    ,SUM(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Total'
    ,AVG(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Avg'
    ,COUNT(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Count'
    ,MIN(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Min'
    ,MAX(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Max'
FROM Sales.SalesOrderDetail 
WHERE SalesOrderID IN(43659,43664);

私はその条項について読みましたが、なぜそれが必要なのか理解できません。関数Overは何をするのですか? Partitioning Byは何をしますか? Group By SalesOrderIDを書いてクエリを作成できないのはなぜですか?

158

あなたできるGROUP BY SalesOrderIDを使うことができます。違いは、GROUP BYでは、GROUP BYに含まれていない列の集計値しか持てないということです。

対照的に、GROUP BYの代わりにウィンドウ集約関数を使用すると、集約値と非集約値の両方を取得できます。つまり、クエリ例ではそうしていませんが、個々のOrderQtyの値と、それらの合計、カウント、平均などを、同じSalesOrderIDのグループに対して取得することができます。

これは、ウィンドウ集約が優れている理由の実例です。すべての値の合計の何パーセントかを計算する必要があるとします。ウィンドウ化された集約がなければ、まず集約された値のリストを派生させてからそれを元の行セットに戻す必要があります。

SELECT
  orig.[Partition],
  orig.Value,
  orig.Value * 100.0 / agg.TotalValue AS ValuePercent
FROM OriginalRowset orig
  INNER JOIN (
    SELECT
      [Partition],
      SUM(Value) AS TotalValue
    FROM OriginalRowset
    GROUP BY [Partition]
  ) agg ON orig.[Partition] = agg.[Partition]

それでは、ウィンドウ集約関数を使って同じことができる方法を見てください。

SELECT
  [Partition],
  Value,
  Value * 100.0 / SUM(Value) OVER (PARTITION BY [Partition]) AS ValuePercent
FROM OriginalRowset orig

はるかに簡単できれいですね。

135
Andriy M

OVER句は、GROUP BYを使用しているかどうかにかかわらず、さまざまな範囲にわたって集計を作成できる(「ウィンドウイング」)という点で強力です。

例:SalesOrderIDごとのカウントとallのカウント

SELECT
    SalesOrderID, ProductID, OrderQty
    ,COUNT(OrderQty) AS 'Count'
    ,COUNT(*) OVER () AS 'CountAll'
FROM Sales.SalesOrderDetail 
WHERE
     SalesOrderID IN(43659,43664)
GROUP BY
     SalesOrderID, ProductID, OrderQty

別のCOUNTsを取得します。GROUP BYは使用しません

SELECT
    SalesOrderID, ProductID, OrderQty
    ,COUNT(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'CountQtyPerOrder'
    ,COUNT(OrderQty) OVER(PARTITION BY ProductID) AS 'CountQtyPerProduct',
    ,COUNT(*) OVER () AS 'CountAllAgain'
FROM Sales.SalesOrderDetail 
WHERE
     SalesOrderID IN(43659,43664)
66
gbn

SalesOrderIDをGROUP BYしたいだけであれば、SELECT句にProductID列とOrderQty列を含めることはできません。

PARTITION BY句を使用して、集計関数を分割しましょう。注文の注文明細行の行番号を生成したい場合は、1つの明白で便利な例があります。

SELECT
    O.order_id,
    O.order_date,
    ROW_NUMBER() OVER(PARTITION BY O.order_id) AS line_item_no,
    OL.product_id
FROM
    Orders O
INNER JOIN Order_Lines OL ON OL.order_id = O.order_id

(私の文法は少しずれているかもしれません)

その後、次のようなものに戻ります。

order_id    order_date    line_item_no    product_id
--------    ----------    ------------    ----------
    1       2011-05-02         1              5
    1       2011-05-02         2              4
    1       2011-05-02         3              7
    2       2011-05-12         1              8
    2       2011-05-12         2              1
43
Tom H

例を挙げて説明しましょう。そうすれば、それがどのように機能するのかがわかります。

次の表DIM_EQUIPMENTがあるとします。

VIN         MAKE    MODEL   YEAR    COLOR
-----------------------------------------
1234ASDF    Ford    Taurus  2008    White
1234JKLM    Chevy   Truck   2005    Green
5678ASDF    Ford    Mustang 2008    Yellow

SQL以下で実行

SELECT VIN,
  MAKE,
  MODEL,
  YEAR,
  COLOR ,
  COUNT(*) OVER (PARTITION BY YEAR) AS COUNT2
FROM DIM_EQUIPMENT

結果は以下のようになります

VIN         MAKE    MODEL   YEAR    COLOR     COUNT2
 ----------------------------------------------  
1234JKLM    Chevy   Truck   2005    Green     1
5678ASDF    Ford    Mustang 2008    Yellow    2
1234ASDF    Ford    Taurus  2008    White     2

何が起こったのか見てください。

Group ByなしでYEARにカウントしてROWと一致させることができます。

下記のようにWITH句を使用しても同じ結果が得られるもう1つの興味深い方法、WITHはインラインのVIEWとして機能し、特に複雑なクエリを単純化することができます。

 WITH EQ AS
  ( SELECT YEAR AS YEAR2, COUNT(*) AS COUNT2 FROM DIM_EQUIPMENT GROUP BY YEAR
  )
SELECT VIN,
  MAKE,
  MODEL,
  YEAR,
  COLOR,
  COUNT2
FROM DIM_EQUIPMENT,
  EQ
WHERE EQ.YEAR2=DIM_EQUIPMENT.YEAR;
38
Sanjay Singh

OVER句とPARTITION BYを組み合わせると、返されたクエリの行を評価して、前の関数呼び出しを分析的に行わなければならないことが示されます。インラインのGROUP BYステートメントと考えてください。

OVER (PARTITION BY SalesOrderID)は、SUM、AVGなどのfunctionに対して、クエリから返されたレコードのサブセットを超える値を返し、そのサブセットを外部キーSalesOrderIDで割り当てることを示しています。

したがって、各SalesOrderIDのすべてのOrderQtyレコードを合計し、その列名を 'Total'とします。

これは、同じ情報を見つけるために複数のインラインビューを使用するよりもはるかに効率的な手段です。このクエリをインラインビュー内に配置し、[合計]でフィルタすることができます。

SELECT ...,
FROM (your query) inlineview
WHERE Total < 200
16
maple_shaft
  • Query Petition句とも呼ばれます。
  • Group By句と同様

    • データをチャンク(またはパーティション)に分割する
    • パーティション境界で区切る
    • 機能はパーティション内で実行
    • パーティング境界を越えると再初期化される

構文:
関数(...)OVER(PARTITION BY col1 col3、...)

  • 関数

    • COUNT()SUM()MIN()MAX()などのよく知られた関数
    • 新機能(ROW_NUMBER()RATION_TO_REOIRT()など)


例付きの詳細情報: http://msdn.Microsoft.com/ja-jp/library/ms189461.aspx

2
Elshan