2017-08-01 12 views
2

私はこのクエリを改善しようとしています。それはずっと大きなテーブルから来たものですが、私は本質的なものを取り上げて、それを以下の問題に絞りました。この表は、月、店舗、製品グループの売上を示しています。各行(月/店舗/商品の組み合わせ)ごとに、その日までの過去2〜4ヶ月の売上の合計が必要です。このクエリを改善してください(過去の売上高の合計を1行ずつ)

正しい値を与えるという点で私が以下にしているのはうまく動作しますが、大きなテーブルではパフォーマンスが向上します。 PRECEDING/FOLLOWING制約でOVER句を調べましたが、SQL Server 2008ではこれをサポートしていません。これを書き直して同じ結果を出す最適な方法がありますか?ありがとう。

create table #sales_by_month 
(
    period int, --YYYYMM 
    store varchar(8), --store number 
    product_group varchar(8), 
    sales int 
) 

insert into #sales_by_month values (201701, 51, 'shoes', 12) 
insert into #sales_by_month values (201701, 51, 'clothes', 15) 
insert into #sales_by_month values (201701, 12, 'shoes', 10) 
insert into #sales_by_month values (201701, 12, 'clothes', 9) 
insert into #sales_by_month values (201702, 51, 'shoes', 0) 
insert into #sales_by_month values (201702, 51, 'clothes', 20) 
insert into #sales_by_month values (201702, 12, 'shoes', 30) 
insert into #sales_by_month values (201702, 12, 'clothes', 8) 
insert into #sales_by_month values (201703, 51, 'shoes', 7) 
insert into #sales_by_month values (201703, 51, 'clothes', 4) 
insert into #sales_by_month values (201703, 12, 'shoes', 21) 
insert into #sales_by_month values (201703, 12, 'clothes', 0) 
insert into #sales_by_month values (201704, 51, 'shoes', 50) 
insert into #sales_by_month values (201704, 51, 'clothes', 4) 
insert into #sales_by_month values (201704, 12, 'shoes', 16) 
insert into #sales_by_month values (201704, 12, 'clothes', 20) 
insert into #sales_by_month values (201705, 51, 'shoes', 21) 
insert into #sales_by_month values (201705, 51, 'clothes', 17) 
insert into #sales_by_month values (201705, 12, 'shoes', 0) 
insert into #sales_by_month values (201705, 12, 'clothes', 5) 

select 
    period, 
    store, 
    product_group, 
    (select sum(sales) 
    from #sales_by_month x2 
    where x2.store = #sales_by_month.store 
     and x2.product_group = #sales_by_month.product_group 
     and left(x2.period, 4) * 12 + right(x2.period, 2) 
       between left(#sales_by_month.period, 4) * 12 + right(#sales_by_month.period, 2) - 1 
        and left(#sales_by_month.period, 4) * 12 + right(#sales_by_month.period, 2)) sales_to_date_last_2_months, 
    (select sum(sales) 
    from #sales_by_month x4 
    where x4.store = #sales_by_month.store 
     and x4.product_group = #sales_by_month.product_group 
     and left(x4.period, 4) * 12 + right(x4.period, 2) 
       between left(#sales_by_month.period, 4) * 12 + right(#sales_by_month.period, 2) - 3 
        and left(#sales_by_month.period, 4) * 12 + right(#sales_by_month.period, 2)) sales_to_date_last_4_months 
from 
    #sales_by_month 

--drop table #sales_by_month 
+6

私がここで見ている最大の問題は、サブクエリのすべての非SARGable述語です。このような関数で列をラップすると、インデックスを利用できないことを意味します。これが正しいデータ型を使用する理由です。日付のデータ型を持つ計算カラムを追加すると、ここで多くの状況に役立ちます。しかし、このクエリでは、これを少し簡単にすることができると思います。 –

+0

@SeanLange - 期間に適切なデータ型を意味していますか?その後、BETWEEN句を動作させるために、すべての数式ではなく、元の日付計算関数(DATEADD()、DATEDIFF())を使用して作業しますか? – bvy

+0

それはまさに私が言っていることです。そして、すべてのあなたの日付計算はGETDATEと保存された日付の値に対して行われます。 –

答えて

1

あなたは、私がapplyをお勧めしますSQL Server 2008で動作しません。それはあなたが各月のデータを持っていると仮定すると、SQL Serverの2012年に開始sum()でウィンドウ句を使用して

select sbm.*, 
     sum(sales) over (partition by store, productgroup 
         order by period 
         rows between 4 preceding and 2 preceding 
         ) as sales_2_4 
from #sales_by_month sbm; 

を保存することができます:

with sbm as (
     select sbm.*, 
       row_number() over (partition by store, productgroup order by period) as seqnum 
     from #sales_by_month sbm 
    ) 
select sbm.*, sbm2.sales_2_4 
from sbm outer apply 
    (select sum(sbm2.sales) as sales_2_4 
     from sbm sbm2 
     where sbm2.store = sbm.store and sbm2.productgroup = sbm2.productgroup and 
      sbm2.seqnum between sbm.seqnum - 4 and sbm.seqnum - 2 
    ) sbm2 
+0

これは面白いですし、試してみるつもりです...しかし、それは私のクエリより速くなることを示唆するものは何も本質的にありますか? – bvy

+0

@bvy。 。 。おそらく単一の列の方がはるかに高速ではありません。ただし、両方の販売数量を 'apply'を使用して単一のサブクエリで計算することができます。これは、1列あたり1回のパスではなく、2列のテーブルを1回だけ通過することになります。 –

0

これを試してみてください。

SELECT CONVERT(DATE,SUBSTRING(CONVERT(NVARCHAR,period),1,4) +'-'+ SUBSTRING(CONVERT(NVARCHAR,period),5,2) + N'-01')periodDt 
,DATEADD(MONTH,2,CONVERT(DATE,SUBSTRING(CONVERT(NVARCHAR,period),1,4) +'-'+ SUBSTRING(CONVERT(NVARCHAR,period),5,2) + N'-01'))periodDt2 
,DATEADD(MONTH,4,CONVERT(DATE,SUBSTRING(CONVERT(NVARCHAR,period),1,4) +'-'+ SUBSTRING(CONVERT(NVARCHAR,period),5,2) + N'-01'))periodDt4 
,* INTO #t1 FROM #sales_by_month 

SELECT periodDt,periodDt2,periodDt4,period,store,product_group, SUM(sales)sales INTO #t2 FROM #t1 GROUP BY periodDt,periodDt2,periodDt4,period,store,product_group 

SELECT ISNULL([2m].store,[4m].store),ISNULL([2m].product_group,[4m].product_group),ISNULL(sales2month,0),ISNULL(sales4month,0) FROM 
(SELECT store,product_group,sales sales2month FROM #t2 WHERE perioddt2 >= GETDATE())[2m] 
FULL JOIN 
(SELECT store,product_group,sales sales4month FROM #t2 WHERE perioddt4 >= GETDATE())[4m] 
ON [2m].store = [4m].store AND [2m].product_group = [4m].product_group 
+0

ありがとうございます。私が働いている環境では、テンポラリテーブルはオプションではありません。CTEを使用することは可能ですが、パフォーマンスが向上するかどうかはわかりません。あなたの答えをもっと詳しく見ていきます。 – bvy

関連する問題