5

よくある問題だと思いますが、何と呼ばれる工程か分からないので、例を挙げて説明します。コンセプトは、まばらなデータセットを完全なシリーズ (曜日、月、順序付きセット (ランキングなど) など) に結合したいというものです。スパース データの空の位置は、完全なシリーズの横に NULL として表示されます。

SQL Server で次のクエリを実行して、毎月の売り上げを調べたとします。

SELECT
    YEAR([timestamp]),
    MONTH([timestamp]),
    COUNT(*)
FROM table1
WHERE YEAR([timestamp]) = YEAR(GETDATE())
GROUP BY
    YEAR([timestamp]),
    MONTH([timestamp])
ORDER BY
    YEAR([timestamp]) DESC,
    MONTH([timestamp]) DESC;

ただし、たとえば、売上が今年の 5 月と 8 月にのみ発生した場合、返される結果は次のようになります。

2010    August    1234
2010    May       5678

返される結果セットを次のようにします。

2010    January
2010    February
2010    March
2010    April
2010    May        1234
2010    June
2010    July
2010    August     5678
2010    September
2010    October
2010    November
2010    December

これを行う唯一の方法は次のとおりです。

SELECT
    YEAR(GETDATE()),
    month_index.month_name,
    sales_data.sales
FROM (
    SELECT 'January' as month_name, 1 as month_number
    UNION
    SELECT 'February', 2
    UNION
    SELECT 'March', 3
    UNION
    SELECT 'April', 4
    UNION
    SELECT 'May', 5
    UNION
    SELECT 'June', 6
    UNION
    SELECT 'July', 7
    UNION
    SELECT 'August', 8
    UNION
    SELECT 'September', 9
    UNION
    SELECT 'October', 10
    UNION
    SELECT 'November', 11
    UNION
    SELECT 'December', 12
) as month_index
LEFT JOIN (
    SELECT
        YEAR([timestamp]) AS year_name,
        MONTH([timestamp]) AS month_name,
        COUNT(*) AS sales
    FROM table1
    WHERE YEAR([timestamp]) = GETDATE()
    GROUP BY
        YEAR([timestamp]),
        MONTH([timestamp])
) AS sales_data
ON month_index.month_name = sales_data.month_name
ORDER BY
    month_index.month_number DESC;

データを結合する完全な日付と英数字のシリーズを作成するより良い方法はありますか? そして、これは何と呼ばれていますか?

ありがとう!

4

5 に答える 5

9

このようなことを試してください:

DECLARE @StartDate datetime
       ,@EndDate datetime
SELECT @StartDate=DATEADD(month,-6,DATEADD(month,DATEDIFF(month,0,GETDATE()),0) )
      ,@EndDate=GETDATE()

;with AllDates AS
(
    SELECT @StartDate AS DateOf
    UNION ALL
    SELECT DateAdd(month,1,DateOf)
        FROM AllDates
    WHERE DateOf<@EndDate
)
SELECT * FROM AllDates

出力:

DateOf
-----------------------
2009-12-01 00:00:00.000
2010-01-01 00:00:00.000
2010-02-01 00:00:00.000
2010-03-01 00:00:00.000
2010-04-01 00:00:00.000
2010-05-01 00:00:00.000
2010-06-01 00:00:00.000
2010-07-01 00:00:00.000

(8 row(s) affected)
于 2010-06-25T18:08:50.520 に答える
4

このようなクエリは、多くの経験豊富な DBA またはデータベース プログラマーがデータベースにカレンダー テーブルを保持する主な理由の 1 つです。

于 2010-06-25T17:57:34.907 に答える
3

私は KM と一緒です。SQL Server 2005 以降では、再帰的な CTE を使用できます。

WITH months AS (
  SELECT DATENAME(mm, '2010-01-01') AS month_name, 
         MONTH('2010-01-01') AS month_number, 
         CAST('2010-01-01' AS DATETIME) AS dt
  UNION ALL
  SELECT DATENAME(mm, DATEADD(mm, 1, m.dt)),
         MONTH(DATEADD(mm, 1, m.dt)),
         DATEADD(mm, 1, m.dt)
    FROM months m
   WHERE DATEADD(mm, 1, m.dt) <= '2010-12-01')
   SELECT x.month_name,
          y.*
     FROM months x
LEFT JOIN your_table y ON MONTH(y.date) = x.month_number

結局のところ、KM とこれについて最後におしゃべりしたとき、再帰 CTE は数値テーブルを使用するよりもわずかに効率的であることがわかりました

于 2010-06-25T18:13:46.680 に答える
3

月のテーブルを作成するこのアプローチが好きです:

SELECT 
  DATENAME(mm, date_val) AS month_name,  
  MONTH(date_val) AS month_number,  
  date_val as dt
FROM ( 
  SELECT DATEADD(mm, number, '2010-01-01') AS date_val
  FROM master.dbo.spt_values
  WHERE type = 'P'
  AND number BETWEEN 0 AND 11
) months

私のテストに基づくと、CTE よりも高速です。SQL Server 2008 Express を実行しています。

SET STATISTICS IO ON と SET STATISTICS TIME ON を使用したテスト結果は次のとおりです。

CTE:

(12 row(s) affected)
Table 'Worktable'. Scan count 2, logical reads 73, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 15 ms,  elapsed time = 64 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

サブクエリ:

(12 row(s) affected)
Table 'spt_values'. Scan count 1, logical reads 2, physical reads 2, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 4 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

あなたの元の質問は、これが何と呼ばれているかを尋ねていますが。名前がわかりません。「シリーズに対する左外部結合」のようなものでしょうか?

追加する 1 つの部分: month テーブルに対して結合する場合、または元のクエリを実行する場合でも、通常、WHERE 句の左側で YEAR([timestamp]) のような関数を使用しないことをお勧めします。

したがって、このコード:

SELECT                     
    YEAR([timestamp]),                     
    MONTH([timestamp]),                     
    COUNT(*)                     
FROM table1                     
WHERE YEAR([timestamp]) = YEAR(GETDATE())                     
GROUP BY                     
    YEAR([timestamp]),                     
    MONTH([timestamp])

... YEAR([timestamp]) をすべての行で評価する必要があるため、インデックススキャンが発生します (タイムスタンプがインデックス化されていると仮定)。1m 以上の行テーブルでは、これはパフォーマンスの低下を意味します。

そのため、通常は代わりに次のような推奨事項が表示されます。

SELECT                     
    YEAR([timestamp]),                     
    MONTH([timestamp]),                     
    COUNT(*)                     
FROM #table1                     
WHERE [timestamp] >= DATEADD(YY, DATEDIFF(YY, 0, GETDATE()), 0) -- First day of this year
AND   [timestamp] < DATEADD(YY, DATEDIFF(YY, 0, GETDATE()) + 1, 0) -- First day of next year
GROUP BY                     
    YEAR([timestamp]),                     
    MONTH([timestamp])

これにより、インデックス シークが使用され (ここでも、timestamp がインデックス付きの列であると仮定します)、論理読み取りが少なくなり、応答が速くなります。これは、実行計画を確認することで確認できます。

于 2010-06-25T20:28:17.933 に答える
1

Months という名前の新しいテーブルを作成して、そこに結合できるデータを入力するのはどうですか?

于 2010-06-25T17:51:45.550 に答える