If time col is null then replace with 00:00 - sql

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;

Related

Return Min-Max value of dates into one column with condition

I have the data shown below:
ID_DATA | DATE
--------------------
1101 | 2020-02-01
1101 | 2020-02-02
1101 | 2020-02-03
1102 | 2020-02-01
1102 | 2020-02-01
What I want is, under similar ID_DATA, there will be one column showing concatenated date range as string 'MIN(date) - MAX(date)'. But if under similar ID_DATA the date is similar, it will only show the DATE as is.
Note that there might be more than 2 rows of DATE for a single ID_DATA. I'm hoping to use case-when.
Following expected result:
ID_DATA DATE
1101 2020-02-01 - 2020-02-03
1102 2020-02-01
Try this option:
SELECT
ID_DATA,
CONVERT(varchar, MIN(DATE), 111) +
CASE WHEN MIN(DATE) < MAX(DATE)
THEN ' - ' + CONVERT(varchar, MAX(DATE), 111)
ELSE '' END AS DATE
FROM yourTable
GROUP BY
ID_DATA;
The logic here is use a CASE expression to check if the minimum date for an ID be smaller than the maximum date. If so, then we display the upper range, otherwise we just report the minimum date.
You can try the below using the min() and max() function.
WITH cte (
ID_DATA
,MinDate
,MaxDate
)
AS (
SELECT ID_DATA
,Min(DtDATE) AS MinDate
,Max(DtDATE) AS MaxDate
FROM TblData
GROUP BY ID_DATA
)
SELECT ID_DATA
,CASE
WHEN MinDate = MaxDate
THEN Convert(VARCHAR(10), MinDate)
ELSE Convert(VARCHAR(10), MinDate) + ' - ' + Convert(VARCHAR(10), MaxDate)
END AS DatePeriod
FROM cte
Here is the live db<>fiddle demo.

Generate a list of dates between 2 dates for more than one record

