How can I write correct query? - sql

WITH Encashment AS (
SELECT T.MachineId, T.Amount, CAST(Occured AS DATETIME) AS Occured
FROM (VALUES
(1, 101, '2017-10-20 09:36:40.057')
,(1, 203, '2017-10-14 12:36:30.081')
,(1, 400, '2017-10-11 04:17:38.023')
) AS T(MachineId, Amount, Occured)
), MoneyAccepted AS (
SELECT T.MachineId, T.Amount, CAST(Occured AS DATETIME) AS Occured
FROM (VALUES
(1, 1, '2017-10-15 09:36:40.057')
,(1, 100, '2017-10-16 12:36:30.081')
,(1, 100, '2017-10-12 16:17:38.023')
,(1, 1, '2017-10-13 09:37:47.057')
,(1, 1, '2017-10-13 09:37:47.057')
,(1, 1, '2017-10-12 15:37:47.057')
,(1, 100, '2017-09-15 12:37:31.081')
,(1, 100, '2017-09-15 16:37:31.081')
,(1, 100, '2017-09-16 13:37:31.081')
,(1, 100, '2017-09-17 13:37:31.081')
) AS T(MachineId, Amount, Occured)
)
I can get Amount among two encashment.(Select Amount from Encashment).
But, I want to get amount from MoneyAccepted for every Encashment.
For example: Encashment happened in 20-10-2017,till this dateTime accepted 101(100(2017-10-16 12:36:30.081)+1(2017-10-15 09:36:40.057)) money.
How can I get that?
Thanks in advance!

I think what you are looking for is:
DECLARE #Encashment AS TABLE (MachineID INT, Amount INT, Occured DATETIME2)
DECLARE #MoneyAccepted AS TABLE (MachineID INT, Amount INT, Occured DATETIME2)
INSERT #Encashment (MachineID, Amount, Occured)
VALUES (1, 101, '20171020 09:36:40.057')
, (1, 203, '20171014 12:36:30.081')
, (1, 400, '20171011 04:17:38.023')
INSERT #MoneyAccepted (MachineID, Amount, Occured)
VALUES (1, 1, '20171015 09:36:40.057')
, (1, 100, '20171016 12:36:30.081')
, (1, 100, '20171012 16:17:38.023')
, (1, 100, '20171014 09:17:38.023')
, (1, 1, '20171013 09:37:47.057')
, (1, 1, '20171013 09:37:47.057')
, (1, 1, '20171012 15:37:31.081')
SELECT E.Occured AS Encashment_Occured
, SUM(MA.Amount) AS SUM_Amount
FROM #MoneyAccepted AS MA
INNER JOIN (
SELECT MachineID
, Amount
, Occured
, LAG(Occured) OVER(PARTITION BY MachineID ORDER BY Occured) AS Previous_Occured
FROM #Encashment
) AS E
ON E.MachineID = MA.MachineID
AND E.Occured > MA.Occured
AND E.Previous_Occured <= MA.Occured
GROUP BY E.Occured
Result:
+-----------------------------+------------+
| Encashment_Occured | SUM_Amount |
+-----------------------------+------------+
| 2017-10-14 12:36:30.0810000 | 203 |
| 2017-10-20 09:36:40.0570000 | 101 |
+-----------------------------+------------+
This uses LAG, which was introduced in sql server 2012, in order to get the range of applicable dates in a single row.

