How to increment DateTime column by a second for every row? - sql

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

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.

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

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.

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;

Related

How to get the each record with some condition

I have following data:
DECLARE #temp TABLE (
ID int
,sn varchar(200)
,comment varchar(2000)
,rownumber int
)
insert into #temp values(1,'sn1',NULL,1)
insert into #temp values(2,'sn1','aaa',2)
insert into #temp values(3,'sn1','bbb',3)
insert into #temp values(4,'sn1',NULL,4)
insert into #temp values(5,'sn2',NULL,1)
insert into #temp values(6,'sn2',NULL,2)
insert into #temp values(7,'sn2',NULL,3)
select * from #temp
And I want to output like this:
2 sn1 aaa 2
5 sn2 NULL 1
same sn, if comment have value, get this lower rownumber's record. For sn1, have two records with comment value, so here, get the the record with rownumber=2
If comment doesn't have value, get the lower rownumber's record. For sn2, get the record with rownumber=1
May I know how to write this SQL?
This is a prioritization query. I think row_number() is the simplest method:
select t.*
from (select t.*,
row_number() over (partition by sn
order by (case when comment is not null then 1 else 2 end),
rownumber
) as seqnum
from #temp t
) t
where seqnum = 1;
Here is a db<>fiddle.

Constructing single select statement that returns order depends on the value of a column in SQL Server

Table1
Id bigint primary key identity(1,1)
Status nvarchar(20)
Insert dummy data
Insert into Table1 values ('Open') --1
Insert into Table1 values ('Open') --2
Insert into Table1 values ('Grabbed') --3
Insert into Table1 values ('Closed') --4
Insert into Table1 values ('Closed') --5
Insert into Table1 values ('Open') --6
How would I construct a single select statement which orders the data where records with 'Grabbed' status is first, followed by 'Closed', followed by 'Open' in SQL Server
Output:
Id Status
3 Grabbed
4 Closed
5 Closed
1 Open
2 Open
6 Open
I think you need something like this:
select *
from yourTable
order by case when Status = 'Grabbed' then 1
when Status = 'Closed' then 2
when Status = 'Open' then 3
else 4 end
, Id;
[SQL Fiddle Demo]
Another way is to using CTE like this:
;with cte as (
select 'Grabbed' [Status], 1 [order]
union all select 'Closed', 2
union all select 'Open', 3
)
select t.*
from yourTable t
left join cte
on t.[Status] = cte.[Status]
order by cte.[order], Id;
[SQL Fiddle Demo]
This could be done much better with a properly normalized design:
Do not store your Status as a textual content. Just imagine a typo (a row with Grabed)...
Further more a lookup table allows you to add side data, e.g. a sort order.
CREATE TABLE StatusLookUp(StatusID INT IDENTITY PRIMARY KEY /*you should name your constraints!*/
,StatusName VARCHAR(100) NOT NULL
,SortRank INT NOT NULL)
INSERT INTO StatusLookUp VALUES
('Open',99) --ID=1
,('Closed',50)--ID=2
,('Grabbed',10)--ID=3
CREATE TABLE Table1(Id bigint primary key identity(1,1) /*you should name your constraints!*/
,StatusID INT FOREIGN KEY REFERENCES StatusLookUp(StatusID));
Insert into Table1 values (1) --1
Insert into Table1 values (1) --2
Insert into Table1 values (3) --3
Insert into Table1 values (2) --4
Insert into Table1 values (2) --5
Insert into Table1 values (1) --6
SELECT *
FROM Table1 AS t1
INNER JOIN StatusLookUp AS s ON t1.StatusID=s.StatusID
ORDER BY s.SortRank;
I find that the simplest method uses a string:
order by charindex(status, 'Grabbed,Closed,Open')
or:
order by charindex(',' + status + ',', ',Grabbed,Closed,Open,')
If you are going to put values in the query, I think the easiest way uses values():
select t1.*
from t1 left join
(values ('Grabbed', 1), ('Closed', 2), ('Open', 3)) v(status, priority)
on t1.status = v.status
order by coalesce(v.priority, 4);
Finally. This need suggests that you should have a reference table for statuses. Rather than putting the string name in other tables, put an id. The reference table can have the priority as well as other information.
Try this:
select Id,status from tablename where status='Grabbed'
union
select Id,status from tablename where status='Closed'
union
select Id,status from tablename where status='Open'

with clause execution order