I am trying to write SQL to generate the following data
Date Count
2018-09-24 2
2018-09-25 2
2018-09-26 2
2018-09-27 2
2018-09-28 2
2018-09-29 1
A sample of the base table I am using is
ID StartDate EndDate
187267 2018-09-24 2018-10-01
187270 2018-09-24 2018-09-30
So I'm trying to get a list of dates between 2 dates and then count how many base data records there are in each date.
I started using a temporary table and attempting to loop through the records to get the results but I'm not sure if this is the right approach.
I have this code so far
WITH ctedaterange
AS (SELECT [Dates] = (select ea.StartWork from EngagementAssignment ea where ea.EngagementAssignmentId IN(SELECT ea.EngagementAssignmentId
FROM EngagementLevel el INNER JOIN
EngagementAssignment ea ON el.EngagementLevelID = ea.EngagementLevelId
WHERE el.JobID = 15072 and ea.AssetId IS NOT NULL))
UNION ALL
SELECT [dates] + 1
FROM ctedaterange
WHERE [dates] + 1 < = (select ea.EndWork from EngagementAssignment ea where ea.EngagementAssignmentId IN(SELECT ea.EngagementAssignmentId
FROM EngagementLevel el INNER JOIN
EngagementAssignment ea ON el.EngagementLevelID = ea.EngagementLevelId
WHERE el.JobID = 15072 and ea.AssetId IS NOT NULL)))
SELECT [Dates], Count([Dates])
FROM ctedaterange
GROUP BY [Dates]
But I get this error
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
I get correct results when the job I use only generates one record in the subselect in the where clause, ie:
SELECT ea.EngagementAssignmentId
FROM EngagementLevel el INNER JOIN
EngagementAssignment ea ON el.EngagementLevelID = ea.EngagementLevelId
WHERE el.JobID = 15047 and ea.AssetId IS NOT NULL
generates one record.
The results look like this:
Dates (No column name)
2018-09-24 02:00:00.000 1
2018-09-25 02:00:00.000 1
2018-09-26 02:00:00.000 1
2018-09-27 02:00:00.000 1
2018-09-28 02:00:00.000 1
2018-09-29 02:00:00.000 1
2018-09-30 02:00:00.000 1
2018-10-01 02:00:00.000 1
you can generate according to your range by changing from and to date
DECLARE
#DateFrom DATETIME = GETDATE(),
#DateTo DATETIME = '2018-10-30';
WITH DateGenerate
AS (
SELECT #DateFrom as MyDate
UNION ALL
SELECT DATEADD(DAY, 1, MyDate)
FROM DateGenerate
WHERE MyDate < #DateTo
)
SELECT
MyDate
FROM
DateGenerate;
Well, if you only have a low date range, you can use a recursive CTE as demonstrated in the other answers. The problem with a recursive CTE is with large ranges, where it starts to be ineffective - So I wanted to show you a different approach, that builds the calendar CTE without using recursion.
First, Create and populate sample table (Please save us this step in your future questions):
DECLARE #T AS TABLE
(
ID int,
StartDate date,
EndDate date
)
INSERT INTO #T (ID, StartDate, EndDate) VALUES
(187267, '2018-09-24', '2018-10-01'),
(187270, '2018-09-24', '2018-09-30')
Then, get the first start date and the number of dates you need in the calendar cte:
DECLARE #DateDiff int, #StartDate Date
SELECT #DateDiff = DATEDIFF(DAY, MIN(StartDate), Max(EndDate)),
#StartDate = MIN(StartDate)
FROM #T
Now, construct the calendar cte based on row_number (that is, unless you already have a numbers (tally) table you can use):
;WITH Calendar(TheDate)
AS
(
SELECT TOP(#DateDiff + 1) DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY ##SPID)-1, #StartDate)
FROM sys.objects t0
-- unremark the next row if you don't get enough records...
-- CROSS JOIN sys.objects t1
)
Note that I'm using row_number() - 1 and therefor have to select top(#DateDiff + 1)
Finally - the query:
SELECT TheDate, COUNT(ID) As NumberOfRecords
FROM Calendar
JOIN #T AS T
ON Calendar.TheDate >= T.StartDate
AND Calendar.TheDate <= T.EndDate
GROUP BY TheDate
Results:
TheDate | NumberOfRecords
2018-09-24 | 2
2018-09-25 | 2
2018-09-26 | 2
2018-09-27 | 2
2018-09-28 | 2
2018-09-29 | 2
2018-09-30 | 2
2018-10-01 | 1
You can see a live demo on rextester.
Can you please try following SQL CTE query where I have used a SQL dates table function [dbo].[DatesTable] which produces a list of dates between min date and max date in the source table
;with boundaries as (
select
min(StartDate) minD, max(EndDate) maxD
from DateRanges
), dates as (
select
dates.[date]
from boundaries
cross apply [dbo].[DatesTable](minD, maxD) as dates
)
select dates.[date], count(*) as [count]
from dates
inner join DateRanges
on dates.date between DateRanges.StartDate and DateRanges.EndDate
group by dates.[date]
order by dates.[date]
The output is as expected
Try this: demo
WITH cte1
AS (SELECT id,sdate,edate from t
union all
select c.id,DATEADD(DAY, 1, c.sdate),c.edate from cte1 c where DATEADD(DAY, 1, c.sdate)<=c.edate
)
SELECT sdate,count(id) as total FROM cte1
group by sdate
OPTION (MAXRECURSION 0)
Output:
sdate total
2018-09-24 2
2018-09-25 2
2018-09-26 2
2018-09-27 2
2018-09-28 2
2018-09-29 2
2018-09-30 1

Count seconds on switch interval SQL Server

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

Find all rows where start date is before a prior end date

Is there a way to pull back all records that have overlapping datetimes based on a user?
For instance;
TableA has the following rows;
TrainerID StartTime EndTime
1234 10-1-2015 08:30 10-1-2015 09:00
1234 10-1-2015 08:45 10-1-2015 09:15
1234 10-1-2015 09:30 10-1-2015 10:00
2345 10-1-2015 08:45 10-1-2015 09:15
2345 10-1-2015 09:30 10-1-2015 10:00
I need a query that can pull ONLY the following record because it's start time is before the previous end time for the trainer (double booked):
1234 10-1-2015 08:45 10-1-2015 09:15
The EXIST code below should give you that answer. The code ensures that the start time of the clashing entry is before the start of the main list entry while the start time of the clash is still after the start time of the mail list entry.
SELECT *
FROM tblTest clashing
WHERE EXISTS
(
SELECT 1
FROM tblTest mainlist
WHERE clashing.trainderid = mainlist.trainderid
AND clashing.starttime < mainlist.endtime
AND clashing.starttime > mainlist.starttime
)
This can also be written with an IN statement, but EXIST is much more efficient
To remove overlapping dates you can use:
Demo
CREATE TABLE #TABLEA( TrainerID INT, StartDate DATETIME, EndDate DATETIME);
INSERT INTO #TABLEA
SELECT 1234, '10-1-2015 08:30', '10-1-2015 09:00'
UNION ALL SELECT 1234 , '10-1-2015 08:45', '10-1-2015 09:15'
UNION ALL SELECT 1234 , '10-1-2015 09:30', '10-1-2015 10:00'
UNION ALL SELECT 2345 , '10-1-2015 08:45', '10-1-2015 09:15'
UNION ALL SELECT 2345 , '10-1-2015 09:30', '10-1-2015 10:00';
SELECT
D.TrainerID,
[StartTime] = D.StartDate,
[EndTime] = (SELECT MIN(E.EndDate)
FROM #TABLEA E
WHERE E.EndDate >= D.EndDate
AND E.TrainerID = D.TrainerID
AND NOT EXISTS (SELECT 1
FROM #TABLEA E2
WHERE E.StartDate < E2.StartDate
AND E.EndDate > E2.StartDate
AND E.TrainerID = E2.TrainerID))
FROM #TABLEA D
WHERE NOT EXISTS ( SELECT 1
FROM #TABLEA D2
WHERE D.StartDate < D2.EndDate
AND D.EndDate > D2.EndDate
AND D.TrainerID = D2.TrainerID);
You can use below code to get the required row, however based on your logic row from next trainer id (i.e. 2345) will also be qualified
DECLARE #Trainers TABLE
(
TrainerId INT,
Start_Time datetime,
End_Time datetime
)
INSERT INTO #Trainers VALUES
(1234,'10-1-2015 08:30','10-1-2015 09:00 '),
(1234,'10-1-2015 08:45','10-1-2015 09:15'),
(1234,'10-1-2015 09:30','10-1-2015 10:00'),
(2345 ,' 10-1-2015 08:45','10-1-2015 09:15'),
(2345 ,' 10-1-2015 09:30 ',' 10-1-2015 10:00')
;WITH TrainersTemp AS
(
SELECT *, ROW_NUMBER() OVER ( ORDER BY trainerid) AS rn
FROM #Trainers
)
SELECT CX.TrainerId, CX.Start_Time, CX.End_Time
FROM TrainersTemp CX JOIN TrainersTemp CY
ON CX.rn = CY.rn + 1
WHERE CY.End_Time < CX.Start_Time
Demo (SQL fiddle is down again)
or if you want to see all rows except the faulty one then use below code
;WITH TrainersTempAll AS
(
SELECT *, ROW_NUMBER() OVER ( ORDER BY trainerid) AS rn
FROM #Trainers
)
SELECT CX.TrainerId, CX.Start_Time, CX.End_Time
FROM TrainersTempAll CX JOIN TrainersTempAll CY
ON CX.rn = CY.rn + 1
Firstly you should sort by trainerId and Start_time. And then join two tables with correct condition.
Try this query:
;WITH TrainersTemp AS
(
SELECT *, ROW_NUMBER() OVER ( ORDER BY trainerid, Start_Time) AS row_num
FROM Trainers
)
select t2.* from TrainersTemp t1
join TrainersTemp t2 on t1.TrainerId = t2.TrainerId and t1.row_num = t2.row_num-1
where t2.Start_Time<t1.End_Time
As you use SQL Server 2012 you can use LAG function, which would be likely more efficient than self-join. The query becomes pretty simple as well.
For each row LAG gives you EndTime from the previous row (partitioned by TrainerID). Then just compare StartTime from the current row with EndTime from the previous row.
SQL Fiddle
WITH
CTE
AS
(
SELECT
TrainerID
,StartTime
,EndTime
,LAG(EndTime) OVER(PARTITION BY TrainerID ORDER BY StartTime) AS PrevEndTime
FROM TableA
)
SELECT
TrainerID
,StartTime
,EndTime
FROM CTE
WHERE StartTime < PrevEndTime
;
Results:
| TrainerID | StartTime | EndTime |
|-----------|---------------------------|---------------------------|
| 1234 | October, 01 2015 08:45:00 | October, 01 2015 09:15:00 |

SQL Statement to Get The Minimum DateTime from 2 String FIelds

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='')