2017-10-27 9 views
3

同じソースから異なる期間の集計データを比較する必要がある状況によく直面します。同じデータセットからの異なる期間の合計に加算する

私は通常、それをこのように扱う:

SELECT 
    COALESCE(SalesThisYear.StoreId, SalesLastYear.StoreId) StoreId 
    , SalesThisYear.Sum_Revenue RevenueThisYear 
    , SalesLastYear.Sum_Revenue RevenueLastYear 
FROM 
    (
     SELECT StoreId, SUM(Revenue) Sum_Revenue 
     FROM  Sales 
     WHERE Date BETWEEN '2017-09-01' AND '2017-09-30' 
     GROUP BY StoreId 
    ) SalesThisYear 
    FULL JOIN (
     SELECT StoreId, SUM(Revenue) Sum_Revenue 
     FROM  Sales 
     WHERE Date BETWEEN '2016-09-01' AND '2016-09-30' 
     GROUP BY StoreId 
    ) SalesLastYear 
    ON (SalesLastYear.StoreId = SalesThisYear.StoreId) 

-- execution time 337 ms 

それが二回表を訪れるので、それは私の意見では非常にエレガントではありませんが、それは動作します。同じことを達成するために

別の同様の方法は次のとおりです。

SELECT 
    Sales.StoreId 
    , SUM(CASE YEAR(Date) WHEN 2017 THEN Revenue ELSE 0 END) RevenueThisYear 
    , SUM(CASE YEAR(Date) WHEN 2016 THEN Revenue ELSE 0 END) RevenueLastYear 
FROM 
    Sales 
WHERE 
    Date BETWEEN '2017-09-01' AND '2017-09-30' 
    or Date BETWEEN '2016-09-01' AND '2016-09-30' 
GROUP BY 
    StoreId 

-- execution time 548 ms 

どちらのソリューションは、私のデータセット(選択された期間中に1929419行、その場所のすべてのインデックス)、最初の少し上のほぼ同じを行い、より良い時間で。また、期間を増やしても問題ありません。最初のデータは常にデータセットの方が優れています。

これは単なる例ですが、2つ以上のインターバルを含み、ロジック(たとえば、月/日の代わりにisoweek /曜日を比較したり、異なる店舗を比較するなど)を含む場合もあります。

私はすでに同じことを達成するためのいくつかの方法を考え出しましたが、同じことを達成するための巧妙な方法があるかどうかは疑問でした。多分もっとクリーンなソリューションかもしれません。あるいは大きなデータセット(TB以上)に適しています。

たとえば、大きなデータセットの場合、2つ目のリソースがリソースを集中的に使用しないとします。これは、テーブルに対して1回のインデックススキャンを実行するためです。一方、最初のスキャンでは、2つの索引スキャンと1つのマージが必要です。テーブルが大きすぎてメモリに収まらない場合はどうなりますか?あるいは、最初の方が常に良いですか?

答えて

0

非常にめったにありません。このようなやり方は、特によく似たようなことをしているときは、常により良いです。

ただし、索引の使用を禁止するため、問合せでスカラー関数の使用を最小限に抑えるなど、できる限りベストプラクティスを活用しようとしています。

SELECT 
    Sales.StoreId 
    , SUM(CASE WHEN Date BETWEEN '2017-09-01' AND '2017-09-30' THEN Revenue ELSE 0 END) RevenueThisYear 
    , SUM(CASE WHEN Date BETWEEN '2016-09-01' AND '2016-09-30' THEN Revenue ELSE 0 END) RevenueLastYear 
FROM 
    Sales 
WHERE 
    Date BETWEEN '2017-09-01' AND '2017-09-30' 
    or Date BETWEEN '2016-09-01' AND '2016-09-30' 
GROUP BY 
    StoreId 
+0

これはまさに私のポイントはありませんでしたが、あなたの推薦は理にかなって、私は、私はなるだろう改善について興味がありました。残念ながら、改善はありませんでした。クエリの平均実行時間は597ミリ秒でした。 –

+0

私のテーブルは日付をVARCHAR形式でYYYYMMDDの形式で格納するので、YEAR(日付)は実際にはLEFT(日付、4)です。ただし、スカラー関数でもあります。 –

+0

大きなデータセットに対するパフォーマンス上の懸念についてどう思いますか?マージ操作はリソースキラーだと思いますか? –

0

秒が良く見える:

は例えば、次のようにあなたの2番目のクエリを変更することで、私はあなたが賢明少なくともいくつかの改善のパフォーマンスを見ることができます想像します。しかし、私は年の部分がクエリを遅くしていると思います。一年を取り出してこれを置くことができます。 2017年01月01日は今年の範囲( '2017-09-01' AND '2017-09-30')より大きく、昨年の範囲はそれよりも小さくなります( '2016-09-01 AND' 2016-09-30 ')。

SELECT 
     Sales.StoreId 
     , SUM(CASE WHEN date > 2017-01-01 THEN Revenue ELSE 0 END) RevenueThisYear 
     , SUM(CASE WHEN date < 2017-01-01 THEN Revenue ELSE 0 END) RevenueLastYear 
    FROM 
     Sales 
    WHERE 
     Date BETWEEN '2017-09-01' AND '2017-09-30' 
     or Date BETWEEN '2016-09-01' AND '2016-09-30' 
    GROUP BY 
     StoreId 

FULLが素晴らしい働いて参加する場合は、これを試すことができます。

SELECT 
    COALESCE(SalesThisYear.StoreId, SalesLastYear.StoreId) StoreId 
    , sum(SalesThisYear.Revenue) RevenueThisYear 
    , sum(SalesLastYear.Revenue) RevenueLastYear 
    FROM  Sales SalesThisYear full join 
      Sales SalesLastYear 
    ON SalesLastYear.StoreId = SalesThisYear.StoreId 
WHERE SalesThisYear.Date BETWEEN '2017-09-01' AND '2017-09-30' 
    AND SalesLastYear.Date BETWEEN '2016-09-01' AND '2016-09-30' 
GROUP BY COALESCE(SalesThisYear.StoreId, SalesLastYear.StoreId) 

編集*

SELECT Sales.StoreId 
     , SUM(CASE WHEN date > '2017-01-01' THEN Revenue ELSE 0 END) RevenueThisYear 
     , SUM(CASE WHEN date < '2017-01-01' THEN Revenue ELSE 0 END) RevenueLastYear 
    FROM 
     (Select store_id, date, revenue 
      from Sales 
      WHERE Date BETWEEN '2017-09-01' AND '2017-09-30' 
       or Date BETWEEN '2016-09-01' AND '2016-09-30') q 

GROUP BY StoreId 
+0

テーブルの平均実行時間は520msでした。少し改善しましたが、「結合」ソリューションはまだまだ時間がかかります。ビッグデータセットに対するパフォーマンス上の懸念についてどう思いますか?マージ操作はリソースキラーだと思いますか? –

+0

私は知っています。時には私たちの理論がうまくいかないここでのように完全な結合は多くの時間を節約しています。あなたの完全な結合クエリを書き直しました。 – Valli

+1

最後の1つはとても面白かったですが、最悪でした。私はそれが1分以上かかったので終了するのを待たずに、私はあきらめる。私は実行計画を分析し、ハッシュ・マッチはマージの後に行われます(以前は、マージの前に2つのハッシュ・マッチ操作が各表に1つありました)。明らかに、私が思ったようにサブクエリはそれほど恐ろしいことではありません。 –

関連する問題