Sql, how to get data in difference of 24 hours - sql

Select p.uhid,p.inpatientno,dateof admission
from adt.inpatientmaster p
where p.uhid='apd1' and status <>0
Here uhid is unique. I want to check that a patient gets admitted in between 24 hours , here if patient gets admitted again then uhid remains same but inpatientno always change.
Ex:
Registraionno inpatientno dateofadmission
Apd1 xy1 18/01/15
Ap1 ab2 19/01/15

We can do arithmetic on Oracle dates. So yesterday is sysdate - 1.
You need to query the table twice. Once to find the patient records, and once to find any previous matches. Use a self-join to achieve this:
select p1.uhid,
p1.inpatientno as current_inpatientno,
p1.dateofadmission as current_dateofadmission
p2.inpatientno as previous_inpatientno,
p2.dateofadmission as previous_dateofadmission
from adt.inpatientmaster p1
join adt.inpatientmaster p2
on p2.uhid = p1.uhid
where p1.uhid='apd1'
and p1.status <> 0
and p2.dateofadmission >= p1.dateofadmission-1
and p2.inpatientno != p1.inpatientno
/
You may need to restrict on p2.status <> 0 as well: not sure what your business rules are.
This query will return one row for each match. If there are several admissions within the same 24 hours the result set will have one row for each combination.

SELECT p.uhid,
p.inpatientno,
p.dateofadmission
FROM adt.inpatientmaster p
WHERE p.status<>0
AND p.dateofadmission <= p.dateofadmission +1
AND p.uhid='APD1'

Related

T-SQL Get Events after an order

So I have data that looks like this:
What I am trying to do in SQL is get all of the CollectedDT that are later than a date and less than a date, for example all of the values in yellow in both columns belong together, the records with just one column in yellow I don't care about and the ones in all green are keepers too. The idea is to try and implicitly say that one set of collection times belong to an order and another set belong to the other. There is no rule for hours difference between each, could be 1 hour, could be 100 hours.
While the query returned what it should have, it is decipherable that the CollectedDT of 11-04-2011 15:35 and newer most likely belongs to the 11-03-2011 21:12 order, there is no hard logic to dictate this, it is simply implied and needs to be treated as such.
Really no good starting point on how to go from here.
The query is as follows:
SELECT ORD.[episode_no],
ORD.[ord_no],
ORD.[pty_name] AS 'Ordering Provider',
ORD.[ent_dtime] AS 'Order Entered',
ASMT.[CollectedDT],
ORD.[str_dtime],
ORD.last_cng_dtime,
PMS.vst_start_dtime AS 'Admit Dt',
PMS.[vst_end_dtime] AS 'Discharge Dt',
ORD.[ord_qty],
CASE
WHEN ORD.[ord_sts] = 27
THEN 'Complete'
WHEN ORD.ord_sts = 34
THEN 'Discontinued'
ELSE ORD.ord_sts
END AS 'Order Status',
ORD.[desc_as_written],
ASMT.[FormUsage] AS 'Assessment',
ASMT.[AssessmentID],
datediff(minute, ORD.ent_dtime, ASMT.CollectedDT) AS [order_entry_to_collected_minutes],
datediff(hour, ORD.ent_dtime, ASMT.CollectedDT) AS [order_entry_to_collected_hours]
FROM [SMSPHDSSS0X0].[smsmir].[mir_sr_ord] AS ORD
LEFT OUTER JOIN [smsmir].[mir_sr_vst_pms] AS PMS ON PMS.episode_no = ORD.episode_no
LEFT OUTER JOIN [smsmir].[mir_sc_Assessment] AS ASMT ON ASMT.PatientVisit_oid = PMS.vst_no
WHERE (ORD.desc_as_written LIKE 'physical Therapy%')
AND (
ASMT.FormUsage IN ('Physical Therapy Initial Asmt', 'Physical Therapy Re-evaluation', 'PT Flowsheet')
AND ASMT.CollectedDT > ORD.ent_dtime
)
AND ORD.ord_sts = 27
AND ASMT.AssessmentStatus = 'Complete'

JOIN other table only if condition is true for ALL joined rows

