SQL query to check if the next row value is same or different - sql

I am joining two tables based on a common column date. However, the column I am trying to get from one the table (cmg) in this case, should get next row value only if it is different from its previous row's value
Table A
Date comp.no
-----------------------
2019-03-08 5
2019-02-26 5
2019-01-17 5
2019-01-10 5
2018-12-27 5
Table B
Date cmg
-----------------
2019-07-17 NULL
2019-04-20 NULL
2019-02-26 RHB
2019-01-19 NULL
2019-01-17 RHB
2019-01-10 RMB
2018-12-28 NULL
2018-12-27 RHB
2018-12-12 RUB
2018-11-28 RUB
2018-10-20 NULL
2018-07-21 NULL
2018-04-21 NULL
2018-01-20 NULL
2017-10-21 NULL
2017-07-29 NULL
2017-05-07 NULL
2017-02-13 NULL
2016-11-22 NULL
2016-08-29 NULL
2016-06-07 NULL
2016-04-06 RUB
2016-03-21 RUB
2016-03-07 RUB

You can use lag function to compare with previous value. And for the first row you'll need an isnull() check since the first row won't have a previous value.
;with cte as(
select case
when isnull(lag(t2.cmg)over (order by t2.cmg desc),'') <>t2.cmg then 1 else 0 end as isresult
,t2.date,t2.cmg
from TableA t1
inner join TableB t2
on t1.date=t2.date
)
select date,cmg from cte where isresult=1

Use lag():
select date, cmg
from (select b.date, b.cmg, lag(b.cmg) over (order by b.date) as prev_cmg
from a join
b
on a.date = b.date
) b
where prev_cmg is null or prev_cmg <> cmg
order by date;

Related

Join based on ID and closest date

I have two tables:
Table 1 which contains phone calls (for every CustomerID there is at most one PhoneCall per day):
ActicityID CustomerID PhoneDate
1 A 2019-11-01
2 A 2019-12-01
3 A 2019-12-20
4 B 2019-11-01
5 B 2019-11-20
6 C 2019-11-03
7 D 2019-11-03
8 D 2019-12-01
9 E 2019-11-05
10 F 2019-11-01
Table 2 which contains Orders (OrdDate is the date when the order was placed and BillingDate is the date when the order was charged)
CustomerID OrdDate BillingDate
A 2019-12-03 2019-12-04
A 2019-12-21 2019-12-21
B 2019-11-03 2019-11-10
D 2019-12-02 2019-12-02
F 2019-11-02 2019-11-02
I want to join the tables. The joined table should have the same number of rows as Table 1.
So basically I want to know if there was order after a phone call. The problem is that if just join on CustomerID I get an OrdDat and a BillingDate for every customer who has ever made an order. For example Customer A made an order after the call on 2019-12-01 and after the call on the 2019-12-20 but not after the first call.
So my desired output would be
ActicityID CustomerID PhoneDate OrdDate BillingDate
1 A 2019-11-01 NULL NULL
2 A 2019-12-01 2019-12-03 2019-12-04
3 A 2019-12-20 2019-12-21 2019-12-21
4 B 2019-11-01 2019-11-03 2019-11-10
5 B 2019-11-20 NULL NULL
6 C 2019-11-03 NULL NULL
7 D 2019-11-03 NULL NULL
8 D 2019-12-01 2019-12-02 2019-12-02
9 E 2019-11-05 NULL NULL
10 F 2019-11-01 2019-11-02 2019-11-02
I think I need to join on CustomerID and the closest date between PhoneDate and OrdDate but my SQL knowledge is quite limited and I couldn't figure out how to do it.
I think you can do what you want by using lead() to get the next phone date and then just joining:
select a.*, b.orddate, b.billdate
from (select a.*,
lead(phonedate) over (partition by customerid order by phonedate) as next_pd
from a
) a left join
b
on b.customerid = a.customerid and
b.orddate >= a.phonedate and
(b.orddate < a.next_pd or a.next_pd is null);
You need to use a sub-query to limit the other table, referencing the TOP 1 associated date record...
SELECT
ActivityID,
CustomerID,
PhoneDate,
(SELECT TOP (1)
OrderDate
FROM
dbo.CustomerBilling AS b
WHERE
a.PhoneDate < OrderDate AND
a.CustomerID = CustomerID
ORDER BY OrderDate) AS BillingDate
FROM
dbo.Activity AS a