Please edit your question, remove html and use plain text for sample data.
I think you could use CROSS APPLY.
Try this:
WITH Encashment AS (
SELECT T.MachineId, T.Amount, CAST(Occured AS DATETIME) AS Occured
FROM (VALUES
(1, 101, '2017-10-20 09:36:40.057')
,(1, 203, '2017-10-14 12:36:30.081')
,(1, 400, '2017-10-11 04:17:38.023')
) AS T(MachineId, Amount, Occured)
), MoneyAccepted AS (
SELECT T.MachineId, T.Amount, CAST(Occured AS DATETIME) AS Occured
FROM (VALUES
(1, 1, '2017-10-15 09:36:40.057')
,(1, 100, '2017-10-16 12:36:30.081')
,(1, 100, '2017-10-12 16:17:38.023')
,(1, 1, '2017-10-13 09:37:47.057')
,(1, 1, '2017-10-13 09:37:47.057')
,(1, 1, '2017-10-12 15:37:47.057')
,(1, 100, '2017-09-15 12:37:31.081')
,(1, 100, '2017-09-15 16:37:31.081')
,(1, 100, '2017-09-16 13:37:31.081')
,(1, 100, '2017-09-17 13:37:31.081')
) AS T(MachineId, Amount, Occured)
)
SELECT M.*, EN.*
FROM MoneyAccepted AS M
CROSS APPLY (
SELECT TOP (1) E.* FROM Encashment AS E
WHERE E.MachineId = M.MachineId AND E.Occured > M.Occured
ORDER BY E.Occured ASC
) AS EN

Related

I want to find the date intervals at which the employee comes on a regular basis

Imagine a employee who works in a company whos having a contract to work on a specific task, he comes in and goes on start and end date respectively. I want to get the interval at which the employee comes to office without any absence.
Example Data:
DECLARE #TimeClock TABLE (PunchID INT IDENTITY, EmployeeID INT, PunchinDate DATE)
INSERT INTO #TimeClock (EmployeeID, PunchInDate) VALUES
(1, '2020-01-01'), (1, '2020-01-02'), (1, '2020-01-03'), (1, '2020-01-04'),
(1, '2020-01-05'), (1, '2020-01-06'), (1, '2020-01-07'), (1, '2020-01-08'),
(1, '2020-01-09'), (1, '2020-01-10'), (1, '2020-01-11'), (1, '2020-01-12'),
(1, '2020-01-13'), (1, '2020-01-14'), (1, '2020-01-16'),
(1, '2020-01-17'), (1, '2020-01-18'), (1, '2020-01-19'), (1, '2020-01-20'),
(1, '2020-01-21'), (1, '2020-01-22'), (1, '2020-01-23'), (1, '2020-01-24'),
(1, '2020-01-25'), (1, '2020-01-26'), (1, '2020-01-27'), (1, '2020-01-28'),
(1, '2020-01-29'), (1, '2020-01-30'), (1, '2020-01-31'),
(1, '2020-02-01'), (1, '2020-02-02'), (1, '2020-02-03'), (1, '2020-02-04'),
(1, '2020-02-05'), (1, '2020-02-06'), (1, '2020-02-07'), (1, '2020-02-08'),
(1, '2020-02-09'), (1, '2020-02-10'), (1, '2020-02-12'),
(1, '2020-02-13'), (1, '2020-02-14'), (1, '2020-02-15'), (1, '2020-02-16');
--the output shall look like this '2020-01-01 to 2020-02-10' as this is the interval at which the employee comes without any leave
SELECT 1 AS ID, FORMAT( getdate(), '2020-01-01') as START_DATE, FORMAT( getdate(), '2020-01-10') as END_DATE union all
SELECT 1 AS ID, FORMAT( getdate(), '2020-01-11') as START_DATE, FORMAT( getdate(), '2020-01-15') as END_DATE union all
SELECT 1 AS ID, FORMAT( getdate(), '2020-01-21') as START_DATE, FORMAT( getdate(), '2020-01-31') as END_DATE union all
SELECT 1 AS ID, FORMAT( getdate(), '2020-02-01') as START_DATE, FORMAT( getdate(), '2020-02-10') as END_DATE
--the output shall look like this '2020-01-01 to 2020-01-15' and '2020 01-21 to 2020-02-10'as these are the intervals at which the employee comes without any leave
Using the example data provided we can query the table like this:
;WITH iterate AS (
SELECT *, DATEADD(DAY,1,PunchinDate) AS NextDate
FROM #TimeClock
), base AS (
SELECT *
FROM (
SELECT *, CASE WHEN DATEADD(DAY,-1,PunchInDate) = LAG(PunchinDate,1) OVER (PARTITION BY EmployeeID ORDER BY PunchinDate) THEN PunchInDate END AS s
FROM iterate
) a
WHERE s IS NULL
), rCTE AS (
SELECT EmployeeID, PunchInDate AS StartDate, PunchInDate AS EndDate, NextDate
FROM base
UNION ALL
SELECT a.EmployeeID, a.StartDate, r.PunchInDate, r.NextDate
FROM rCTE a
INNER JOIN iterate r
ON a.NextDate = r.PunchinDate
AND a.EmployeeID = r.EmployeeID
)
SELECT EmployeeID, StartDate, MAX(EndDate) AS EndDate, DATEDIFF(DAY,StartDate,MAX(EndDate)) AS Streak
FROM rCTE
GROUP BY rCTE.EmployeeID, rCTE.StartDate
This is known as a recursive common table expression, and allows us to compare values between related rows. In this case we're looking for rows where they follow a streak, and we want o re-start that streak anytime we encounter a break. We're using a windowed function called LAG to look back a row to the previous value, and compare it to the one we have now. If it's not yesterday, then we start a new streak.
EmployeeID StartDate EndDate Streak
------------------------------------------
1 2020-01-01 2020-01-15 14
1 2020-01-17 2020-02-10 24
1 2020-02-12 2020-02-16 4

