Complex rolling scenario (CROSS APPLY and OUTER APPLY example) - sql

I currently have data like the following (but bigger!)
/*--:::::::::::
DROP TABLE #target
DROP TABLE #Fact
*/--:::::::::::
CREATE TABLE #target
(
PlayerKey INT,
Name VARCHAR(8),
LiveKey INT
);
INSERT INTO #target
values
(1,'michael',20130103),
(2,'jackson',20130107);
CREATE TABLE #Fact
(
DateKey INT,
PlayerKey INT,
Amount INT
);
INSERT INTO #Fact
values
(20130101,1,10),
(20130102,1,90),
(20130103,1,18),
(20130103,2,79),
(20130103,3,99),
(20130104,2,15),
(20130105,1,12),
(20130105,2,15),
(20130106,1,60),
(20130107,1,96),
(20130107,2,88),
(20130107,4,28),
(20130108,1,13),
(20130108,2,15),
(20130109,1,33),
(20130109,2,67),
(20130110,1,19),
(20130110,2,17)
;
The start of the query is as follows.
DECLARE #NumDays INT = 3;
WITH basic_cte AS
(
SELECT rn = ROW_NUMBER() OVER(PARTITION BY d.Name ORDER BY f.DateKey),
f.DateKey,
d.Name,
f.Amount
FROM #Fact f
INNER JOIN #target d ON
f.PlayerKey = d.PlayerKey AND
f.DateKey >= d.LiveKey AND
f.DateKey < CONVERT(CHAR(8),CONVERT(DATETIME,CONVERT(DATETIME,CONVERT(CHAR(8),d.LiveKey,112))+#NumDays),112)
)
SELECT x.*,
"RollingAmount" = SUM(Amount) OVER(PARTITION BY Name ORDER BY DateKey)
FROM basic_cte x;
This gives the following:
Assuming that we have a DimDate production view available how do I ensure that michael has a row for 20130104 with an amount of 0?
Also is it possible in the same script, to add new columns "AmountAll" and "AmountAllRolling" which would give numbers across all the players including PlayerKeys 3 and 4? I'm guessing this would involve changing the INNER JOIN to a LEFT OUTER JOIN?
So given the above the final result would be as follows:
EDIT
via all the excellent help from Bogdan I've got the following.
I've added an extra total AmountGroup that is the total across the specified players - this was just "nice-to-have" and not part of the original specification.
DECLARE #NumDays INT = 3;
WITH basic_cte AS
(
SELECT rn = ROW_NUMBER() OVER(PARTITION BY Name ORDER BY x.DateKey),
x.DateKey,
d.Name,
Amount = ISNULL(f.Amount,0),
AmountGroup = ISNULL(f.AmountGroup,0),
AmountAll = ISNULL(f.AmountAll,0)
FROM (
SELECT t.*,
EndLiveKey = CONVERT(INT,CONVERT(CHAR(8),CONVERT(DATETIME,CONVERT(DATETIME,CONVERT(CHAR(8),t.LiveKey,112))+#NumDays),112))
FROM #target t
) d
CROSS APPLY
(
SELECT dm.DateKey
FROM WHData.dbo.vw_DimDate dm
WHERE dm.DateKey >= d.LiveKey AND
dm.DateKey < d.EndLiveKey
) x
OUTER APPLY
(
SELECT Amount = SUM(CASE WHEN PlayerKey1 = PlayerKey2 THEN fbase.Amount END),
AmountGroup = SUM(CASE WHEN inGroup = 1 THEN fbase.Amount ELSE 0 END),
AmountAll = SUM(fbase.Amount)
FROM
(
SELECT fct.Amount,
fct.PlayerKey AS PlayerKey1,
d.PlayerKey AS PlayerKey2,
CASE WHEN tt.PlayerKey IS NULL THEN 0 ELSE 1 END AS inGroup
FROM #Fact fct
LEFT OUTER JOIN #target tt ON
fct.PlayerKey = tt.PlayerKey
WHERE fct.DateKey = x.DateKey
) fbase
) f
)
SELECT y.*,
"RollingAmount" = SUM(Amount) OVER(PARTITION BY Name ORDER BY DateKey),
"RollingAmountGroup" = SUM(AmountGroup) OVER(PARTITION BY Name ORDER BY DateKey),
"RollingAmountAll" = SUM(AmountAll) OVER(PARTITION BY Name ORDER BY DateKey)
FROM basic_cte y;

I assume that you have a DimDate table with the following structure:
CREATE TABLE DimDate
(
DateKey INT PRIMARY KEY
);
and DateKey column doesn't has gaps.
Solution:
DECLARE #NumDays INT = 3;
WITH basic_cte AS
(
SELECT x.DateKey,
d.Name,
Amount = ISNULL(f.Amount,0)
FROM
(
SELECT t.*, CONVERT(INT,CONVERT(CHAR(8),CONVERT(DATETIME,CONVERT(DATETIME,CONVERT(CHAR(8),t.LiveKey,112))+#NumDays),112)) AS EndLiveKey
FROM #target t
) d
CROSS APPLY
(
SELECT dm.DateKey
FROM DimDate dm
WHERE dm.DateKey >= d.LiveKey
AND dm.DateKey < d.EndLiveKey
) x
LEFT OUTER JOIN #Fact f
ON f.PlayerKey = d.PlayerKey
AND f.DateKey = x.DateKey
)
SELECT rn = ROW_NUMBER() OVER(PARTITION BY Name ORDER BY DateKey),
y.*,
"RollingAmount" = SUM(Amount) OVER(PARTITION BY Name ORDER BY DateKey)
FROM basic_cte y;
Edit #1:
DECLARE #NumDays INT = 3;
WITH basic_cte AS
(
SELECT rn = ROW_NUMBER() OVER(PARTITION BY Name ORDER BY x.DateKey),
x.DateKey,
d.Name,
Amount = ISNULL(f.Amount,0),
AmountAll = ISNULL(fall.AmountAll,0)
FROM
(
SELECT t.*, CONVERT(INT,CONVERT(CHAR(8),CONVERT(DATETIME,CONVERT(DATETIME,CONVERT(CHAR(8),t.LiveKey,112))+#NumDays),112)) AS EndLiveKey
FROM #target t
) d
CROSS APPLY
(
SELECT dm.DateKey
FROM DimDate dm
WHERE dm.DateKey >= d.LiveKey
AND dm.DateKey < d.EndLiveKey
) x
OUTER APPLY
(
SELECT SUM(fct.Amount) AS Amount
FROM #Fact fct
WHERE fct.DateKey = x.DateKey
AND fct.PlayerKey = d.PlayerKey
) f
OUTER APPLY
(
SELECT SUM(fct.Amount) AS AmountAll
FROM #Fact fct
WHERE fct.DateKey = x.DateKey
) fall
)
SELECT
y.*,
"RollingAmount" = SUM(Amount) OVER(PARTITION BY Name ORDER BY DateKey),
"RollingAmountAll" = SUM(AmountAll) OVER(PARTITION BY Name ORDER BY DateKey)
FROM basic_cte y;
Edit #2:
DECLARE #NumDays INT = 3;
WITH basic_cte AS
(
SELECT rn = ROW_NUMBER() OVER(PARTITION BY Name ORDER BY x.DateKey),
x.DateKey,
d.Name,
Amount = ISNULL(f.Amount,0),
AmountAll = ISNULL(f.AmountAll,0)
FROM
(
SELECT t.*, EndLiveKey = CONVERT(INT,CONVERT(CHAR(8),CONVERT(DATETIME,CONVERT(DATETIME,CONVERT(CHAR(8),t.LiveKey,112))+#NumDays),112))
FROM #target t
) d
CROSS APPLY
(
SELECT dm.DateKey
FROM DimDate dm
WHERE dm.DateKey >= d.LiveKey
AND dm.DateKey < d.EndLiveKey
) x
OUTER APPLY
(
SELECT AmountAll = SUM(fbase.Amount),
Amount = SUM(CASE WHEN PlayerKey1 = PlayerKey2 THEN fbase.Amount END)
FROM
(
SELECT fct.Amount, fct.PlayerKey AS PlayerKey1, d.PlayerKey AS PlayerKey2
FROM #Fact fct
WHERE fct.DateKey = x.DateKey
) fbase
) f
)
SELECT
y.*,
"RollingAmount" = SUM(Amount) OVER(PARTITION BY Name ORDER BY DateKey),
"RollingAmountAll" = SUM(AmountAll) OVER(PARTITION BY Name ORDER BY DateKey)
FROM basic_cte y;

Related

The query I ran returns 2 of the same column which isn't allowed in tableau and I can't fix the query

I need to be able to get rid of one of the workdate and one of the sr_name columns but I can't figure out how to.
I'm getting the query returned like this:
Query
I'm also getting this error message when entered into tableau:
The column 'sr_name' was specified multiple times for 'Custom SQL Query'.
Below is the code I have. If I remove a sr_name from either subquery there will be an error in the join clause.
select *
from
(
select s.sr_name, cast(punchdatetime as date) as workdate,
((datediff(second, min(case when p.InOut = 1 then punchdatetime end),
max(case when p.InOut = 0 then punchdatetime end))/3600) - .5) as
hoursworked
from PunchClock p join ServiceReps s on p.ServRepID = s.ServRepID
where punchyear >= 2019
group by s.sr_name, cast(punchdatetime as date)
) v
join
(
select sr_name, t.*,
calls = (select count(*) from CRM_Correspondence cr where
cast(cr.DateCreated as date) = workdate and StatusType like '%call%' and
cr.ServRepID = t.servrepid),
reaches = (select count(*) from CRM_Correspondence cr where
cast(cr.DateCreated as date) = workdate and (StatusType = 'call reached'
or StatusType like '%SCHEDULE%') and cr.ServRepID = t.servrepid),
books = (select count(*) from os_appointments o where cast(o.DateCreated
as date) = workdate and isnull(o.confirmedby, o.booked_by) =
t.servrepid),
attends = (select count(*) from os_appointments o where
cast(o.DateCreated as date) = workdate and isnull(o.confirmedby,
o.booked_by) = t.servrepid and o.appointmentStatus = 'attended')
from
(
select cast(cor.datecreated as date) workdate, cor.ServRepID
from CRM_Correspondence cor
where cor.datecreated > '2019-01-01'
group by cast(cor.datecreated as date), cor.servrepid
) t
join ServiceReps sr on t.ServRepID = sr.ServRepID
) u on v.sr_name = u.sr_name and v.workdate = u.workdate
I need the same results just without the duplicate column so I can enter the query into tableau.
This is challenging because you have so many subqueries here. This could be refactored to use a single query which is what I would do. But using the existing query you could do something along these lines.
I had to format this very differently so I could isolate each piece.
select v.sr_name
, v.workdate
, v.hoursworked
, u.ServRepID
, u.calls
, u.reaches
, u.books
, u.attends
from
(
select s.sr_name
, cast(punchdatetime as date) as workdate
, ((datediff(second, min(case when p.InOut = 1 then punchdatetime end), max(case when p.InOut = 0 then punchdatetime end))/3600) - .5) as hoursworked
from PunchClock p
join ServiceReps s on p.ServRepID = s.ServRepID
where punchyear >= 2019
group by s.sr_name
, cast(punchdatetime as date)
) v
join
(
select sr_name
, t.*
, calls =
(
select count(*)
from CRM_Correspondence cr
where cast(cr.DateCreated as date) = workdate
and StatusType like '%call%'
and cr.ServRepID = t.servrepid
)
, reaches =
(
select count(*)
from CRM_Correspondence cr
where cast(cr.DateCreated as date) = workdate
and (StatusType = 'call reached' or StatusType like '%SCHEDULE%')
and cr.ServRepID = t.servrepid
)
, books =
(
select count(*)
from os_appointments o
where cast(o.DateCreated as date) = workdate and isnull(o.confirmedby, o.booked_by) = t.servrepid
)
, attends =
(
select count(*)
from os_appointments o
where cast(o.DateCreated as date) = workdate
and isnull(o.confirmedby, o.booked_by) = t.servrepid
and o.appointmentStatus = 'attended'
)
from
(
select cast(cor.datecreated as date) workdate
, cor.ServRepID
from CRM_Correspondence cor
where cor.datecreated > '2019-01-01'
group by cast(cor.datecreated as date)
, cor.servrepid
) t
join ServiceReps sr on t.ServRepID = sr.ServRepID
) u on v.sr_name = u.sr_name
and v.workdate = u.workdate

How can I find DATEDIFF for records in the same field?

I have a reporting table that looks like this - BEFORE:
The FREQ_CALC is the number of months between EFFECTIVEDATE and EXPIRY_DATE, divided by the noMonths field, FREQ_CODE, after the M.
I need to get everything into this shape - AFTER.
I am trying to figure out how to calculate the 'FREQUENCY' as well as the fields in blue, green, and pink (pink is very easy). Basically, 'FREQ_CODE' has an 'M' character and after that I have months and days in a month. If noMonths is 3, I need to start mxDays with 90, and then find the difference in the number of days from the maturityDate field, so it's not the DATEDIFF() between two fields, but the DATEDIFF between increasing dates in the same field, grouped by Credit_Line_NO. So, the three cells in yellow start mxDays. Also, mxFactor is 1 when mxDays is 30 or 90, and it is 365/360, when mxDays is 365. Finally, the Calc is the mxDays * Amount. This is super-easy. I just can't figure out how to get the mxDays and mxFactor setup.
For additional clarity, 91 days = 6/30/2018 - 3/31/2018 and 92 days = 9/30/2018 - 6/30/2018. Also, 1.0111 = 91/90 and 1.0222 = 92/90. Similarly, 0.8111 = 73/90. Finally, 1.0139 = 365/360 because noMonths = 12.
Maybe this requires a CTE and a couple Case...When...Then statements. Not sure...
I am using SQL Server 2008.
-- Here is my DDL
-- Drop table Reporting_Table
CREATE TABLE Reporting_Table (
Credit_Line_NO Varchar(10),
noMonths INT,
EFFECTIVEDATE Date,
EXPIRY_DATE Date,
Amount Money,
mxDays INT,
mxFactor decimal(5,4),
Calc Money)
INSERT INTO Reporting_Table (Credit_Line_NO, noMonths, EFFECTIVEDATE, EXPIRY_DATE, Amount, mxDays, mxFactor, Calc)
Values('9938810','3','3/31/2018','6/12/2020','11718.75','90','1','11718.75')
INSERT INTO Reporting_Table (Credit_Line_NO, noMonths, EFFECTIVEDATE, EXPIRY_DATE, Amount, mxDays, mxFactor, Calc)
Values('2235461','1','6/30/2018','6/6/2019','12345','30','1','12345')
INSERT INTO Reporting_Table (Credit_Line_NO, noMonths, EFFECTIVEDATE, EXPIRY_DATE, Amount, mxDays, mxFactor, Calc)
Values('3365434','12','6/30/2018','6/30/2019','298523.36085','365','1.01388888888889','302669.518639583')
For SQL 2008 you need to order your table with row_number and join each row with previous one. Then make calculations
with cte as (
select
*, rn = row_number() over (partition by Credit_Line_NO order by maturityDate)
from
Reporting_Table
)
select
a.*, mxDay = isnull(q.dayDiff, q.mDay), z.mxFactor
, Calc = z.mxFactor * a.Amount
from
cte a
left join cte b on a.Credit_Line_NO = b.Credit_Line_NO and a.rn - 1 = b.rn
cross apply (select
mDay = case
when a.noMonths = 1 then 30
when a.noMonths = 3 then 90
when a.noMonths = 12 then 365
end, dayDiff = datediff(dd, b.maturityDate, a.maturityDate)) q
cross apply (select mxFactor = cast(1.0 * isnull(q.dayDiff, q.mDay) / q.mDay as decimal(10,4))) z
Edit:
This is update query:
with cte as (
select
*, rn = row_number() over (partition by Credit_Line_NO order by maturityDate)
from
Reporting_Table
)
, cte2 as (
select
a.Credit_Line_NO, a.noMonths, a.maturityDate, a.Amount, mxDay = isnull(q.dayDiff, q.mDay), z.mxFactor
, Calc = z.mxFactor * a.Amount
from
cte a
left join cte b on a.Credit_Line_NO = b.Credit_Line_NO and a.rn - 1 = b.rn
cross apply (select
mDay = case
when a.noMonths = 1 then 30
when a.noMonths = 3 then 90
when a.noMonths = 12 then 365
end, dayDiff = datediff(dd, b.maturityDate, a.maturityDate)) q
cross apply (select mxFactor = cast(1.0 * isnull(q.dayDiff, q.mDay) / q.mDay as decimal(10,4))) z
)
update r
set r.mxDay = c.mxDay, r.mxFactor = c.mxFactor, r.Calc = c.Calc
from
Reporting_Table r
join cte2 c on r.Credit_Line_NO = c.Credit_Line_NO and r.noMonths = c.noMonths and r.maturityDate = c.maturityDate

Normalizing the data from denormalized table

I have data in my table like this
RepID|Role|Status|StartDate |EndDate |
-----|----|------|----------|----------|
10001|R1 |Active|01/01/2015|01/31/2015|
-----|----|------|----------|----------|
10001|R1 |Leavee|02/01/2015|02/12/2015|
-----|----|------|----------|----------|
10001|R1 |Active|02/13/2015|02/28/2015|
-----|----|------|----------|----------|
10001|R2 |Active|03/01/2015|03/18/2015|
-----|----|------|----------|----------|
10001|R2 |Leave |03/19/2015|04/10/2015|
-----|----|------|----------|----------|
10001|R2 |Active|04/11/2015|05/10/2015|
-----|----|------|----------|----------|
10001|R1 |Active|05/11/2015|06/13/2015|
-----|----|------|----------|----------|
10001|R1 |Leave |06/14/2015|12/31/9998|
-----|----|------|----------|----------|
I am looking for the output like this,
RepID|Role|StartDate |EndDate |
-----|----|----------|----------|
10001|R1 |01/01/2015|02/28/2015|
-----|----|----------|----------|
10001|R2 |03/01/2015|05/10/2015|
-----|----|----------|----------|
10001|R1 |05/11/2015|12/31/9998|
-----|----|----------|----------|
Whenever only the role change happens, I need to capture the start and EndDate. I tried different ways but couldn't get the output.
Any help is appreciated.
Below is the SQL i tried with but it doesnt help,
SELECT T1.RepID, T1.Role, Min(T1.StartDate) AS StartDate, Max(T1.EndDate) AS EndDate
FROM
(SELECT rD1.RepID, rD1.Role, rD1.StartDate, rD1.EndDate
FROM repDetails rD1
INNER JOIN repDetails rD2
ON rD2.RepID = rD1.RepID AND rD2.StartDate = DateAdd (Day, 1, rD1.EndDate) AND (rD2.Role = rD1.Role OR (rD2.Role IS NULL AND rD1.Role IS NULL) OR (rD2.Role = '' AND rD1.Role = ''))
UNION
SELECT rD2.RepID, rD2.Role, rD2.StartDate, rD2.EndDate
FROM repDetails rD1
INNER JOIN repDetails rD2
ON rD2.RepID = rD1.RepID AND rD2.StartDate = DateAdd (Day, 1, rD1.EndDate) AND (rD2.Role = rD1.Role OR (rD2.Role IS NULL AND rD1.Role IS NULL) OR (rD2.Role = '' AND rD1.Role = ''))
) T1
GROUP BY T1.RepID, T1.Role
UNION
SELECT EP.RepID, EP.Role AS DataValue, EP.StartDate, EP.EndDate
FROM repDetails EP
LEFT OUTER JOIN
(SELECT rD1.RepID, rD1.Role, rD1.StartDate, rD1.EndDate
FROM repDetails rD1
INNER JOIN repDetails rD2
ON rD2.RepID = rD1.RepID AND rD2.StartDate = DateAdd (Day, 1, rD1.EndDate) AND (rD2.Role = rD1.Role OR (rD2.Role IS NULL AND rD1.Role IS NULL) OR (rD2.Role = '' AND rD1.Role = ''))
UNION
SELECT rD2.RepID, rD2.Role , rD2.StartDate, rD2.EndDate
FROM repDetails rD1
INNER JOIN repDetails rD2
ON rD2.RepID = rD1.RepID AND rD2.StartDate = DateAdd (Day, 1, rD1.EndDate) AND (rD2.Role = rD1.Role OR (rD2.Role IS NULL AND rD1.Role IS NULL) OR (rD2.Role = '' AND rD1.Role = ''))
) T1
ON EP.RepID = T1.RepID AND EP.StartDate = T1.StartDate
WHERE T1.RepID IS NULL
The key here is to identify continuous rows until the role changes. This can be done by comparing the next row's role using the lead function and some additional logic to categorize all the previous rows into the same group.
After classifying them into groups, you just need to use min and max to get the start and end dates.
with groups as (
select x.*
,case when grp = 1 then 0 else 1 end + sum(grp) over(partition by repid order by startdate) grps
from (select t.*
,case when lead(role) over(partition by repid order by startdate) = role then 0 else 1 end grp
from t) x
)
select distinct repid,role
,min(startdate) over(partition by repid,grps) startdt
,max(enddate) over(partition by repid,grps) enddt
from groups
order by 1,3
Sample demo
do you just want the min(start) / max(end) dates for each repID and role?
If so, try:
Select
repID, role,
min(starDate),
max(endDate)
from
tbl
group by
repID, role
--
A more verbose solution, equivalent to VKP's:
SELECT
repid, ROLE, grpID,
MIN(startdate) AS min_startDateOverRole,
MAX(endDate) AS max_endDateOverRole
FROM
(SELECT
*, CASE WHEN isGrpEnd = 1 THEN 0 ELSE 1 end +
-- when on group end row, don't increment grpID.
-- Wait until start of next group
SUM(isGrpEnd) OVER(ORDER BY startdate) grpID
-- sum(all group end rows up to this one)
FROM
(SELECT
*,
CASE WHEN lead(ROLE) OVER(ORDER BY startdate) = ROLE
THEN 0 ELSE 1 end isGrpEnd
FROM t) x )
GROUP BY
repid, ROLE, grpid
ORDER BY
1,3

FIFO Balance at every month end with sql

I am trying to get the balance quantity at the end of every month i.e. running total at every month end with FIFO only but its not showing any result. Pls help
Here is my query.
declare #Stock table (Item char(3) not null,Date date not null,TxnType varchar(3) not null,Qty int not null,Price decimal(10,2) null)
insert into #Stock(Item , [Date] , TxnType, Qty, Price) values
('ABC','20120401','IN', 200, 750.00),
('ABC','20120402','OUT', 100 ,null ),
('ABC','20120403','IN', 50, 700.00),
('ABC','20120404','IN', 75, 800.00),
('ABC','20120405','OUT', 175, null ),
('XYZ','20120406','IN', 150, 350.00),
('XYZ','20120407','OUT', 120 ,null ),
('XYZ','20120408','OUT', 10 ,null ),
('XYZ','20120409','IN', 90, 340.00),
('ABC','20120510','IN', 200, 750.00),
('ABC','20120511','OUT', 100 ,null ),
('ABC','20120512','IN', 50, 700.00),
('ABC','20120513','IN', 75, 800.00),
('ABC','20120514','OUT', 175, null ),
('XYZ','20120515','IN', 150, 350.00),
('XYZ','20120516','OUT', 120 ,null ),
('XYZ','20120517','OUT', 10 ,null ),
('XYZ','20120518','IN', 90, 340.00);
;WITH OrderedIn as (
select *,ROW_NUMBER() OVER (PARTITION BY month(date) ORDER BY DATE) as rn
from #Stock
where TxnType = 'IN'
), RunningTotals as (
select Item,Qty,Price,Qty as Total,0 as PrevTotal,rn from OrderedIn where rn = 1
union all
select rt.Item,oi.Qty,oi.Price,rt.Total + oi.Qty,rt.Total,oi.rn
from
RunningTotals rt
inner join
OrderedIn oi
on
rt.Item = oi.Item and
rt.rn = oi.rn - 1
), TotalOut as (
select Item,SUM(Qty) as Qty from #Stock where TxnType='OUT' group by Item
)
select
rt.Item,SUM(CASE WHEN PrevTotal > out.Qty THEN rt.Qty ELSE rt.Total - out.Qty END * Price)
from
RunningTotals rt
inner join
TotalOut out
on
rt.Item = out.Item
where
rt.Total > out.Qty
group by rt.Item
Output
Month Item (No column name(qty*price))
4 ABC 40000
4 XYZ 37600
5 ABC 77500
5 XYZ 76100
With you sample data.Are you looking for this,else clearly sketch your desired output .
;WITH CTE AS
(
select Item ,[Date] ,TxnType ,s.Qty
,Price ,ROW_NUMBER() OVER (PARTITION BY month(date),TxnType ORDER BY DATE) as rn
from #Stock S
)
,
CTE1 AS
(
SELECT ITEM ,[DATE] ,TXNTYPE ,QTY,MONTH(DATE) MONTHDT,RN FROM CTE WHERE RN=1
UNION ALL
SELECT S.ITEM ,S.[DATE] ,S.TXNTYPE ,S.QTY+A.QTY,A.MONTHDT ,S.RN
FROM CTE S INNER JOIN CTE1 A
ON S.TXNTYPE=A.TXNTYPE AND MONTH(S.[DATE])=A.MONTHDT AND S.RN-A.RN=1
)
,CTE2 AS
(
SELECT *
,ROW_NUMBER() OVER (PARTITION BY month(date),TxnType ORDER BY QTY DESC )RN1
FROM CTE1 S
)
SELECT A.DATE,A.QTY-(SELECT B.Qty from CTE2 B
where a.MONTHDT=b.MONTHDT and b.RN1=1 AND b.TxnType='OUT')
from CTE2 A
WHERE A.RN1=1 AND a.TxnType='IN'
Try this.
;WITH cte
AS (SELECT Row_number()OVER(partition BY item, Datepart(mm, date) ORDER BY date DESC) rn,
item,
Month(date) AS [month],
TxnType,
Price,
date
FROM #Stock
WHERE TxnType = 'IN'),
cte1
AS (SELECT Row_number() OVER(partition BY item, Datepart(mm, date) ORDER BY date DESC) rn,
item,
Month(date) [month],
(SELECT Sum(CASE
WHEN TxnType = 'out' THEN -1 * qty
ELSE qty
END) AS qty
FROM #Stock b
WHERE a.item = b.item
AND a.Date >= b.Date) qty
FROM #Stock a)
SELECT a.[month],
a.Item,
( a.Price * b.qty ) running_tot
FROM cte a
JOIN cte1 b
ON a.Item = b.Item
AND a.[month] = b.[month]
WHERE a.rn = 1
AND b.rn = 1

combining two CTE in a single query

Here i have two CTE and i need to select values from these two CTE as a single query. I can able to select values from a single CTE at a time but i dont know like how to merge these two CTE and select values from both CTE.
Here int the below query i can able to select values from second CTE
DECLARE #now DateTime;
DECLARE #Firstweekstart DateTime;
DECLARE #FirstweekEnd DateTime;
SET #now = CONVERT(datetime, CONVERT(date,getdate()));
SET #Firstweekstart = DATEADD(
DD,
-(DATEPART(DW, #now - 7) - 1),
#now - 7);
SET #Firstweekstart = dateadd(ms, -1, #Firstweekstart)
SET #Firstweekend = DATEADD(
DD,
7 - (DATEPART(DW, #now - 7)),
#now - 7);
SET #Firstweekend = dateadd(ms, -3, #Firstweekend+1)
;WITH CTE1 AS
(
SELECT CHINFO.CHILDID,CHINFO.CONSUMERID,
DATEADD(DD, -(DATEPART(DW, CHINFO.Adddate)-1), CHINFO.Adddate) AS APPLICATIONUSAGESTARTDATE,
DATEDIFF(WW,CHINFO.Adddate,#now) AS WEEKNUMBER,
ROW_NUMBER() OVER
(PARTITION BY CHINFO.CHILDID ORDER BY CHINFO.Adddate ASC) AS RN
FROM BKA.CHILDINFORMATION CHINFO
LEFT OUTER JOIN BKA.CHILDEVENTS CHE
ON CHE.CHILDID = CHINFO.CHILDID
GROUP BY CHINFO.CHILDID,CHINFO.CONSUMERID,CHINFO.Adddate
)
,CTE2 as
(SELECT Distinct CHINFO.CHILDID ,CHE.TIMESTAMP
,ROW_NUMBER() OVER (PARTITION BY CHINFO.CHILDID ORDER BY CHE.TIMESTAMP) row
FROM BKA.CHILDINFORMATION CHINFO
JOIN BKA.CHILDEVENTS CHE
ON CHE.CHILDID = CHINFO.CHILDID
WHERE CHE.TYPE = 'pottybreak'
AND CHE.ADDDATE BETWEEN #Firstweekstart AND #Firstweekend
GROUP BY CHINFO.CHILDID,CHE.TIMESTAMP
)
SELECT
a.CHILDID ,
AVG(CONVERT(DECIMAl,DATEDIFF ( minute , b.TIMESTAMP , a.TIMESTAMP))) as CURRENTWEEKTIMERRESTART
FROM
CTE2 a
LEFT JOIN CTE2 b
on a.CHILDID = b.CHILDID
and a.row = b.row+1
group by a.childid
I need to merge the below query with the above one
SELECT CTE1.CONSUMERID,
CTE1.CHILDID,
CTE1.APPLICATIONUSAGESTARTDATE,
CTE1.WEEKNUMBER
FROM CTE1
WHERE RN = 1 ORDER BY CTE1.CHILDID ASC
Any suggestion?
If you are joining values from CTE1 and CTE2 based on CHILDID column then you can write as:
SELECT
CTE1.CONSUMERID,
CTE1.CHILDID,
CTE1.APPLICATIONUSAGESTARTDATE,
CTE1.WEEKNUMBER,
-- a.CHILDID ,
AVG(CONVERT(DECIMAl,DATEDIFF ( minute , b.TIMESTAMP , a.TIMESTAMP)))
as CURRENTWEEKTIMERRESTART
FROM
CTE2 a
LEFT JOIN CTE2 b on a.CHILDID = b.CHILDID and a.row = b.row+1
LEFT JOIN CTE1 on CTE1.CHILDID = a.CHILDID and CTE1.RN = 1
group by CTE1.CONSUMERID,CTE1.CHILDID,CTE1.APPLICATIONUSAGESTARTDATE,CTE1.WEEKNUMBER
ORDER BY CTE1.CHILDID ASC