【问题标题】:Creating a date table for multiple primary keys between a specified open and close date for each primary key在每个主键的指定打开和关闭日期之间为多个主键创建日期表
【发布时间】:2019-07-05 01:25:03
【问题描述】:

早安,

在过去的几个小时里,我一直在绞尽脑汁来构建一个如下所示的表格:

我有大约 33 000 个条目,它们都有自己的开始和结束日期。我正在尝试构建一个表,将每个日期(包括开始和结束日期)放在 SQL Server Management Studio 中每个条目(JOURNAL_NO)的一行中。

我需要每天检查每个条目,并且我尝试通过使用我的主表和我创建的日历表来构建表。请参考下面的代码(我在下面也包含了日历代码):

我正在尝试使用的代码:

DECLARE @START_DATE date = '2014-01-01',
        @END_DATE date = '2018-12-31'
WHILE @START_DATE <= @END_DATE
BEGIN
    SET @START_DATE = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY N) - 1, @START_DATE)

    SELECT LO_MASTER.JOURAL_NO, --(33000 entries)
            LO_MASTER.START_DATE, -- each entry has a different opening date 
            CAL.DATE -- date in calendar table
            CAL.DAY AS DATEPART(DAY,[DATE]),
            CAL.MONTH AS DATEPART(MONTH,[DATE]),
            CAL.YEAR AS DATEPART(YEAR,[DATE]), -- these will be the date parts between the OPEN_DATE & CLOSE_DATE for each account
            LO_MASTER.END_DATE -- each entry has a different closing date
    FROM [dbo.][F1_master] as LO_MASTER
         inner join CALENDAR_TABLE as CAL LO_MASTER.DATE = CAL.DATE
    WHERE CAL.DATE between LO_MASTER.START_DATE and LO_MASTER.END_DATE -- range where the fields from table b need to be populated for each account

END

表定义:

USE [master]
GO

/****** Object:  Table [dbo].[dummy_data]    Script Date: 11/02/2019 07:31:59 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[dummy_data](
    [JOURNAL_NO] [int] NULL,
    [START_DATE] [date] NULL,
    [END_DATE] [date] NULL,
    [DURATION_ON_BOOK] [int] NULL
) ON [PRIMARY]
GO

样本数据:

JOURNAL_NO  START_DATE  END_DATE    DURATION_ON_BOOK
101388  15/01/2014  01/01/2017  35
101499  14/01/2014  21/01/2017  36
101502  17/01/2014  17/02/2017  53
101876  06/01/2014  30/09/2017  35
101877  06/01/2014  24/01/2018  60
101878  07/01/2014  28/02/2018  60
101879  07/01/2014  19/01/2017  50
101881  07/01/2014  16/04/2018  58
101882  07/01/2014  13/11/2018  58
101883  08/01/2014  17/11/2016  59
101884  13/01/2014  26/06/2018  60
101886  13/01/2014  20/12/2016  59
101887  13/01/2014  13/12/2016  60
101888  13/01/2014  11/09/2017  43
101889  14/01/2014  07/12/2017  68
101890  14/01/2014  02/01/2018  58
101892  15/01/2014  21/02/2017  38
101893  17/01/2014  15/09/2017  64
101894  21/01/2014  02/02/2017  40
101896  21/01/2014  09/05/2016  38
101904  27/01/2014  21/12/2016  69
101906  27/01/2014  11/11/2016  36
101966  07/01/2014  10/03/2017  36
101967  07/01/2014  09/07/2018  40
102073  07/01/2014  15/09/2016  60
102074  13/01/2014  20/06/2017  40
102076  14/01/2014  15/06/2016  40
102077  15/01/2014  01/12/2016  60
102079  17/01/2014  12/10/2016  40
102081  21/01/2014  20/04/2017  40
102082  23/01/2014  14/02/2017  38
102234  02/01/2014  20/09/2017  46
102236  08/01/2014  05/05/2017  36
102237  15/01/2014  23/08/2017  68
102240  21/01/2014  27/02/2018  50
102241  21/01/2014  25/08/2016  37
102253  06/01/2014  26/10/2016  27
102254  07/01/2014  17/11/2016  61
102255  07/01/2014  26/04/2017  38
102256  10/01/2014  11/10/2017  42
102258  13/01/2014  18/05/2017  26
102263  17/01/2014  29/11/2018  74
102265  20/01/2014  31/10/2016  31
102372  06/01/2014  25/04/2017  35
102463  09/01/2014  25/08/2017  69
102464  09/01/2014  01/07/2016  26
102465  15/01/2014  24/12/2016  36
102530  06/01/2014  24/12/2017  48
102531  07/01/2014  28/08/2017  76
102532  09/01/2014  21/06/2017  39

有人可以帮助我吗?我没有这么长时间编码,我的故障排除尝试没有提供任何正确的解决方案。将不胜感激。

谢谢。

【问题讨论】:

  • 您绝对不希望或不需要这里的循环。您的“日历”表实际上是一个计数表。这就是你想要使用的。另外,不要使用过时的连接语法。您应该使用 ANSI-92 样式的连接,它们已经存在超过 25 年了。 sqlblog.org/2009/10/08/bad-habits-to-kick-using-old-style-joins 别名应该有一些含义,而不是 a,b,c sqlblog.org/2009/10/08/…
  • 好的。让我们在这里只看几个期刊(101388 和 101499) 预期输出是多少?您想在 2014 年 1 月 15 日到 2017 年 1 月 1 日之间的每一天获取 101388?
  • 是的。我想让日记帐编号保持不变,并且在 start_date 和 end_date 之间的每一行中更改天数。在该期刊具有表中的所有条目后,我希望它移至下一个期刊并重复该过程。感谢您的内联编辑。由于严重的雷暴,我的互联网连接经常断断续续

标签: sql sql-server while-loop


【解决方案1】:

使用计数表非常简单。我在我的系统上保留了一个视图,因为它根本不需要任何磁盘 I/O。它与您最初发布的内容惊人地相似。

create View [dbo].[cteTally] as

WITH
    E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
    E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
    E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
    cteTally(N) AS 
    (
        SELECT  ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
    )
select N from cteTally
GO

接下来我们需要一些样本数据。我将其精简为几个期刊来展示。

declare @dummy_data table
(
    JOURNAL_NO int
    , START_DATE datetime
    , END_DATE datetime
    , DURATION_ON_BOOK int
)

insert @dummy_data values
(101388, '20140115', '20170101', 35)
, (101499, '20160114', '20170121', 36)

现在我们只需要利用计数表的强大功能来处理您的数据。这就是您的查询可能的样子。不需要循环,只需要一些基本的日期数学。

select *
    , EachDay = dateadd(day, t.N - 1, d.Start_Date)
    , MyDay = t.N
    , MyMonth = datediff(month, d.Start_DATE, dateadd(day, t.N - 1, d.Start_Date)) + 1
    , MyYear = datepart(year, dateadd(day, t.N - 1, d.Start_Date))
from @dummy_data d
join cteTally t on t.N <= datediff(day, d.START_DATE, d.END_DATE) + 1

【讨论】:

  • 哇!你真的是肖恩大师。感谢您的详细解释,并通过我不需要使用循环的逻辑与我交谈。非常感谢先生的帮助!附言您是否可以推荐任何其他博客来改进我的编码?
  • 您可以查看 sql server central.com。很多很棒的文章,完全专注于 sql server。
猜你喜欢
  • 2021-09-25
  • 1970-01-01
  • 2021-03-14
  • 1970-01-01
  • 2018-10-16
  • 1970-01-01
  • 2020-12-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多