如何为每一行将 DateTime 列增加一秒?
Posted
技术标签:
【中文标题】如何为每一行将 DateTime 列增加一秒?【英文标题】:How to increment DateTime column by a second for every row? 【发布时间】:2013-03-28 21:52:33 【问题描述】:假设我有这个时间值:09:00:00
我有一个包含时间列的表,其中包含三条记录。
我想用那个时间更新这 3 条记录,但每次(对于每条记录)时间值都会增加一秒。
类似这样的:
ColumnA ColumnB
1 09:00:00
2 09:00:01
3 09:00:02
我该怎么做?
我的解决方案:
在我自己的解决方案工作了一段时间后,这就是我想出的
update tor1
set ColumnB = dateadd(s,tor2.inc, ColumnB)
from table1 tor1
inner join (select ColumnA, ROW_NUMBER() OVER (Order by ColumnA) as inc from table1) tor2 on tor1.ColumnA=tor2.ColumnA
【问题讨论】:
如果我们不能使用ColumnA
来订购,你如何建议我们知道哪一行加0秒,哪一行加1,哪一行加2?表是一组无序的行,因此如果您想要一个可预测的结果,您需要指明该顺序是如何定义的。
假设ColumnA
是PK,它是身份。现在假设第 2 条记录被删除了,你会给第 3 行加 2 秒吗?
您是否在问自己自己的要求是什么?
我只想将秒 (1++) 添加到该列 B,而不管其他列。第一行,同一时间,第二行+1,第三行2,以此类推
@Somebody 既然你已经有了答案,那么十亿美元的问题就是你为什么要这样做?
【参考方案1】:
您没有指定任何特定的顺序。
对于不确定/未记录的结果,您可以尝试古怪的更新方法。
CREATE TABLE table1
(
ColumnB datetime NULL
);
INSERT INTO table1 DEFAULT VALUES;
INSERT INTO table1 DEFAULT VALUES;
INSERT INTO table1 DEFAULT VALUES;
DECLARE @ColumnB datetime;
SET @ColumnB = '19000101 09:00:00';
UPDATE table1
SET @ColumnB = ColumnB = DATEADD(s, 1, @ColumnB);
SELECT *
FROM table1;
DROP TABLE table1;
否则,您将需要使用光标或找到某种方法来模拟 2000 年的ROW_NUMBER
。
【讨论】:
@mattedgod - 虽然它没有记录/保证(如果依赖于任何类型的确定性排序尤其脆弱),并且应该在更高版本中更全面地实现OVER
子句时过时。一篇关于 SQL Server 中心的有趣文章 Solving the Running Total and Ordinal Rank Problems (Rewritten)。但是,如果 OP 只是设置测试数据,例如应该没问题。
Richard 身体变形了,因为您的 DECLARE
行在 2000 年不起作用。
@RichardTheKiwi - 保证根据ORDER
子句分配IDENTITY
值。不保证插入按任何特定顺序。 2012年this shows up more as it often used to follow the ORDER BY
just in case the plan was run with SET ROWCOUNT N
【参考方案2】:
这是一个使用 #temp 表但不使用不受支持的古怪更新(无意冒犯 Martin)并且不依赖于无法保证的神奇身份排序的版本(无意冒犯 Richard)。
CREATE TABLE dbo.Whatever
(
ColumnA INT IDENTITY(1,1),
ColumnB DATETIME
);
INSERT dbo.Whatever(ColumnB) SELECT '09:00';
INSERT dbo.Whatever(ColumnB) SELECT '09:00';
INSERT dbo.Whatever(ColumnB) SELECT '09:00';
INSERT dbo.Whatever(ColumnB) SELECT '09:00';
INSERT dbo.Whatever(ColumnB) SELECT '09:00';
-- just to demonstrate a gap
DELETE dbo.Whatever WHERE ColumnA = 3;
SELECT w.ColumnA, w.ColumnB,
c = (SELECT COUNT(*) FROM dbo.Whatever WHERE ColumnA < w.ColumnA)
INTO #x
FROM dbo.Whatever AS w;
UPDATE w
SET ColumnB = DATEADD(SECOND, x.c, w.ColumnB)
FROM dbo.Whatever AS w
INNER JOIN #x AS x
ON x.ColumnA = w.ColumnA;
SELECT ColumnA, ColumnB FROM dbo.Whatever;
结果:
ColumnA ColumnB
------- -----------------------
1 1900-01-01 09:00:00.000
2 1900-01-01 09:00:01.000
4 1900-01-01 09:00:02.000
5 1900-01-01 09:00:03.000
【讨论】:
可能不建议用于大型表,因为SELECT COUNT(*)
子查询使其 O(n^2)。但否则一个很好的解决方案如果 ColumnA 是唯一的(OP 没有说明)。
OP 声明它是一个身份列。虽然这本身并不能保证唯一性,但我只看到由于故意破坏或极端愚蠢而导致的重复。
OP 提出了一个问题“如果......是一个身份”。 OP还在mattedgod的回答下说“认为该列甚至不存在”。当然,在这种情况下我的解决方案也行不通【参考方案3】:
如果您可以假设 ColumnA
是您需要添加的秒数(或像您的示例那样成正比),那么您可以使用它。
UPDATE myTable SET ColumnB = DATEADD(s, (ColumnA - 1), ColumnB)
如果没有,您将需要确定每列的排名(谷歌上有很多结果)并添加排名。
【讨论】:
遗憾的是,我不能将该列用作增量值。此外认为该列甚至不存在 @Somebody 哪一栏不存在? A 列?您将其包含在示例中...如果不是 ColumnA,请提供一种确定顺序的方法,我们还应该如何知道?【参考方案4】:对于 SQL Server 2005 及更高版本,您可以使用 OVER 子句来控制更新的顺序。
;with T as (
select *, rn=row_number() over (order by columnA)
from Tbl)
update T
set columnB = DateAdd(s, rn-1, '09:00:00');
SQL Fiddle
对于 SQL Server 2000,您可以使用临时表。使用INSERT INTO..SELECT to guarantee IDENTITY 列的计算。
CREATE TABLE table1
(
ColumnA int,
ColumnB datetime NULL
);
INSERT INTO table1 values (1, null);
INSERT INTO table1 values (2, getdate());
INSERT INTO table1 values (3, '02:02:02');
create table #tmp (ColumnA int, ID int identity(0,1))
insert into #tmp
select ColumnA
from table1
order by ColumnA;
update a
set ColumnB = dateadd(s,t.ID,'09:00:00')
from #tmp t
join table1 a on a.ColumnA = t.ColumnA;
select * from table1;
【讨论】:
这是一个定义了 IDENTITY 列的表。您正在使用不同的 IDENTITY 函数。 support.microsoft.com/default.aspx?scid=kb;en-us;Q273586 我认为也可能存在涉及并行性和身份值分配方式的错误,所以如果你要依赖你所观察到的,请使用MAXDOP 1
。对于 OP 的示例(三行),这不是问题,但我们真的不知道我们在谈论多少行。
@RichardTheKiwi - 我认为 Aaron 的观点是文章保证顺序但不保证顺序。
另见sqlmag.com/article/sql-server/…(关于 IDENTITY() 函数)。
@Aaron 我想我们已经在几条评论前移除了 IDENTITY() 函数以上是关于如何为每一行将 DateTime 列增加一秒?的主要内容,如果未能解决你的问题,请参考以下文章