How to join a table to another one depending two date columns?

I have two tables which are
T1:
UserID Tier BeginDate EndDate
8278020 1 2019-03-02 18:33:04.893 2019-03-28 10:34:33.837
8278020 2 2019-03-28 10:34:33.837 2019-04-01 16:48:22.107
8278020 3 2019-04-01 16:48:22.107 2019-04-07 21:44:40.060
8278020 4 2019-04-07 21:44:40.060 2019-06-30 23:59:59.999
T2:
UserID GiftCardID UseDate OrderID IsUsed
8278020 165491838 2019-03-06 23057796 1
8278020 165491839 2019-03-10 23106429 1
8278020 165491840 2019-03-24 23277217 1
8278020 166418161 NULL NULL 0
8278020 166418162 NULL NULL 0
8278020 167026357 2019-04-22 23594414 1
8278020 167026358 2019-04-28 23668492 1
I want to match two tables such that I show the each tier of the customer when he/she used the giftcard.
For example, when the user used the Giftcard with '165491839' he was in tier 1.
Or at GiftCardID = '167026357' the tier is 4.
I couldn't find how to match the tables according to that.
I wait for your help...
Just use JOIN:
select t2.*, t1.tier
from table2 t2 left join
table1 t1
on t2.userid = t1.userid and
t2.usedate >= t1.begindate and
t2.userdate < t1.enddate;
This is a left join, so you won't lose rows if, for some reason, the dates don't match.

CREATE TEMP TABLE BASED ON SELECT DISTINCT ON 3 COLUMNS BUT WITH 1 EXTRA COLUMN

I need to make a temporary file with in it:
Partcode, MutationDate, MovementType, Qty
Every partcode has multiple mutationdates per Movementtype (there are max 9 movementtypes possible)
I need to get the last mutationdate per movementtype per partcode and the quantity that goes with that.
An example with partcode 003307
003307 2018-05-31 1 -100
003307 2018-06-11 2 -33
003307 2018-04-25 3 +25
and so on for all 9 movementtypes.
What did I get so far:
create table #LMUT(
MutationDate T_Date
,PartCode T_Code_Part
,CumInvQty T_Quantum_Qty10_3
,MovementType T_Type_PMOverInvt
)
insert #LMUT(
MutationDate,
Partcode,
CumInvQty,
MovementType)
SELECT
cast (max(MOV.MutationDate) as date)
,MOV.PartCode
,INV.MutationQty
,INV.PMOverInvtType
FROM dbo.T_PartMovementMain as MOV
inner join dbo.T_PartMovementOverInvt as INV on
INV.PMMainCode=MOV.PMMainCode
WHERE
MOV.PartMovementType = 1
group by MOV.PartCode,INV.PMOverInvtType,INV.MutationQty,MOV.MutationDate
SELECT * FROM #LMUT where partcode='003007'
drop table #LMUT
results in:
2016-12-06 00:00:00.000 003007 -24.000 2
2016-09-29 00:00:00.000 003007 -24.000 2
2016-11-09 00:00:00.000 003007 -24.000 2
2016-11-22 00:00:00.000 003007 -24.000 2
2016-10-26 00:00:00.000 003007 -24.000 2
2016-09-12 00:00:00.000 003007 -42.000 2
2016-10-13 00:00:00.000 003007 -24.000 2
2016-12-03 00:00:00.000 003007 100.000 5
2017-01-12 00:00:00.000 003007 -48.000 2
2016-10-04 00:00:00.000 003007 306.000 7
Not what I need, still have 8 times type 2
What else have I tried:
SELECT distinct MOV.Partcode,INV.PMOverInvtType,mov.MutationDate
FROM dbo.T_PartMovementMain as MOV
inner join dbo.T_PartMovementOverInvt as INV on
INV.PMMainCode=MOV.PMMainCode
WHERE
mov.MutationDate = (SELECT MAX (c.MutationDate) FROM
dbo.T_PartMovementMain as c
inner join dbo.T_PartMovementOverInvt as d on D.PMMainCode=c.PMMainCode
WHERE
C.PartMovementType = 1 AND
C.PartCode=mov.PartCode AND
D.PMMainCode = C.PMMainCode AND
D.PMOverInvtType=inv.PMOverInvtType
)
and MOV.PartMovementType = 1 and mov.partcode='003007'
order by MOV.Partcode,INV.PMOverInvtType
Results in:
3007 2 2017-01-12 00:00:00.000
3007 5 2016-12-03 00:00:00.000
3007 7 2016-10-04 00:00:00.000
That is what I want but I need to get the Qty too.
use row_number() window function
with cte as
( SELECT MOV.*,INV.*,
row_number() over(partition by INV.PMOverInvtType order by MOV.MutationDate desc)rn
FROM dbo.T_PartMovementMain as MOV
inner join dbo.T_PartMovementOverInvt as INV on
INV.PMMainCode=MOV.PMMainCode
) select cte.* from cte where rn=1
Solved it like this:
create table #LMUT(
PartCode T_Code_Part
,MovementType T_Type_PMOverInvt
,MutationDate T_Date
,CumInvQty T_Quantum_Qty10_3
)
insert #LMUT(Partcode,MovementType,MutationDate,CumInvQty)
select Artikel,Type,Datum,Aant
from (
SELECT MOV.Partcode as Artikel,INV.PMOverInvtType as Type,mov.MutationDate as Datum,INV.MutationQty as Aant,
row_number() over(partition by MOV.Partcode,INV.PMOverInvtType order by MOV.Partcode,INV.PMOverInvtType,MOV.MutationDate desc) rn
FROM dbo.T_PartMovementMain as MOV
inner join dbo.T_PartMovementOverInvt as INV on INV.PMMainCode=MOV.PMMainCode) cse
where rn=1
select * from #LMUT order by Partcode
drop table #LMUT

