Excluding events within date-range while joining tables in SSMS - ssms

SQL newbie here. Using Microsoft SQL management studio. Having trouble excluding records that are outside of a particular date range while joining two tables. Please Help!
I have two tables - Event and DateRange.
Event
CustomerID ---------------- EventDate
1 ------------------------- 1/2/14
1 -------------------------- 2/5/15
1 -------------------------- 6/1/13
DateRange
CustomerID ---------------- StartDate ----------- EndDate
1 -------------------------- 1/1/14 -------------- 1/3/14
1 -------------------------- 2/1/15 -------------- 2/3/15
1 -------------------------- 5/30/13 ------------- 6/2/13
I want to join those two tables using CustomerID but I only want to ouput the date that was outside of the Startdate and EndDate date range (row 2 in Event table - 2/5/15).
I tried the following query but what keeps happening is that it kept giving me all the dates back in multiple because for each event date there were two StartDate and EndDate where it didn't fit into.
SQL Query
SELECT
E.CustomerID
,E.EventDate
,D.StartDate
,D.EndDate
FROM
Event AS E
INNER JOIN DateRange AS D
ON E.CustomerID = D.CustomerID
WHERE
(E.EventDate NOT BETWEEN D.StartDate and D.EndDate)
ORDER BY E.CustomerID
The output I got was something like this...
CustomerID ----- EventDate ----- StartDate ----- EndDate
1 -------------- 1/2/14 -------- 2/1/15 -------- 2/3/15
1 -------------- 1/2/14 -------- 5/30/13-------- 6/2/15
1 -------------- 2/5/15 -------- 1/1/14 -------- 1/3/14
1 -------------- 2/5/15 -------- 5/30/13 ------- 6/2/13
1 -------------- 6/1/13 -------- 1/1/14 -------- 1/3/14
1 -------------- 6/1/13 -------- 2/1/15 -------- 2/3/15
It's kind of doing a cross join because all the customerIDs are the same. And I need to keep the customerID because that's the only way to join those two tables. So how can I join these two table to only get the output that I want. Even if I do SELECT DISTINCT just the E.EventDate column by itself it'll still give me all three EventDates.
I pretty much need a count of how many event dates there are that don't fall into the startdate and enddate.
Thanks.

The problem is that you don't have a unique ID on your Event table to exclude your result set from. Since you're joining by CustomerID, it's giving you every row that doesn't meet your criteria. You might want to try something like this.
CREATE TABLE Event (ID INT NOT NULL IDENTITY PRIMARY KEY, CustomerID INT, EventDate DATETIME)
SELECT DISTINCT
E.EventDate
,E.CustomerID
FROM
Event AS E
INNER JOIN DateRange AS D
ON E.CustomerID = D.CustomerID
WHERE
E.ID NOT IN (
SELECT E.ID FROM Event AS E
INNER JOIN DateRange AS D
ON E.CustomerID = D.CustomerID
WHERE E.EventDate BETWEEN D.StartDate and D.EndDate
)
ORDER BY E.CustomerID
As you can see from above, I've created an Identity ID for the Event table. Now you can use a sub query to exclude the "events" from the result set that have dates that fall within a date range in the DateRange table. Which will give you what you're looking for. Hope that helps.
EventDate ------------- CustomerID
2015-02-05 00:00:00.000 1

Related

Count values separately until certain amount of duplicates SQL