I have two tables I'm trying to conditionally JOIN.
dbo.Users looks like this:
UserID
------
24525
5425
7676
dbo.TelemarketingCallAudits looks like this (date format dd/mm/yyyy):
UserID Date CampaignID
------ ---------- ----------
24525 21/01/2018 1
24525 26/08/2018 1
24525 17/02/2018 1
24525 12/01/2017 2
5425 22/01/2018 1
7676 16/11/2017 2
I'd like to return a table that contains ONLY users that I called at least 30 days ago (if CampaignID=1) and at least 70 days ago (if CampaignID=2).
The end result should look like this (today is 02/09/18):
UserID Date CampaignID
------ ---------- ----------
5425 22/01/2018 1
7676 16/11/2017 2
Note that because I called user 24524 with Campaign 1 only 7 days ago, I shall not see the user at all.
I tried this simple AND/OR condition and then I found out it will still return the users I shouldn't see because they do have rows indicating other calls and it simply ignoring the conditioned calls... which misses the goal obviously.
I have no idea on how to condition the overall appearance of the user if ANY of his associated rows in the second table did not meet the condition.
AND
(
internal_TelemarketingCallAudits.CallAuditID IS NULL --No telemarketing calls is fine
OR
(
internal_TelemarketingCallAudits.CampaignID = 1 --Campaign 1
AND
DATEADD(dd, 75, MAX(internal_TelemarketingCallAudits.Date)) < GETDATE() --Last call occured at least 10 days ago
)
OR
(
internal_TelemarketingCallAudits.CampaignID != 1 --Other campaigns
AND
DATEADD(dd, 10, MAX(internal_TelemarketingCallAudits.Date)) < GETDATE() --Last call occured at least 10 days ago
)
)
I really appreciate your help.
Try this: SQL Fiddle
select *
from dbo.Users u
inner join ( --get the most recent call per user (taking into account different campaign timescales)
select tca.UserId
, tca.CampaignId
, tca.[Date]
, case when DateAdd(Day,c.DaysSinceLastCall, tca.[Date]) > getutcdate() then 1 else 0 end LastCalledInWindow
, row_number() over (partition by tca.UserId order by case when DateAdd(Day,c.DaysSinceLastCall, tca.[Date]) > getutcdate() then 1 else 0 end desc, tca.[Date] desc) r
from dbo.TelemarketingCallAudits tca
inner join (
values (1, 60)
, (2, 70)
) c (CampaignId, DaysSinceLastCall)
on tca.CampaignId = c.CampaignId
) mrc
on mrc.UserId = u.UserId
and mrc.r = 1 --only accept the most recent call
and mrc.LastCalledInWindow = 0 --only include if they haven't been contacted in the last x days
I'm not comparing all rows here; but rather saw that you're interested in when the most recent call is; then you only care if that's in the X day window. There's a bit of additional complexity given the X days varies by campaign; so it's not the most recent call you care about so much as the most likely to fall within that window. To get around that, I sort each users' calls by those which are in the window first followed by those which aren't; then sort by most recent first within those 2 groups. This gives me the field r.
By filtering on r = 1 for each user, we only get the most recent call (adjusted for campaign windows). By filtering on LastCalledInWindow = 0 we exclude those who have been called within the campaign's window.
NB: I've used an inner query (aliased c) to hold the campaign ids and their corresponding windows. In reality you'd probably want a campaigns table holding that same information instead of coding inside the query itself.
Hopefully everything else is self-explanatory; but give me a nudge in the comments if you need any further information.
UPDATE
Just realised you'd also said "no calls is fine"... Here's a tweaked version to allow for scenarios where the person has not been called.
SQL Fiddle Example.
select *
from dbo.Users u
left outer join ( --get the most recent call per user (taking into account different campaign timescales)
select tca.UserId
, tca.CampaignId
, tca.[Date]
, case when DateAdd(Day,c.DaysSinceLastCall, tca.[Date]) > getutcdate() then 1 else 0 end LastCalledInWindow
, row_number() over (partition by tca.UserId order by case when DateAdd(Day,c.DaysSinceLastCall, tca.[Date]) > getutcdate() then 1 else 0 end desc, tca.[Date] desc) r
from dbo.TelemarketingCallAudits tca
inner join (
values (1, 60)
, (2, 70)
) c (CampaignId, DaysSinceLastCall)
on tca.CampaignId = c.CampaignId
) mrc
on mrc.UserId = u.UserId
where
(
mrc.r = 1 --only accept the most recent call
and mrc.LastCalledInWindow = 0 --only include if they haven't been contacted in the last x days
)
or mrc.r is null --no calls at all
Update: Including a default campaign offset
To include a default, you could do something like the code below (SQL Fiddle Example). Here, I've put each campaign's offset value in the Campaigns table, but created a default campaign with ID = -1 to handle anything for which there is no offset defined. I use a left join between the audit table and the campaigns table so that we get all records from the audit table, regardless of whether there's a campaign defined, then a cross join to get the default campaign. Finally, I use a coalesce to say "if the campaign isn't defined, use the default campaign".
select *
from dbo.Users u
left outer join ( --get the most recent call per user (taking into account different campaign timescales)
select tca.UserId
, tca.CampaignId
, tca.[Date]
, case when DateAdd(Day,coalesce(c.DaysSinceLastCall,dflt.DaysSinceLastCall), tca.[Date]) > getutcdate() then 1 else 0 end LastCalledInWindow
, row_number() over (partition by tca.UserId order by case when DateAdd(Day,coalesce(c.DaysSinceLastCall,dflt.DaysSinceLastCall), tca.[Date]) > getutcdate() then 1 else 0 end desc, tca.[Date] desc) r
from dbo.TelemarketingCallAudits tca
left outer join Campaigns c
on tca.CampaignId = c.CampaignId
cross join Campaigns dflt
where dflt.CampaignId = -1
) mrc
on mrc.UserId = u.UserId
where
(
mrc.r = 1 --only accept the most recent call
and mrc.LastCalledInWindow = 0 --only include if they haven't been contacted in the last x days
)
or mrc.r is null --no calls at all
That said, I'd recommend not using a default, but rather ensuring that every campaign has an offset defined. i.e. Presumably you already have a campaigns table; and since this offset value is defined per campaign, you can include a field in that table for holding this offset. Rather than leaving this as null for some records, you could set it to your default value; thus simplifying the logic / avoiding potential issues elsewhere where that value may subsequently be used.
You'd also asked about the order by clause. There is no order by 1/0; so I assume that's a typo. Rather the full statement is row_number() over (partition by tca.UserId order by case when DateAdd(Day,coalesce(c.DaysSinceLastCall,dflt.DaysSinceLastCall), tca.[Date]) > getutcdate() then 1 else 0 end desc, tca.[Date] desc) r.
The purpose of this piece is to find the "most important" call for each user. By "most important" I basically mean the most recent, since that's generally what we're after; though there's one caveat. If a user is part of 2 campaigns, one with an offset of 30 days and one with an offset of 60 days, they may have had 2 calls, one 32 days ago and one 38 days ago. Though the call from 32 days ago is more recent, if that's on the campaign with the 30 day offset it's outside the window, whilst the older call from 38 days ago may be on the campaign with an offset of 60 days, meaning that it's within the window, so is more of interest (i.e. this user has been called within a campaign window).
Given the above requirement, here's how this code meets it:
row_number() produces a number from 1, counting up, for each row in the (sub)query's results. The counter is reset to 1 for each partition
partition by tca.UserId says that we're partitioning by the user id; so for each user there will be 1 row for which row_number() returns 1, then for each additional row for that user there will be a consecutive number returned.
The order by part of this statement defines which of each users' rows gets #1, then how the numbers progress thereafter; i.e. the first row according to the order by gets number 1, the next number 2, etc.
case when DateAdd(Day,coalesce(c.DaysSinceLastCall,dflt.DaysSinceLastCall), tca.[Date]) > getutcdate() then 1 else 0 end returns 1 for calls within their campaign's window, and 0 for those outside of the window. Since we're ordering by this result in ascending order, that says that any records within their campaign's window should be returned before any outside of their campaign's window.
we then order by tca.[Date] desc; i.e. the more recent calls are returned before the later calls.
finally, we name the output of this row number as r and in the outer query filter on r = 1; meaning that for each user we only take one row, and that's the first row according to the order criteria above; i.e. if there's a row in its campaign's window we take that, after which it's whichever call was most recent (within those in the window if there were any; then outside that window if there weren't).
Take a look at the output of the subquery to get a better idea of exactly how this works: SQL Fiddle
I hope that explanation makes some sense / helps you to understand the code? Sadly I can't find a way to explain it more concisely than the code itself does; so if it doesn't make sense try playing with the code and seeing how that affects the output to see if that helps your understanding.

SQL: Selecting between date range

My query returns 1 value if I use the Max(SampleDateTime) or Min( ) on the Date/Time field I want, but it returns no values if I leave out the Max or Min. I want to return ALL the values, but I can't seem to figure this out.
I want all the Quality Samples between the Start and Stop times of a Production Run.
RunSamples:
Select Max([SampleDateTime])
FROM [QualitySamples] AS [GoodSamples]
WHERE [GoodSamples].[SampleDateTime] >= [ProductionRuns_tbl].[RunStartDate]
AND [GoodSamples].[SampleDateTime] <= [ProductionRuns_tbl].[RunEndDate]
ProductionRuns_tbl:
RunStartDate RunEndDate
1/1/2017 12 AM 1/5/17 12 AM
...
QualitySamples Tbl:
ID SampleDateTime
1 1/1/2017 2 am
2 1/1/2017 3 am
...
Here's the full SQL code:
SELECT ProductionRuns_tbl.RunName, ProductionRuns_tbl.RunStartDate,
ProductionRuns_tbl.RunEndDate,
(Select Max([SampleDateTime])
FROM [QualitySamples] AS [GoodSamples]
WHERE [GoodSamples].[SampleDateTime] >= [ProductionRuns_tbl].[RunStartDate]
AND [GoodSamples].[SampleDateTime] <= [ProductionRuns_tbl].[RunEndDate])
AS RunSamples
FROM ProductionRuns_tbl
WHERE (((ProductionRuns_tbl.RunName)=[Forms]![Home]![RunName]));
Try to use join instead:
SELECT ProductionRuns_tbl.RunName,
ProductionRuns_tbl.RunStartDate,
ProductionRuns_tbl.RunEndDate,
GoodSamples.SampleDateTime
FROM QualitySamples GoodSamples INNER JOIN ProductionRuns_tbl ON
GoodSamples.SampleDateTime >= ProductionRuns_tbl.RunStartDate AND
GoodSamples.SampleDateTime <= ProductionRuns_tbl.RunEndDate
WHERE ProductionRuns_tbl.RunName=[Forms]![Home]![RunName]
I'm taking a risk posting right now, because I had to try to read your mind on what you're trying to do (plus, I don't know if this will work in Access, but it will work in SQL server)
Since you want all the data, is this what you're looking for?
SELECT
ProductionRuns_tbl.RunName,
ProductionRuns_tbl.RunStartDate,
ProductionRuns_tbl.RunEndDate,
[QualitySamples].[SampleDateTime]
FROM
ProductionRuns_tbl
LEFT JOIN
[QualitySamples]
ON
[QualitySamples].[SampleDateTime] >= [ProductionRuns_tbl].[RunStartDate]
AND
[QualitySamples].[SampleDateTime] <= [ProductionRuns_tbl].[RunEndDate]
WHERE
(((ProductionRuns_tbl.RunName)=[Forms]![Home]![RunName]));
This should list the RunName, Start and End dates repeated for each individual SampleDateTime. Based on your more specific requirements, you can then refine the results from there.
Dont have WHERE, MAX or MIN. Just have the SELECT query.
Select [SampleDateTime]
FROM [QualitySamples] AS [GoodSamples]

Return overlapping date records in SQL

I used the following query to fetch the overlapping records in SQL:
SELECT QUOTE_ID,FUNCTION_ID,FUNCTION_DT,FUNC_SPACE_ID,FN_START_TIME,FN_END_TIME,DATE_AUTH_LEVEL
FROM R_13_ALL_RESERVED A
WHERE
A.FUNC_SPACE_ID = '401-ZFU-52'
AND A.FUNCTION_DT = TO_DATE('09/03/2015','MM/DD/YYYY')
AND EXISTS ( SELECT 'X'
FROM R_13_ALL_RESERVED B
WHERE A.PROPERTY = B.PROPERTY
AND A.FUNCTION_DT = B.FUNCTION_DT
AND A.FUNCTION_ID <> B.FUNCTION_ID
AND ( ( A.FN_START_TIME > B.FN_START_TIME
AND A.FN_START_TIME < B.FN_END_TIME)
OR ( B.FN_START_TIME > A.FN_START_TIME
AND B.FN_START_TIME < A.FN_END_TIME)
OR ( A.FN_START_TIME = B.FN_START_TIME
AND A.FN_END_TIME = B.FN_END_TIME)
)
)
But eventhough the dates are not overlapping it still returns the records as overlapping.
I am missing some thing here?
Also if the date records overlap, I need to compare the count of function_id records with DATE_AUTH_LEVEL, if 2 function_id records overlap and the count of function_id would be 2 and DATE_AUTH_LEVEL is 1, such record should in the result set.
Please find the data set in SQLFiddle
http://sqlfiddle.com/#!9/95874/1
Desired Output : The SQL should return overlapping FN_START_TIME and FN_END_TIME for a function_space_id and it's function_dt
In the provided example, row 5 and 6 overlap for the function space id '401-ZFU-12' and function_dt 'August, 15 2015' and all others are not overlapping
The simplest predicate (where clause condition) for detecting the overlap of two ranges is to compare the start of the first range with the end of the 2nd range, and the start of the 2nd range with the end of the first range:
WHERE R1.Start_Date <= R2.End_Date
AND R2.Start_Date <= R1.End_Date
As you can see each of the two inequalities looks at a start and end value from separate records (R1 and R2 and then R2 and R1 respectively) all that remains is to add the conditions that will correlate the records, and also ensure that you aren't comparing a row to itself So if you want to find all Common_IDs that have Distinct_IDs with over lapping date ranges:
select *
from Your_Table R1
where exists (select 1 from Your_Table R2
where R1.Common_ID = R2.Common_ID
and R1.Distinct_ID <> R2.Distinct_ID
and R1.Start_Date <= R2.End_Date
and R2.Start_Date <= R1.End_Date)
If there is no Distinct_ID to use, you can use R1.rowid <> R2.rowid in place of R1.Distinct_ID <> R2.Distinct_ID
Here is an approach to troubleshooting the issue on your end.
My first suspicion is that the results of your exists clause are too broad and thus returning rows for every record matching in the outer clause unexpectedly. Likely there are rows that do not fall on the desired date or spaceid that share one component of their interval with your inner criteria.
Inspect the results of the inner select statement (the one within the exists clause) for an example row, exchanging all the 'A' aliased values with actual values from one of the rows returned you did not expect to receive.
Additionally, you can inspect what I think would be a semi join in the execution profile to see what the join criteria are. If you expect it to be filtered by a constant for 'FUNC_SPACE_ID' of '401-ZFU-52', you will discover that it is not.

Need some help in creating a query in SQL?

I have 2 following tables :
Ticket(ID, Problem, Status,Priority, LoggedTime,CustomerID*, ProductID*);
TicketUpdate(ID,Message, UpdateTime,TickedID*,StaffID*);
Here is a question to be answered:
Close all support tickets which have not been updated for at least 24 hours. This will be records that have received at least one update from a staff member and no further updates from the customer (or staff member) for at least 24 hours.
My query is:
UPDATE Ticket SET Status = 'closed' FROM TicketUpdate
WHERE(LoggedTime - MAX(UpdateTime))> 24
AND Ticket.ID = TicketUpdate.TicketID;
When I run this query on mysql it says that "<" does not exist.
Can you tell me is my query right to for calculating the records which have not been updated for at least 24 hours and if it is right what should I do use instead of "<"?
... records that have received at least one update from a staff member and
no further updates from the customer (or staff member) for at least 24
hours.
So, effectively, the last update must have been done by a staff member and be older than 24 hours. That covers it all.
(BTW, you have a typo: TickedID -> I use ticketid here.)
UPDATE ticket t
SET status = 'closed'
FROM (
SELECT DISTINCT ON (1)
ticketid
,first_value(updatetime) OVER w AS last_up
,first_value(staffid) OVER w AS staffid
FROM ticketupdate
-- you could join back to ticket here and eliminate 'closed' ids right away
WINDOW w AS (PARTITION BY ticketid ORDER BY updateTime DESC)
) tu
WHERE tu.ticketid = t.id
AND tu.last_up < (now()::timestamp - interval '24 hours')
AND tu.staffid > 1 -- whatever signifies "update from a staff member"
AND t.status IS DISTINCT FROM 'closed'; -- to avoid pointless updates
Note that PostgreSQL folds identifiers to lower case if not double-quoted. I advise to stay away from mixed case identifiers to begin with.
If you are working with postgreSQL then this should work
UPDATE Ticket SET Status = 'closed' FROM TicketUpdate
WHERE abs(extract(epoch from LoggedTime - MAX(UpdateTime))) >24
AND Ticket.ID = TicketUpdate.TicketID;