12

このようなテーブルがある場合

begin date      end date        data
 2013-01-01     2013-01-04       7
 2013-01-05     2013-01-06       9

どうやったらこんな風に返せるんだろう...

    date         data
 2013-01-01       7
 2013-01-02       7
 2013-01-03       7
 2013-01-04       7
 2013-01-05       9
 2013-01-06       9

私が考えていたことの 1 つは、すべての日付だけを含む別のテーブルを作成し、and を使用して上記のテーブルに日付のみを含むテーブルを結合するdate>=begin dateことdate<=end dateですが、繰り返しの日付だけで余分なテーブルを維持する必要があるのは少し不格好に思えます.

場合によっては、データ範囲がなく、as of基本的に最初の例のように見える日付だけがありますが、end date. これend dateは、次の行の「現在」の日付によって暗示されます (つまり、終了日は次の行のas of-1 である必要があります)。row_number() 関数を使用して次の値を取得する「解決策」がありましたが、ネストされた自己結合が多数ある方法が非常に長いクエリ時間の原因になっていると思われます。

4

2 に答える 2

16

いくつかのサンプル データを使用して...

create table data (begindate datetime, enddate datetime, data int);
insert data select 
 '20130101', '20130104', 7 union all select
 '20130105', '20130106', 9;

クエリ: (注: 数値/集計表が既にある場合は、それを使用してください)

select dateadd(d,v.number,d.begindate) adate, data
  from data d
  join master..spt_values v on v.type='P'
       and v.number between 0 and datediff(d, begindate, enddate)
order by adate;

結果

|                       COLUMN_0 | DATA |
-----------------------------------------
| January, 01 2013 00:00:00+0000 |    7 |
| January, 02 2013 00:00:00+0000 |    7 |
| January, 03 2013 00:00:00+0000 |    7 |
| January, 04 2013 00:00:00+0000 |    7 |
| January, 05 2013 00:00:00+0000 |    9 |
| January, 06 2013 00:00:00+0000 |    9 |

または、その場で数値表 (0-99) または必要な数の数値を生成できます。

;WITH Numbers(number) AS (
  select top(100) row_number() over (order by (select 0))-1
  from sys.columns a
  cross join sys.columns b
  cross join sys.columns c
  cross join sys.columns d
  )
select dateadd(d,v.number,d.begindate) adate, data
  from data d
  join Numbers v on v.number between 0 and datediff(d, begindate, enddate)
order by adate;

SQL フィドルのデモ

于 2013-05-03T12:32:29.670 に答える
2

再帰 CTE を使用して、2 つの日付の間のすべての日付を取得できます。もう 1 つの CTE は、不足している EndDates を解決するために ROW_NUMBER を取得することです。

DECLARE @startDate DATE
DECLARE @endDate DATE

SELECT @startDate = MIN(begindate) FROM Table1
SELECT @endDate = MAX(enddate) FROM Table1

;WITH CTE_Dates AS 
(
    SELECT @startDate AS DT
    UNION ALL
    SELECT DATEADD(DD, 1, DT)
    FROM CTE_Dates
    WHERE DATEADD(DD, 1, DT) <= @endDate
)
,CTE_Data AS 
(
    SELECT *, ROW_NUMBER() OVER (ORDER BY BeginDate) AS RN FROM Table1

)
SELECT DT, t1.data FROM CTE_Dates d
LEFT JOIN CTE_Data t1 on d.DT 
BETWEEN t1.[BeginDate] AND COALESCE(t1.EndDate, 
        (SELECT DATEADD(DD,-1,t2.BeginDate) FROM CTE_Data t2 WHERE t1.RN + 1 = t2.RN))

SQLFiddle デモ

于 2013-05-03T13:01:15.543 に答える