TSQL - Select Latest Date by 2 Type

I need select data in SQL Server.
I have a Data example like this
DECLARE #tblA TABLE (ID int, SubId int, Createdate DateTime)
INSERT INTO #tblA VALUES (1, 1, '10/21/2020')
, (2, 1, '10/21/2020')
, (3, 1, '10/27/2020')
, (4, 2, '10/21/2020')
, (5, 2, '10/21/2020')
, (6, 1, '10/21/2020')
, (7, 2, '10/23/2020')
, (8, 2, '10/22/2020')
, (9, 1, '10/25/2020')
, (10, 3, '10/21/2020')
I want to get 4 records (in table will have a lot of SubId)
2 records latest date by SubId = 1
2 records latest date by SubId = 2
Following example, I expect Select with like table Output
DECLARE #tblOutput Table (ID int, SubId int, Createdate DateTime)
INSERT INTO #tblOutput VALUES (3, 1, '10/27/2020')
, (9, 1, '10/25/2020')
, (7, 2, '10/23/2020')
, (8, 2, '10/22/2020')
"tooltip"
I try with Union but It's only Order after Union. It's not the result I want.
Please, Help me to select this.
Thank you.
You can use the Row_Number() function as shown below:
Create table tblA (ID int, SubId int, Createdate DateTime)
Insert Into tblA values (1, 1, '10/21/2020')
, (2, 1, '10/21/2020')
, (3, 1, '10/27/2020')
, (4, 2, '10/21/2020')
, (5, 2, '10/21/2020')
, (6, 1, '10/21/2020')
, (7, 2, '10/23/2020')
, (8, 2, '10/22/2020')
, (9, 1, '10/25/2020')
, (10, 3, '10/21/2020')
select Id
, SubId
, Createdate
from(
select *
, Row_Number() Over (Partition By SubId order by Createdate desc) as RNo
from tblA
)temp where RNo <= 2 and SubId in (1, 2)
Order by SubId
Live db<>fiddle demo

How to join two tables with Date Ranges in SQL Server?