I need a Statement that selects all patients and the amount of their appointments and when there are 3 or more appointments that are taking place on the same date they should be counted as one appointment
That is what my Statement looks so far
SELECT PATSuchname, Count(DISTINCT AKTDATUM) AS AKTAnz
FROM tblAktivitaeten
LEFT OUTER JOIN tblPatienten ON (tblPatienten.PATID=tblAktivitaeten.PATID)
WHERE (AKTDeleted<>'J' OR AKTDeleted IS Null)
GROUP BY PATSuchname
ORDER BY AKTAnz DESC
The result should look like this
PATSuchname Appointments
----------------------------------------
Joey Patner 13
Billy Jean 15
Example Name 13
As you can see Joey Patner has 13 Appointments, in the real table though he has 15 appointments but three of them have the same Date and because of that they are only counted as 1
So how can i write a Statement that does exactly that?
(I am new to Stack Overflow, sorry if the format I use is wrong and tell me if it is.
In the table it looks like this.
tblPatienten
----------
PATSuchname PATID
------------------------
Joey Patner 1
Billy Jean 2
Example Name 3
tblAktivitaeten
----------
AKTDatum PATID AKTID
-----------------------------------------
08.02.2021 1 1000 ----
08.02.2021 1 1001 ---- So these 3 should counted as 1
08.02.2021 1 1002 ----
09.05.2021 1 1003
09.07.2021 2 1004 -- these 2 shouldn't be counted as 1
09.07.2021 2 1005 --
Two GROUP BY should do it:
SELECT
x.PATID, PATSuchname, SUM(ApptCount)
FROM (
SELECT
PATID, AKTDatum, CASE WHEN COUNT(*) < 3 THEN COUNT(*) ELSE 1 END AS ApptCount
FROM tblAktivitaeten
GROUP BY
PATID, AKTDatum
) AS x
LEFT JOIN tblPatienten ON tblPatienten.PATID = x.PATID
GROUP BY
x.PATID, PATSuchname

Get Status value based on timesheet date

I have an Assets table that has an audit log of when a particular status of that Asset changes Status... so look's something similar to this
AssetId CapexStatus Date
------- ----------- -----
AM706 1 2017-02-03
AM706 0 2017-02-07
AM706 1 2017-02-10
I then have a timesheet table which has the AssetID and a transaction date on it. I basically want to pull the Capex Status out of the AssetLog table based on the AssetId and the current Capex Status at the time of the transaction date. eg. If the transaction date is 8th Feb then the Capex Status should be "0".
Timesheet table
TimesheetId AssetId TimesheetDate
----------- ------- -------------
1 AM706 2017-02-01
2 AM706 2017-02-08
3 AM706 2017-02-12
I think something like this might do it:
select
t.*,
a.CapexStatus
from
TimeSheet t
outer apply (Select top 1 * from AssetLog al
where
al.AssetID = t.AssetID
and al.Date < t.TimesheetDate
order by al.Date desc) a
create view vwMaxCapex
as
select top 1 capexStatus, date, AssetId from AssetsLog
order by date asc
go
select a.AssetId, a.timesheetDate,
(select capexstatus
from vwMaxCapex
where date<=a.timesheetDate and assetId=a.AssetId) capex
from timetable a

Comparing a Date Range to a set of Date Ranges SQL

Ok, so this particular use case is giving me quite a bit of headache.
What i am trying to do is figure out if any StartDate and EndDates in a set is overlapping with a set of Start and EndDates, if they are, the overlapping rows must be returned.
DateRange Table -- contains all DateRanges
DateRangeId StartDate EndDate CustomerJobQuery_Id WorkWish_Id
----------- --------------------------- --------------------------- ------------------- -----------
1 2014-03-31 00:00:00.0000000 2014-08-18 00:00:00.0000000 NULL 1
2 2014-08-25 00:00:00.0000000 2014-09-15 00:00:00.0000000 NULL 1
3 2013-08-24 00:00:00.0000000 2014-09-25 00:00:00.0000000 1 NULL
Workwish Table
Id Candidate_Id
----------- ------------
1 5
CustomerJobQuery Table
Id Customer_CustomerId
----------- -------------------
1 2
this is my DateRange table.. what my StoredProcedure accepts is a CandidateId; i wish to find the Candidate Id's WorkWishes , Match the DateRanges of the WorkWish with ALL of the CustomerJobQuery DateRanges and return the CustomerJobQueries that had overlapping dates.
I figured out how to do the check if a specific DateRange overlaps with another DateRange.. but i am stumped as to how to do this comparison on a set.
Any help is much appreciated!
Try this query:
SELECT c.*
FROM CustomerJobQuery c
WHERE EXISTS (
SELECT 1
FROM Workwish w
INNER JOIN DateRange d1 ON d1.CustomerJobQuery_Id = c.Id
INNER JOIN DateRange d2 ON d2.WorkWish_Id = w.Id
WHERE w.Candidate_Id = 5 -- CandidateId in procedure
-- overlap check:
AND d1.StartDate <= d2.EndDate
AND d2.StartDate <= d1.EndDate
)
Test it here (SQL Fiddle): http://sqlfiddle.com/#!3/10253/3

SQL: Identify distinct blocks of treatment over multiple start and end date ranges for each member

Objective: Identify distinct episodes of continuous treatment for each member in a table. Each member has a diagnosis and a service date, and an episode is defined as all services where the time between each consecutive service is less than some number (let's say 90 days for this example). The query will need to loop through each row and calculate the difference between dates, and return the first and last date associated with each episode. The goal is to group results by member and episode start/end date.
A very similar question has been asked before, and was somewhat helpful. The problem is that in customizing the code, the returned tables are excluding first and last records. I'm not sure how to proceed.
My data currently looks like this:
MemberCode Diagnosis ServiceDate
1001 ----- ABC ----- 2010-02-04
1001 ----- ABC ----- 2010-03-20
1001 ----- ABC ----- 2010-04-18
1001 ----- ABC ----- 2010-05-22
1001 ----- ABC ----- 2010-09-26
1001 ----- ABC ----- 2010-10-11
1001 ----- ABC ----- 2010-10-19
2002 ----- XYZ ----- 2010-07-10
2002 ----- XYZ ----- 2010-07-21
2002 ----- XYZ ----- 2010-11-08
2002 ----- ABC ----- 2010-06-03
2002 ----- ABC ----- 2010-08-13
In the above data, the first record for Member 1001 is 2010-02-04, and there is not a difference of more than 90 days between consecutive services until 2010-09-26 (the date at which a new episode starts). So Member 1001 has two distinct episodes: (1) Diagnosis ABC, which goes from 2010-02-04 to 2010-05-22, and (2) Diagnosis ABC, which goes from 2010-09-26 to 2010-10-19.
Similarly, Member 2002 has three distinct episodes: (1) Diagnosis XYZ, which goes from 2010-07-10 to 2010-07-21, (2) Diagnosis XYZ, which begins and ends on 2010-11-08, and (3) Diagnosis ABC, which goes from 2010-06-03 to 2010-08-13.
Desired output:
MemberCode Diagnosis EpisodeStartDate EpisodeEndDate
1001 ----- ABC ----- 2010-02-04 ----- 2010-05-22
1001 ----- ABC ----- 2010-09-26 ----- 2010-10-19
2002 ----- XYZ ----- 2010-07-10 ----- 2010-07-21
2002 ----- XYZ ----- 2010-11-08 ----- 2010-11-08
2002 ----- ABC ----- 2010-06-03 ----- 2010-08-13
I've been working on this query for too long, and still can't get exactly what I need. Any help would be appreciated. Thanks in advance!
SQL Server 2012 has the lag() and cumulative sum functions, which makes it easier to write such a query. The idea is to find the first in each sequence. Then take the cumulative sum of the first flag to identify each group. Here is the code:
select MemberId, Diagnosis, min(ServiceDate) as EpisodeStartDate,
max(ServiceStartDate) as EpisodeEndDate
from (select t.*, sum(ServiceStartFlag) over (partition by MemberId, Diagnosis order by ServiceDate) as grp
from (select t.*,
(case when datediff(day,
lag(ServiceDate) over (partition by MemberId, Diagnosis
order by ServiceDate),
ServiceDate) < 90
then 0
else 1 -- handles both NULL and >= 90
end) as ServiceStartFlag
from table t
) t
group by grp, MemberId, Diagnosis;
You can do this in earlier versions of SQL Server but the code is more cumbersome.
For versions of SQL Server prior to 2012, here's some code snippets that should work.
First, you'll need a temp table (as opposed to a CTE, as the lookup of the edge event will fire the newid() function again, rather than retriving the value for that row)
DECLARE #Edges TABLE (MemberCode INT, Diagnosis VARCHAR(3), ServiceDate DATE, GroupID VARCHAR(40))
INSERT INTO #Edges
SELECT *
FROM Treatments E
CROSS APPLY (
SELECT
CASE
WHEN EXISTS (
SELECT TOP 1 E2.ServiceDate
FROM Treatments E2
WHERE E.MemberCode = E2.MemberCode
AND E.Diagnosis = E2.Diagnosis
AND E.ServiceDate > E2.ServiceDate
AND DATEDIFF(dd,E2.ServiceDate,E.ServiceDate) BETWEEN 1 AND 90
ORDER BY E2.ServiceDate DESC
) THEN 'Group'
ELSE CAST(NEWID() AS VARCHAR(40))
END AS GroupID
) z
The EXISTS operator contains a query that looks into the past for a date between 1 and 90 days ago. Once the Edge cases are gathered, this query will provide the results you posted as desired from the test data you posted.
SELECT MemberCode, Diagnosis, MIN(ServiceDate) AS StartDate, MAX(ServiceDate) AS EndDate
FROM (
SELECT
MemberCode
, Diagnosis
, ServiceDate
, CASE GroupID
WHEN 'Group' THEN (
SELECT TOP 1 GroupID
FROM #Edges E2
WHERE E.MemberCode = E2.MemberCode
AND E.Diagnosis = E2.Diagnosis
AND E.ServiceDate > E2.ServiceDate
AND GroupID != 'Group'
ORDER BY ServiceDate DESC
)
ELSE GroupID END AS GroupID
FROM #Edges E
) Z
GROUP BY MemberCode, Diagnosis, GroupID
ORDER BY MemberCode, Diagnosis, MIN(ServiceDate)
Like Gordon said, more cumbersome, but it can be done if your server is not SQL 2012 or greater.

Creating a view with sums from multiple tables (Oracle SQL)

I'm trying to create a view that gets the sums of a couple of different rows in various tables. (I'm not sure how to explain this properly)
Here is how my tables are set out:
Visitors:
VISITORID FNAME LNAME PHONE HOTELID
---------- --------------- --------------- --------------- ----------
23 Bella Morgan 0394110625 3
Bookings:
BOOKINGID HOTELID ROOMNO BOOKINGDATE BOOKINGDAYS BEDANDBREA VISITORID
---------- ---------- ---------- ------------------- ----------- ---------- ----------
28 3 509 28-04-2013 00:00:00 3 Yes 23
Rooms:
ROOMNO HOTELID ROOMTYPE PRICE
---------- ---------- ------------------------- ----------
509 3 Double 700
Services:
SERVICEID SERVICENAME COST HOTELID
---------- -------------------------------------------------- ---------- ----------
1-CLTH Cloth Cleaning 14.95 1
2-RMSV Room Service 9.95 2
Booking_services:
SERVICEID BOOKINGID
---------- ----------
2-RMSV 32
1-CLTH 32
I want to create a view called bills that gives me the total of room cost and cost of all services.
To get the room price, the sum is rooms.price*bookings.bookingdays.
For the services, it's the sum of all the rows in the services table that match the SERVICEID in booking_services for the matching bookingID.
Currently there are more rows in all of the tables than I've shown (so it doesn't take up too much space on here) and I have a query but it's only showing 2 of the visitors that i'd like the total for. I know it's because of line 5, but I'm not sure how I can get it to calculate that as well as those who do not have a row in booking_services.
Here is that query:
CREATE VIEW bills AS
SELECT v.fname, SUM((r.price*b.bookingdays)+s.cost) AS total
FROM visitors v, rooms r, bookings b, services s, booking_services bs
WHERE v.visitorid = b.visitorid
AND
s.serviceid in(select bs.serviceid from booking_services where bs.bookingid = b.bookingid)
AND
b.roomno = r.roomno
GROUP BY v.fname;
Any help to get what I'm after (if this makes any sense) would be appreciated.
Here is the SQLFiddel Demo
You can try below query for your view:
Select v.fname, sum((r.price*b.bookingdays)+nvl(bso.cost,0))
From visitors v
Join bookings b
on v.visitorid = b.visitorid
Join rooms r
on b.roomno = r.roomno
left outer join (select bs.BOOKINGID,sum(cost) as cost
from booking_services bs
Join services s
on s.SERVICEID = bs.SERVICEID
group by bs.BOOKINGID) bso
on bso.BOOKINGID = b.BOOKINGID
GROUP BY v.fname;