Select distinct records based on max(date) or NULL date

I am trying to get a list of employees based on their employee status or their most recent termination date. If the employee is active, the termination date will be NULL. There are also employees that have worked in multiple companies within our organization, I only want the record from the most recent company, whether active or terminated. An employee may also have different Employee numbers in the different companies, so the selection will have to be based on the SSN (Fica) number.
Here is an original data set:
company employee Fica First_name emp_status Term_date
5 7026 Jason T1 2013-09-16 00:00:00.000
500 7026 Jason T1 2010-11-30 00:00:00.000
7 7026 Jason T1 2009-07-31 00:00:00.000
2 90908 Jason A1 NULL
505 293866 William T1 2008-05-23 00:00:00.000
7 7243 Ashley T1 2010-07-11 00:00:00.000
2 90478 Michael T1 2013-01-11 00:00:00.000
500 90478 Michael T1 2011-09-26 00:00:00.000
500 311002 Andreas A1 NULL
3 365463 Matthew A1 NULL
500 248766 Chris T1 2007-04-23 00:00:00.000
500 90692 Kaitlyn T1 2012-03-13 00:00:00.000
2 90692 Kaitlyn A5 NULL
500 90236 Jeff T1 2011-09-26 00:00:00.000
2 90236 Jeff A1 NULL
2 90433 Nathan T1 2012-03-26 00:00:00.000
500 90433 Nathan T1 2011-09-26 00:00:00.000
Here are the results I am trying to get:
company employee Fica First_name emp_status Term_date
2 90908 Jason A1 NULL
505 293866 William T1 2008-05-23 00:00:00.000
7 7243 Ashley T1 2010-07-11 00:00:00.000
2 90478 Michael T1 2013-01-11 00:00:00.000
500 311002 Andreas A1 NULL
3 365463 Matthew A1 NULL
500 248766 Chris T1 2007-04-23 00:00:00.000
2 90692 Kaitlyn A5 NULL
2 90236 Jeff A1 NULL
2 90433 Nathan T1 2012-03-26 00:00:00.000
Thanks for any help you are able to give. I need to run this on a SQL2005 server which will be connecting to an Oracle server via ODBC.
If the dates were all populated, you could do this with a "standard" not exists query. The NULLs introduce a problem, but that problem can be solved using coalesce():
select t.*
from table t
where not exists (select 1
from table t2
where t2.employee = t.employee and
coalesce(t2.term_date, '9999-01-01') > coalesce(t.term_date, '9999-01-01)
);
NOTE: If you need for this to work on Oracle, then you need a different format for the date constant.
EDIT:
Another way to solve this uses row_number():
select t.*
from (select t.*,
row_number() over (partition by employee
order by (case when term_date is null then 0 else 1 end),
term_date desc
) as seqnum
from table t
) t
where seqnum = 1;
The rule for choosing the "last" row are embedded in the order by clause. Put the NULL value first, followed by the term_date in descending order.

How to make a status by comparing the dates

Using SQL Server 2005
Leave Table
ID StartDate EndDate
001 04/01/2010 04/02/2010
002 04/02/2010 04/03/2010
…
Event Table
ID Date PresentDate Status
001 03/30/2010 03/30/2010 Present
001 03/31/2010 null absent
001 04/01/2010 null Leave
001 04/02/2010 null Leave
001 04/03/2010 null absent
001 04/04/2010 04/04/2010 Present
….
All the Datecolumn datatype is datetime
In the Status Column, if Present Date is null then it will display as “absent”, if not null then it will display as “present”. Now if we apply a leave for the date then it will display as “Leave” in status column.
Query
Select
id, date, present date
, CASE WHEN t2.id IS NULL THEN t1.Status ELSE ‘Leave’ END AS status
from event table as t1
left outer join leave table as t2 on
t1.id = t2.id and t1.date between t2.startdate and t2.enddate
The above method is working, but I need to add one more condition.
Once if we applied the leave for the particular employee in the Leave Table then it should compare the Present Date column, if Present Date Column is empty then it should display as “leave”
Expected Output
ID Date PresentDate Status
001 03/30/2010 03/30/2010 Present
001 03/31/2010 null absent
001 04/01/2010 null Leave
001 04/02/2010 null Leave
001 04/03/2010 null Leave (Expect this value)
001 04/04/2010 04/04/2010 Present
….
From the above output Leave is starting from 04/01/2010 to 04/02/2010, then next column of present date is null then status should display as a “Leave”, once present date is not null then it should display as “Present.
Method
We can display as "Leave" in status column from Start Date to end date of leave table, after that leave date end then we can compare with PresentDate column, if PresentDate column is null then it should display as "Leave", once data is available in present column then status should display with normal condition.
How to make a query for the above condition.
Need Query Help
select E.id, E.date, E.presentdate, *,
case
when E.presentdate is not null then 'Present'
when E2.presentdate is not null then 'Absent'
when L.ID is not null then 'Leave'
else 'Absent'
end
from Event E
outer apply (
select top 1 *
from Leave L
where E.presentdate is null and E.date >= L.startdate
AND e.ID = L.ID
order by L.startDate desc) L
outer apply (
select top 1 *
from Event E2
where E.presentdate is null
and E2.presentdate is not null
and E.date >= E2.date and E2.date > L.startdate
AND e2.ID = e.ID
order by E2.presentdate desc) E2
order by E.date
Leave table
ID StartDate EndDate
----------- ----------------------- -----------------------
1 2010-04-01 00:00:00.000 2010-04-02 00:00:00.000
1 2010-04-02 00:00:00.000 2010-04-03 00:00:00.000
1 2010-04-05 00:00:00.000 2010-04-05 00:00:00.000
Output
id date presentdate
----------- ----------------------- ----------------------- -------
1 2010-03-30 00:00:00.000 2010-03-30 00:00:00.000 Present
1 2010-03-31 00:00:00.000 NULL Absent
1 2010-04-01 00:00:00.000 NULL Leave
1 2010-04-02 00:00:00.000 NULL Leave
1 2010-04-03 00:00:00.000 NULL Leave -**
1 2010-04-04 00:00:00.000 2010-04-04 00:00:00.000 Present
1 2010-04-05 00:00:00.000 NULL Leave
1 2010-04-06 00:00:00.000 NULL Leave -**
1 2010-04-07 00:00:00.000 NULL Leave -**
1 2010-04-08 00:00:00.000 2010-04-08 00:00:00.000 Present
1 2010-04-09 00:00:00.000 NULL Absent
1 2010-04-10 00:00:00.000 NULL Absent
1 2010-04-11 00:00:00.000 2010-04-11 00:00:00.000 Present
The ones marked -** are not covered by Leave records, but they show leave because they follow a Leave period, correct? 2010-04-09 for example remains "Absent" because it follows a Present record (without actually being present).