SQL Server : complicated query - sql-server

I have two tables document and documentd; the first one contains the numbers of the invoices doc_num as primary key, document types doc_type (FACA, BLCO, BLCM, BLCK .....) and the document date doc_date.
Each invoice has one DOC_TYPE and one date, and each date my be contained in one or more invoices.
Table DOCUMENT:
DOC_NUM
DOC_TYPE
DOC_DATE
INVOICE1901221
FACA
22/01/2019
INVOICE1902221
FACA
22/02/2019
INVOICE1902222
FACA
22/02/2019
INVOICE1903221
FACA
22/03/2019
BLCO190122001
BLCO
22/01/2019
BLCO190123001
BLCO
23/01/2019
BLCM190122001
BLCM
22/01/2019
INVOICE1901021
FACA
02/01/2019
INVOICE1903011
FACA
01/03/2019
INVOICE1904221
FACA
22/04/2019
INVOICE1904222
FACA
22/04/2019
The second table is the details of each invoices he contains as foreign key doc_num the code of products for each invoice art_code and finally the prices of the products art_price.
Table DOCUMENTD:
DOC_NUM
ART_CODE
ART_PRICE
INVOICE1901221
PRODUCT1
1000
INVOICE1901221
PRODUCT2
2000
INVOICE1902221
PRODUCT3
950
INVOICE1902221
PRODUCT4
980
INVOICE1904221
PRODUCT1
1200
INVOICE1903011
PRODUCT2
900
BLCO190122001
ARTICLE1
900
BLCO190123001
ARTICLE2
800
[DOCUMENTD TABLE][2]
My goal in first step is to join the two tables using doc_num selects all FACA type invoices and their products except the prices they must be THE LAST UPDATED PRICE IN FACA TYPE.
RESULT:
INVOICE1904221
PRODUCT1
1200
22/04/2019
INVOICE1903011
PRODUCT2
900
01/03/2019
INVOICE1902221
PRODUCT3
950
22/02/2019
INVOICE1902221
PRODUCT4
980
22/02/2019
The second step I have another table how contain ORDER and ART_CODE:
ORDER
ART_CODE
1
PRODUCT1
2
PRODUCT2
3
PRODUCT3
I want to fetch the first result depend on this table:
INVOICE1904221
PRODUCT1
1200
22/04/2019
INVOICE1903011
PRODUCT2
900
01/03/2019
INVOICE1902221
PRODUCT3
950
22/02/2019
I try this but he fetch same product with different prices
SELECT
d1.DOC_NUM, dd1.ART_CODE, dd2.ART_PRICE, d2.DOC_DATE
FROM
document d1
INNER JOIN
documentd dd1 ON dd1.DOC_NUM = d1.DOC_NUM
INNER JOIN
documentd dd2 ON dd2.ART_CODE = dd1.ART_CODE
INNER JOIN
document d2 ON d2.DOC_NUM = dd2.DOC_NUM
AND d2.DOC_TYPE <> d1.DOC_TYPE
WHERE
d1.DOC_TYPE = 'FACA'

