Weird SQL Conditions date , STUCK - sql

I and creating view in SQL Server
Below is my Results , based on this I have to actually achieved the results shown below in the second table
PersonID MonthYear DateTimevalue
---------- ---------- --------------
1 201101 NULL
1 201102 NULL
1 201103 2011-03-10 09:35:57.387
1 201104 2011-04-26 13:25:00.050
1 201105 NULL
1 201106 NULL
1 201107 2011-07-30 16:49:26.050
1 201108 NULL
1 201109 2011-09-21 13:33:42.273
1 201110 2011-10-20 08:55:59.873
1 201111 NULL
1 201112 NULL
So , the case when the DateTimeValue is NULL , we need to take the value from previous month
As shown in the below table
PersonID MonthYear DateTimevalue
---------- ---------- --------------
1 201101 NULL
1 201102 NULL
1 201103 2011-03-10 09:35:57.387
1 201104 2011-04-26 13:25:00.050
1 201105 2011-04-26 13:25:00.050
1 201106 2011-04-26 13:25:00.050
1 201107 2011-07-30 16:49:26.050
1 201108 2011-07-30 16:49:26.050
1 201109 2011-09-21 13:33:42.273
1 201110 2011-10-20 08:55:59.873
1 201111 2011-10-20 08:55:59.873
1 201112 2011-10-20 08:55:59.873
Any help would be great !!!!
Thanks !!!!
Thanks !!!

Try the following:
SELECT A.PersonID,
A.MonthYear,
ISNULL(A.DateTimeValue,B.DateTimeValue) DateTimeValue
FROM YourTable A
OUTER APPLY ( SELECT TOP 1 DateTimeValue
FROM YourTable
WHERE PersonID = A.PersonID
AND MonthYear < A.MonthYear
AND DateTimeValue IS NOT NULL
ORDER BY MonthYear DESC) B

Try this:
select t1.personid
, t1.monthyear
, datetimevalue = prev.datetimevalue
from t1
outer apply
(
select top 1 datetimevalue
, personid
, monthyear
from t1 prev
where prev.monthyear <= t1.monthyear
and prev.datetimevalue is not null
order by monthyear desc
) prev
order by t1.personid
, t1.monthyear
SQLFiddle demo.
Edited to change cross apply to outer apply, #Lamak had it right.

Does Sql Server allow nested SQL in the Select clause?
If it does:
select personId, monthYear,
(select T2.DateTimeValue from Tbl T2
where T2.monthYear=(select max(T3.monthYear) from Tbl T3
where T3.personid=T1.personId and T3.monthYear<=T1.monthYear
and T3.DateTimeValue is not null
)
)
from Tbl T1
i.e.
For each month, find the month less than equal to this month, but
with a not null DateTime;
Look up the DateTime value to the month found in the step above

Related

How can I get distinct data from one col

