【问题标题】:SQL Server : populate table with datesSQL Server:用日期填充表
【发布时间】:2017-08-12 06:06:22
【问题描述】:

我正在使用 SQL Server。我有一个具有挑战性的问题(至少对我来说!)。

我有一张关于电视节目信息的表格;第一栏是节目的Title,第二栏是Genre,第三栏是Language,第四栏是节目的首播日期,格式为:2017-11-03

我想做的是在从首映日期到未来 4 个月的日期范围内复制每一行。所以,如果当前行读取 2017-11-03,我想为这个范围内的每个日期复制这一行,然后为下一个电视节目(下一行)做同样的事情。

这可以在 SQL 中完成吗?

谢谢。

【问题讨论】:

  • 它可以,带有交叉应用和日历表。不过,一些示例数据和预期输出总是很好。 Here is a good dimension table 可用于连接,但您也可以使用 CTE 和数字/计数表来完成此操作
  • 嗨。谢谢你,我一定会在未来发布示例数据!
  • @Dr.IkjyotSinghKohli 也许不久的将来?该站点可以帮助何时将制表符分隔的文本(例如,通过从 Excel 或大多数结果网格复制)格式化为文本表:senseful.github.io/text-table

标签: sql sql-server tsql


【解决方案1】:

另一个选项是 CROSS APPLY 与 ad-hoc 计数表相结合

这将为源数据中的每条记录生成 4 个月的数据。

示例

Select A.*
      ,B.D
 From  YourTable A
 Cross Apply (
                Select Top (DateDiff(DAY,A.Premier,DateAdd(Month,4,A.Premier))+1) 
                       D=DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),A.Premier) 
                 From  master..spt_values n1
              ) B

退货

-- 截断 ---

谷歌搜索...我爱露西于 1951 年 10 月 15 日首播应该先这样做。

【讨论】:

  • 我正在使用此代码创建一个类似的表。如何在 D 列中添加另一列(让第 D2 天),其中每一天经过的天数:0 表示 1950 年 7 月 3 日; 1 代表 1950-07-04 等?谢谢
  • @DanielaRodrigues 视觉上有一点问题。也许如果您发布带有示例数据和所需结果的问题。
  • 当然,感谢@JohnCappelletti。我刚刚发布了我的问题:stackoverflow.com/questions/62837665/…
【解决方案2】:

您可以使用日历或日期表来处理这类事情。

内存中只有 152kb,您可以在一个表中包含 30 年的日期:

/* dates table */
declare @fromdate date = '20000101';
declare @years    int  = 30;
/* 30 years, 19 used data pages ~152kb in memory, ~264kb on disk */
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
select top (datediff(day, @fromdate,dateadd(year,@years,@fromdate)))
    [Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,@fromdate))
into dbo.Dates
from n as deka cross join n as hecto cross join n as kilo
               cross join n as tenK cross join n as hundredK
order by [Date];
create unique clustered index ix_dbo_Dates_date
  on dbo.Dates([Date]);

无需执行创建表的实际步骤,您可以在 common table expression 中使用类似的方法,只需这样做:

declare @fromdate date = '20170101';
declare @years    int  = 4;
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
, dates as (
  select top (datediff(day, @fromdate,dateadd(year,@years,@fromdate)))
      [Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,@fromdate))
  from n as deka cross join n as hecto cross join n as kilo
                cross join n as tenK cross join n as hundredK
   order by [Date]
)

像这样使用:

select d.date, t.*
from dates d
  inner join tv_shows t
    on d.date >= t.premiere_date
   and d.date <= dateadd(month,4,t.premiere_date)

rextester 演示:http://rextester.com/XSRG45354

返回:

+------------+-------+---------+----------+---------------+
|    date    | title |  genre  | language | premiere_date |
+------------+-------+---------+----------+---------------+
| 2017-11-03 | one   | example | english  | 2017-11-03    |
| 2017-11-04 | one   | example | english  | 2017-11-03    |
| 2017-11-05 | one   | example | english  | 2017-11-03    |
| 2017-11-06 | one   | example | english  | 2017-11-03    |
....                
| 2018-02-28 | one   | example | english  | 2017-11-03    |
| 2018-03-01 | one   | example | english  | 2017-11-03    |
| 2018-03-02 | one   | example | english  | 2017-11-03    |
| 2018-03-03 | one   | example | english  | 2017-11-03    |
+------------+---------------+---------+----------+---------------+

数字和日历表参考:

【讨论】:

    【解决方案3】:

    最简单的方法是使用数字表。这是构建足够大的一种方法:

    with n as (
          select 0 as n union all
          select n.n from n where n.n < 50
         )
    select tv.*, dateadd(week, n.n, tv.premierdate) as showdate
    from tvshows tv join
         n
         on dateadd(week, n.n, tv.premierdate) <= dateadd(month, 4, tv.premierdate);
    

    【讨论】:

    • 嗨。这是一个很好的建议。你能解释一下你为什么写“n.n
    • @IkjyotSinghKohli 博士。 . .四个月就够了(好吧,实际上 18 个月就足够了,但谁在数呢?)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-05
    • 1970-01-01
    • 2014-11-04
    • 2019-07-20
    • 1970-01-01
    • 1970-01-01
    • 2015-07-08
    相关资源
    最近更新 更多