#SQL - Order By matching record first - sql

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

Related

How to get the row for the current date?

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;

Finding correct date pair and eliminate the overlapped one in T-SQL

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)

How to Calculate Employment Experience with Special Case

I am trying to calculate the years of experience
Let's say someone has multiple employments as follow:
startdate enddate
2007-08-27 2008-05-09
2007-08-27 2008-05-09
2012-01-01 2018-07-31
2013-01-06 2019-03-25
What would be the correct sql syntax to just select startdate, enddate which will be later pluged in a function to calculate the number of days for each employment?
Here's my expected result:
1st row: startdate 8/27/2007, enddate 5/9/2008
2nd row: startdate 1/1/2012, enddate 3/25/2019
The second employment took place during the same time for the 1st employment so, it will not be counted. The fourth employment started before the third employment ended so we should use the startdate for the third employment and enddate for the fourth employment
Use Distinct to remove dplicated records. Then you can use this query:
Select totaldays / 365 from
(Select Sum(
DATEDIFF(day, srartdate, enddate )
) As totaldays
)
This query returns the dates without overlapping:
select
v.startdate startdate,
min(vv.enddate) enddate
from view_appemployment v
inner join view_appemployment vv
on v.startdate <= vv.enddate
and not exists (
select * from view_appemployment vvv
where vv.enddate >= vvv.startdate and vv.enddate < vvv.enddate
)
where not exists (
select * from view_appemployment vvvv
where v.startdate > vvvv.startdate and v.startdate <= vvvv.enddate
)
group by v.startdate
See the demo
Results:
startdate | enddate
------------------ | ------------------
27/08/2007 00:00:00 | 09/05/2008 00:00:00
01/01/2012 00:00:00 | 25/03/2019 00:00:00
This is a Gaps & Islands in Sequences problem, the following query should do what you want:
CREATE TABLE #emp (empid int, startdate date,enddate date)
INSERT INTO #emp VALUES
(1,'2007-08-27','2008-05-09'),
(1,'2007-08-27','2008-05-09'),
(1,'2012-01-01','2018-07-31'),
(1,'2013-01-06','2019-03-25')
;WITH starts AS (
SELECT em.*,
(CASE WHEN EXISTS (SELECT 1
FROM #emp em2
WHERE em2.EmpID = em.EmpID AND
em2.StartDate < em.StartDate AND
em2.EndDate >= em.StartDate
) THEN 0 ELSE 1 END) AS [IsStart]
FROM #emp em )
SELECT EmpID
,MIN(StartDate) AS [StartDate]
,MAX(EndDate) AS [EndDate]
FROM (SELECT s.*, SUM(isstart) OVER (PARTITION BY EmpID ORDER BY StartDate) AS [grp]
FROM starts s
) s
GROUP BY EmpID, grp
ORDER BY EmpID
Please try this:
SELECT concat(id," row: start date ",date_format(start_date,'%d/%m/%y')," end date ",date_format(end_date,'%d/%m/%y'))as dateinfo FROM `dates`

Merge two records date if dates are continuous and key values are same

I have two different scenarios. In the first scenario I need something like:
create table test
(
ItemID int,
ItemStartDate datetime,
ItemEndDate datetime,
itemType varchar(100)
)
Table test:
ItemID ItemStartDate ItemEndDate itemType
------ ------------- ----------- --------
item_1 1/1/2011 3/2/2011 value A
item_1 3/3/2011 12/31/2011 value A
item_2 1/3/2011 12/31/2011 value B
It should show only two records:
ItemID ItemStartDate ItemEndDate itemType
------ ------------- ----------- --------
item_1 1/1/2011 12/31/2011 value A
item_2 1/1/2011 12/31/2011 value B
Scenario 2.
Here I would like to split data value to separate year periods if it's across multiple years.
Table test
create table #Scenario_2
(
ItemID int,
priceStartDate datetime,
priceEndDate datetime,
price int
)
item startdate enddate value
---- --------- ---------- -----
11 1/1/2011 5/4/2013 500
12 7/1/2013 11/12/2013 600
It should show like
item startdate enddate value
---- --------- ---------- -----
11 1/1/2011 12/31/2011 500
11 1/1/2012 12/31/2012 500
11 1/1/2013 5/4/2013 500
12 7/1/2013 11/12/2013 600
Please advise how I can achieve this.
Try this. from your question this is what i understood!!
SCENARIO 2
----------
CREATE TABLE #datt
(
itemid int,startd DATE,endat DATE,price int
)
INSERT INTO #datt
VALUES (11,'2011-01-01','2013-05-04',500),
(12,'2013-7-1','2013-11-12',600)
;WITH cte
AS (SELECT itemid,
startd st,
case when year(endat)<> YEAR(startd) then Dateadd(yy, Year(startd) - 1899, -1)
else endat end ed,price
FROM #datt
UNION ALL
SELECT a.itemid,
Dateadd(yy, 1, st),
CASE
WHEN Dateadd(yy, 1, ed) > b.endat THEN b.endat
ELSE Dateadd(yy, 1, ed)
END,a.price
FROM cte a
JOIN #datt b
ON a.itemid = b.itemid
AND a.ed < b.endat)
SELECT *
FROM cte order by itemid,st
For scenario1 you could see this answer.
For scenario2 there also have a similar answer you could reference.
But your question can be simplified like this:
with dates as
(
select number,cast(ltrim(number*10000+1231) as date) as dt
from master..spt_values
inner join
(select min(year(startdate)) as s_year
,max(year(enddate)) as e_year
from Scenario_2) as y
on number between y.s_year and y.e_year AND TYPE='P'
)
select
s.item
,case when year(dt) = year(startdate)
then startdate
else dateadd(year,-1,dateadd(day,1,dt)) end --or cast(ltrim(year(dt)*10000+101) as date)
,case when year(dt) = year(enddate)
then enddate
else dt end
,s.value
from
Scenario_2 s
inner join
dates d
on
d.number between year(s.startdate) and year(s.enddate)
SQL FIDDLE DEMO

How to group by Date Range starting from initial date

I have the following table structure
Key int
MemberID int
VisitDate DateTime
How can group all the dates falling with a given date range say 15 days..The first visit for the sameMember should be considered as the starting date.
eg
Key ID VisitDate(MM/dd/YY)
1 1 02/01/11
2 1 02/09/11
3 1 02/12/11
4 1 02/17/11
5 2 02/03/11
6 2 02/19/11
In this case the result should be
ID StartDate EndDate
1 02/01/11 02/12/11
1 02/17/11 02/17/11
2 02/03/11 02/03/11
2 02/19/11 02/19/11
One way to do this would be to use window aggregating. Here's how:
Setup:
DECLARE #data TABLE (
[Key] int, ID int, VisitDate date
);
INSERT INTO #data ([Key], ID, VisitDate)
SELECT 1, 1, '02/01/2011' UNION ALL
SELECT 2, 1, '02/09/2011' UNION ALL
SELECT 3, 1, '02/12/2011' UNION ALL
SELECT 4, 1, '02/17/2011' UNION ALL
SELECT 5, 2, '02/03/2011' UNION ALL
SELECT 6, 2, '02/19/2011';
Query:
WITH marked AS (
SELECT
*,
Grp = DATEDIFF(DAY, MIN(VisitDate) OVER (PARTITION BY ID), VisitDate) / 15
FROM #data
)
SELECT
ID,
StartDate = MIN(VisitDate),
EndDate = MAX(VisitDate)
FROM marked
GROUP BY ID, Grp
ORDER BY ID, StartDate
Output:
ID StartDate EndDate
----------- ---------- ----------
1 2011-02-01 2011-02-12
1 2011-02-17 2011-02-17
2 2011-02-03 2011-02-03
2 2011-02-19 2011-02-19
Basically, for each row, the query is calculating the difference of days between VisitDate and the first VisitDate for the same ID and divides it by 15. The result is then used as a grouping criterion. Note that SQL Server uses integer division when both operands of the / operator are integers.