#MOHAMED NEJI it's really hard to understand what you meant. Please try to be more clear on your questions.
The proposed solution below shows how to get the results you asked in both RESULTS tables with the input data that you gave.
CREATE TABLE #DOCUMENT (
DOC_NUM VARCHAR(30)
, DOC_TYPE CHAR(4)
, DOC_DATE DATE)
CREATE TABLE #DOCUMENTD (
DOC_NUM VARCHAR(30)
, ART_CODE VARCHAR(20)
, ART_PRICE DECIMAL(10,4))
CREATE TABLE #OTHERTABLE(
[ORDER] int
, ART_CODE VARCHAR(20)
)
INSERT INTO #DOCUMENT
SELECT 'INVOICE1901221' DOC_NUM, 'FACA' DOC_TYPE, '2019-01-22' DOC_DATE
UNION ALL SELECT 'INVOICE1902221' DOC_NUM, 'FACA' DOC_TYPE, '2019-02-22' DOC_DATE
UNION ALL SELECT 'INVOICE1902222' DOC_NUM, 'FACA' DOC_TYPE, '2019-02-22' DOC_DATE
UNION ALL SELECT 'INVOICE1903221' DOC_NUM, 'FACA' DOC_TYPE, '2019-03-22' DOC_DATE
UNION ALL SELECT 'BLCO190122001' DOC_NUM, 'BLCO' DOC_TYPE, '2019-01-22' DOC_DATE
UNION ALL SELECT 'BLCO190123001' DOC_NUM, 'BLCO' DOC_TYPE, '2019-01-23' DOC_DATE
UNION ALL SELECT 'BLCM190122001' DOC_NUM, 'BLCM' DOC_TYPE, '2019-01-22' DOC_DATE
UNION ALL SELECT 'INVOICE1901021' DOC_NUM, 'FACA' DOC_TYPE, '2019-01-02' DOC_DATE
UNION ALL SELECT 'INVOICE1903011' DOC_NUM, 'FACA' DOC_TYPE, '2019-03-01' DOC_DATE
UNION ALL SELECT 'INVOICE1904221' DOC_NUM, 'FACA' DOC_TYPE, '2019-04-22' DOC_DATE
UNION ALL SELECT 'INVOICE1904222' DOC_NUM, 'FACA' DOC_TYPE, '2019-04-22' DOC_DATE
INSERT INTO #DOCUMENTD
SELECT 'INVOICE1901221' DOC_NUM, 'PRODUCT1' ART_CODE, 1000 ATR_PRICE
UNION ALL SELECT 'INVOICE1901221' DOC_NUM, 'PRODUCT2' ART_CODE, 2000 ATR_PRICE
UNION ALL SELECT 'INVOICE1902221' DOC_NUM, 'PRODUCT3' ART_CODE, 950 ATR_PRICE
UNION ALL SELECT 'INVOICE1902221' DOC_NUM, 'PRODUCT4' ART_CODE, 980 ATR_PRICE
UNION ALL SELECT 'INVOICE1904221' DOC_NUM, 'PRODUCT1' ART_CODE, 1200 ATR_PRICE
UNION ALL SELECT 'INVOICE1903011' DOC_NUM, 'PRODUCT2' ART_CODE, 900 ATR_PRICE
UNION ALL SELECT 'BLCO190122001' DOC_NUM, 'ARTICLE1' ART_CODE, 900 ATR_PRICE
UNION ALL SELECT 'BLCO190123001' DOC_NUM, 'ARTICLE2' ART_CODE, 800 ATR_PRICE
INSERT INTO #OTHERTABLE
SELECT 1 [ORDER], 'PRODUCT1' ART_CODE
UNION ALL SELECT 2 [ORDER], 'PRODUCT2' ART_CODE
UNION ALL SELECT 3 [ORDER], 'PRODUCT3' ART_CODE
;WITH Docs AS (
SELECT dd.DOC_NUM, dd.ART_CODE, dd.ART_PRICE, d.DOC_DATE ,ROW_NUMBER() OVER (PARTITION BY art_code ORDER BY DOC_DATE DESC ) rn
FROM #DOCUMENTD dd
INNER JOIN #DOCUMENT d
ON d.DOC_NUM = dd.DOC_NUM
WHERE DOC_TYPE = 'FACA'
)
SELECT DOC_NUM, ART_CODE, ART_PRICE, DOC_DATE
FROM Docs
WHERE rn = 1
;WITH Docs AS (
SELECT dd.DOC_NUM, dd.ART_CODE, dd.ART_PRICE, d.DOC_DATE ,ROW_NUMBER() OVER (PARTITION BY art_code ORDER BY DOC_DATE DESC ) rn
FROM #DOCUMENTD dd
INNER JOIN #DOCUMENT d
ON d.DOC_NUM = dd.DOC_NUM
WHERE DOC_TYPE = 'FACA'
)
SELECT DOC_NUM, Docs.ART_CODE, ART_PRICE, DOC_DATE
FROM Docs
INNER JOIN #OTHERTABLE ot
ON ot.ART_CODE = Docs.ART_CODE
WHERE rn = 1

Related

TSQL - Display the date which exist in all Model

