3

顧客の月/年の合計を引き出し、ntile ランキングを追加するクエリがあります。ntile 1、2、3、4、および 5 の最大小計を引き出すことができた場合、私が求めているものはほぼ得られますが、続行する方法がわかりません。

たとえば、私が望む結果は次のようになります。

Month   Year   CustomerCode   SubTotal   ntile
1       2012   CCC            131.45     1
1       2012   CCC            342.95     2
1       2012   ELITE          643.92     3
1       2012   CCC            1454.05    4
1       2012   CCC            12971.78   5
2       2012   CCC            135.99     1
2       2012   CCI            370.47     2
2       2012   NOC            766.84     3
2       2012   ELITE          1428.26    4
2       2012   VBC            5073.20    5
3       2012   CCC            119.02     1
3       2012   CCC            323.78     2
3       2012   HUCC           759.66     3
3       2012   ELITE          1402.95    4
3       2012   CCC            7964.20    5 

例外 - ランキングは月 2 のように異なる顧客になると予想しますが、私のベース クエリではその結果が得られません - SQL SERVER 2005 の T-SQL でそれを取得する方法が明らかにわかりません - 実際、私は私が何を得ているのか分かりません。

私の次のオプションは、C# で DataTable を取得し、そこに到達するためにいくつかの体操を行うことですが、もっと簡単な方法が必要です :)

私の基本クエリは

SELECT 
i.DateOrdered
,LTRIM(STR(DATEPART(MONTH,i.DateOrdered))) AS [Month]   
,LTRIM(STR(YEAR(i.Dateordered))) AS [Year]   
,c.CustomerCode 
,SUM(i.Jobprice) AS Subtotal  
,NTILE(5) OVER(ORDER BY SUM(i.JobPrice)) AS [ntile]
FROM Invoices i 
JOIN 
Customers c 
ON i.CustomerID = c.ID 
WHERE i.DateOrdered >= '1/1/2012'
AND i.DateOrdered <= '9/30/2012' 
GROUP BY YEAR(i.DateOrdered),  MONTH(i.DateOrdered), i.DateOrdered, c.CustomerCode
ORDER BY LTRIM(STR(DATEPART(MONTH,i.DateOrdered))),   
TRIM(STR(YEAR(i.Dateordered))),     
SUM(i.JobPrice), c.CustomerCode ASC

これを正しく行うための助けをいただければ幸いです。

前もって感謝します

4

4 に答える 4

3

私があなたを正しく読めば、あなたが求めているのは

範囲内の各月について、その月に
最大の SUM を持つ 5 人の顧客を表示し、
各顧客に対して、対応する SUM を表示します。

その場合、このSQL Fiddleはサンプル テーブルを作成し、上記の出力を提供するクエリを実行します。作成されたテーブルの内容を確認したい場合は、右側のパネルで単純な SELECT を実行してください。

クエリは次のとおりです。

;     WITH G as -- grouped by month and customer
(
    SELECT DATEADD(D,1-DAY(i.DateOrdered),i.DateOrdered) [Month],
           c.CustomerCode,
           SUM(i.Jobprice) Subtotal
      FROM Invoices i
      JOIN Customers c ON i.CustomerID = c.ID
     WHERE i.DateOrdered >= '1/1/2012' AND i.DateOrdered <= '9/30/2012'
  GROUP BY DATEADD(D,1-DAY(i.DateOrdered),i.DateOrdered), c.CustomerCode
)
    SELECT MONTH([Month]) [Month],
           YEAR([Month]) [Year],
           CustomerCode,
           SubTotal,
           Rnk [Rank]
      FROM
(
    SELECT *, RANK() OVER (partition by [Month] order by Subtotal desc) Rnk
      FROM G
) X
     WHERE Rnk <= 5
  ORDER BY Month, Rnk

説明すると、最初の部分 (WITH ブロック) は、月と顧客ごとにデータをグループ化するサブクエリを作成する単なる凝った方法です。この式DATEADD(D,1-DAY(i.DateOrdered),i.DateOrdered)は、データを月ごとに簡単にグループ化できるように、すべての日付をその月の最初の日に変換します。従来の形式で記述された次のサブクエリは、小計によって各月内に RANK 列を追加し、最終的に上位 5* を与えるために選択されます。

