Filling table with datetime's incremented by one second each - sql

I have a MyDatabase.MyTable.DateCol with few thousand rows, which I want to fill up with datetime. I want each date to be bigger than the previous one by 1 second. How can I do that?

Sample Table
CREATE Table DateTable
(ID INT IDENTITY(1,1),Name NVARCHAR(300), Data Datetime)
GO
Test Data
INSERT INTO DateTable (Name)
VALUES ('John'),('Mark'),('Phil'),('Simon'),('Sam'),('Pete'),('Josh')
GO
Query
;WITH CTE
AS
(
SELECT *, rn = ROW_NUMBER() OVER (ORDER BY ID ASC) FROM DateTable
)
UPDATE CTE
SET Data = DATEADD(SECOND, CTE.rn, GETDATE())
Result Set
SELECT * FROM DateTable
ID Name Data
1 John 2013-11-06 20:34:59.310
2 Mark 2013-11-06 20:35:00.310
3 Phil 2013-11-06 20:35:01.310
4 Simon 2013-11-06 20:35:02.310
5 Sam 2013-11-06 20:35:03.310
6 Pete 2013-11-06 20:35:04.310
7 Josh 2013-11-06 20:35:05.310

Not quite sure of your ordering criteria, but you can use:
MIN(DateCol) OVER()
To get the first date, and
ROW_NUMBER() OVER(ORDER BY DateCol, ID)
To get the number of seconds to add (your ordering criteria may be different). Then combine the two to update a common table expression:
WITH CTE AS
( SELECT ID,
DateCol,
NewDate = DATEADD(SECOND,
MIN(DateCol) OVER(),
ROW_NUMBER() OVER(ORDER BY DateCol, ID))
FROM MyDatabase.MyTable
)
UPDATE CTE
SET DateCol = NewDate;
If you have no dates in your column then you can just enter a start date (GETDATE() below):
WITH CTE AS
( SELECT ID,
DateCol,
NewDate = DATEADD(SECOND,
GETDATE(),
ROW_NUMBER() OVER(ORDER BY DateCol, ID))
FROM MyDatabase.MyTable
)
UPDATE CTE
SET DateCol = NewDate;

Related

Finding the maximum value in a 24h period SQL

In need of some help :)
So I have a table of records with the following columns:
Key (PK, FK, int)
DT (smalldatetime)
Value (real)
The DT is a datetime for every half hour of the day with an associated value
E.g.
Key DT VALUE
1000 2010-01-01 08:00:00 80
1000 2010-01-01 08:30:00 75
1000 2010-01-01 09:00:00 100
I need to find the max value and associated DT for every 24 hour period. for a particular key and date range
Currently I have:
SELECT CAST(LEFT([DT],11) as smalldatetime) as 'DATE'
,max([VALUE]) as 'MAX_HH'
FROM TABLE 1
WHERE DT > '6-nov-2016' and [KEY] = '1000'
GROUP BY CAST(LEFT([DT],11) as smalldatetime)
ORDER BY 'DATE'
But this returns the max values for the date e.g.
Key DT VALUE
1000 2010-01-01 00:00:00 100
Any ideas on how to pull the full DT ?
Thanks guys!
Assuming you're using a database with support for windowed functions, we can use ROW_NUMBER() (or RANK if you want to support/pull in values that are tied for first place):
declare #t table ([Key] int not null , DT smalldatetime not null, Value int not null)
insert into #t([Key],DT,VALUE) values
(1000,'2010-01-01T08:00:00',80 ),
(1000,'2010-01-01T08:30:00',75 ),
(1000,'2010-01-01T09:00:00',100)
;With Numbered as (
select *,
ROW_NUMBER() OVER (PARTITION BY [Key],CAST(DT as date) ORDER BY Value desc) as rn
from #t
)
select * from Numbered
where rn=1
Damien's answer is very good, if you can't (or want) to use windowed function, try this:
SELECT T1.*
FROM TABLE_1 AS T1
INNER JOIN (
SELECT CAST([DT] as date) as 'DATE'
, MAX([VALUE]) as 'MAX_HH'
FROM TABLE_1
WHERE DT > '6-nov-2016' and [KEY] = '1000'
GROUP BY CAST([DT] as date)
) AS MAX_DT
ON MAX_DT.[DATE] = CAST(T1.[DT] as date)
AND T1.VALUE = MAX_DT.MAX_HH
WHERE DT > '6-nov-2016' and [KEY] = '1000'
ORDER BY DT
By the way, it's best not to use reserved keywords as object names (i.e. date)