In a simplified scenario I have table T that looks somthing like:
Key Value
1 NULL
1 NULL
1 NULL
2 NULL
2 NULL
3 NULL
3 NULL
I also have a very time-consuming function Foo(Key) which must be considered as a black box (I must use it, I can't change it).
I want to update table T but in a more efficient way than
UPDATE T SET Value = dbo.Foo(Key)
Basically I would execute Foo only one time for each Key.
I tried something like
WITH Tmp1 AS
(
SELECT DISTINCT Key FROM T
)
, Tmp2 AS
(
SELECT Key, Foo(Key) Value FROM Tmp1
)
UPDATE T
SET T.Value = Tmp2.Value
FROM T JOIN Tmp2 ON T.Key = Tmp2.Key
but unexpectedly computing time doesn't change at all, because Sql Server seems to run Foo again on every row.
Any idea to solve this without other temporary tables?
One method is to use a temporary table. You don't have much control over how SQL Server decides to optimize its queries.
If you don't want a temporary table, you could do two updates:
with toupdate as (
select t.*, row_number() over (partition by id order by id) as seqnum
from t
)
update toupdate
set value = db.foo(key)
where seqnum = 1;
Then you can run a similar update again:
with toupdate as (
select t.*, max(value) over (partition by id) as as keyvalue
from t
)
update toupdate
set value = keyvalue
where value is null;
You might try it like this:
CREATE FUNCTION dbo.Foo(#TheKey INT)
RETURNS INT
AS
BEGIN
RETURN (SELECT #TheKey*2);
END
GO
CREATE TABLE #tbl(MyKey INT,MyValue INT);
INSERT INTO #tbl(MyKey) VALUES(1),(1),(1),(2),(2),(3),(3),(3);
SELECT * FROM #tbl;
DECLARE #tbl2 TABLE(MyKey INT,TheFooValue INT);
WITH DistinctKeys AS
(
SELECT DISTINCT MyKey FROM #tbl
)
INSERT INTO #tbl2
SELECT MyKey,dbo.Foo(MyKey) TheFooValue
FROM DistinctKeys;
UPDATE #tbl SET MyValue=TheFooValue
FROM #tbl
INNER JOIN #tbl2 AS tbl2 ON #tbl.MyKey=tbl2.MyKey;
SELECT * FROM #tbl2;
SELECT * FROM #tbl;
GO
DROP TABLE #tbl;
DROP FUNCTION dbo.Foo;

Add empty rows to results

I want to add empty rows to results fetched from a select statement. For example, if the select query fetch 4 rows then 2 empty rows needs to be fetched. Objective should be the number of rows fetched should be 6 every time. The number of rows fetched will be 6 maximum if there are 6 rows with data.
Any idea?
In SQL-SERVER You can create temp table to update It with empty rows and you can use WHILE to insert desired number of rows with empty values. Something like:
-- Create temp table to update data with empty rows
CREATE TABLE #HoldEmptyRows
(
Id NVARCHAR(20),
CustomerName NVARCHAR(20),
CustomerEmail NVARCHAR(20)
)
-- Insert data from SourceTable to temp
INSERT INTO #HoldEmptyRows
SELECT * FROM SourceTable
-- Do while count from temp table < of desired number insert empty rows
WHILE ((SELECT COUNT(*) cnt FROM #HoldEmptyRows) < 6)
BEGIN
INSERT INTO #HoldEmptyRows VALUES ('', '', '')
END
SELECT * FROM #HoldEmptyRows
DEMO AT SQL FIDDLE
Try the below logic:
with cte as
(
select 0 as col1
union all
select col1+1 from cte where cte.col1<10
)
select * into #temp1 from cte
create table #test
(rownum int,col1 varchar(100))
declare #i int=1
while (#i<=6)
begin
insert into #test
select * from
(select row_Number() over (order by (Select 0))rownum, * from #temp1)x
where rownum=#i
Set #i=#i+1
end
select case when rownum>4 then '' else col1 end as col1 from #test

Select records with order of IN clause

I have
SELECT * FROM Table1 WHERE Col1 IN(4,2,6)
I want to select and return the records with the specified order which i indicate in the IN clause
(first display record with Col1=4, Col1=2, ...)
I can use
SELECT * FROM Table1 WHERE Col1 = 4
UNION ALL
SELECT * FROM Table1 WHERE Col1 = 6 , .....
but I don't want to use that, cause I want to use it as a stored procedure and not auto generated.
I know it's a bit late but the best way would be
SELECT *
FROM Table1
WHERE Col1 IN( 4, 2, 6 )
ORDER BY CHARINDEX(CAST(Col1 AS VARCHAR), '4,2,67')
Or
SELECT CHARINDEX(CAST(Col1 AS VARCHAR), '4,2,67')s_order,
*
FROM Table1
WHERE Col1 IN( 4, 2, 6 )
ORDER BY s_order
You have a couple of options. Simplest may be to put the IN parameters (they are parameters, right) in a separate table in the order you receive them, and ORDER BY that table.
The solution is along this line:
SELECT * FROM Table1
WHERE Col1 IN(4,2,6)
ORDER BY
CASE Col1
WHEN 4 THEN 1
WHEN 2 THEN 2
WHEN 6 THEN 3
END
select top 0 0 'in', 0 'order' into #i
insert into #i values(4,1)
insert into #i values(2,2)
insert into #i values(6,3)
select t.* from Table1 t inner join #i i on t.[in]=t.[col1] order by i.[order]
Replace the IN values with a table, including a column for sort order to used in the query (and be sure to expose the sort order to the calling application):
WITH OtherTable (Col1, sort_seq)
AS
(
SELECT Col1, sort_seq
FROM (
VALUES (4, 1),
(2, 2),
(6, 3)
) AS OtherTable (Col1, sort_seq)
)
SELECT T1.Col1, O1.sort_seq
FROM Table1 AS T1
INNER JOIN OtherTable AS O1
ON T1.Col1 = O1.Col1
ORDER
BY sort_seq;
In your stored proc, rather than a CTE, split the values into table (a scratch base table, temp table, function that returns a table, etc) with the sort column populated as appropriate.
I have found another solution. It's similar to the answer from onedaywhen, but it's a little shorter.
SELECT sort.n, Table1.Col1
FROM (VALUES (4), (2), (6)) AS sort(n)
JOIN Table1
ON Table1.Col1 = sort.n
I am thinking about this problem two different ways because I can't decide if this is a programming problem or a data architecture problem. Check out the code below incorporating "famous" TV animals. Let's say that we are tracking dolphins, horses, bears, dogs and orangutans. We want to return only the horses, bears, and dogs in our query and we want bears to sort ahead of horses to sort ahead of dogs. I have a personal preference to look at this as an architecture problem, but can wrap my head around looking at it as a programming problem. Let me know if you have questions.
CREATE TABLE #AnimalType (
AnimalTypeId INT NOT NULL PRIMARY KEY
, AnimalType VARCHAR(50) NOT NULL
, SortOrder INT NOT NULL)
INSERT INTO #AnimalType VALUES (1,'Dolphin',5)
INSERT INTO #AnimalType VALUES (2,'Horse',2)
INSERT INTO #AnimalType VALUES (3,'Bear',1)
INSERT INTO #AnimalType VALUES (4,'Dog',4)
INSERT INTO #AnimalType VALUES (5,'Orangutan',3)
CREATE TABLE #Actor (
ActorId INT NOT NULL PRIMARY KEY
, ActorName VARCHAR(50) NOT NULL
, AnimalTypeId INT NOT NULL)
INSERT INTO #Actor VALUES (1,'Benji',4)
INSERT INTO #Actor VALUES (2,'Lassie',4)
INSERT INTO #Actor VALUES (3,'Rin Tin Tin',4)
INSERT INTO #Actor VALUES (4,'Gentle Ben',3)
INSERT INTO #Actor VALUES (5,'Trigger',2)
INSERT INTO #Actor VALUES (6,'Flipper',1)
INSERT INTO #Actor VALUES (7,'CJ',5)
INSERT INTO #Actor VALUES (8,'Mr. Ed',2)
INSERT INTO #Actor VALUES (9,'Tiger',4)
/* If you believe this is a programming problem then this code works */
SELECT *
FROM #Actor a
WHERE a.AnimalTypeId IN (2,3,4)
ORDER BY case when a.AnimalTypeId = 3 then 1
when a.AnimalTypeId = 2 then 2
when a.AnimalTypeId = 4 then 3 end
/* If you believe that this is a data architecture problem then this code works */
SELECT *
FROM #Actor a
JOIN #AnimalType at ON a.AnimalTypeId = at.AnimalTypeId
WHERE a.AnimalTypeId IN (2,3,4)
ORDER BY at.SortOrder
DROP TABLE #Actor
DROP TABLE #AnimalType
ORDER BY CHARINDEX(','+convert(varchar,status)+',' ,
',rejected,active,submitted,approved,')
Just put a comma before and after a string in which you are finding the substring index or you can say that second parameter.
And first parameter of CHARINDEX is also surrounded by , (comma).