RANKは均等なランキングを可能にすることに注意RANKしてください。1 か月で 6 人の顧客が表示される可能性があります。そのうちの 3 人が 4 位で均等にランク付けされている場合です。ROW_NUMBER等しい小計の間で区切ります。

于 2012-09-14T08:05:23.087 に答える
0

これを試して:

declare @tab table
(
[month] int,
[year] int,
CustomerCode varchar(20),
SubTotal float 
)
insert into @tab
select
1,2012,'ccc',131.45 union all 
select
1,2012,'ccc',343.45 union all
select 
1,2012,'ELITE',643.92 union all
select 
2,2012,'ccc',131.45 union all 
select
2,2012,'ccc',343.45 union all
select 
2,2012,'ELITE',643.92 union all
select 
3,2012,'ccc',131.45 union all 
select
3,2012,'ccc',343.45 union all
select 
3,2012,'ELITE',643.92

;with cte as 
(
 select NTILE(3) OVER(partition by [month] ORDER BY [month]) AS [ntile],* from @tab
)
select * from cte

partition by基本クエリでも、正しい出力が得られるようにを追加する必要があります。

于 2012-09-14T05:44:55.790 に答える
0

月と年の日付部分のみを取得するには、クエリを変更する必要があります。同じ顧客が同じ月に複数回表示されるという問題は、select 句と group by 句に i.DateOrdered が含まれていることが原因です。

次のクエリは、必要なものを提供するはずです。また、クエリの最後の行の次の行のタイプミスであると思われますが、tsql には LTRIM と RTRIM のみの TRIM() 関数がありません。

SELECT 

LTRIM(STR(DATEPART(MONTH,i.DateOrdered))) AS [Month]   
,LTRIM(STR(YEAR(i.Dateordered))) AS [Year]   
,c.CustomerCode 
,SUM(i.Jobprice) AS Subtotal  
,NTILE(5) OVER(ORDER BY SUM(i.JobPrice)) AS [ntile]
FROM Invoices i 
JOIN 
Customers c 
ON i.CustomerID = c.ID 
WHERE i.DateOrdered >= '1/1/2012'
AND i.DateOrdered <= '9/30/2012' 
GROUP BY YEAR(i.DateOrdered),  MONTH(i.DateOrdered),  c.CustomerCode
ORDER BY LTRIM(STR(DATEPART(MONTH,i.DateOrdered))),   
LTRIM(STR(YEAR(i.Dateordered))),     
SUM(i.JobPrice), c.CustomerCode ASC

これにより、これらの結果が得られます

Month   Year    CustomerCode    Subtotal    ntile
 1      2012    ELITE            643.92      2
 1      2012    CCC            14900.23      5
 2      2012    CCC              135.99      1
 2      2012    CCI              370.47      1
 2      2012    NOC              766.84      3
 2      2012    ELITE           1428.26      4
 2      2012    VBC             5073.20      4
 3      2012    HUCC             759.66      2
 3      2012    ELITE           1402.95      3
 3      2012    CCC             8407.00      5
于 2012-09-14T07:18:53.287 に答える
0

ダブルランキングなしでこの問題を解決する方法がわかりません:

  1. 顧客および月ごとに最大の金額を取得する必要があります。

  2. 次に、毎月、見つかった合計の上位 5 つを取得する必要があります。

これが私がこれにアプローチする方法です:

;
WITH MaxSubtotals AS (
  SELECT DISTINCT
    CustomerID,
    MonthDate = DATEADD(MONTH, DATEDIFF(MONTH, 0, DateOrdered), 0),
    Subtotal  = MAX(SUM(JobPrice)) OVER (
      PARTITION BY Customer, DATEADD(MONTH, DATEDIFF(MONTH, 0, DateOrdered), 0)
      ORDER BY SUM(JobPrice)
    )
  FROM Invoices
  GROUP BY
    CustomerID,
    DateOrdered
),
TotalsRanked AS (
  SELECT
    CustomerID,
    MonthDate,
    Subtotal,
    Ranking = ROW_NUMBER() OVER (PARTITION BY MonthDate ORDER BY Subtotal DESC)
  FROM MaxDailyTotals
)
SELECT
  Month = MONTH(i.MonthDate),
  Year  = YEAR(i.MonthDate),
  c.CustomerCode,
  i.Subtotal,
  i.Ranking