Day after max date in data

I am loading data into a table. I don't have any info on how frequent or when the source data is loaded, all I know is I need data from the source to run my script.
Here's the issue, if I run max(date) I get the latest date from the source, but I don't know if the data is still loading. I've ran into cases where I've only gotten a percentage of the data. Thus, I need the next business day after max date.
I want to know is there a way to get the second latest date in the system. I know I can get max(date) - 1, but that give me literally the day after. I don't need the literal day after.
Example, if I run the script on Tuesday, max(date) will be Monday, but since weekend are not in the source system, I need to get Friday instead of Monday.
DATE
---------
2017-04-29
2017-04-25
2017-04-21
2017-04-19
2017-04-18
2017-04-15
2017-04-10
max(date) = 2017-04-29
how do I get 2017-04-25?
Depending on your version of SQL Server, you can use a windowing function like row_number:
select [Date]
from
(
select [Date],
rn = row_number() over(order by [Date] desc)
from #yourtable
) d
where rn = 2
Here is a demo.
Should you have multiple of the same date, you can perform a distinct first:
;with cte as
(
select distinct [date]
from #yourtable
)
select [date]
from
(
select [date],
rn = row_number() over(order by [date] desc)
from cte
) x
where rn = 2;
You can use row_number and get second as below
select * from ( select *, Rown= row_number() over (order by date desc) from yourtable ) a
where a.RowN = 2
More recent SQL Server versions support FETCH FIRST:
select date
from tablename
order by date desc
offset 1 fetch first 1 row only
OFFSET 1 means skip one row. (The 2017-04-29 row.)
;With cte([DATE])
AS
(
SELECT '2017-04-29' union all
SELECT '2017-04-25' union all
SELECT '2017-04-21' union all
SELECT '2017-04-19' union all
SELECT '2017-04-18' union all
SELECT '2017-04-15' union all
SELECT '2017-04-10'
)
SELECT [DATE] FROM
(
SELECT *,ROW_NUMBER()OVER(ORDER BY Seq)-1 As Rno FROM
(
SELECT *,MAX([DATE])OVER(ORDER BY (SELECT NULL))Seq FROM cte
)dt
)Final
WHERE Final.Rno=1
OutPut
DATE
-----
2017-04-25
You can also use FIRST_VALUE with a dynamic date something like DATEADD(DD, -1, GETDATE()). The example below has the date hard coded.
SELECT DISTINCT
FIRST_VALUE([date]) OVER(ORDER BY [date] DESC) AS FirstDate
FROM CTE
WHERE [date] < '2017-04-25'
Another way
DECLARE #T TABLE ([DATE] DATE)
INSERT INTO #T VALUES
('2017-04-29'),
('2017-04-25'),
('2017-04-21'),
('2017-04-19'),
('2017-04-18'),
('2017-04-15'),
('2017-04-10');
SELECT
MAX([DATE]) AS [DATE]
FROM #T
WHERE DATENAME(DW,[DATE]) NOT IN ('Saturday','Sunday')
Another way of doing it, just for example sake...
SELECT MIN(A.date)
FROM
(
SELECT TOP 2 DISTINCT date
FROM YourTable AS C
ORDER BY date DESC
) AS A

How to select the current date row from multiple date rows using system date