I have 2 tables:
Appointments
Patient Visit.
I would like to join the two tables on the basis of dates. though the dates are completely different in each table. What I would like is to join the vital date with appt date that comes right after it.
Like I want appt-date '22-07-2020' to be joined with '23-07-2020' and '22-09-2020' with '25-09-2020'.
Simple joining on patient id is not enough.
Solution should not contain cursors.
Here's an example you can try ...
IF OBJECT_ID('tempdb..#Appointments') IS NOT NULL DROP TABLE #Appointments
IF OBJECT_ID('tempdb..#Visits') IS NOT NULL DROP TABLE #Visits
SELECT * INTO #Appointments FROM (VALUES
(6, 'Fred', CAST('2020-07-22' as datetime)),
(6, 'Fred', '2020-09-22'),
(6, 'Fred', '2015-05-14'),
(7, 'Barney', '2020-07-25'),
(8, 'Wilma', '2020-08-15'),
(9, 'Betty', '2020-09-10')
) S(patient_id, patient_name, appt_date)
SELECT * INTO #Visits FROM (VALUES
(6, CAST('2020-07-23' as datetime), 'BP: 51, HR: 56', 1),
(6, '2020-09-25', 'BP: 52, HR: 56', 2),
(6, '2015-05-22', 'BP: 53, HR: 56', 3),
(7, '2020-07-27', 'BP: 54, HR: 56', 4),
(7, '2020-08-22', 'BP: 55, HR: 56', 5),
(8, '2020-08-22', 'BP: 56, HR: 56', 6),
(8, '2020-09-02', 'BP: 57, HR: 56', 7),
(8, '2020-10-31', 'BP: 58, HR: 56', 8),
(9, '2020-09-11', 'BP: 59, HR: 56', 9),
(9, '2020-10-21', 'BP: 60, HR: 56', 10)
) S(patient_id, vitals_date, vitals_info, visit_id)
SELECT * FROM #Appointments
SELECT * FROM #Visits
SELECT
A.*,
V.*
FROM
#Appointments A
LEFT JOIN #Visits V ON
V.visit_id = (
SELECT TOP 1 V2.visit_id FROM #Visits V2 WHERE V2.patient_id = A.patient_id AND V2.vitals_date >= A.appt_date ORDER BY V2.vitals_date
)
-- updated to change date format
You can select by getting the top(1) from Visits table where vitalDate >= apptDate. ie:
select a.PatientId, a.ApptDate,
v.VitalDate, v.vitalInfo, v.VitalId
from appointments a
outer apply (select top(1) VitalDate, vitalInfo, VitalId
from Visits v
where a.PatientId = v.PatientId
and v.VitalDate >= a.apptDate
order by VitalDate) v(VitalDate, vitalInfo, VitalId);
EDIT: Later I saw you are saying you could have duplicates. In reality that is not something that happens with "appointed" patient visits. Anyway, for covering the duplicates I am adding another one:
select a.PatientId, a.ApptDate,
v.VitalDate, v.vitalInfo, v.VitalId
from appointments a
outer apply (select VitalDate, vitalInfo, VitalId
from Visits v1
where a.PatientId = v1.PatientId
and v1.VitalDate = (select min(Vitaldate) from
Visits v2
where v1.patientId = v2.patientId
and v2.VitalDate >= a.apptDate)) v(VitalDate, vitalInfo, VitalId);
PS: Here is DBFiddle demo

Constraints w/ Recursive Postgres Query