FROM TotalsRanked i
  INNER JOIN Customers ON i.CustomerID = c.ID
WHERE i.Ranking <= 5
;

最初の CTE はMaxSubtotals、顧客および月ごとの最大小計を決定します。ウィンドウ集計DISTINCT関数を含む、これは本質的に次の 2 段階のクエリの「ショートカット」です。

SELECT
  CustomerID,
  MonthDate,
  Subtotal = MAX(Subtotal)
FROM (
  SELECT
    CustomerID,
    MonthDate = DATEADD(MONTH, DATEDIFF(MONTH, 0, DateOrdered), 0),
    Subtotal = SUM(JobPrice)
  FROM Invoices
  GROUP BY
    CustomerID,
    DateOrdered
) s
GROUP BY
  CustomerID,
  MonthDate

もう 1 つの CTE はTotalsRanked、見つかった総合計のランキング番号を単純に追加し、顧客と月ごとに分割します。最後のステップとして、ランキングが 5 以下の行 (または別の機会に選択するもの) にのみ行を制限する必要があります。

この場合、を使用して行をランク付けすると、フィルターROW_NUMBER()で 5 行を超えないことが保証されることに注意してください。Ranking <= 5小計が同じ行が 2 つ以上ある場合、ランキングが異なり、最終的には次のような出力になる可能性があります。

   Month  Year  CustomerCode  Subtotal  Ranking
   -----  ----  ------------  --------  -------
   1      2012  CCC           1500.00   1
   1      2012  ELITE         1400.00   2
   1      2012  NOC           900.00    3
   1      2012  VBC           700.00    4
   1      2012  HUCC          700.00    5

-- 1      2012  ABC           690.00    6   -- not returned
-- 1      2012  ...           ...       ...

同じ月の小計が 700.00 の他の顧客がいる可能性がありますが、これらの顧客には 5 より後のランキングが割り当てられるため、返されません。

RANK()それを説明する代わりに使用できますROW_NUMBER()。ただし、その場合、次のような出力で、1 か月あたり5を超える可能性があることに注意してください。

   Month  Year  CustomerCode  Subtotal  Ranking
   -----  ----  ------------  --------  -------
   1      2012  CCC           1500.00   1
   1      2012  ELITE         1400.00   2
   1      2012  NOC           900.00    3
   1      2012  VBC           700.00    4
   1      2012  HUCC          700.00    4
   1      2012  ABC           700.00    4

-- 1      2012  DEF           690.00    7   -- not returned
-- 1      2012  ...           ...       ...

小計が 700.00 未満の顧客は、ランキングが 7 から始まるため、出力に含まれませんROW_NUMBER()

もう 1 つのオプションがありますDENSE_RANK()。出力で 1 か月あたり最大 5 つの個別の合計が必要な場合は、これを使用することをお勧めします。を使用するとDENSE_RANK()、出力には を使用した場合よりも 1 か月あたりの行数がさらに多くなる可能性がありますRANK()が、個別の小計の数は正確に 5 になります (元のデータセットで 5 を提供できない場合はそれより少なくなります)。つまり、出力は次のようになります。

   Month  Year  CustomerCode  Subtotal  Ranking
   -----  ----  ------------  --------  -------
   1      2012  CCC           1500.00   1
   1      2012  ELITE         1400.00   2
   1      2012  NOC           900.00    3
   1      2012  VBC           700.00    4
   1      2012  HUCC          700.00    4
   1      2012  ABC           700.00    4
   1      2012  DEF           650.00    5
   1      2012  GHI           650.00    5
   1      2012  JKL           650.00    5

-- 1      2012  MNO           600.00    5   -- not returned
-- 1      2012  ...           ...       ...

と同様RANK()に、このDENSE_RANK()関数は同じランキングを同一の値に割り当てますが、とは異なり RANK()、ランキング シーケンスにギャップを生じさせません。


参考文献:

于 2012-09-14T08:39:45.827 に答える