I have a table with many rows, they contain different dates, any one of them will be for the current period. There is no end date as a field otherwise i would have compared system date between from and to date. I have tried using max function but still it displays many rows.
The data is grouped by a type identifier, so for each type there will be a current date row.
What can be the best query to get the current row (single) which is active considering the current date?
Below is the original query:
Select Group1,Group2,FromDate,FPFrom, FpTo FROM [DB].[dbo].[HGD] AS GD, [DB].[dbo].[HDT] AS TD WHERE GD.GRoup1 = TD.MainGroup
Thanks
SELECT TOP 1 * FROM yourTable WHERE procStart <= getdate() ORDER BY procStart DESC
or something like
SELECT * FROM (SELECT TOP 1 * FROM yourTable row_number OVER(GROUP BY TypeId, Order By procStart DESC) RN WHERE procStart <= getdate()) DQ WHERE DQ.RN = 1
Please try to be more precise. I think you are looking something like shown below:
CREATE TABLE #temp(
SomeDate datetime,
SomeType int
)
INSERT #temp VALUES
('2016-07-20', 1),
('2016-07-23', 1),
('2016-07-27', 1),
('2016-07-30', 1),
('2016-01-25', 3),
('2016-01-31', 3),
('2016-02-21', 3),
('2016-07-23', 3),
('2016-09-30', 3)
WITH Numbered AS
(
SELECT SomeDate, SomeType, ROW_NUMBER() OVER (PARTITION BY SomeType ORDER BY SomeDate) RowNumber
FROM #temp
),
Ranges AS
(
SELECT T1.SomeDate StartPeriod, COALESCE(T2.SomeDate, DATEADD(year,1,GETDATE())) EndPeriod, T1.SomeType
FROM Numbered T1
LEFT JOIN Numbered T2 ON T1.RowNumber+1=T2.RowNumber AND T1.SomeType=T2.SomeType
)
SELECT * FROM Ranges
WHERE GETDATE() BETWEEN StartPeriod AND EndPeriod
ORDER BY SomeType
This yields:
StartPeriod EndPeriod SomeType
2016-07-23 00:00:00.000 2016-07-27 00:00:00.000 1
2016-07-23 00:00:00.000 2016-09-30 00:00:00.000 3
#Paweł Dyl gave me an idea and I added a condition to my query and got the desired results.
ToDate field was not available , so I created a field by adding 180 days to it.
AND GetDate() BETWEEN cast(FromDate as Date) AND DATEADD(DAY, 180,cast(FromDate as DATE))
Thanks again.

Convert a list of dates to date ranges in SQL Server

I have a query as following:
SELECT [Date] FROM [TableX] ORDER BY [Date]
The result is:
2016-06-01
2016-06-03
2016-06-10
2016-06-11
How can I get following pairs?
From To
2016-06-01 2016-06-03
2016-06-03 2016-06-10
2016-06-10 2016-06-11
If you're using SQL Server 2012 or later, you can use the LEAD method.
Accesses data from a subsequent row in the same result set without the use of a self-join in SQL Server 2016. LEAD provides access to a row at a given physical offset that follows the current row.
I think it would look like this for you:
SELECT [Date] AS [From], LEAD([Date], 1) OVER (ORDER BY [Date]) AS [To]
FROM TableX
ORDER BY [Date]
Note that on the last row, the [To] field will be NULL. If you wanted to remove that row, you could put it in an inner query:
SELECT *
FROM
(
SELECT [Date] AS [From], LEAD([Date], 1) OVER (ORDER BY [Date]) AS [To]
FROM TableX
) x
WHERE [To] IS NOT NULL
All you need to do is add a row number for each date.
Then unite all these rows by the next row (except the last row)
WITH cteDates AS
(
SELECT [Date],
ROW_NUMBER() OVER (ORDER BY (SELECT [Date])) As RowNum
FROM TableX
)
SELECT TOP(SELECT COUNT(*) - 1 FROM cteDates)
[Date] [From],
(SELECT [Date] FROM cteDates WHERE RowNum = d.RowNum + 1) [To]
FROM cteDates d
A little tricky solution for SQL 2008.
declare #tbl table(dt datetime)
insert #tbl values
('2016-06-01'),
('2016-06-03'),
('2016-06-10'),
('2016-06-11')
;with cte as (
select dt, ROW_NUMBER() over(order by dt) rn --add number
from #tbl
),
newTbl as (
select t1.dt start, t2.dt [end]
from cte t1 inner join cte t2 on t1.rn+1=t2.rn
)
select *
from newTbl
The result is what you wish.
Since there are never any gaps as you stated, you can just used DATEADD()
SELECT DISTINCT
[Date] as [FROM],
DATEADD(DAY,1,[Date]) as [TO]
FROM TableX
ORDER BY [Date] DESC

Find the start and end date (set based) in T-SQL

