--demo setup
drop table if exists dbo.product
go
create table dbo.Product
(
ProductId int,
ProductTitle varchar(55),
ProductCategory varchar(255),
Loaddate datetime
)
insert into dbo.Product
values (1, 'Table', 'ABCD', '3/4/2018'),
(1, 'Table', 'ABCD', '3/5/2018'),
(1, 'Table', 'ABCD', '3/6/2018'),
(1, 'Table', 'XYZ', '3/7/2018'),
(1, 'Table', 'XYZ', '3/8/2018'),
(1, 'Table', 'XYZ', '3/9/2018'),
(1, 'Table', 'GHI', '3/10/2018'),
(1, 'Table', 'GHI', '3/11/2018'),
(1, 'Table', 'XYZ', '3/12/2018'),
(1, 'Table', 'XYZ', '3/13/2018')
SELECT
product.productid,
product.producttitle,
product.productcategory,
MIN(product.loaddate) AS BeginDate,
-- ,max(product.LoadDate) as BeginDate1
CASE
WHEN MAX(product.loaddate) = MAX(oa.enddate1)
THEN '12/31/9999'
ELSE MAX(product.loaddate)
END AS EndDate
FROM
dbo.product product
CROSS APPLY
(SELECT MAX(subproduct.loaddate) EndDate1
FROM dbo.product subproduct
WHERE subproduct.productid = product.productid) oa
GROUP BY
productid, producttitle, productcategory
Output:
productid
producttitle
productcategory
BeginDate
EndDate
1
Table
ABCD
2018-03-04 00:00:00.000
2018-03-06 00:00:00.000
1
Table
XYZ
2018-03-07 00:00:00.000
9999-12-31 00:00:00.000
1
Table
GHI
2018-03-10 00:00:00.000
2018-03-11 00:00:00.000
Desired output:
productid
producttitle
productcategory
BeginDate
EndDate
1
Table
ABCD
2018-03-04 00:00:00.000
2018-03-06 00:00:00.000
1
Table
XYZ
2018-03-07 00:00:00.000
2018-03-09 00:00:00.000
1
Table
GHI
2018-03-10 00:00:00.000
2018-03-11 00:00:00.000
1
Table
XYZ
2018-03-12 00:00:00.000
9999-12-31 00:00:00.000
The last two inserted rows repeat the data from Loaddate '3/7/2018'-'3/9/2018', this doesn't happen if any of the new inserted rows doesn't repeat data. The only thing that changes is the LoadDate, giving me incorrect output. how can i get something like that desired output?
Well, first of all, you need to find a sequence number over all your records. If you already have a primary key, that's good. In example you gave us, there's no such column, so let's generate it.
Then, we make pairs with start and end dates for each product's category change. Another thing is to group all these product's category changes.
Finally, we make just a simple group by:
;
with cte as ( select *,
row_number() over(partition by ProductId order by Loaddate) as rn
from product
), cte2 as ( select t1.ProductId,
t1.ProductTitle,
t1.ProductCategory,
t1.Loaddate as BeginDate,
case
when t1.ProductCategory <> t2.ProductCategory
then t1.Loaddate
else coalesce(t2.Loaddate, null)
end as EndDate,
row_number() over(order by t1.ProductId, t1.Loaddate) as rn_overall,
row_number() over(partition by t1.ProductId, t1.ProductCategory order by t1.Loaddate) as rn_category
from cte as t1
left join cte as t2
on t2.ProductId = t1.ProductId
and t2.rn = t1.rn + 1
), cte3 as ( select *,
min(rn_overall) over (partition by ProductId, ProductCategory, rn_overall - rn_category) as product_group
from cte2
)
select ProductId, ProductTitle, ProductCategory,
min(BeginDate) as BeginDate,
case
when max(case when EndDate is null then 1 else 0 end) = 0
then max(EndDate)
else null
end as EndDate
from cte3
group by ProductId, ProductTitle, ProductCategory, product_group
order by ProductId, BeginDate
Related
I have a table that has entries like
Acct Nurse EntryDateTime DBCode Answer FormSeq
123 Sally 9/8/2020 09:22 Code1 Ans1 0001
123 Jim 9/8/2020 10:25 Code1 Ans2 0001
123 Sally 9/8/2020 09:15 Code2 C2Ans1 0001
I have a query that is pivoting this to get the answer from the last entry based on DBCode and EntryDateTime that works great. What I need to do is get the NURSE as well as the answer.
So my row would be
Acct Code1 Code1Nurse Code2 Code2Nurse
123 Ans2 Jim C2Ans1 Sally
Is there a way to do this? I would need the nurse for each unique DBCode
Here is my pivot code:
SELECT * FROM (
SELECT
[AcctNumber],
[Answer],
[DBCode],
[EntryDate],
[FormCode],[FormSeq]
FROM V_FAC_MULTIAPP_FORM_WITH_HOURLY
) MultiApp
PIVOT (
MAX( [Answer])
FOR [DBCode]
IN (
[AST],
[SDRM],
[SDRF],[SDAAS],[SDDCT],[SDDAS],[SDABY],[SDDAT],[SDTRCC],[SDADMTW],[Prptic],[Pdcrt]
)
) AS PivotTable WHERE EntryDate >='8/15/2020' and FormCode='LL003' ORDER BY EntryDate
You could use conditional aggregation.
Data
drop table if exists #tTEST;
go
select * INTO #tTEST from (values
(123, 'Sally', '9/8/2020 09:22', 'Code1', 'Ans1', '0001'),
(123, 'Jim', '9/8/2020 10:25', 'Code1', 'Ans2', '0001'),
(123, 'Sally', '9/8/2020 09:15', 'Code2', 'C2Ans1', '0001')) V(Acct, Nurse, EntryDateTime, DBCode, Answer, FormSeq);
Query
;with rn_cte as (
select *, row_number() over (partition by DBCode order by EntryDateTime desc) rn
from #tTEST)
select Acct,
max(case when DBCode='Code1' and rn=1 then Answer else null end) Code1,
max(case when DBCode='Code1' and rn=1 then Nurse else null end) Code1Nurse,
max(case when DBCode='Code2' and rn=1 then Answer else null end) Code2,
max(case when DBCode='Code2' and rn=1 then Nurse else null end) Code2Nurse
from rn_cte
group by Acct;
Output
Acct Code1 Code1Nurse Code2 Code2Nurse
123 Ans2 Jim C2Ans1 Sally
Here is something to play with. It would take some tinkering, but you could make the query dynamic to build out the columns to select based upon the DBCodes you have in your table...
IF OBJECT_ID('tempdb..#V_FAC_MULTIAPP_FORM_WITH_HOURLY') IS NOT NULL
DROP TABLE #V_FAC_MULTIAPP_FORM_WITH_HOURLY;
CREATE TABLE #V_FAC_MULTIAPP_FORM_WITH_HOURLY
(
Acct INT,
Nurse VARCHAR(20),
EntryDateTime DATETIME,
DBCode VARCHAR(10),
Answer VARCHAR(10),
FormSeq VARCHAR(10)
)
INSERT #V_FAC_MULTIAPP_FORM_WITH_HOURLY
VALUES
(123,'Sally','9/8/2020 09:22','Code1','Ans1','0001'),
(123,'Jim','9/8/2020 10:25','Code1','Ans2','0001'),
(123,'Sally','9/8/2020 09:15','Code2','C2Ans1','0001');
WITH Top_Row_Per_DBCode_By_EntryDate AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY DBCode ORDER BY EntryDateTime DESC) AS top_row,
Acct,
DBCode,
Nurse+':'+Answer AS Answer
FROM #V_FAC_MULTIAPP_FORM_WITH_HOURLY
), Filtered_CTE AS
(
SELECT *
FROM Top_Row_Per_DBCode_By_EntryDate
WHERE top_row = 1
)
SELECT Acct,
Substring([Code1],CHARINDEX(':',[Code1])+1,LEN([Code1])-CHARINDEX(':',[Code1])) AS Code1,
Substring([Code1],0,CHARINDEX(':',[Code1])) AS Code1Nurse,
Substring([Code2],CHARINDEX(':',[Code2])+1,LEN([Code2])-CHARINDEX(':',[Code2])) AS Code2,
Substring([Code2],0,CHARINDEX(':',[Code2])) AS Code2Nurse
FROM Filtered_CTE
PIVOT
(
MAX( [Answer]) FOR [DBCode]
IN ([Code1],[Code2])
) AS PivotTable
Consider the following table structure and sample data -
EmpID InputDateTime StatusINOUT
-------------------------------------
1 2018-05-26 08:44 1
1 2018-05-26 08:44 2
2 2018-05-28 08:44 1
2 2018-05-28 12:44 2
1 2018-05-21 08:44 1
1 2018-05-21 10:44 2
2 2018-05-23 08:44 1
2 2018-05-23 08:44 2
Now I want to separate column InputDateTime into two columns i.e., INTIME(1) and OUTTIME(2). The logic behind this is the date for which StatusInOut is 1 will be InTime and for StatusInOut is 2 that date value will be OUTTIME(2).
The expected output format is as shown below:
Empid INTIME(1) OUTIME(2)
--------------------------------------------
1 2018-05-26 08:44 2018-05-26 08:44
2 2018-05-28 08:44 2018-05-28 12:44
1 2018-05-21 08:44 2018-05-21 10:44
2 2018-05-23 08:44 2018-05-23 08:44
use case when
select empid,max(case when statusINOut=1 then Datetime end) as INtime,
max(case when statusINOut=2 then Datetime end) as Outtime
from table_name t
group by empid,convert(date,Datetime)
Try the following query using join and update.
create table #tempStatus (EmpId int, intTime datetime, sStatus int)
insert into #tempStatus
values(1, '2018-05-26 08:44', 1),
(1, '2018-05-26 08:44', 2),
(2, '2018-05-28 08:44', 1),
(2, '2018-05-28 12:44', 2),
(1, '2018-05-21 08:44', 1),
(1, '2018-05-21 10:44', 2),
(2, '2018-05-23 08:44', 1),
(2, '2018-05-23 08:44', 2)
,(3, '2018-05-23 08:44', 1)
select EmpId, MIN(intTime) as intTime, MAX(intTime) as OutTime into #tempA from (
select EmpId, intTime, intTime as OutTime
from #tempStatus where sStatus = 1
)a
group by EmpId, intTime
update s
set s.OutTime = t.outTime
from #tempA s
left join
(
select EmpId, MAX(outTime) as outTime from(
select EmpId, intTime as outTime
from #tempStatus where sStatus = 2
)b
group by empId,outTime) t
on s.EmpId = t.EmpId and Convert(Varchar,s.OutTime,112) = Convert(Varchar,t.outTime,112)
select * from #tempA order by EmpId
drop table #tempA
DROP TABLE #tempStatus
OR you can also try the below one query
select empid,
max(case when sStatus = 1 then intTime end) as INTIME,
max(case when sStatus = 2 then intTime end) as OUTIME
from (select t.*,
row_number () over ( order by inttime) as seq1,
row_number () over (partition by empid order by inttime) as seq2
from #tempStatus t
) t
group by empid, (seq1-seq2);
Check for the similar answers - here
try:
select EmpID
, min(DateTime) INTIME(1)
, max(DateTime) OUTIME(2)
from TABLE
group by EmpID;
In a same id, if any of row's effective date and enddate overlaps then we need group it up in a unique id
In below image dategroup is the desired output column
Data is sorted in order by ID asc, EffectiveDate ASC, EndDate Desc
CREATE TABLE #DataTable (id int , EffectiveDate datetime, Enddate Datetime )
INSERT [dbo].#DataTable ([id], [EffectiveDate], [Enddate]) VALUES (1, CAST(N'2017-01-01 00:00:00.000' AS DateTime), CAST(N'2017-01-11 00:00:00.000' AS DateTime))
GO
INSERT [dbo].#DataTable ([id], [EffectiveDate], [Enddate]) VALUES (1, CAST(N'2017-01-02 00:00:00.000' AS DateTime), CAST(N'2017-01-05 00:00:00.000' AS DateTime))
GO
INSERT [dbo].#DataTable ([id], [EffectiveDate], [Enddate]) VALUES (1, CAST(N'2017-01-03 00:00:00.000' AS DateTime), CAST(N'2017-01-12 00:00:00.000' AS DateTime))
GO
INSERT [dbo].#DataTable ([id], [EffectiveDate], [Enddate]) VALUES (1, CAST(N'2017-01-06 00:00:00.000' AS DateTime), CAST(N'2017-01-09 00:00:00.000' AS DateTime))
GO
INSERT [dbo].#DataTable ([id], [EffectiveDate], [Enddate]) VALUES (1, CAST(N'2017-01-13 00:00:00.000' AS DateTime), CAST(N'2017-01-19 00:00:00.000' AS DateTime))
GO
INSERT [dbo].#DataTable ([id], [EffectiveDate], [Enddate]) VALUES (2, CAST(N'2017-02-01 00:00:00.000' AS DateTime), CAST(N'2017-02-11 00:00:00.000' AS DateTime))
GO
INSERT [dbo].#DataTable ([id], [EffectiveDate], [Enddate]) VALUES (2, CAST(N'2017-02-06 00:00:00.000' AS DateTime), CAST(N'2017-02-16 00:00:00.000' AS DateTime))
GO
Try this, Hope it helps. Not the most attractive code but it should work. I may clean it up later if I find some time.
;WITH cte_StepOne as
(
SELECT ROW_NUMBER() OVER (ORDER BY a.[id],
a.[EffectiveDate],
a.[Enddate]) AS SeqNo,
a.[id],
a.[EffectiveDate],
a.[Enddate],
b.[id] AS OverLapID,
b.[EffectiveDate] AS [OverLapEffectiveDate],
b.[Enddate] AS [OverLapEnddate]
FROM ##DataTable a
LEFT JOIN ##DataTable b
ON a.EffectiveDate BETWEEN b.EffectiveDate
AND b.EndDate
AND a.EffectiveDate <> b.EffectiveDate
AND a.EndDate <> b.EndDate --and a.ID <> b.ID
)
,cte_StepTwo AS
(
SELECT SeqNo,
id,
EffectiveDate,
Enddate,
LEAD(OverLapEffectiveDate, 1) OVER (ORDER BY SeqNo) AS LeadValue,LAG(id, 1) OVER (ORDER BY SeqNo) AS LeadValueID,
OverLapID,
OverLapEffectiveDate,
OverLapEnddate
FROM cte_StepOne
)
,cte_Result AS
(
SELECT id,
EffectiveDate,
Enddate,
CASE
WHEN LeadValue = EffectiveDate AND OverLapEffectiveDate IS NULL THEN ID
WHEN OverLapID IS NULL THEN LeadValueID + 1
ELSE OverLapID
END AS OverLapID,
CASE
WHEN LeadValue = EffectiveDate AND OverLapEffectiveDate IS NULL THEN EffectiveDate
ELSE OverLapEffectiveDate
END AS OverLapEffectiveDate,
CASE
WHEN LeadValue = EffectiveDate AND OverLapEffectiveDate IS NULL THEN Enddate
ELSE OverLapEnddate
END AS OverLapEnddate
FROM cte_StepTwo
)
SELECT DISTINCT id,
EffectiveDate,
Enddate,
DENSE_RANK() OVER (ORDER BY ID,OverLapID) AS DateGroup
FROM cte_Result
ORDER BY id,EffectiveDate
Result:
This answer takes the approach of trying to identify records for which the running DateGroup counter should be incremented. Ultimately, we will assign a value of 1 to such records. With this assignment in hand, we can then simply take a cumulative sum to generate the DateGroup.
-- this CTE identifies all new ID records
WITH cte1 AS (
SELECT t.ID, t.EffectiveDate, t.EndDate
FROM
(
SELECT ID, EffectiveDate, EndDate,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY EffectiveDate) rn
FROM yourTable
) t
WHERE t.rn = 1
),
-- this CTE identifies all records whose both effective and end dates
-- do not fall within the range of the start ID record
cte2 AS (
SELECT t1.ID, t1.EffectiveDate, t1.EndDate
FROM yourTable t1
INNER JOIN cte1 t2
ON t1.ID = t2.ID AND
t1.EffectiveDate NOT BETWEEN t2.EffectiveDate AND t2.EndDate AND
t1.EndDate NOT BETWEEN t2.EffectiveDate AND t2.EndDate
),
-- this CTE returns the original table with a new column, amount, which
-- contains a value of 1 should that record cause the DateGroup to be
-- incremented by 1
cte3 AS (
SELECT t1.ID, t1.EffectiveDate, t1.EndDate,
CASE WHEN t2.ID IS NOT NULL OR t3.ID IS NOT NULL THEN 1 ELSE 0 END AS amount
FROM yourTable t1
LEFT JOIN cte1 t2
ON t1.ID = t2.ID AND
t1.EffectiveDate = t2.EffectiveDate AND
t1.EndDate = t2.EndDate
LEFT JOIN cte2 t3
ON t1.ID = t3.ID AND
t1.EffectiveDate = t3.EffectiveDate AND
t1.EndDate = t3.EndDate
)
-- finally, take a cumulative sum of the 'amount' column to generate the DateGroup
SELECT t1.ID,
t1.EffectiveDate,
t1.EndDate,
SUM(t2.amount) AS DateGroup
FROM cte3 t1
INNER JOIN cte3 t2
ON t1.ID >= t2.ID AND
t1.EffectiveDate >= t2.EffectiveDate
GROUP BY t1.id, t1.EffectiveDate, t1.EndDate;
Output:
Demo here:
Rextester
Data used:
CREATE TABLE yourTable (ID int, EffectiveDate datetime, EndDate datetime);
INSERT INTO yourTable
VALUES
(1, '2017-01-01 00:00:00.000', '2017-01-11 00:00:00.000'),
(1, '2017-01-02 00:00:00.000', '2017-01-05 00:00:00.000'),
(1, '2017-01-03 00:00:00.000', '2017-01-12 00:00:00.000'),
(1, '2017-01-06 00:00:00.000', '2017-01-09 00:00:00.000'),
(1, '2017-01-13 00:00:00.000', '2017-01-19 00:00:00.000'),
(2, '2017-02-01 00:00:00.000', '2017-02-11 00:00:00.000'),
(2, '2017-02-06 00:00:00.000', '2017-02-16 00:00:00.000');
What about this? It's simpler that other solutions posted:
WITH
CTE_GetFirstRecordForEachId AS
(
SELECT
id,
EffectiveDate,
Enddate,
rn = ROW_NUMBER() OVER (PARTITION BY id ORDER BY EffectiveDate, EndDate)
FROM
#DataTable
),
CTE_GetOutOfDateRange AS
(
SELECT
a.*,
OutOfDateRange =
CASE WHEN (b.EffectiveDate>=a.EffectiveDate AND b.EffectiveDate<=b.Enddate) OR (b.Enddate>=a.EffectiveDate AND b.Enddate<=b.Enddate)
THEN 0
ELSE 1
END
FROM
#DataTable a
INNER JOIN
CTE_GetFirstRecordForEachId b ON a.id = b.id AND b.rn=1
)
SELECT
id,
Effectivedate,
Enddate,
DateGroup = DENSE_RANK() OVER (ORDER BY id, OutOfDateRange)
FROM
CTE_GetOutOfDateRange
ORDER BY
id, Effectivedate, Enddate
Output:
id Effectivedate Enddate DateGroup
----------- ----------------------- ----------------------- --------------------
1 2017-01-01 00:00:00.000 2017-01-11 00:00:00.000 1
1 2017-01-02 00:00:00.000 2017-01-05 00:00:00.000 1
1 2017-01-03 00:00:00.000 2017-01-12 00:00:00.000 1
1 2017-01-06 00:00:00.000 2017-01-09 00:00:00.000 1
1 2017-01-13 00:00:00.000 2017-01-19 00:00:00.000 2
2 2017-02-01 00:00:00.000 2017-02-11 00:00:00.000 3
2 2017-02-06 00:00:00.000 2017-02-16 00:00:00.000 3
What about this (I am still testing it)
WITH Z AS
(SELECT * FROM (SELECT ID, [EffectiveDate], ENDDate
, LAG(ID) OVER (PARTITION BY ID ORDER BY EffectiveDate, ENDDate Desc) AS ID_Prec
, LAG(EffectiveDate) OVER (PARTITION BY ID ORDER BY EffectiveDate, ENDDate Desc) AS EffDate_Prec
, LAG(ENDDate) OVER (PARTITION BY ID ORDER BY EffectiveDate, ENDDate Desc) AS EndDate_Prec
, ROW_NUMBER() OVER (ORDER BY ID, EffectiveDate,ENDDate DESC) AS RN
, 1 AS DATEGROUP
FROM #DataTable ) C WHERE RN = 1
UNION ALL
SELECT A.ID, A.EffectiveDate, A.Enddate
, A.ID_Prec, A.EffDate_Prec
, A.EndDate_Prec
, A.RN
, CASE WHEN A.ID = A.ID_PREC AND (A.EffectiveDate <=A.EndDate_Prec /* OR A.EndDate>=A.EffDate_Prec*/) THEN Z.DATEGROUP
ELSE Z.DATEGROUP+1 END AS DATEGROUP
FROM (SELECT A.ID, A.EffectiveDate, A.ENDDate
, LAG(A.ID) OVER (PARTITION BY A.ID ORDER BY A.EffectiveDate, A.ENDDate Desc) AS ID_Prec
, LAG(A.EffectiveDate) OVER (PARTITION BY A.ID ORDER BY A.EffectiveDate, A.ENDDate Desc) AS EffDate_Prec
, LAG(A.ENDDate) OVER (PARTITION BY A.ID ORDER BY A.EffectiveDate, A.ENDDate Desc) AS EndDate_Prec
, ROW_NUMBER() OVER (ORDER BY A.ID, A.EffectiveDate,A.ENDDate DESC) AS RN
, 1 AS DATEGROUP
FROM #DataTable A) A
INNER JOIN Z ON A.RN -1= Z.RN
)
SELECT ID, EffectiveDate, Enddate, DATEGROUP FROM Z
Output:
ID EffectiveDate Enddate DATEGROUP
----------- ----------------------- ----------------------- -----------
1 2017-01-01 00:00:00.000 2017-01-11 00:00:00.000 1
1 2017-01-02 00:00:00.000 2017-01-05 00:00:00.000 1
1 2017-01-03 00:00:00.000 2017-01-12 00:00:00.000 1
1 2017-01-06 00:00:00.000 2017-01-09 00:00:00.000 1
1 2017-01-13 00:00:00.000 2017-01-19 00:00:00.000 2
2 2017-02-01 00:00:00.000 2017-02-11 00:00:00.000 3
2 2017-02-06 00:00:00.000 2017-02-16 00:00:00.000 3
guess you are missing some test scenario in your sample date.
;with CTE as
(
select *,ROW_NUMBER()over(order by id, effectivedate)rn
from #DataTable
)
,CTE1 AS
(
select *, 1 New_ID
from cte
where rn=1
union ALL
select c.id,c.effectivedate,c.enddate,c.rn
,case when c.effectivedate between c1.effectivedate
and c1.enddate
and c.id=c1.id then c1.New_ID
else c1.New_ID+1
END
from cte c
inner join cte1 c1
on c.rn=c1.rn+1
and c.rn>1 and c.rn<=7
)
select * from cte1
drop table #DataTable
this may help you. I posted here shortest and simplest version of tsql...
WITH CTE AS (
SELECT *,
ISNULL(LAG(EffectiveDate) OVER (PARTITION BY id ORDER BY id,EffectiveDate,Enddate),EffectiveDate) AS PreviousEffDate,
ISNULL(LAG(Enddate) OVER (PARTITION BY id ORDER BY id,EffectiveDate,Enddate),Enddate) AS PreviousEndDate
FROM #DataTable)
SELECT id,
EffectiveDate,
Enddate,
DENSE_RANK() OVER (ORDER BY id,CASE
WHEN EffectiveDate BETWEEN PreviousEffDate AND PreviousEndDate OR
Enddate BETWEEN PreviousEffDate AND PreviousEndDate OR
PreviousEffDate BETWEEN EffectiveDate AND Enddate OR
PreviousEndDate BETWEEN EffectiveDate AND Enddate
THEN 0
ELSE 1
END) AS DateGroup
FROM CTE
Result:
Have got this one from another forum; altered as per my requirement . looks simple and effective.
WITH C1 AS (
SELECT *,
CASE WHEN EffectiveDate <= MAX(ISnull(EndDate,'9999-12-31 00:00:00.000')) OVER(partition by id ORDER BY EffectiveDate ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) THEN 0 ELSE 1 END AS isstart
FROM #DataTable
)
SELECT ID,EffectiveDate,EndDate,
SUM(isstart) OVER(ORDER BY ID ROWS UNBOUNDED PRECEDING) AS DG
FROM C1
I have a table which has only 1 row per ID/Date while the other one can have overlapping ID/Dates. I need to JOIN them in such a way that only top row is selected (actually any row from duplicates table is fine!). Its fairly easy to do this in 2 steps (Insert & Update) but I'm looking if it can be done in a single step.
CREATE TABLE #Row1Each (ID VARCHAR(10), Date_ DATETIME, Value FLOAT) INSERT INTO #Row1Each SELECT 'AAPL', '1/10/2015', 100 INSERT INTO #Row1Each SELECT 'MSFT', '1/10/2015', 20
CREATE TABLE #Table1 (ID VARCHAR(10), Date_ DATETIME, Qty FLOAT) INSERT INTO #Table1 SELECT 'AAPL', '1/10/2015', 55000
CREATE TABLE #Duplicates (ID VARCHAR(10), StartDate DATETIME, EndDate DATETIME, Quote FLOAT) INSERT INTO #Duplicates SELECT 'AAPL', '1/2/2015', '12/31/2016', 0.1 INSERT INTO #Duplicates SELECT 'AAPL', '1/4/2015', '10/05/2016', 0.11
/*
AAPL 2015-01-10 00:00:00.000 100
MSFT 2015-01-10 00:00:00.000 20
AAPL 2015-01-10 00:00:00.000 55000
AAPL 2015-01-02 00:00:00.000 2016-12-31 00:00:00.000 0.1
AAPL 2015-01-04 00:00:00.000 2016-10-05 00:00:00.000 0.1
*/
SELECT A.*
, B.Qty
, C.Quote
FROM #Row1Each A
LEFT JOIN #Table1 B ON B.ID = A.ID
AND B.Date_ = A.Date_
LEFT JOIN #Duplicates C ON C.ID = A.ID
AND A.Date_ BETWEEN C.StartDate AND C.EndDate
DROP TABLE #Row1Each DROP TABLE #Table1 DROP TABLE #Duplicates
/* Desired output
AAPL 2015-01-10 00:00:00.000 100 55000 0.1
MSFT 2015-01-10 00:00:00.000 20 NULL NULL
*/
You can do this using APPLY.
SELECT
r.Id,
r.Date_,
r.Value,
t.Qty,
d.Quote
FROM #Row1Each r
LEFT JOIN #Table1 t
ON t.ID = r.ID
AND t.Date_ = r.Date_
OUTER APPLY(
SELECT TOP 1 *
FROM #Duplicates d
WHERE
d.ID = r.ID
AND r.Date_ BETWEEN d.StartDate AND d.EndDate
ORDER BY EndDate DESC -- Returns Top 1 Based on EndDate
)d
I have single table with below fields.
id name startdate enddate
1 u1 2013-01-15 00:00:00.000 2013-01-17 00:00:00.000
2 u2 2013-01-22 00:00:00.000 2013-01-23 00:00:00.000
3 u3 2013-01-23 00:00:00.000 2013-01-23 00:00:00.000
Now, I want multiple rows depends on start and end dates. So as per above rows.. It returns with three rows for first record.. date 15 to 17 which returns 3 rows (3 days).
I am bit confused for query. Is there any better way or any sample to achieve?
Thanks.
You could use a CTE to solve that:
DECLARE #Id int
SELECT #Id = 1
;
WITH Multiple AS
(
SELECT 1 Sequence, Id, Name, StartDate, EndDate
FROM ( VALUES
(1, 'u1', '2013-01-15', '2013-01-17'),
(2, 'u2', '2013-01-22', '2013-01-23'),
(3, 'u3', '2013-01-23', '2013-01-23')
) AS Sample(Id, Name, StartDate, EndDate)
WHERE Id = #Id
UNION ALL
SELECT Sequence + 1, Id, Name, StartDate, EndDate
FROM Multiple
WHERE Id = #Id AND DATEADD(d, Sequence, StartDate) <= EndDate
)
SELECT *
FROM Multiple
If you have a 'Dates' table with a 'Date' column in there, just join your table to the 'Dates' on 'Dates.Date BETWEEN startdate AND enddate'.
I am not pretty sure but below is what I have tried..
SELECT c1.*
FROM master..spt_values p
INNER JOIN tempQuery c1
ON RIGHT(CAST(c1.stdate AS DATE),2) <= (CASE WHEN p.number = 0 THEN 1 ELSE p.number END)
WHERE TYPE='p'
AND p.number BETWEEN RIGHT(CAST(c1.stdate AS DATE),2) AND RIGHT(CAST(c1.endate AS DATE),2)