Pretend today 2022-10-24
case 1
id
productCode
version
startDate
endDate
1
AAA
1
2022-10-01
2022-10-28
2
AAA
2
2022-10-29
NULL
case 1 depend on table above, I want to return only 1 row at id 1, why cause today 2022-10-24 still between startDate and endDate
case 2
id
productCode
version
startDate
endDate
1
AAA
1
2022-10-01
2022-10-28
2
AAA
2
2022-10-01
NULL
case 2 depends on table above. I want to return only 1 row at id 2. Why cause when startDate has the same value between id 1 & 2, so choose endDate with NULL value.
I am still confused about how to implement this with query.
I want to make for one query logic. When running query so when use case 1 return id 1 and when I use for case 2 return id 2.
As I mention in the comments, seems you just need some simple >= and <(=) logic (while handling NULLs) and a "Top 1 per group":
WITH CTE AS(
SELECT id,
productCode,
version,
startDate,
endDate,
ROW_NUMBER() OVER (PARTITION BY productCode ORDER BY Version DESC) AS RN --Guessed the required partition and order clauses
FROM dbo.YourTable
WHERE startDate <= CONVERT(date,GETDATE())
AND (endDate >= CONVERT(date,GETDATE()) OR endDate IS NULL))
SELECT id,
productCode,
version,
startDate,
endDate
FROM CTE
WHERE RN = 1;
Related
I have a Dates like startdate as one column and Enddate as another column. I need to find eliminate Continuous date ranges in data in sQL.I need to find the overlapped items and i need to delete.I already using one code to find Overlap items.And i am giving startdate and enddate as parameter.
Code i am using to find overlap
Select * from #t
where
((cast(#StartDate as datetime2)>=StartDate and cast(#EndDate as datetime2)<=EndDate)
OR (StartDate>= cast(#StartDate as datetime2) and EndDate<= cast(#EndDate as datetime2))
OR (cast(#StartDate as datetime2)>=StartDate AND cast(#StartDate as datetime2)<=EndDate)
OR (cast(#EndDate as datetime2)>=StartDate AND cast(#EndDate as datetime2)<=EndDate))
Above query is ok to find normal overlap like
Id
Startdate
Enddate
1
01/01/2020
01/11/2020
2
01/01/2020
01/03/2021
In above condition i will delete one data and i will keep other one
But it fails in below type of data example.When run for below type of query 1 id is overlapped with 2 and 2 is overlapped with both 1 and 3.So it show both 1 and 2 to delete.but in my case is not to delete 1 and 3.only 2 need to be deleted.since 2 is overlapped between both data and 1& 3 is already in good date periods
For example
Id
Startdate
Enddate
1
01/01/2020
01/11/2020
2
01/01/2020
01/03/2021
3
02/11/2020
05/04/2022
In above example we have three pair of dates and id 1 and 3 are in correct interval and 2 is overlapped between both id. I need to find overlapped one or non overlapped items. Any case is ok for me to find the result.
My Expected Result is
Id
Startdate
Enddate
2
01/01/2020
01/03/2021
Another example is
Id
Startdate
Enddate
1
01/01/2020
01/11/2020
2
02/11/2020
06/05/2022
3
02/11/2020
05/04/2022
Above if you see 1 and 2 is in correct date periods but id 3 is overlapped with 2 id.Now i want to find only that overlapped result and i don't need other data.
Another example is
Id
Startdate
Enddate
3
02/11/2020
05/04/2022
I used second set of data, But this should work for first set of data as well. But I have a doubt on your first record set expected output. If you can clear it up, i can check again,
Create table OverlapData_1
(
id int
, Startdate date
, EndDate date
)
insert into OverlapData_1 values(1, '01/01/2020','01/11/2020')
insert into OverlapData_1 values(2, '01/01/2020','01/03/2021')
insert into OverlapData_1 values(3, '02/11/2020','05/04/2022')
SELECT A.[id]
,A.Startdate
,A.EndDate FROM
(
SELECT
CASE WHEN Startdate between LAG(StartDate) OVER ( order by id) and LAG(EndDate) OVER ( order by id) THEN 1 else 0 end as [status_1]
, CASE WHEN EndDate between LAG(StartDate) OVER ( order by id) and LAG(EndDate) OVER ( order by id) THEN 1 else 0 end as [status_2]
, CASE WHEN StartDate between LEAD(StartDate) OVER ( order by id) and LEAD(EndDate) OVER ( order by id) THEN 1 else 0 end as [status_3]
, CASE WHEN EndDate between LEAD(StartDate) OVER ( order by id) and LEAD(EndDate) OVER ( order by id) THEN 1 else 0 end as [status_4]
,*
FROM OverlapData_1
) AS A
WHERE (A.status_1 = 1 AND A.status_2 = 1)
OR (A.status_1 = 1 AND A.status_4 = 1)
Create table OverlapData_2
(
id int
, Startdate date
, EndDate date
)
insert into OverlapData_2 values(1, '01/01/2020','01/11/2020')
insert into OverlapData_2 values(2, '01/01/2020','06/05/2022')
insert into OverlapData_2 values(3, '02/11/2020','05/04/2022')
SELECT A.[id]
,A.Startdate
,A.EndDate FROM
(
SELECT
CASE WHEN Startdate between LAG(StartDate) OVER ( order by id) and LAG(EndDate) OVER ( order by id) THEN 1 else 0 end as [status_1]
, CASE WHEN EndDate between LAG(StartDate) OVER ( order by id) and LAG(EndDate) OVER ( order by id) THEN 1 else 0 end as [status_2]
, CASE WHEN StartDate between LEAD(StartDate) OVER ( order by id) and LEAD(EndDate) OVER ( order by id) THEN 1 else 0 end as [status_3]
, CASE WHEN EndDate between LEAD(StartDate) OVER ( order by id) and LEAD(EndDate) OVER ( order by id) THEN 1 else 0 end as [status_4]
,*
FROM OverlapData_2
) AS A
WHERE (A.status_1 = 1 AND A.status_2 = 1)
OR (A.status_1 = 1 AND A.status_4 = 1)
For multiple rows with identical features, I hope two add few marks/new columns in the original table.
The original table is as below:
ID Start_date End_Date Amount
1 2005-01-01 2010-01-01 5
1 2000-07-01 2009-06-01 10
1 2017-08-01 2018-03-01 30
I wish to keep one record with the earliest start date, latest end date, added amount and an indicator to tell me to use this record. For the others, just use the indicator to tell me not to use.
The updated table should be as below:
ID Start_date End_Date Amount Amount_new Usable Start End
1 2005-01-01 2010-01-01 5 45 0 2000-07-01 2018-03-01
1 2000-07-01 2009-06-01 10 1
1 2017-08-01 2018-03-01 30 1
It does not matter which row to keep, as long as there is one row with Usable=0, and Amount_new, Start and End are updated.
If not considering the end date, I was thinking of grouping by ID and Start_date, then update the column Usable and Amount_new of the first row. However I still have the problem of how to select the first row from the group by group. Considering the End_Date makes my mind even more messy!
Could anyone help to shed some light upon this issue?
You seem to want something like this:
alter table original
add amount_new int,
add usable bit,
add new_start,
add new_end;
Then, you can update it using window functions:
with toupdate as (
select o.*,
sum(amount) over (partition by id) as x_amount,
(case when row_number() over (partition by id order by start_date) as x_usable,
min(start_date) as x_start_date,
max(end_date) as x_end_date
from original o
)
update toupdate
set new_amount = x_amount,
usable = x_usable,
new_start = x_start_date,
new_end = x_end_date;
The following query should do what you want:
CREATE TABLE #temp (ID INT, [Start_date] DATE, End_Date DATE, Amount NUMERIC(28,0), Amount_new NUMERIC(28,0), Usable BIT, Start [Date], [End] [Date])
INSERT INTO #temp (ID, [Start_date], End_Date, Amount) VALUES
(1,'2005-01-01','2010-01-01',5),
(1,'2000-07-01','2009-06-01',10),
(1,'2017-08-01','2018-03-01',30),
(2,'2001-07-01','2009-06-01',5),
(2,'2017-08-01','2019-03-01',35)
UPDATE t1
SET Amount_new = t2.[Amount_new],
Usable = 1,
Start = t2.[Start],
[End] = t2.[End]
FROM (SELECT *,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY (SELECT 1)) AS RNO FROM #temp) t1
INNER JOIN
(
SELECT ID,[Start_date],[End_Date],[Amount]
,SUM(Amount) OVER(PARTITION BY ID) AS [Amount_new]
,MIN([Start_date]) OVER(PARTITION BY ID) AS [Start]
,MAX(End_Date) OVER(PARTITION BY ID) AS [End]
,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY (SELECT 1)) AS RNO
FROM #temp ) t2 ON t1.id = t2.id AND t2.rno = t1.RNO AND t2.RNO = 1
SELECT * FROM #temp
The result is as below,
ID Start_date End_Date Amount Amount_new Usable Start End
1 2005-01-01 2010-01-01 5 45 1 2000-07-01 2018-03-01
1 2000-07-01 2009-06-01 10 NULL NULL NULL NULL
1 2017-08-01 2018-03-01 30 NULL NULL NULL NULL
2 2001-07-01 2009-06-01 5 40 1 2001-07-01 2019-03-01
2 2017-08-01 2019-03-01 35 NULL NULL NULL NULL
i need help to order this table (named "season") , by matching actual date with the BEGINDATE
ID NAME BEGINDATE
----------- -------------------- ----------
1 2014-2015 2014-10-01
2 2015-2016 2015-10-01
3 2016-2017 2016-10-01
4 2017-2018 2017-10-01
for example:
actual date is 2016/10/28 so we are in season 2016-2017 (id=3)
so the result should be
ID NAME BEGINDATE
----------- -------------------- ----------
3 2016-2017 2016-10-01
1 2014-2015 2014-10-01
2 2015-2016 2015-10-01
4 2017-2018 2017-10-01
UPDATE (SOLVED)
what i finally did was:
DECLARE #IDACTIVE AS INT = (SELECT MAX(ID) FROM SEASON WHERE BEGINDATE < GETDATE())
SELECT
1 AS ORDERBY,
ID,
NAME,
BEGINDATE
FROM SEASON
WHERE ID = #IDACTIVE
UNION
SELECT
2 AS ORDERBY,
ID,
NAME,
BEGINDATE
FROM SEASON
WHERE ID = #IDACTIVE
Follow the next approach:
1) Get The only matched row by using Top and Where clauses.
2) Get the all records except the one that you getting on point #1
3) Combine the result of two Selects via using UNION ALL.
Demo:-
Create table season (id int , NAME varchar(20),BEGINDATE date)
go
insert into season values (1,'2014-2015','2014-10-01')
insert into season values (2,'2015-2016','2015-10-01')
insert into season values (3,'2016-2017','2016-10-01')
insert into season values (4,'2017-2018','2017-10-01')
go
select * from (
select top 1 * from season
where BEGINDATE < getdate()
order by BEGINDATE desc
) a
union all
select * from season
where BEGINDATE != (
select top 1 BEGINDATE from season
where BEGINDATE < getdate()
order by BEGINDATE desc)
-- an another Soluation
select * from season
where DATEPART(Year,BEGINDATE) =DATEPART(Year,getdate())
union all
select * from season
where DATEPART(Year,BEGINDATE) !=DATEPART(Year,getdate())
The Result:
First move all future dates to the end, then order by beginDate
SELECT *
FROM season
ORDER BY CASE WHEN beginDate > GETDATE() THEN 0 ELSE 1 END,
beginDate
I think this is most easily done using window functions:
select s.*
from season s
order by (case when begindate = max(case when getdate() >= begindate then begindate end) over ()
then 1 else 2
end),
id
I have this bit of code:
;WITH MyCTE AS
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY CardUser ORDER BY CardTableID) AS NewVariation
FROM CardChecker
)
UPDATE MyCTE
SET Status = NewVariation
which currently updates the status column, however what I want to happen is over a 24 hour period, the status starts again the next day at 1, and counts again based on the CardUser like specified above:
Current data and what happens:
2 aaa 1 2015-06-25 08:00:00.000 123 1 NULL
3 ccc 1 2015-06-25 00:00:00.000 124 1 NULL
4 aaa 1 2015-06-25 17:30:00.000 125 2 NULL
5 aaa 1 2015-06-26 17:30:00.000 125 *3* NULL
what I want to happen:
2 aaa 1 2015-06-25 08:00:00.000 123 1 NULL
3 ccc 1 2015-06-25 00:00:00.000 124 1 NULL
4 aaa 1 2015-06-25 17:30:00.000 125 2 NULL
5 aaa 1 2015-06-26 17:30:00.000 125 *1* NULL
im not quite sure how I could add this to the above query so would it be possible for someone to point me in the right direction?
the main problem is the EventTime field contains both the date and the time, so adding it is as a PARTITION means the status would always be 1 based on the time parameter of the field
thanks for the help
Current CardTable structure:
CREATE TABLE CardTable (CardTableID INT IDENTITY (1,1) NOT NULL,
CardUser VARCHAR(50),
CardNumber VARCHAR(50),
EventTime DATETIME,
Status INT)
You can CONVERT() the EventTime to DATE type and then PARTITION:
;WITH MyCTE AS
(
SELECT Status,
ROW_NUMBER() OVER(PARTITION BY CardUser, CONVERT(DATE, EventTime)
ORDER BY CardTableID) AS NewVariation
FROM CardChecker
)
UPDATE MyCTE
SET Status = NewVariation
Your query basically unnecessarily updating entire table everytime. If EventTime is current date time of the system, having a flag to mark already updated status would improve the performance.
;WITH MyCTE AS
(
SELECT Status,
ROW_NUMBER() OVER(PARTITION BY CardUser, CONVERT(DATE, EventTime)
ORDER BY CardTableID) AS NewVariation
FROM CardChecker
WHERE Status IS NULL OR
CONVERT(DATE, EventTime) = CONVERT(DATE, GETDATE())
)
UPDATE MyCTE
SET Status = NewVariation
I am not quite sure how to go about doing this. Basically I have have a table like this
UserId DateRequested Approved ApprovedBy Notes
------------ ----------------------- -------- ----------- -----
1 2011-05-26 0 NULL NULL
1 2011-05-27 0 NULL NULL
1 2011-05-28 0 NULL NULL
1 2011-06-05 0 NULL NULL
1 2011-06-06 0 NULL NULL
1 2011-06-25 0 NULL NULL
Which basically contains the days an employee requests a holiday. Now, when a day or days is granted, this data needs to be copied over to a table of the form
UserId DateFrom DateTo
So basically for the above data i want:
UserId DateFrom DateTo
-------------------------------
1 2011-05-26 2011-05-28
1 2011-06-05 2011-06-06
1 2011-06-25 2011-06-25
I.e I want consecutive days in the DateFrom and DateTo. Now I am not sure how to do this without using a while loop. This is SQL, So i would prefer a non-iterative solution.
Please advise!!!
;WITH cte AS
(
SELECT *,
DATEDIFF(DAY,0,DateRequested)-
ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY DateRequested) AS Grp
FROM YourTable
WHERE Approved = 1 /*Presumably - but your example data doesn't show this.*/
)
SELECT UserId,
MIN(DateRequested) AS DateFrom,
MAX(DateRequested) AS DateTo
FROM cte
GROUP BY UserId,Grp
In Oracle PL/SQL it would be written as follows:
WITH cte
AS (SELECT a.*,
daterequested - TRUNC (SYSDATE)
- ROW_NUMBER ()
OVER (PARTITION BY UserId ORDER BY DateRequested)
AS Grp
FROM yourtable a
WHERE Approved = 0)
SELECT UserId, MIN (DateRequested) AS DateFrom, MAX (DateRequested) AS DateTo
FROM cte
GROUP BY UserId, Grp;