I have the below.
Name Date
A 2011-01-01 01:00:00.000
A 2011-02-01 02:00:00.000
A 2011-03-01 03:00:00.000
B 2011-04-01 04:00:00.000
A 2011-05-01 07:00:00.000
The desired output is
Name StartDate EndDate
-------------------------------------------------------------------
A 2011-01-01 01:00:00.000 2011-04-01 04:00:00.000
B 2011-04-01 04:00:00.000 2011-05-01 07:00:00.000
A 2011-05-01 07:00:00.000 NULL
How to achieve the same using TSQL in a set based approach.
DDL is as under
DECLARE #t TABLE(PersonName VARCHAR(32), [Date] DATETIME)
INSERT INTO #t VALUES('A', '2011-01-01 01:00:00')
INSERT INTO #t VALUES('A', '2011-01-02 02:00:00')
INSERT INTO #t VALUES('A', '2011-01-03 03:00:00')
INSERT INTO #t VALUES('B', '2011-01-04 04:00:00')
INSERT INTO #t VALUES('A', '2011-01-05 07:00:00')
Select * from #t
;WITH cte1
AS (SELECT *,
ROW_NUMBER() OVER (ORDER BY Date) -
ROW_NUMBER() OVER (PARTITION BY PersonName
ORDER BY Date) AS G
FROM #t),
cte2
AS (SELECT PersonName,
MIN([Date]) StartDate,
ROW_NUMBER() OVER (ORDER BY MIN([Date])) AS rn
FROM cte1
GROUP BY PersonName,
G)
SELECT a.PersonName,
a.StartDate,
b.StartDate AS EndDate
FROM cte2 a
LEFT JOIN cte2 b
ON a.rn + 1 = b.rn
Because the result of CTEs are not generally materialised however
you may well find you get better performance if you materialize the
intermediate result yourself as below.
DECLARE #t2 TABLE (
rn INT IDENTITY(1, 1) PRIMARY KEY,
PersonName VARCHAR(32),
StartDate DATETIME );
INSERT INTO #t2
SELECT PersonName,
MIN([Date]) StartDate
FROM (SELECT *,
ROW_NUMBER() OVER (ORDER BY Date) -
ROW_NUMBER() OVER (PARTITION BY PersonName
ORDER BY Date) AS G
FROM #t) t
GROUP BY PersonName,
G
ORDER BY StartDate
SELECT a.PersonName,
a.StartDate,
b.StartDate AS EndDate
FROM #t2 a
LEFT JOIN #t2 b
ON a.rn + 1 = b.rn
SELECT
PersonName,
StartDate = MIN(Date),
EndDate
FROM (
SELECT
PersonName,
Date,
EndDate = (
/* get the earliest date after current date
associated with a different person */
SELECT MIN(t1.Date)
FROM #t AS t1
WHERE t1.Date > t.Date
AND t1.PersonName <> t.PersonName
)
FROM #t AS t
) s
GROUP BY PersonName, EndDate
ORDER BY 2
Basically, for every Date we find the nearest date after it such that is associated with a different PersonName. That gives us EndDate, which now distinguishes for us consecutive groups of dates for the same person.
Now we only need to group the data by PersonName & EndDate and get the minimal Date in every group as StartDate. And yes, sort the data by StartDate, of course.
Get a row number so you will know where the previous record is. Then, take a record and the next record after it. When the state changes we have a candidate row.
select
state,
min(start_timestamp),
max(end_timestamp)
from
(
select
first.state,
first.timestamp_ as start_timestamp,
second.timestamp_ as end_timestamp
from
(
select
*, row_number() over (order by timestamp_) as id
from test
) as first
left outer join
(
select
*, row_number() over (order by timestamp_) as id
from test
) as second
on
first.id = second.id - 1
and first.state != second.state
) as agg
group by state
having max(end_timestamp) is not null
union
-- last row wont have a ending row
--(select state, timestamp_, null from test order by timestamp_ desc limit 1)
-- I think it something like this for sql server
(select top state, timestamp_, null from test order by timestamp_ desc)
order by 2
;
Tested with PostgreSQL but should work with SQL Server as well
The other answer with the cte is a good one. Another option would be to iterate over the collection in any case. It's not set based, but it is another way to do it.
You will need to iterate to either A. assign a unique id to each record that corresponds to its transaction, or B. to actually get your output.
TSQL is not ideal for iterating over records, especially if you have a lot, and so I would recommend some other way of doing it, a small .net program or something that is better at iterating.
There's a very quick way to do this using a bit of Gaps and Islands theory:
WITH CTE as (SELECT PersonName, [Date]
, Row_Number() over (ORDER BY [Date])
- Row_Number() over (ORDER BY PersonName, [Date]) as Island
FROM #t)
Select PersonName, Min([Date]), Max([Date])
from CTE
GROUP BY Island, PersonName
ORDER BY Min([Date])