I'm looking to skip a certain city as I traverse my data. Currently, this query works to find all available flights from SLC to LA, including trips with layovers. You'll see this in the picture below.
However, I want to be able to exclude certain cities in a flight plan. For example, if Montreal is a stop between SLC and LA, that trip wouldn't be considered.
I've tried putting various things in the WHERE clauses, but to no avail. Any other suggestions? Sample data an queries are given below.
WITH RECURSIVE segs AS (
SELECT f0.flight_num::text as flight
, src_city, dest_city
, dep_time AS departure
, arr_time AS arrival
, airfare, mileage
, 1 as hops
, (arr_time - dep_time)::interval AS total_time
, '00:00'::interval as waiting_time
FROM flight f0
WHERE src_city = 'SLC' -- <SRC_CITY>
UNION ALL
SELECT s.flight || '-->' || f1.flight_num::text as flight
, s.src_city, f1.dest_city
, s.departure AS departure
, f1.arr_time AS arrival
, s.airfare + f1.airfare as airfare
, s.mileage + f1.mileage as mileage
, s.hops + 1 AS hops
, s.total_time + (f1.arr_time - f1.dep_time)::interval AS total_time
, s.waiting_time + (f1.dep_time - s.arrival)::interval AS waiting_time
FROM segs s
JOIN flight f1
ON f1.src_city = s.dest_city
AND f1.dep_time > s.arrival -- you can't leave until you are there
)
SELECT *
FROM segs
WHERE dest_city = 'LA' -- <DEST_CITY>
ORDER BY airfare desc
;
create table flight
( flight_num BIGSERIAL PRIMARY KEY
, src_city varchar
, dest_city varchar
, dep_time TIME
, arr_time TIME
, airfare INTEGER
, mileage INTEGER
);
insert into flight VALUES
(101, 'Montreal', 'NY', '05:30', '06:45', 180, 170),
(102, 'Montreal', 'Washington', '01:00', '02:35', 100, 180),
(103, 'NY', 'Chicago', '08:00', '10:00', 150, 300),
(105, 'Washington', 'KansasCity', '06:00', '08:45', 200, 600),
(106, 'Washington', 'NY', '12:00', '13:30', 50, 80),
(107, 'Chicago', 'SLC', '11:00', '14:30', 220, 750),
(110, 'KansasCity', 'Denver', '14:00', '15:25', 180, 300),
(111, 'KansasCity', 'SLC', '13:00', '15:30', 200, 500),
(112, 'SLC', 'SanFran', '18:00', '19:30', 85, 210),
(113, 'SLC', 'LA', '17:30', '19:00', 185, 230),
(115, 'Denver', 'SLC', '15:00', '16:00', 75, 300),
(116, 'SanFran', 'LA', '22:00', '22:30', 50, 75),
(118, 'LA', 'Seattle', '20:00', '21:00', 150, 450);
To exclude certain cities from the flight plan you should add where clauses at 2 places in your query as following:
Right after src_city condition
...
WHERE src_city = 'SLC' -- <SRC_CITY>
AND dest_city <> 'Montreal'
...
In the recursive join condition
...
AND f1.dep_time > s.arrival -- you can't leave until you are there
AND f1.dest_city <> 'Montreal'
...
I don't have Postgress but I tried it with SQL server and it seems to work.

Recursive/Hierarchical Query Using Postgres