I need to get member personal data for all our members whose subscriptions have lapsed i.e. have a subscription end date before 31/03/2020, however I want to show one member record only (distinct by membership number) ideally the most recent one
I've tried a ROW_NUMBER() solution SQL - Distinct One Col, Select Multiple other? and a cross apply solution sql distinct, getting 2 columns but I can't get it to work.
SELECT membershipnumber AS Id,
subscription.enddate
FROM [dbo].[userprofile]
INNER JOIN dbo.subscription
ON userprofile.id = subscription.userprofileid
INNER JOIN dbo.subscriptiontype
ON subscriptiontype.id = subscription.subscriptiontypeid
Output is
Id Enddate
1 2006-04-01 00:00:00.000
1 2001-04-01 00:00:00.000
1 1999-04-01 00:00:00.000
1 1998-04-01 00:00:00.000
1 2008-04-01 00:00:00.000
1 2007-04-01 00:00:00.000
1 2011-04-01 00:00:00.000
1 2005-04-01 00:00:00.000
1 2000-04-01 00:00:00.000
1 1997-04-01 00:00:00.000
2 1999-04-01 00:00:00.000
2 2012-04-01 00:00:00.000
2 2004-04-01 00:00:00.000
2 2001-04-01 00:00:00.000
2 2018-04-01 00:00:00.000
2 2009-04-01 00:00:00.000
2 2005-04-01 00:00:00.000
2 1997-04-01 00:00:00.000
Desired output
Id Enddate
1 2011-04-01 00:00:00.000
2 2018-04-01 00:00:00.000
Solved sql answer
;WITH cte
AS (SELECT membershipnumber AS Id,
subscription.enddate,
Row_number()
OVER (
partition BY membershipnumber
ORDER BY subscription.enddate DESC) AS rownumber
FROM [dbo].[userprofile]
INNER JOIN dbo.subscription
ON userprofile.id = subscription.userprofileid
INNER JOIN dbo.subscriptiontype
ON subscriptiontype.id = subscription.subscriptiontypeid
)
SELECT *
FROM cte
WHERE rownumber = 1
https://stackoverflow.com/a/6841644/5859743
Not sure if I got your question right.
but you can use DISTINCT in the SELECT, that would show only one record for each member.
SELECT DISTINCT Membershipnumber as Id
,'P' as PartyType
,'A' as Status
,case
when Name = 'Standard Membership paid annually.' and EndDate > '2020-03-31' then 'Member'
when Name = 'Lapsed subscription renewal' and EndDate > '2020-03-31' then 'Member'
when Name = '3 Year Subscription (members outside of UK and Ireland, Jersey, Guernsey and the Channel Islands)' and EndDate > '2020-03-31' then 'Overseas member'
when Name = '1 Year Subscription (members outside of UK and Ireland, Jersey, Guernsey and the Channel Islands).' and EndDate > '2020-03-31' then 'Overseas member'
when Name = 'Lapsed subscription renewal' and EndDate > '2020-03-31' then 'Member'
when Name = 'Lifetime membership' then 'Lifetime member'
when Name = 'Retired membership paid annually' and EndDate > '2020-03-31' then 'Retired member'
else 'Non member'
end As MemberType
,Title as NamePrefix
,FirstName as FirstName
,Surname as LastName
,DateOfBirth as BirthDate
,'Home' as AddressPurpose
,'Default' as CommunicationReasons
,AddressLine1
,AddressLine2
,AddressLine3
,Addressline4 as CityName
,'' as CountrySubEntityName
,Country as CountryCode
,'' as CountryName
,Postcode as PostalCode
,EmailAddress as Email
FROM [dbo].[UserProfile]
inner join dbo.Subscription on
UserProfile.Id = Subscription.UserProfileId
inner join dbo.SubscriptionType on
SubscriptionType.id = Subscription.SubscriptionTypeId```
If you are getting as above mentioned output. Then from that, your desired output will easily get using distinct.
; with cte as (
----- query which gives you above mentioned output
)
select distinct id, max(Enddate) as Enddate from cte
I suspect you want something like this:
select *
from (select . . ., -- all the columns you want
row_number() over (partition by Membershipnumber as Id order by s.Enddate) as seqnum
from [dbo].[UserProfile] up inner join
dbo.Subscription s
on up.Id = s.UserProfileId inner join
dbo.SubscriptionType st
on st.id = s.SubscriptionTypeId
) x
where seqnum = 1;

SQL - Find if column dates include at least partially a date range

I need to create a report and I am struggling with the SQL script.
The table I want to query is a company_status_history table which has entries like the following (the ones that I can't figure out)
Table company_status_history
Columns:
| id | company_id | status_id | effective_date |
Data:
| 1 | 10 | 1 | 2016-12-30 00:00:00.000 |
| 2 | 10 | 5 | 2017-02-04 00:00:00.000 |
| 3 | 11 | 5 | 2017-06-05 00:00:00.000 |
| 4 | 11 | 1 | 2018-04-30 00:00:00.000 |
I want to answer to the question "Get all companies that have been at least for some point in status 1 inside the time period 01/01/2017 - 31/12/2017"
Above are the cases that I don't know how to handle since I need to add some logic of type :
"If this row is status 1 and it's date is before the date range check the next row if it has a date inside the date range."
"If this row is status 1 and it's date is after the date range check the row before if it has a date inside the date range."
I think this can be handled as a gaps and islands problem. Consider the following input data: (same as sample data of OP plus two additional rows)
id company_id status_id effective_date
-------------------------------------------
1 10 1 2016-12-15
2 10 1 2016-12-30
3 10 5 2017-02-04
4 10 4 2017-02-08
5 11 5 2017-06-05
6 11 1 2018-04-30
You can use the following query:
SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
FROM company_status_history AS t
OUTER APPLY
(
SELECT COUNT(*) AS cnt
FROM company_status_history AS c
WHERE c.status_id = 1
AND c.company_id = t.company_id
AND c.effective_date < t.effective_date
) AS x
ORDER BY company_id, effective_date
to get:
id company_id status_id effective_date grp
-----------------------------------------------
1 10 1 2016-12-15 0
2 10 1 2016-12-30 1
3 10 5 2017-02-04 2
4 10 4 2017-02-08 2
5 11 5 2017-06-05 0
6 11 1 2018-04-30 0
Now you can identify status = 1 islands using:
;WITH CTE AS
(
SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
FROM company_status_history AS t
OUTER APPLY
(
SELECT COUNT(*) AS cnt
FROM company_status_history AS c
WHERE c.status_id = 1
AND c.company_id = t.company_id
AND c.effective_date < t.effective_date
) AS x
)
SELECT id, company_id, status_id, effective_date,
ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY effective_date) -
cnt AS grp
FROM CTE
Output:
id company_id status_id effective_date grp
-----------------------------------------------
1 10 1 2016-12-15 1
2 10 1 2016-12-30 1
3 10 5 2017-02-04 1
4 10 4 2017-02-08 2
5 11 5 2017-06-05 1
6 11 1 2018-04-30 2
Calculated field grp will help us identify those islands:
;WITH CTE AS
(
SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
FROM company_status_history AS t
OUTER APPLY
(
SELECT COUNT(*) AS cnt
FROM company_status_history AS c
WHERE c.status_id = 1
AND c.company_id = t.company_id
AND c.effective_date < t.effective_date
) AS x
), CTE2 AS
(
SELECT id, company_id, status_id, effective_date,
ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY effective_date) -
cnt AS grp
FROM CTE
)
SELECT company_id,
MIN(effective_date) AS start_date,
CASE
WHEN COUNT(*) > 1 THEN DATEADD(DAY, -1, MAX(effective_date))
ELSE MIN(effective_date)
END AS end_date
FROM CTE2
GROUP BY company_id, grp
HAVING COUNT(CASE WHEN status_id = 1 THEN 1 END) > 0
Output:
company_id start_date end_date
-----------------------------------
10 2016-12-15 2017-02-03
11 2018-04-30 2018-04-30
All you want know is those records from above that overlap with the specified interval.
Demo here with somewhat more complicated use case.
Maybe this is what you are looking for? For these kind of questions, you need to join two instance of your table, in this case I am just joining with next record by Id, which probably is not totally correct. To do it better, you can create a new Id using a windowed function like row_number, ordering the table by your requirement criteria
If this row is status 1 and it's date is before the date range check
the next row if it has a date inside the date range
declare #range_st date = '2017-01-01'
declare #range_en date = '2017-12-31'
select
case
when csh1.status_id=1 and csh1.effective_date<#range_st
then
case
when csh2.effective_date between #range_st and #range_en then true
else false
end
else NULL
end
from company_status_history csh1
left join company_status_history csh2
on csh1.id=csh2.id+1
Implementing second criteria:
"If this row is status 1 and it's date is after the date range check
the row before if it has a date inside the date range."
declare #range_st date = '2017-01-01'
declare #range_en date = '2017-12-31'
select
case
when csh1.status_id=1 and csh1.effective_date<#range_st
then
case
when csh2.effective_date between #range_st and #range_en then true
else false
end
when csh1.status_id=1 and csh1.effective_date>#range_en
then
case
when csh3.effective_date between #range_st and #range_en then true
else false
end
else null -- ¿?
end
from company_status_history csh1
left join company_status_history csh2
on csh1.id=csh2.id+1
left join company_status_history csh3
on csh1.id=csh3.id-1
I would suggest the use of a cte and the window functions ROW_NUMBER. With this you can find the desired records. An example:
DECLARE #t TABLE(
id INT
,company_id INT
,status_id INT
,effective_date DATETIME
)
INSERT INTO #t VALUES
(1, 10, 1, '2016-12-30 00:00:00.000')
,(2, 10, 5, '2017-02-04 00:00:00.000')
,(3, 11, 5, '2017-06-05 00:00:00.000')
,(4, 11, 1, '2018-04-30 00:00:00.000')
DECLARE #StartDate DATETIME = '2017-01-01';
DECLARE #EndDate DATETIME = '2017-12-31';
WITH cte AS(
SELECT *
,ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY effective_date) AS rn
FROM #t
),
cteLeadLag AS(
SELECT c.*, ISNULL(c2.effective_date, c.effective_date) LagEffective, ISNULL(c3.effective_date, c.effective_date)LeadEffective
FROM cte c
LEFT JOIN cte c2 ON c2.company_id = c.company_id AND c2.rn = c.rn-1
LEFT JOIN cte c3 ON c3.company_id = c.company_id AND c3.rn = c.rn+1
)
SELECT 'Included' AS RangeStatus, *
FROM cteLeadLag
WHERE status_id = 1
AND effective_date BETWEEN #StartDate AND #EndDate
UNION ALL
SELECT 'Following' AS RangeStatus, *
FROM cteLeadLag
WHERE status_id = 1
AND effective_date > #EndDate
AND LagEffective BETWEEN #StartDate AND #EndDate
UNION ALL
SELECT 'Trailing' AS RangeStatus, *
FROM cteLeadLag
WHERE status_id = 1
AND effective_date < #EndDate
AND LeadEffective BETWEEN #StartDate AND #EndDate
I first select all records with their leading and lagging Dates and then I perform your checks on the inclusion in the desired timespan.
Try with this, self-explanatory. Responds to this part of your question:
I want to answer to the question "Get all companies that have been at
least for some point in status 1 inside the time period 01/01/2017 -
31/12/2017"
Case that you want to find those id's that have been in any moment in status 1 and have records in the period requested:
SELECT *
FROM company_status_history
WHERE id IN
( SELECT Id
FROM company_status_history
WHERE status_id=1 )
AND effective_date BETWEEN '2017-01-01' AND '2017-12-31'
Case that you want to find id's in status 1 and inside the period:
SELECT *
FROM company_status_history
WHERE status_id=1
AND effective_date BETWEEN '2017-01-01' AND '2017-12-31'

SQL missing dates null value how to fill

Hi how do i fill up the null value with e.g id, name
from the left join table
SQL:
DECLARE #startDate DATETIME, #endDate DATETIME
SELECT #startDate = '2015-11-18', #endDate = '2015-11-20' --yyyy-mm-dd
;WITH Calender AS (
SELECT #startDate AS WorkDate
UNION ALL
SELECT WorkDate + 1 FROM Calender
WHERE WorkDate + 1 <= #endDate
), -- generate date from to --
cte AS (
SELECT COUNT(*) OVER() AS 'total' ,ROW_NUMBER()OVER (ORDER BY c.dt DESC) as row
, c.*
FROM Calender d
LEFT JOIN clockL c ON c.dt = d.WorkDate AND c.id = 17
) -- select user log and add missing dates --
SELECT *
FROM cte
--WHERE row BETWEEN 0 AND 15
--option (maxrecursion 0)
Result:
total row badgenumber id name title deptname ld dt min mout ain aout nin nout
----------- -------------------- ----------- ----------- ---------------------------------------- -------------------- ------------------------------ ---------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------
3 1 1 17 James HQ 2015-11-19 08:31 2015-11-19 08:31 NOC NOC NOC NOC NOC
3 2 1 17 James HQ 2015-11-18 20:37 2015-11-18 08:30 13:14 16:28 20:37 NOC NOC
3 3 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
^ ^ ^ ^ ^
should be 1 shoud be 17 James HQ 2015-11-20
thank you for looking :), i looked and tried but failed.

Finding occupied dates from date ranges

This is the data that I have on SQL-Sever 2005 mode
ID FromDate ToDate Diff
ZIM145876-01 03/01/2011 02/29/2012 1
ZIM145876-01 03/01/2012 02/28/2013 1
ZIM145876-01 03/01/2013 02/28/2014 NULL
ZIM145881-02 02/01/2012 03/31/2012 1
ZIM145881-02 04/01/2012 06/30/2012 1
ZIM145881-02 07/01/2012 09/30/2012 1
ZIM145881-02 10/01/2012 03/31/2013 1
ZIM145881-02 04/01/2013 06/30/2013 NULL
ZIM145878-01 05/15/2010 05/14/2011 201
ZIM145878-01 12/01/2011 11/30/2012 1
ZIM145878-01 12/01/2012 11/30/2013 NULL
Now on first case I want to have
ZIM145876-01 03/01/2011 02/28/2014
ZIM145881-02 02/01/2012 06/30/2013
However in 3rd case we have two occupied dates for same IDs and this is what I want
ZIM145878-01 05/15/2010 05/14/2011
ZIM145878-01 12/01/2011 11/30/2013
So any hint would be highly appreciated
(SQLFiddle)
This seems to do the job. Unfortunately, it's not going to be too efficient on large data sets:
declare #t table (ID varchar(50),FromDate date,ToDate date, Diff int)
insert into #t(ID,FromDate,ToDate,Diff) values
('ZIM145876-01','20110301','20120229',1 ),
('ZIM145876-01','20120301','20130228',1 ),
('ZIM145876-01','20130301','20140228',NULL),
('ZIM145881-02','20120201','20120331',1 ),
('ZIM145881-02','20120401','20120630',1 ),
('ZIM145881-02','20120701','20120930',1 ),
('ZIM145881-02','20121001','20130331',1 ),
('ZIM145881-02','20130401','20130630',NULL),
('ZIM145878-01','20100515','20110514',201 ),
('ZIM145878-01','20111201','20121130',1 ),
('ZIM145878-01','20121201','20131130',NULL)
;With Islands as (
select ID,FromDate,ToDate from #t t1 where not exists (select * from #t t2 where t2.ToDate = DATEADD(day,-1,t1.FromDate) and t1.ID = t2.ID)
union all
select i.ID,i.FromDate,t.ToDate
from
Islands i
inner join
#t t
on
i.ID = t.ID and
i.ToDate = DATEADD(day,-1,t.FromDate)
)
select ID,FromDate,MAX(ToDate) from Islands
group by ID,FromDate
Results:
ID FromDate
-------------------------------------------------- ---------- ----------
ZIM145878-01 2010-05-15 2011-05-14
ZIM145876-01 2011-03-01 2014-02-28
ZIM145878-01 2011-12-01 2013-11-30
ZIM145881-02 2012-02-01 2013-06-30
It could be more efficient if the occupancy was recorded as a semi-open interval (e.g. Inclusive FromDate, Exclusive ToDate) - because then we wouldn't have to call DATEADD to find the place where the periods join together.
I don't know what the Diff column is about, and I haven't used it.

How to find date ranges in records with consecutive dates?

I have a table with data like so:
ID StartDate EndDate EffectiveDate
-- --------- ------- -------------
1 1/1/2009 12/31/2009 12/31/2009
1 7/1/2009 12/31/2009 7/1/2009
1 8/1/2009 12/31/2009 8/1/2009
2 1/1/2010 12/31/2010 12/31/2010
2 3/1/2010 12/31/2010 12/31/2010
I need a query that will format it like this, however:
ID StartDate EndDate EffectiveDate
-- --------- ------- -------------
1 1/1/2009 6/30/2009 null
1 7/1/2009 7/31/2009 7/1/2009
1 8/1/2009 12/31/2009 8/1/2009
2 1/1/2010 2/28/2010 null
2 3/1/2010 12/31/2010 12/31/2010
...
It's basically like a timeline with segmented "points", and each point is a new StartDate with an EndDate of the next point, and so on.
I've tried using a CTE, as well as the following query:
SELECT t1.RfqItemOptionId,
t1.StartDate,
MIN(t2.EffectiveDate) EndDate,
t1.EffectiveDate EffectiveDate
FROM #OptionPeriods t1
LEFT JOIN #OptionPeriods t2 ON t1.RfqItemOptionId = t2.RfqItemOptionId
AND t1.EffectiveDate < t2.EffectiveDate
GROUP BY t1.RfqItemOptionId, t1.StartDate, t1.EffectiveDate
Which is close...but no cigar :(
Can anyone help?
;With Base As
(
Select
*
, ROW_NUMBER() Over (Partition By ID Order By StartDate) RowNum
From
#OptionPeriods
)
Select
B1.*
, IsNull(B2.StartDate - 1, B1.EndDate) As NewEndDate
, Case B1.RowNum When 1 then Null Else B1.StartDate End As NewEffectiveDate
From
Base B1
Left Join
Base B2
On
B1.ID = B2.ID
AND
B1.RowNum + 1 = B2.RowNum
I am an oracle person, here is the oracle version of SQL....
You please substitute relevant analytic SQL with equivalent SQL-Server SQL...
SELECT id,
startdate,
enddate,
case WHEN enddate < effectivedate THEN null ELSE effectivedate end AS effectivedate
FROM (
SELECT id,
startdate,
nvl2(next_startdate,(next_startdate-1),enddate) enddate,
effectivedate
FROM (
SELECT a.*,
lead(startdate,1) over (partition by id order by id,startdate) next_startdate
FROM tblTest a
ORDER BY 1,2
)
);
Thanks,
Venkat