2

Suppose that I have this Time value: 09:00:00

And I have a table with a column Time, and I have three records in it.

I want to update those 3 records with that time but with the Time value incremented in one second each time (for every record).

Something like this:

ColumnA   ColumnB
1         09:00:00
2         09:00:01
3         09:00:02

How can I do that?

My Solution:

After some time working on my own solution, this is what I came up with

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
Somebody
  • 2,667
  • 14
  • 60
  • 100
  • And if we can't use `ColumnA` to order, how do you propose we know which row to add 0 seconds to, which to add 1, and which to add 2? A table is an unordered set of rows, so if you want a predictable result, you need to indicate how that order is defined. – Aaron Bertrand Mar 28 '13 at 21:59
  • Let's say that `ColumnA` is the PK and it is identity. Now suppose that record 2 was deleted, would you add 2 seconds to row number 3? – Somebody Mar 28 '13 at 22:03
  • 5
    Are you asking yourself what your own requirements are? – Aaron Bertrand Mar 28 '13 at 22:03
  • I just want to add seconds (1++) to that columnB regardless the other columns. First row, same time, second row +1, third row 2, and so on – Somebody Mar 28 '13 at 22:07
  • The problem is, how on earth do you define "first row" if you want us to pretend that ColumnA doesn't exist? – Aaron Bertrand Mar 28 '13 at 22:07
  • @Somebody So you don't care about the order at all as long as they get sequential seconds? – Cade Roux Mar 28 '13 at 22:08
  • That is corrent @CadeRoux – Somebody Mar 28 '13 at 22:09
  • 2
    @Somebody So now that you have an answer, the bavillion dollar question is WHY DO YOU WANT TO DO THIS? – Cade Roux Mar 28 '13 at 22:15
  • @CadeRoux trying to do a logic to solve a problem using asp.net and sql :) – Somebody Mar 29 '13 at 00:03
  • @Somebody OK, I'll go there. What is the underlying problem you are trying to solve using this technique? – Cade Roux Mar 29 '13 at 00:29
  • @CadeRoux I really appreciate your help, but I just got the answer to my problem, thank you :) – Somebody Mar 29 '13 at 01:07
  • @CadeRoux This solution may be useful to someone who wants to begin to enforce a unique value for a datetime column. – tuespetre Nov 29 '13 at 14:11
  • @tuespetre indeed, but we may never know about this one since the OP gave little motivation – Cade Roux Nov 29 '13 at 19:20

4 Answers4

8

You don't specify any particular ordering.

For undeterministic/undocumented results you could try the quirky update approach.

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;

Otherwise you will need to use a cursor or find some way of simulating ROW_NUMBER in 2000.

Martin Smith
  • 438,706
  • 87
  • 741
  • 845
  • @mattedgod - It isn't documented/guaranteed though (especially fragile if relying on any kind of deterministic ordering) and should be obsolete with the fuller implementation of the `OVER` clause in later versions. An interesting article on SQL Server central about it [Solving the Running Total and Ordinal Rank Problems (Rewritten)](http://www.sqlservercentral.com/articles/T-SQL/68467/). But if the OP is just setting up test data for example should be fine. – Martin Smith Mar 28 '13 at 22:16
  • Richard is bent out of shape because your `DECLARE` line doesn't work in 2000. – Aaron Bertrand Mar 28 '13 at 22:20
  • @RichardTheKiwi - The `IDENTITY` values are guaranteed to be allocated as per the `ORDER` clause. The insert isn't guaranteed to be in any particular order. In 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`](http://stackoverflow.com/questions/11222043/table-valued-function-order-by-is-ignored-in-output/11231935#11231935) – Martin Smith Mar 28 '13 at 22:29
5

Here's a version that uses a #temp table but doesn't use the unsupported quirky update (no offense Martin) and doesn't rely on magical identity ordering, which is not guaranteed (no offense 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;

Results:

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
Aaron Bertrand
  • 272,866
  • 37
  • 466
  • 490
  • Probably not advised for large tables due to the `SELECT COUNT(*)` subquery making it O(n^2). But otherwise a good solution ***if ColumnA is unique*** (OP hasn't stated). – RichardTheKiwi Mar 28 '13 at 22:41
  • OP stated it was an identity column. While that on its own doesn't guarantee uniqueness, I've only seen duplicates as a result of intentional sabotage or extreme daftness. – Aaron Bertrand Mar 28 '13 at 22:44
  • OP asked a question "what if ... is an identity". OP also stated under mattedgod's answer "think that column not even exists". Of course, my solution wouldn't work either in that case – RichardTheKiwi Mar 28 '13 at 22:46
3

If you can assume ColumnA is the number of seconds you need to add (or directly proportional like your example), then you can use that.

UPDATE myTable SET ColumnB = DATEADD(s, (ColumnA - 1), ColumnB)

If not, you will need to determine the rank of each column (plenty of results on google for that) and add the rank instead.

Matt Dodge
  • 10,833
  • 7
  • 38
  • 58
  • Sadly I can't use that column as the incremental value. Moreover think that column not even exists – Somebody Mar 28 '13 at 21:58
  • 1
    @Somebody What column doesn't exist? ColumnA? You included it in your example...provide a method of determining the order if it's not ColumnA, how else should we know? – Matt Dodge Mar 28 '13 at 22:03
0

For SQL Server 2005 onwards, you can use the OVER clause to control the ordering of the updates.

;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

For SQL Server 2000, you can use a temporary table. Use INSERT INTO..SELECT to guarantee the computation of the IDENTITY column.

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;
RichardTheKiwi
  • 105,798
  • 26
  • 196
  • 262
  • 1
    That is for a table that has an IDENTITY column defined. You are using the IDENTITY function, which is different. http://support.microsoft.com/default.aspx?scid=kb;en-us;Q273586 – Aaron Bertrand Mar 28 '13 at 22:31
  • I think there can also be bugs that involve parallelism and how identity values are assigned, so if you are going to rely on what you observe, please use `MAXDOP 1`. For the OP's example (three rows) this won't be an issue, but we don't really know how many rows we're talking about. – Aaron Bertrand Mar 28 '13 at 22:34
  • @RichardTheKiwi - I think Aaron's point is that the article guarantees ordering but not sequential. – Martin Smith Mar 28 '13 at 22:41
  • Also see http://www.sqlmag.com/article/sql-server/identity-function-isn-t-reliable-for-imposing-order-on-a-result-set-43553 (regarding the IDENTITY() function). – Aaron Bertrand Mar 28 '13 at 22:42
  • @Aaron I think we've moved off the IDENTITY() *function* a few comment ago – RichardTheKiwi Mar 28 '13 at 22:43
  • [You might find this thread interesting](http://bytes.com/topic/sql-server/answers/501619-out-order-identity-field-sql2000). Not only because others have observed the same incorrect behavior (fixed by adding `MAXDOP` as I suggested), but also because they point out that the blog post you referenced also states that *most* of those rules apply to SQL Server 2000, but not which ones. I also have a note (sadly under NDA), from someone higher up the MS food chain than the blogger, who is let's say less confident in the blogger's statements (particularly for 2000). – Aaron Bertrand Mar 28 '13 at 23:01
  • And remember that not all bug reports are public (exposed on Connect), and bugs against 2000 were never filed against Connect and would never be dealt with there anyway... – Aaron Bertrand Mar 28 '13 at 23:02