The table: Flight (flight_num, src_city, dest_city, dep_time, arr_time, airfare, mileage)
I need to find the cheapest fare for unlimited stops from any given source city to any given destination city. The catch is that this can involve multiple flights, so for example if I'm flying from Montreal->KansasCity I can go from Montreal->Washington and then from Washington->KansasCity and so on. How would I go about generating this using a Postgres query?
Sample Data:
create table flight(
flight_num BIGSERIAL PRIMARY KEY,
source_city varchar,
dest_city varchar,
dep_time int,
arr_time int,
airfare int,
mileage int
);
insert into flight VALUES
(101, 'Montreal', 'NY', 0530, 0645, 180, 170),
(102, 'Montreal', 'Washington', 0100, 0235, 100, 180),
(103, 'NY', 'Chicago', 0800, 1000, 150, 300),
(105, 'Washington', 'KansasCity', 0600, 0845, 200, 600),
(106, 'Washington', 'NY', 1200, 1330, 50, 80),
(107, 'Chicago', 'SLC', 1100, 1430, 220, 750),
(110, 'KansasCity', 'Denver', 1400, 1525, 180, 300),
(111, 'KansasCity', 'SLC', 1300, 1530, 200, 500),
(112, 'SLC', 'SanFran', 1800, 1930, 85, 210),
(113, 'SLC', 'LA', 1730, 1900, 185, 230),
(115, 'Denver', 'SLC', 1500, 1600, 75, 300),
(116, 'SanFran', 'LA', 2200, 2230, 50, 75),
(118, 'LA', 'Seattle', 2000, 2100, 150, 450);
[this answer is based on Gordon's]
I changed arr_time and dep_time to TIME datatypes, which makes calculations easier.
Also added result columns for total_time and waiting_time. Note: if there are any loops possible in the graph, you will need to avoid them (possibly using an array to store the path)
WITH RECURSIVE segs AS (
SELECT f0.flight_num::text as flight
, src_city, dest_city
, dep_time AS departure
, arr_time AS arrival
, airfare, mileage
, 1 as hops
, (arr_time - dep_time)::interval AS total_time
, '00:00'::interval as waiting_time
FROM flight f0
WHERE src_city = 'SLC' -- <SRC_CITY>
UNION ALL
SELECT s.flight || '-->' || f1.flight_num::text as flight
, s.src_city, f1.dest_city
, s.departure AS departure
, f1.arr_time AS arrival
, s.airfare + f1.airfare as airfare
, s.mileage + f1.mileage as mileage
, s.hops + 1 AS hops
, s.total_time + (f1.arr_time - f1.dep_time)::interval AS total_time
, s.waiting_time + (f1.dep_time - s.arrival)::interval AS waiting_time
FROM segs s
JOIN flight f1
ON f1.src_city = s.dest_city
AND f1.dep_time > s.arrival -- you can't leave until you are there
)
SELECT *
FROM segs
WHERE dest_city = 'LA' -- <DEST_CITY>
ORDER BY airfare desc
;
FYI: the changes to the table structure:
create table flight
( flight_num BIGSERIAL PRIMARY KEY
, src_city varchar
, dest_city varchar
, dep_time TIME
, arr_time TIME
, airfare INTEGER
, mileage INTEGER
);
And to the data:
insert into flight VALUES
(101, 'Montreal', 'NY', '05:30', '06:45', 180, 170),
(102, 'Montreal', 'Washington', '01:00', '02:35', 100, 180),
(103, 'NY', 'Chicago', '08:00', '10:00', 150, 300),
(105, 'Washington', 'KansasCity', '06:00', '08:45', 200, 600),
(106, 'Washington', 'NY', '12:00', '13:30', 50, 80),
(107, 'Chicago', 'SLC', '11:00', '14:30', 220, 750),
(110, 'KansasCity', 'Denver', '14:00', '15:25', 180, 300),
(111, 'KansasCity', 'SLC', '13:00', '15:30', 200, 500),
(112, 'SLC', 'SanFran', '18:00', '19:30', 85, 210),
(113, 'SLC', 'LA', '17:30', '19:00', 185, 230),
(115, 'Denver', 'SLC', '15:00', '16:00', 75, 300),
(116, 'SanFran', 'LA', '22:00', '22:30', 50, 75),
(118, 'LA', 'Seattle', '20:00', '21:00', 150, 450);
You want to use a recursive CTE for this. However, you will have to make a decision about how many flights to include. The following (untested) query shows how to do this, limiting the number of flight segments to 5:
with recursive segs as (
select cast(f.flight_num as varchar(255)) as flight, src_city, dest_city, dept_time,
arr_time, airfare, mileage, 1 as numsegs
from flight f
where src_city = <SRC_CITY>
union all
select cast(s.flight||'-->'||cast(f.flight_num as varchar(255)) as varchar(255)) as flight, s.src_city, f.dest_city,
s.dept_time, f.arr_time, s.airfare + f.airfare as airfare,
s.mileage + f.mileage as milage, s.numsegs + 1
from segs s join
flight f
on s.src_city = f.dest_city
where s.numsegs < 5
)
select *
from segs
where dest_city = <DEST_CITY>
order by airfare desc
limit 1;
Something like this:
select * from
(select flight_num, airfare from flight where src_city = ? and dest_city = ?
union
select f1.flight_num || f2.flight_num, f1.airfare+f2.airfare
from flight f1, flight f2 where f1.src_city = ? and f2.dest_city = ? and f1.dest_city = f2.src_city
union
...
) s order by airfare desc
I didn't test that as I'm leaving that for you so there might be subtle problems that require testing. This is clearly homework since no airline plans things this way. So I don't mind leaving you extra work.