MODEL DateCreated
----------------------
MODEL_1 2017-07-20
MODEL_1 2017-07-19
MODEL_1 2017-06-10
MODEL_1 2017-06-02
MODEL_2 2017-07-18
MODEL_2 2017-07-17
MODEL_2 2017-06-10
MODEL_2 2017-06-02
MODEL_3 2017-07-20
MODEL_3 2017-07-12
MODEL_3 2017-06-10
MODEL_3 2017-06-02
MODEL_3 2017-05-16
Expected result
DateCreated_Exist_In_All_Model
------------------------------
2017-06-10
2017-06-02
This means, only those DateCreated exist in all model will be displayed
Thanks to everyone who willing to help me.
One approach would be to aggregate on the creation date and then compare the count of distinct models appearing on each date against the total number of models appearing in the table.
SELECT
DateCreated AS DateCreated_Exist_In_All_Model
FROM [yourTable]
GROUP BY DateCreated
HAVING COUNT(DISTINCT MODEL) = (SELECT COUNT(DISTINCT MODEL) FROM yourTable)
Note that even though there appear to be only 3 models in your data set, we can make the query more flexible by using a subquery which counts the number of distinct models in the entire table.
DECLARE #Temp table(Model varchar(1000), DateCreated date)
INSERT INTO #Temp
SELECT 'MODEL_1' , '2017-07-20'
UNION ALL
SELECT 'MODEL_1', '2017-07-19'
UNION ALL
SELECT 'MODEL_1', '2017-06-10'
UNION ALL
SELECT 'MODEL_1', '2017-06-02'
UNION ALL
SELECT 'MODEL_2', '2017-07-17'
UNION ALL
SELECT 'MODEL_2', '2017-06-10'
UNION ALL
SELECT 'MODEL_2', '2017-06-02'
UNION ALL
SELECT 'MODEL_3', '2017-07-12'
UNION ALL
SELECT 'MODEL_3 ', '2017-06-10'
UNION ALL
SELECT 'MODEL_3 ', '2017-06-02'
UNION ALL
SELECT 'MODEL_3' , '2017-05-16'
DECLARE #ModelCount int
SELECT #ModelCount = COUNT(distinct Model) from #Temp
SELECT DISTINCT DateCreated FROM (
SELECT
COUNT(MODEL) OVER(PARTITION BY DateCreated) AS Counts, Model,
DateCreated
FROM #Temp) AS D
WHERE D.Counts = #ModelCount
Or
SELECT DateCreated FROM (
SELECT
ROW_NUMBER() OVER(PARTITION BY DateCreated ORDER BY DateCreated) as Rownum,
COUNT(MODEL) OVER(PARTITION BY DateCreated) AS Counts, Model,
DateCreated
FROM #Temp) AS D
WHERE D.Counts = #ModelCount and D.Rownum = 1

SQL Server: fill a range with dates from overlapping intervals with priority

I need to fill the range from 2017-04-01 to 2017-04-30 with the data from this table, knowing that the highest priority records should prevail over those with lower priorities
id startValidity endValidity priority
-------------------------------------------
1004 2017-04-03 2017-04-30 1
1005 2017-04-10 2017-04-22 2
1010 2017-04-19 2017-04-23 3
1006 2017-04-24 2017-04-28 2
1008 2017-04-26 2017-04-28 3
In practice I would need to get a result like this:
id startValidity endValidity priority
--------------------------------------------
1004 2017-04-03 2017-04-09 1
1005 2017-04-10 2017-04-18 2
1010 2017-04-19 2017-04-23 3
1006 2017-04-24 2017-04-25 2
1008 2017-04-26 2017-04-28 3
1004 2017-04-29 2017-04-30 1
can't think of anything elegant or more efficient solution right now . . .
-- Sample Table
declare #tbl table
(
id int,
startValidity date,
endValidty date,
priority int
)
-- Sample Data
insert into #tbl select 1004, '2017-04-03', '2017-04-30', 1
insert into #tbl select 1005, '2017-04-10', '2017-04-22', 2
insert into #tbl select 1010, '2017-04-19', '2017-04-23', 3
insert into #tbl select 1006, '2017-04-24', '2017-04-28', 2
insert into #tbl select 1008, '2017-04-26', '2017-04-28', 3
-- Query
; with
date_range as -- find the min and max date for generating list of dates
(
select start_date = min(startValidity), end_date = max(endValidty)
from #tbl
),
dates as -- gen the list of dates using recursive CTE
(
select rn = 1, date = start_date
from date_range
union all
select rn = rn + 1, date = dateadd(day, 1, d.date)
from dates d
where d.date < (select end_date from date_range)
),
cte as -- for each date, get the ID based on priority
(
select *, grp = row_number() over(order by id) - rn
from dates d
outer apply
(
select top 1 x.id, x.priority
from #tbl x
where x.startValidity <= d.date
and x.endValidty >= d.date
order by x.priority desc
) t
)
-- final result
select id, startValidity = min(date), endValidty = max(date), priority
from cte
group by grp, id, priority
order by startValidity
I do not understand the purpose of Calendar CTE or table.
So I am not using any REcursive CTE or calendar.
May be I hvn't understood the requirement completly.
Try this with diff sample data,
declare #tbl table
(
id int,
startValidity date,
endValidty date,
priority int
)
-- Sample Data
insert into #tbl select 1004, '2017-04-03', '2017-04-30', 1
insert into #tbl select 1005, '2017-04-10', '2017-04-22', 2
insert into #tbl select 1010, '2017-04-19', '2017-04-23', 3
insert into #tbl select 1006, '2017-04-24', '2017-04-28', 2
insert into #tbl select 1008, '2017-04-26', '2017-04-28', 3
;With CTE as
(
select * ,ROW_NUMBER()over(order by startValidity)rn
from #tbl
)
,CTE1 as
(
select c.id,c.startvalidity,isnull(dateadd(day,-1, c1.startvalidity)
,c.endValidty) Endvalidity
,c.[priority],c.rn
from cte c
left join cte c1
on c.rn+1=c1.rn
)
select id,startvalidity,Endvalidity,priority from cte1
union ALL
select id,startvalidity,Endvalidity,priority from
(
select top 1 id,ca.startvalidity,ca.Endvalidity,priority from cte1
cross apply(
select top 1
dateadd(day,1,endvalidity) startvalidity
,dateadd(day,-1,dateadd(month, datediff(month,0,endvalidity)+1,0)) Endvalidity
from cte1
order by rn desc)CA
order by priority
)t4
--order by startvalidity --if req

TSQL - Groups and Islands dates

I need a help on writing an optimal query for the below problem. Have attached the query I have with me but it is highly utilizing resources.
Below is the code to achieve above said logic. Please suggest some optimal way to achieve the same
-- drop table #me
create table #ME (memid int , EffectiveDate datetime , termdate datetime)
Insert into #ME values ('123','3-Dec-16','10-Jan-17')
Insert into #ME values ('123','11-Jan-17','6-Feb-17')
Insert into #ME values ('123','7-Feb-17','5-Mar-17')
Insert into #ME values ('123','8-Mar-17','15-Apr-17')
Insert into #ME values ('123','16-Apr-17','24-May-17')
--drop table #dim
select * from #ME
declare #StartDate datetime , #CutoffDate datetime
select #StartDate= min(effectivedate),#CutoffDate = max(termdate) From #me where termdate<>'9999-12-31 00:00:00.000'
SELECT d
into #dim
FROM
(
SELECT d = DATEADD(DAY, rn - 1, #StartDate)
FROM
(
SELECT TOP (DATEDIFF(DAY, #StartDate, #CutoffDate))
rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM sys.all_objects AS s1
CROSS JOIN sys.all_objects AS s2
-- on my system this would support > 5 million days
ORDER BY s1.[object_id]
) AS x
) AS y;
--drop table #MemEligibilityDateSpread
select MemID, D As DateSpread Into #MemEligibilityDateSpread From #Dim dim JOIN #me ME on dim.d between ME.effectivedate and me.termdate
--drop table #DateClasified
WITH CTE AS
(
SELECT MEmID,
UniqueDate = DateSpread,
DateGroup = DATEADD(dd, - ROW_NUMBER() OVER (PARTITION BY Memid ORDER BY Memid,DateSpread), DateSpread)
FROM #MemEligibilityDateSpread
GROUP BY Memid,DateSpread
)
--===== Now, if we find the MIN and MAX date for each DateGroup, we'll have the
-- Start and End dates of each group of contiguous daes. While we're at it,
-- we can also figure out how many days are in each range of days.
SELECT Memid,
StartDate = MIN(UniqueDate),
EndDate = MAX(UniqueDate)
INTO #DateClasified
FROM cte
GROUP BY Memid,DateGroup
ORDER BY Memid,StartDate
select ME.MemID,ME.EffectiveDate,ME.TermDate,DC.StartDate,DC.EndDate from #DateClasified dc join #me ME ON Me.MemID = dc.MemID
and (ME.EffectiveDate BETWEEN DC.StartDate AND DC.EndDate
OR ME.TermDate BETWEEN DC.StartDate AND DC.EndDate)
In cte0 and cte1, we create an ad-hoc tally/calendar table. Once we have that, it is a small matter to calculate and group by Island.
Currently, the tally is has a max of 10,000 days (27 years), but you can easily expand the tally table by adding , cte0 N5
;with cte0(N) as (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N))
,cte1(R,D) as (Select Row_Number() over (Order By (Select Null))
,DateAdd(DD,-1+Row_Number() over (Order By (Select Null)),(Select MinDate=min(EffectiveDate) From #ME))
From cte0 N1, cte0 N2, cte0 N3, cte0 N4)
Select MemID
,EffectiveDate
,TermDate
,SinceFrom = Min(EffectiveDate) over (Partition By Island)
,Tildate = Max(TermDate) over (Partition By Island)
From (
Select *,Island = R - Row_Number() over (Partition By MemID Order by TermDate)
From #ME A
Join cte1 B on D Between EffectiveDate and TermDate
) A
Group By MemID,Island,EffectiveDate,TermDate
Order By 1,2
Returns
MemID EffectiveDate TermDate SinceFrom Tildate
123 2016-12-03 2017-01-10 2016-12-03 2017-03-05
123 2017-01-11 2017-02-06 2016-12-03 2017-03-05
123 2017-02-07 2017-03-05 2016-12-03 2017-03-05
123 2017-03-08 2017-04-15 2017-03-08 2017-05-24
123 2017-04-16 2017-05-24 2017-03-08 2017-05-24
Edit - Now if you want a compressed dataset
Select MemID
,EffectiveDate = Min(EffectiveDate)
,TermDate = Max(TermDate)
From (
Select *,Island = R - Row_Number() over (Partition By MemID Order by TermDate)
From #ME A
Join cte1 B on D Between EffectiveDate and TermDate
) A
Group By MemID,Island
Order By 1,2
Returns
MemID EffectiveDate TermDate
123 2016-12-03 2017-03-05
123 2017-03-08 2017-05-24

Need help in SQL Query 5

I am using SQL Server 2008. I have data by each employee for each day. Below is the sample data.
WITH RawData as
(
SELECT '10001' AS EmpNo,'2015-01-01' as AttendanceDate,'FS' AS ShiftCode UNION
SELECT '10001','2015-01-02','WO' UNION
SELECT '10001','2015-01-03','FS' UNION
SELECT '10001','2015-01-04','FS' UNION
SELECT '10001','2015-01-05','FS' UNION
SELECT '10001','2015-01-06','FS' UNION
SELECT '10001','2015-01-07','FS' UNION
SELECT '10001','2015-01-08','FS' UNION
SELECT '10001','2015-01-09','WO' UNION
SELECT '10001','2015-01-10','FS' UNION
SELECT '10001','2015-01-11','FS' UNION
SELECT '10001','2015-01-12','FS' UNION
SELECT '10001','2015-01-13','FS' UNION
SELECT '10001','2015-01-14','FS' UNION
SELECT '10001','2015-01-15','FS' UNION
SELECT '10001','2015-01-16','WO' UNION
SELECT '10001','2015-01-17','FS' UNION
SELECT '10001','2015-01-18','FS' UNION
SELECT '10001','2015-01-19','FS' UNION
SELECT '10001','2015-01-20','FS' UNION
SELECT '10001','2015-01-21','FS' UNION
SELECT '10001','2015-01-22','FS' UNION
SELECT '10001','2015-01-23','WO' UNION
SELECT '10001','2015-01-24','FS' UNION
SELECT '10001','2015-01-25','FS' UNION
SELECT '10001','2015-01-26','FS' UNION
SELECT '10001','2015-01-27','FS' UNION
SELECT '10001','2015-01-28','FS' UNION
SELECT '10001','2015-01-29','FS' UNION
SELECT '10001','2015-01-30','WO' UNION
SELECT '10001','2015-01-31','FS' UNION
SELECT '10002','2015-01-01','FS' UNION
SELECT '10002','2015-01-02','WO' UNION
SELECT '10002','2015-01-03','WO' UNION
SELECT '10002','2015-01-04','FS' UNION
SELECT '10002','2015-01-05','FS' UNION
SELECT '10002','2015-01-06','FS' UNION
SELECT '10002','2015-01-07','FS' UNION
SELECT '10002','2015-01-08','FS' UNION
SELECT '10002','2015-01-09','WO' UNION
SELECT '10002','2015-01-10','WO' UNION
SELECT '10002','2015-01-11','FS' UNION
SELECT '10002','2015-01-12','FS' UNION
SELECT '10002','2015-01-13','FS' UNION
SELECT '10002','2015-01-14','FS' UNION
SELECT '10002','2015-01-15','FS' UNION
SELECT '10002','2015-01-16','WO' UNION
SELECT '10002','2015-01-17','WO' UNION
SELECT '10002','2015-01-18','FS' UNION
SELECT '10002','2015-01-19','FS' UNION
SELECT '10002','2015-01-20','FS' UNION
SELECT '10002','2015-01-21','FS' UNION
SELECT '10002','2015-01-22','FS' UNION
SELECT '10002','2015-01-23','WO' UNION
SELECT '10002','2015-01-24','WO' UNION
SELECT '10002','2015-01-25','FS' UNION
SELECT '10002','2015-01-26','FS' UNION
SELECT '10002','2015-01-27','FS' UNION
SELECT '10002','2015-01-28','FS' UNION
SELECT '10002','2015-01-29','FS' UNION
SELECT '10002','2015-01-30','WO' UNION
SELECT '10002','2015-01-31','WO')
SELECT * FROM RawData Order By EmpNo,AttendanceDate
How to write SQL Query to get following output based on this sample data ? The workweek of each employee starts on a Day after weekly off and it can be any day (mon, tue etc). The shift code denotes WO: weekly off, FS: First Shift, SS: Second Shift.
EmpNo WeekFrom WeekTo
10001 2015-01-01 2015-01-02
10001 2015-01-03 2015-01-09
10001 2015-01-10 2015-01-16
10001 2015-01-17 2015-01-23
10001 2015-01-24 2015-01-30
10001 2015-01-31 2015-01-31
10002 2015-01-01 2015-01-03
10002 2015-01-04 2015-01-10
10002 2015-01-11 2015-01-17
10002 2015-01-18 2015-01-24
10002 2015-01-25 2015-01-31
Got a solution. But its taking quite a long time on live table with 1 Million rows. Have I done something wrong in a query ? Or there is a better way of doing this.
WITH RawData as
(
-- Insert above data here.
)
,ProcessData AS (
SELECT EmpNo,AttendanceDate,ShiftCode,RowID = ROW_NUMBER() OVER (
ORDER BY EmpNo, AttendanceDate
), WeekNo = 1 FROM RawData
)
,FinalData
AS (
SELECT EmpNo, AttendanceDate, ShiftCode, RowID, WeekNo = 1
FROM ProcessData DA
WHERE RowID = 1
UNION ALL
SELECT DA.EmpNo, DA.AttendanceDate, DA.ShiftCode, DA.RowID,
WeekNo = (CASE WHEN FinalData.EmpNo != DA.EmpNo THEN 1 ELSE FinalData.WeekNo + (CASE WHEN (FinalData.ShiftCode = 'WO' AND DA.ShiftCode != 'WO') THEN 1 ELSE 0 END) END)
FROM FinalData
INNER JOIN ProcessData DA ON DA.RowID = FinalData.RowID + 1
)
SELECT EmpNo, MIN(AttendanceDate) AS StartDate, MAX(AttendanceDate) AS EndDate, WeekNo
FROM FinalData
GROUP BY EmpNo, WeekNo
ORDER BY EmpNo, WeekNo
Try this:
SQL Fiddle
;WITH RawData AS (
-- Your insert statements here
),
Cte AS(
SELECT *,
RN = ROW_NUMBER() OVER(PARTITION BY EmpNo, grp ORDER BY AttendanceDate DESC)
FROM (
SELECT *,
grp = DATEADD(DAY, -ROW_NUMBER() OVER(PARTITION BY EmpNo ORDER BY AttendanceDate), AttendanceDate)
FROM RawData
WHERE ShiftCode = 'WO'
)t
),
CteWeekOff AS(
SELECT EmpNo, AttendanceDate, ShiftCode FROM cte WHERE RN = 1
),
CteFinal AS(
SELECT
EmpNo,
WeekFrom = MIN(AttendanceDate),
Weekto = MAX(AttendanceDate)
FROM (
SELECT *,
grp = DATEADD(DAY, - ROW_NUMBER() OVER(PARTITION BY EmpNo ORDER BY AttendanceDate), AttendanceDate)
FROM RawData
WHERE ShiftCode <> 'WO'
)t
GROUP BY EmpNo, grp
)
SELECT
EmpNo,
WeekFrom = x.WeekFrom,
WeekTo = w.AttendanceDate
FROM CteWeekOff w
CROSS APPLY(
SELECT TOP 1 WeekFrom
FROM CteFinal r
WHERE
r.EmpNo = w.EmpNo
AND r.WeekFrom <= w.AttendanceDate
ORDER BY r.WeekFrom DESC
)x(WeekFrom)
UNION ALL
SELECT
EmpNo,
WeekFrom = x.WeekFrom,
WeekTo = t.AttendanceDate
FROM (
SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY EmpNo ORDER BY AttendanceDate DESC)
FROM RawData
)t
CROSS APPLY(
SELECT TOP 1 AttendanceDate
FROM CteFinal r
WHERE
r.EmpNo = t.EmpNo
AND r.WeekFrom < t.AttendanceDate
ORDER BY r.WeekFrom DESC
)x(WeekFrom)
WHERE
RN = 1
AND ShiftCode <> 'WO'
ORDER BY EmpNo, WeekFrom
Finally this worked. 5 seconds on 230,000 records. I will go ahead with my solution. Thanks for your time. Hope this solution helps someone.
-- Step 1 : Save it to temp table
SELECT EmpNo,AttendanceDate,ShiftCode,RowID = ROW_NUMBER() OVER (
ORDER BY EmpNo, AttendanceDate
), WeekNo = 1 into #RawData FROM -- My table
-- Step 2 : Use temp table
;WITH FinalData
AS (
SELECT EmpNo, AttendanceDate, ShiftCode, RowID, WeekNo = 1
FROM #RawData DA
WHERE RowID = 1
UNION ALL
SELECT DA.EmpNo, DA.AttendanceDate, DA.ShiftCode, DA.RowID,
WeekNo = (CASE WHEN FinalData.EmpNo != DA.EmpNo THEN 1 ELSE FinalData.WeekNo + (CASE WHEN (FinalData.ShiftCode = 'WO' AND DA.ShiftCode != 'WO') THEN 1 ELSE 0 END) END)
FROM FinalData
INNER JOIN #RawData DA ON DA.RowID = FinalData.RowID + 1
)
SELECT EmpNo, MIN(AttendanceDate) AS StartDate, MAX(AttendanceDate) AS EndDate, WeekNo
FROM FinalData
GROUP BY EmpNo, WeekNo
ORDER BY EmpNo, WeekNo
OPTION (MAXRECURSION 0)

Pivoting the employee column

I have a table like so.
I need the final result show only one record for each order (essentially combine the suborders). Only sum up the revenue for distinct suborders (for order 0935744, sum up 575.04 + 31.68). An order can have a maximum of 2 employees
Final result should be like this:
order totalrevenue employee1 employee2
0813700 258.57 CREW NULL
0935744 606.72 95liv 95nat
I've tried using row_number and doing some joins but I've had no luck.
Sample code
SELECT N'0813700' AS [OrderNum], N'1077980' AS [SubOrder], N'CREW' AS [employeeid], N'258.57' AS [revenue] UNION ALL
SELECT N'0935744' AS [OrderNum], N'1257060' AS [SubOrder], N'95LIV' AS [employeeid], N'575.04' AS [revenue] UNION ALL
SELECT N'0935744' AS [OrderNum], N'1342944' AS [SubOrder], N'95LIV' AS [employeeid], N'31.68' AS [revenue] UNION ALL
SELECT N'0935744' AS [OrderNum], N'1257060' AS [SubOrder], N'95NAT' AS [employeeid], N'575.04' AS [revenue] UNION ALL
SELECT N'0935744' AS [OrderNum], N'1342944' AS [SubOrder], N'95NAT' AS [employeeid], N'31.68' AS [revenue]
this should give the result you are looking for:
create table #temp
(
ordernum int,
suborder int,
employeeid varchar(50),
revenue money
)
insert into #temp values(0813700, 1077980, 'CREW', 258.57)
insert into #temp values(0935744, 1257060, '95LIV', 575.04)
insert into #temp values(0935744, 1342944, '95LIV', 31.68)
insert into #temp values(0935744, 1257060, '95NAT', 575.04)
insert into #temp values(0935744, 1342944, '95NAT', 31.68)
select ordernum
, sum(revenueperorder) as total
, employee1
, case when employee1 = employee2 then null else employee2 end as employee2
from
(
select ordernum
, revenue as revenueperorder
, min(employeeid) as employee1
, max(employeeid) as employee2
from #temp
group by ordernum, revenue
) x
group by ordernum, employee1, employee2
drop table #temp
Results:
813700 258.57 CREW NULL
935744 606.72 95LIV 95NAT
Answers so far would require a hard coded pivot with employee1 & 2 defined as pivot entities, of couse, if your data is dynamic i'd imagine you'll have a varying number of employees (and thus would need a varying number of columns)? If so, i'd suggest you adopt a hybrid dynamic SQL / Pivot example such as:
Pivot Table and Concatenate Columns
or this:
PIVOT in sql 2005
DECLARE #SubOrder TABLE
(
OrderNum INT NOT NULL,
SubOrder INT NOT NULL,
EmployeeID NVARCHAR(50) NOT NULL,
Revenue NUMERIC(10, 2) NOT NULL
);
INSERT #SubOrder (OrderNum, SubOrder, EmployeeID, Revenue)
SELECT N'0813700' AS [OrderNum], N'1077980' AS [SubOrder], N'CREW' AS [employeeid], N'258.57' AS [revenue] UNION ALL
SELECT N'0935744' AS [OrderNum], N'1257060' AS [SubOrder], N'95LIV' AS [employeeid], N'575.04' AS [revenue] UNION ALL
SELECT N'0935744' AS [OrderNum], N'1342944' AS [SubOrder], N'95LIV' AS [employeeid], N'31.68' AS [revenue] UNION ALL
SELECT N'0935744' AS [OrderNum], N'1257060' AS [SubOrder], N'95NAT' AS [employeeid], N'575.04' AS [revenue] UNION ALL
SELECT N'0935744' AS [OrderNum], N'1342944' AS [SubOrder], N'95NAT' AS [employeeid], N'31.68' AS [revenue];
SELECT pvt.OrderNum,
pvt.TotalRevenue,
pvt.[1] AS Emp1,
pvt.[2] AS Emp2
FROM
(
SELECT dt.OrderNum,
dt.EmployeeID,
DENSE_RANK() OVER(PARTITION BY dt.OrderNum ORDER BY dt.EmployeeID) AS Rnk,
SUM(dt.Revenue) OVER(PARTITION BY dt.OrderNum) AS TotalRevenue
FROM
(
SELECT so.OrderNum,
so.EmployeeID,
ROW_NUMBER() OVER(PARTITION BY so.OrderNum, so.SubOrder ORDER BY ##SPID) AS RowNum,
so.Revenue
FROM #SubOrder so
) dt
WHERE dt.RowNum = 1
) src
PIVOT ( MAX(src.EmployeeID) FOR src.Rnk IN ([1], [2]) ) pvt
Results:
OrderNum TotalRevenue Emp1 Emp2
-------- ------------ ------ -----
813700 258.57 CREW NULL
935744 606.72 95LIV 95NAT
Intermediate results (...) src:
OrderNum EmployeeID Rnk TotalRevenue
-------- ---------- --- ------------
813700 CREW 1 258.57
935744 95LIV 1 606.72
935744 95NAT 2 606.72
Intermediate results (...) dt:
OrderNum EmployeeID RowNum Revenue
-------- ---------- ------ -------
813700 CREW 1 258.57
935744 95LIV 1 575.04
935744 95NAT 2 575.04
935744 95NAT 1 31.68
935744 95LIV 2 31.68
How about this? (Revised after comment from OP)
Assumptions:
No more than two employees per order.
One employee per sub-order (as in the example)
Revenue for a sub order is consistently duplicated on rows for the suborder (per example)
Code Example
select order, sum(revenue) as totalrevenue, max(employee1) as employee1,
case
when max(employee1) = max(employee2) then null
else max(employee2)
end as employee2
from (
select order, suborder, max(revenue) as revenue, max(employeeid)
from orders
group by order, suborder
) SubOrderTotal
group by order
Generally I would not recommend the rigid transform to two employees or the duplication of suborder revenue. Making such rigid assumptions often leads to bugs when dealing with real world data. But, I don't know your data.

Resources