I've got a bit of a messy table on my hands that has two fields, a date field and a time field that are both strings. What I need to do is get the minimum date from those fields, or just the record itself if there is no date/time attached to it. Here's some sample data:
ID First Last Date Time
1 Joe Smith 2013-09-06 04:00
1 Joe Smith 2013-09-06 02:00
2 Jack Jones
3 John Jack 2013-09-05 06:00
3 John Jack 2013-09-15 15:00
What I would want from a query is to get the following:
ID First Last Date Time
1 Joe Smith 2013-09-06 02:00
2 Jack Jones
3 John Jack 2013-09-05 06:00
The min date/time for ID 1 and 3 and then just ID 2 back because he doesn't have a date/time. I cam up with the following query that gives me ID's 1 and 3 exactly as I would want them:
SELECT *
FROM test as t
where
cast(t.date + ' ' + t.time as Datetime ) = (select top 1 cast(p.date + ' ' + p.time as Datetime ) as dtime from test as p where t.ID = p.ID order by dtime)
But it doesn't return row number 2 at all. I imagine there's a better way to go about doing this. Any ideas?
You can do this with row_number():
select ID, First, Last, Date, Time
from (select t.*,
row_number() over (partition by id order by date, time) as seqnum
from test t
) t
where seqnum = 1;
Although storing dates and times as strings is not recommended, you at least do it right. The values use the ISO standard format (or close enough) so alphabetic sorting is the same as date/time sorting.
Assuming [Date] and [Time] are the types I think they are, and not strings:
SELECT ID,[First],[Last],[Date],[Time] FROM
(
SELECT ID,[First],[Last],[Date],[Time],rn = ROW_NUMBER()
OVER (PARTITION BY ID ORDER BY [Date], [Time])
FROM dbo.test
) AS t WHERE rn = 1;
Example:
DECLARE #x TABLE
(
ID INT,
[First] VARCHAR(32),
[Last] VARCHAR(32),
[Date] DATE,
[Time] TIME(0)
);
INSERT #x VALUES
(1,'Joe ','Smith','2013-09-06','04:00'),
(1,'Joe ','Smith','2013-09-06','02:00'),
(2,'Jack','Jones',NULL, NULL ),
(3,'John','Jack ','2013-09-05','06:00'),
(3,'John','Jack ','2013-09-15','15:00');
SELECT ID,[First],[Last],[Date],[Time] FROM
(
SELECT ID, [First],[Last],[Date],[Time],rn = ROW_NUMBER()
OVER (PARTITION BY ID ORDER BY [Date], [Time])
FROM #x
) AS x WHERE rn = 1;
Results:
ID First Last Date Time
-- ----- ----- ---------- --------
1 Joe Smith 2013-09-06 02:00:00
2 Jack Jones NULL NULL
3 John Jack 2013-09-05 06:00:00
Try:
SELECT
*
FROM
test as t
WHERE
CAST(t.date + ' ' + t.time as Datetime) =
(
select top 1 cast(p.date + ' ' + p.time as Datetime ) as dtime
from test as p
where t.ID = p.ID
order by dtime
)
OR (t.date='' AND t.time='')
Related
I am working on a hospital database and table details are
Patient number Dischargeto Date Time
212 Hospital1 16/10/2018 14:00:00
212 Hospital2 18/10/2018 10:00:00
212 Hospital3 20/10/2018 18:00:00
212 Home 22/10/2018 10:00:00
213 Hostpital1 11/11/2018 11:00:00
213 Death 14/11/2018 18:00:00
214 Hospital 1 28/12/2011 14:00:00
214 Home 05/01/2012 NULL
Info:
Final destination of the patient
212 is Home
213 is Death
214 is home
I want patients whose final destination is not death
so I wrote this query
select *
from
(select
Patient number, DischargeTo, Date, Time,
ROW_NUMBER() OVER(PARTITION BY Patientnumber order by Date desc, Time desc) as testcount
from tablename) abc
where testcount = 1
and
DischargeTo not like '%Death%'
results are not correct where time is null. I want to convert
if time is null then it converts to 00:00:00
and so the sorting could be corrected.
Thanks behorehand
make your date time as valid sql datetime before sorting.
select *
from
(select
Patient number, DischargeTo, Date, Time,
ROW_NUMBER() OVER(PARTITION BY Patientnumber order by cast(concat(RIGHT([Date],4) + '' + SUBSTRING([Date],4,2) + '' + LEFT([Date],2), ' ', [Time]) as datetime)) as testcount
from tablename) abc
where testcount = 1
and
DischargeTo not like '%Death%'
I had switch the day and month in the date string as the datetime conversion was giving out-of-bounds error. Try the following:
with hospitaldt(PatientId, DischargeTo, dt) as
(
select PatientId, DischargeTo, convert(datetime, concat(Date, " ", isnull(Time, "00:00:00")))
from hospital
)
select t1.PatientId, t1.DischargeTo, t1.dt
from hospitaldt t1
where t1.dt =
(
select max(t2.dt)
from hospitaldt t2
where t2.DischargeTo not like "%Death%"
and t2.PatientId = t1.PatientId
I think "death" only happens once, so you don't need to look for the final status. Just check for whether death is ever there:
select patient_number
from tablename
group by patient_number
having sum(case when dischargeto = 'Death' then 1 else 0 end) = 0;
Date Time Mode ID
2017-01-01 13:00:00.0000000 3 10
2017-01-01 14:00:00.0000000 1 10
2017-01-01 15:00:00.0000000 3 10
2017-01-01 15:30:00.0000000 1 10
This is a temp table.I just want to display time column as 2 columns,1 column with mode =3 and other with mode=1.
This is a temp table.I just want the below output:
Date InTime(Mode-3) OutTime(Mode-1) ID
2017-01-01 13:00:00.0000000 14:00:00.0000000 10
2017-01-01 15:00:00.0000000 15:30:00.0000000 10
Guessing you want a method to create alternating rows with fixed values (1 and 3).
you can use
case when ROW_NUMBER() over (order by [Date])%2 = 0 then 1 else 3
as the logic for your mode column
Try this,
DECLARE #TB TABLE (DATETIME VARCHAR(30),ID INT)
INSERT INTO #TB VALUES
('2017-01-01 13:00:00.0000000',10),
('2017-01-01 14:00:00.0000000',10),
('2017-01-01 15:00:00.0000000',10),
('2017-01-01 15:30:00.0000000',10 )
SELECT SUBSTRING(DATETIME,0,11) DATE
,SUBSTRING(DATETIME,12,LEN(DATETIME)) TIME
,CASE WHEN ROW_NUMBER() OVER (ORDER BY DATETIME)%2 = 0 THEN 1 ELSE 3 END MODE
,ID
FROM #TB
This works, depending on the data and datatypes/schema (and if the name of the table is timeTable):
SELECT DATE, time AS 'InTime(Mode-3)',
(SELECT TOP 1 time FROM timeTable
WHERE mode = 1
AND id = outerTable.id
AND date = outerTable.date
AND time > outerTable.time
ORDER BY date, time) AS 'OutTime(Mode-1)',
ID
FROM timeTable AS outerTable
WHERE mode = 3
the outerQuery only selects the in-times mode = 3
in innerQuery selects the next out-time, that correspondes to the selected in-time, and only returns the first one. since ordered by date and time, it should be the next one. Only tested with your given data
Output:
Date | InTime(Mode-3) | OutTime(Mode-1) | ID
---------------|---------------------|---------------------|------
2017-01-01 | 13:00:00.0000000 | 14:00:00.0000000 | 10
2017-01-01 | 15:00:00.0000000 | 15:30:00.0000000 | 10
Just for reference:
I used this table schema
CREATE TABLE timeTable(
date DATE,
time TIME,
mode INTEGER,
id INTEGER
);
Update:
With time - difference:
SELECT *, DATEDIFF(MINUTE,INTIME,OUTTIME) AS [DIFFERENCE] FROM (
SELECT [DATE], [time] AS INTIME,
(SELECT TOP 1 [time] FROM timeTable
WHERE [mode] = 1
AND [id] = outerTable.id
AND [date] = outerTable.date
AND [time] > outerTable.time
ORDER BY [date], [time]) AS OUTTIME,
[ID]
FROM [timeTable] AS outerTable
WHERE [mode] = 3
) WholeData
i need help to order this table (named "season") , by matching actual date with the BEGINDATE
ID NAME BEGINDATE
----------- -------------------- ----------
1 2014-2015 2014-10-01
2 2015-2016 2015-10-01
3 2016-2017 2016-10-01
4 2017-2018 2017-10-01
for example:
actual date is 2016/10/28 so we are in season 2016-2017 (id=3)
so the result should be
ID NAME BEGINDATE
----------- -------------------- ----------
3 2016-2017 2016-10-01
1 2014-2015 2014-10-01
2 2015-2016 2015-10-01
4 2017-2018 2017-10-01
UPDATE (SOLVED)
what i finally did was:
DECLARE #IDACTIVE AS INT = (SELECT MAX(ID) FROM SEASON WHERE BEGINDATE < GETDATE())
SELECT
1 AS ORDERBY,
ID,
NAME,
BEGINDATE
FROM SEASON
WHERE ID = #IDACTIVE
UNION
SELECT
2 AS ORDERBY,
ID,
NAME,
BEGINDATE
FROM SEASON
WHERE ID = #IDACTIVE
Follow the next approach:
1) Get The only matched row by using Top and Where clauses.
2) Get the all records except the one that you getting on point #1
3) Combine the result of two Selects via using UNION ALL.
Demo:-
Create table season (id int , NAME varchar(20),BEGINDATE date)
go
insert into season values (1,'2014-2015','2014-10-01')
insert into season values (2,'2015-2016','2015-10-01')
insert into season values (3,'2016-2017','2016-10-01')
insert into season values (4,'2017-2018','2017-10-01')
go
select * from (
select top 1 * from season
where BEGINDATE < getdate()
order by BEGINDATE desc
) a
union all
select * from season
where BEGINDATE != (
select top 1 BEGINDATE from season
where BEGINDATE < getdate()
order by BEGINDATE desc)
-- an another Soluation
select * from season
where DATEPART(Year,BEGINDATE) =DATEPART(Year,getdate())
union all
select * from season
where DATEPART(Year,BEGINDATE) !=DATEPART(Year,getdate())
The Result:
First move all future dates to the end, then order by beginDate
SELECT *
FROM season
ORDER BY CASE WHEN beginDate > GETDATE() THEN 0 ELSE 1 END,
beginDate
I think this is most easily done using window functions:
select s.*
from season s
order by (case when begindate = max(case when getdate() >= begindate then begindate end) over ()
then 1 else 2
end),
id
I have a table which contains datetime rows like below.
ID | DateTime
1 | 12:00
2 | 12:02
3 | 12:03
4 | 12:04
5 | 12:05
6 | 12:10
I want to identify those rows where there is a 'gap' of 5 minutes between rows (for example, row 5 and 6).
I know that we need to use DATEDIFF, but how can I only get those rows which are consecutive with each other?
You can use LAG, LEAD window functions for this:
SELECT ID
FROM (
SELECT ID, [DateTime],
DATEDIFF(mi, LAG([DateTime]) OVER (ORDER BY ID), [DateTime]) AS prev_diff,
DATEDIFF(mi, [DateTime], LEAD([DateTime]) OVER (ORDER BY ID)) AS next_diff
FROM mytable) AS t
WHERE prev_diff >= 5 OR next_diff >= 5
Output:
ID
==
5
6
Note: The above query assumes that order is defined by ID field. You can easily substitute this field with any other field that specifies order in your table.
You might try this (I'm not sure if it's really fast)
SELECT current.datetime AS current_datetime,
previous.datetime AS previous_datetime,
DATEDIFF(minute, previous.datetime, current.datetime) AS gap
FROM my_table current
JOIN my_table previous
ON previous.datetime < current.datetime
AND NOT EXISTS (SELECT *
FROM my_table others
WHERE others.datetime < current.datetime
AND others.datetime > previous.datetime);
update SS2012: Use LAG
DECLARE #tbl TABLE(ID INT, T TIME)
INSERT INTO #tbl VALUES
(1,'12:00')
,(2,'12:02')
,(3,'12:03')
,(4,'12:04')
,(5,'12:05')
,(6,'12:10');
WITH TimesWithDifferenceToPrevious AS
(
SELECT ID
,T
,LAG(T) OVER(ORDER BY T) AS prev
,DATEDIFF(MI,LAG(T) OVER(ORDER BY T),T) AS MinuteDiff
FROM #tbl
)
SELECT *
FROM TimesWithDifferenceToPrevious
WHERE ABS(MinuteDiff) >=5
The result
6 12:10:00.0000000 12:05:00.0000000 5
I have a table like this:
Value TimeStamp
1 2016-04-01 00:01:09.000
0 2016-04-01 00:01:09.000
0 2016-04-01 00:01:37.000
1 2016-04-01 00:01:37.000
1 2016-04-01 00:04:52.000
1 2016-04-01 00:09:58.000
1 2016-04-01 00:15:05.000
1 2016-04-01 00:20:11.000
1 2016-04-01 00:24:49.000
1 2016-04-01 00:29:55.000
1 2016-04-01 00:31:19.000
0 2016-04-01 00:31:19.000
0 2016-04-01 00:31:46.000
1 2016-04-01 00:31:46.000
1 2016-04-01 00:35:01.000
1 2016-04-01 00:40:07.000
1 2016-04-01 00:44:46.000
1 2016-04-01 00:49:52.000
1 2016-04-01 00:54:58.000
1 2016-04-01 01:00:04.000
1 2016-04-01 01:01:28.000
0 2016-04-01 01:01:28.000
0 2016-04-01 01:05:10.000
0 2016-04-01 01:09:49.000
And i want to count the seconds where value is 1 (switch ON) PER DAY, here is the deal; When the timeStamp repeats it means that there was a change from 0 to 1 or viceversa in the switch value, I already had many aproches like:
Q1 AS (SELECT ROW_NUMBER() OVER (ORDER BY TimeStamp) AS id,
Value, Timestamp
FROM Q2
GROUP BY idVBox, sensorType, sensorSubtype, timeStamp
HAVING COUNT(TimeStamp) > 1)
Then:
SELECT A.Value, DATEDIFF(SECOND,A.TimeStamp,B.TimeStamp)
FROM Q1 AS A
INNER JOIN Q1 AS B
ON B.ID = A.ID + 1
AND B.ID%2 = 0
Then Group by and Sum, but here the problem is that i don't know if the value comes in 1 or 0 from the past day, and the switch can change it's state quick and never get an actual value of it's actual state. Any other idea?
What you want to do, is add a dummy sensor state switch into your set at the beginning of the day before you start your calculation.
The extra records added are:
0, '2016-04-01 00:00:00'
1, '2016-04-01 00:00:00' -- This is conditional on the first record in your set having a value of 1
The overall query is below
Note: in order to determine what record is actually the first in sequence I used "ID" column.
;WITH Q0 AS(
-- Inserts a new record ( 0, '2016-04-01 00:00:00' ) to the beginning of the day
SELECT TOP 1 0 AS Value, CONVERT( DATETIME, CONVERT( DATE, LogDate )) AS LogDate
FROM #SwitchLog
UNION ALL
-- Inserts a new record ( 1, '2016-04-01 00:00:00' ) to the beginning of the day when the first record has Value = 1
SELECT Value, CONVERT( DATETIME, CONVERT( DATE, LogDate )) AS LogDate
FROM
( SELECT TOP 1 ID, Value, LogDate
FROM #SwitchLog
ORDER BY LogDate ASC, ID ASC ) AS DummyRecord --<-- NOTE: the use of a table ID column
WHERE Value = 1
UNION ALL
SELECT Value, LogDate
FROM #SwitchLog
)
,
Q1 AS (SELECT ROW_NUMBER() OVER (ORDER BY LogDate) AS id,
SUM( Value ) AS Value, LogDate
FROM Q0
GROUP BY LogDate
HAVING COUNT(LogDate) > 1)
SELECT A.Value, DATEDIFF(SECOND,A.LogDate,B.LogDate) AS Total
FROM Q1 AS A
INNER JOIN Q1 AS B
ON B.ID = A.ID + 1 AND B.ID%2 = 0
Output:
Value Total
----------- -----------
1 69
1 1782
1 1782
Same approach should be used to insert dummy record(s) at the end of the period/day ((day + 1) 00:00:00) to cater for situations where sensor value is 1 at the end of the day.
If using SQL Server 2012 then you could make good use of the LAG() function.
First, join the table on duplicate dates where value=1. Next, calculate difference between the on and the previous on. Finally, sum it up.
NOTE : The LAG() will return null for first on of the day.
SELECT
Seconds=SUM(X.Seconds)
FROM
(
SELECT
Seconds=DATEDIFF(SECOND,LAG(T1.TimeStamp) OVER (ORDER BY T1.TimeStamp),T1.TimeStamp)
FROM
MyTable T1
INNER JOIN MyTable T2 ON T2.TimeStamp=T1.TimeStamp AND T1.Value<>T2.Value
WHERE
T1.Value=1
)AS X