SQL Server-复制行并在两个日期值之间添加日期迭代器列
我有一个表,类型为varchar、varchar、date和date:SQL Server-复制行并在两个日期值之间添加日期迭代器列,sql,sql-server,tsql,Sql,Sql Server,Tsql,我有一个表,类型为varchar、varchar、date和date: NAME | ID | FROM | THRU Bob | A123 | 10/30/2010 | 11/2/2010 Bob | B567 | 10/30/2010 | 11/2/2010 我想添加一个服务日期DOS列,该列复制行,并在开始日期和结束日期之间(包括开始日期和结束日期)每天进行迭代。完成的表格应如下所示: NAME | ID | FROM | THRU | D
NAME | ID | FROM | THRU
Bob | A123 | 10/30/2010 | 11/2/2010
Bob | B567 | 10/30/2010 | 11/2/2010
我想添加一个服务日期DOS列,该列复制行,并在开始日期和结束日期之间(包括开始日期和结束日期)每天进行迭代。完成的表格应如下所示:
NAME | ID | FROM | THRU | DOS
Bob | A123 | 10/30/2010 | 11/02/2010 | 10/30/2010
Bob | A123 | 10/30/2010 | 11/02/2010 | 10/31/2010
Bob | A123 | 10/30/2010 | 11/02/2010 | 11/01/2010
Bob | A123 | 10/30/2010 | 11/02/2010 | 11/02/2010
Bob | B567 | 10/30/2010 | 11/02/2010 | 10/30/2010
Bob | B567 | 10/30/2010 | 11/02/2010 | 10/31/2010
Bob | B567 | 10/30/2010 | 11/02/2010 | 11/01/2010
Bob | B567 | 10/30/2010 | 11/02/2010 | 11/02/2010
我看到了另一个答案,它使用了cte,但没有保留原始的日期值,并添加了DOS列。我怎样才能在SQL Server中实现这一点呢?听起来您需要一个解决方案。然后它就变得像这样简单:
SELECT YT.Name,
YT.ID,
YT.[From],
YT.Thru,
CT.CalendarDate AS DOS
FROM dbo.YourTable YT
JOIN dbo.CalendarTable CT ON CONVERT(date,YT.[From]) <= CT.CalendarDate
AND CONVERT(date,YT.Thru) >= CT.CalendarDate;
注意,我使用了我自己的日历表,没有与链接相同的列名,但是,链接提供了如何设计日历表所需的所有信息。您只需确保使用适合表的列名。您可以将服务日期创建为计算列。要增加日期,可以尝试以下操作:
SELECT DATEADD(day, 1, '2017/08/25') AS DateAdd;
我认为日历表在这里不太合适。既然你想要连续的日期,理货台似乎是个不错的选择 首先,让我们设置您的数据
declare @Something table
(
NAME varchar(10)
, ID varchar(10)
, DateFrom date
, THRU date
)
insert @Something values
('Bob', 'A123', '20101030', '20101102')
, ('Bob', 'B567', '20101030', '20101102')
接下来我们需要理货台。我在我的系统上保留了一个视图,它的速度非常快,读取量为零。请随意调整行数以满足您的需要
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
现在,对您的情况的查询非常简单
select s.Name
, s.ID
, s.DateFrom
, s.THRU
, DOS = DATEADD(day, t.N - 1, DateFrom)
from @Something s
join cteTally t on t.N <= datediff(day, DateFrom, THRU) + 1
order by s.Name
, s.ID
, t.N
如果您没有强烈推荐的日历表,另一个选项是临时理货表 范例 返回
我经常使用递归CTE来处理这类事情:
with cte as (
select t.ame, t.id, t.from, t.thru, t.from as dos
from t
union all
select cte.ame, cte.id, cte.from, cte.thur, dateadd(day, 1, dos)
from cte
where dos < t.thru
)
select cte.*
from cte
option (maxrecursion 0);
看来交叉申请就行了
CREATE TABLE T(
[NAME] varchar(3),
[ID] varchar(4),
[FROM] datetime,
[THRU] datetime
);
INSERT INTO T
([NAME], [ID], [FROM], [THRU])
VALUES
('Bob', 'A123', '2001-10-30 00:00:00', '2001-11-02 00:00:00'),
('Bob', 'B567', '2001-10-30 00:00:00', '2001-11-02 00:00:00');
SELECT T.*,
DATEADD(Day, TT.N, [FROM]) DOS
FROM T CROSS APPLY (VALUES (0), (1), (2), (3)) TT(N)
返回:
10/30不是datetime的有效值。datetime数据类型返回一个精确到1/300秒的值,不确定这是什么;月/日那么,哪一年?月/年那么哪一天??您拥有的真实值或真实数据类型是什么?如果您真的是以MM/dd格式存储日期,那么当您从一年到另一年时,这将是不可能的。@Larnu您是对的。我添加了一个年份值,以便更加清晰,并将datetime更改为date。这是日历表的一个很好的用例。日历表中的每个日期都有一行,它将包含所有日期。网上有很多关于如何快速创建的资源。一旦你有了这些,你就可以加入到你现有的表中,比如选择你的表。*,calendartable.calendardate FROM yourtable,其中calendartable.calendardate在yourtable.FROM和yourtable之间。这如何回答他们的问题?他们想要从开始到结束之间的每个日期。这如何生成额外的行?这样的表达式提供了一个额外的列,而不是额外的行。如果范围很小,这没什么大不了的,但是对于1000左右的大范围,这真的会陷入困境。递归CTE生成顺序值实际上是隐藏的RBAR@西兰格。问题是,当进入大范围时,联接也可能需要很长时间。在我看来,一个很好的起点是亚伦·伯特兰的测量。我的偏好是因为这是标准的SQL,不需要额外的表,并且适用于任意数量的值。我不认为这是执行最快的方法。在他的文章中,递归cte是他测试的第二个最慢的选项。我从未对标准的符合ANSI标准的sql给出过太多的信条。在我20年的商业生涯中,我为一个系统切换数据库的次数为零。即使发生了这种情况,也会有许多其他的事情需要发生。我在视图中使用递归cte技术,因此它不会存储在任何地方,而且速度非常快。看起来与我的非常相似+1@SeanLange苹果和桔子:你有一个加入,我有一个交叉申请。不过,我敢打赌你的表现会更好+1如果是,则可能无法在这个小数据集上检测到。唯一的性能差异是创建理货表。我们之间有4个或更多正确答案中的2个:@这是我们必须承担的负担。我的桌子有7.5亿多行。对于这么大的桌子,日历桌或理货桌会更快吗?可能没什么区别。无论哪种方式,您都要将7.5亿行连接到另一个表,以大大增加行数。
with cte as (
select t.ame, t.id, t.from, t.thru, t.from as dos
from t
union all
select cte.ame, cte.id, cte.from, cte.thur, dateadd(day, 1, dos)
from cte
where dos < t.thru
)
select cte.*
from cte
option (maxrecursion 0);
CREATE TABLE T(
[NAME] varchar(3),
[ID] varchar(4),
[FROM] datetime,
[THRU] datetime
);
INSERT INTO T
([NAME], [ID], [FROM], [THRU])
VALUES
('Bob', 'A123', '2001-10-30 00:00:00', '2001-11-02 00:00:00'),
('Bob', 'B567', '2001-10-30 00:00:00', '2001-11-02 00:00:00');
SELECT T.*,
DATEADD(Day, TT.N, [FROM]) DOS
FROM T CROSS APPLY (VALUES (0), (1), (2), (3)) TT(N)
+------+------+---------------------+---------------------+---------------------+
| NAME | ID | FROM | THRU | DOS |
+------+------+---------------------+---------------------+---------------------+
| Bob | A123 | 30/10/2001 00:00:00 | 02/11/2001 00:00:00 | 30/10/2001 00:00:00 |
| Bob | A123 | 30/10/2001 00:00:00 | 02/11/2001 00:00:00 | 31/10/2001 00:00:00 |
| Bob | A123 | 30/10/2001 00:00:00 | 02/11/2001 00:00:00 | 01/11/2001 00:00:00 |
| Bob | A123 | 30/10/2001 00:00:00 | 02/11/2001 00:00:00 | 02/11/2001 00:00:00 |
| Bob | B567 | 30/10/2001 00:00:00 | 02/11/2001 00:00:00 | 30/10/2001 00:00:00 |
| Bob | B567 | 30/10/2001 00:00:00 | 02/11/2001 00:00:00 | 31/10/2001 00:00:00 |
| Bob | B567 | 30/10/2001 00:00:00 | 02/11/2001 00:00:00 | 01/11/2001 00:00:00 |
| Bob | B567 | 30/10/2001 00:00:00 | 02/11/2001 00:00:00 | 02/11/2001 00:00:0 |
+------+------+---------------------+---------------------+---------------------+