Related
I am reviewing reports that contain date ranges by a member ID and upload date. This looks like the following:
+--------------------+---------------------+---------------------+--------------------+
| UploadDate | MemberID | StartDate | EndDate |
| | | | |
+-------------------------------------------------------------------------------------+
| 08/01/2020 | 12345 | 04/01/2020 | 10/31/2020 |
| | | | |
+-------------------------------------------------------------------------------------+
| 08/01/2020 | 12345 | 01/01/2020 | 03/31/2020 |
| | | | |
+-------------------------------------------------------------------------------------+
| 06/01/2020 | 12345 | 01/01/2020 | 03/31/2020 |
| | | | |
+-------------------------------------------------------------------------------------+
| 06/01/2020 | 98765 | 02/01/2020 | 03/31/2020 |
| | | | |
+-------------------------------------------------------------------------------------+
| 06/01/2020 | 98765 | 05/01/2020 | 08/31/2020 |
| | | | |
+-------------------------------------------------------------------------------------+
| 07/01/2020 | 34568 | 01/01/2020 | 12/31/2020 |
| | | | |
+-------------------------------------------------------------------------------------+
| 07/01/2020 | 34568 | 03/31/2020 | 06/01/2020 |
| | | | |
+--------------------+---------------------+---------------------+--------------------+
I need to merge rows with the same UploadDate and the same MemberID where their are no gaps in the date range StartDate - EndDate. If there are gaps the rows will not be merged.
The expected output would be:
+--------------------+---------------------+---------------------+--------------------+
| UploadDate | MemberID | StartDate | EndDate |
| | | | |
+-------------------------------------------------------------------------------------+
| 08/01/2020 | 12345 | 01/01/2020 | 10/31/2020 |
| | | | |
+-------------------------------------------------------------------------------------+
| 06/01/2020 | 12345 | 01/01/2020 | 03/31/2020 |
| | | | |
+-------------------------------------------------------------------------------------+
| 06/01/2020 | 98765 | 02/01/2020 | 03/31/2020 |
| | | | |
+-------------------------------------------------------------------------------------+
| 06/01/2020 | 98765 | 05/01/2020 | 08/31/2020 |
| | | | |
+-------------------------------------------------------------------------------------+
| 07/01/2020 | 34568 | 01/01/2020 | 12/31/2020 |
| | | | |
+--------------------+---------------------+---------------------+--------------------+
I had been trying the following without success:
SELECT
ROW_NUMBER() OVER(ORDER BY [MemberID],[StartDate],[EndDate]) AS RN,
[MemberID],
[StartDate],
[EndDate],
LAG([EndDate],1) OVER (ORDER BY [MemberID],[StartDate], [EndDate]) AS PreviousEndDate
FROM
[dbo].[RCNI]
SELECT
*,
CASE WHEN Groups.PreviousEndDate >= [StartDate] THEN 0 ELSE 1 END AS IslandStartInd,
SUM(CASE WHEN Groups.PreviousEndDate >= [StartDate] THEN 0 ELSE 1 END) OVER (ORDER BY Groups.RN) AS IslandId
FROM
(
SELECT
ROW_NUMBER() OVER(ORDER BY [UploadDate], [MemberID],[StartDate], [Benefit End Date]) AS RN,
[UploadDate],
[MemberID],
[StartDate],
[EndDate],
LAG([EndDate],1) OVER (ORDER BY [UploadDate],[MemberID],[StartDate], [EndDate]) AS PreviousEndDate
FROM
[dbo].[RCNI]
) Groups
The solution below appears to work for the given sample data. In words with would be something like:
Filter out rows that have a period that falls completely within another row for the same UploadDate and MemberId (the not exists clause in the common table expression cte).
Look at the remaining rows for each UploadDate and MemberId combination (over(partition by r.UploadDate, r.MemberId ...) and sort them by StartDate (... order by r.StartDate)).
If the start date of a row comes before, is equal to or comes one day after the end date of the previous row for the combination (lag(r.EndDate) over(partition by r.UploadDate, r.MemberId order by r.StartDate)), then they must be merged.
If rows must be merged, then the start dates becomes the smallest start date of the combination (min(r.StartDate) over(partition by r.UploadDate, r.MemberId)). All rows that must be merged now have the same start date (StartDateNew).
Determine the new end date by grouping on UploadDate, MemberId and StartDateNew and taking the maximum value for EndDate.
Sample data
create table rcni
(
UploadDate date,
MemberId int,
StartDate date,
EndDate date
);
insert into rcni (UploadDate, MemberId, StartDate, EndDate) values
('08/01/2020', 12345, '04/01/2020', '10/31/2020'),
('08/01/2020', 12345, '01/01/2020', '03/31/2020'),
('06/01/2020', 12345, '01/01/2020', '03/31/2020'),
('06/01/2020', 98765, '02/01/2020', '03/31/2020'),
('06/01/2020', 98765, '05/01/2020', '08/31/2020'),
('07/01/2020', 34568, '01/01/2020', '12/31/2020'),
('07/01/2020', 34568, '03/31/2020', '06/01/2020');
Solution
with cte as
(
select r.UploadDate,
r.MemberId,
case
when r.StartDate <= dateadd(dd, 1, lag(r.EndDate) over(partition by r.UploadDate, r.MemberId order by r.StartDate))
then min(r.StartDate) over(partition by r.UploadDate, r.MemberId)
else r.StartDate
end as StartDateNew,
r.EndDate
from rcni r
where not exists ( select 'x'
from rcni r2
where r2.UploadDate = r.UploadDate
and r2.MemberId = r.MemberId
and r2.StartDate < r.StartDate
and r2.EndDate > r.EndDate )
)
select c.UploadDate,
c.MemberId,
c.StartDateNew,
max(c.EndDate) as EndDateNew
from cte c
group by c.UploadDate,
c.MemberId,
c.StartDateNew;
Fiddle
I will provide examples and code where I can. Assume everything except [CycleStart] and [CycleEnd] datatypes are Varchar, I'm not too fussed about this at this stage.
Table A consists of the following RAW sample data:
+-------+---------+----------------+------------+------------+
| JobID | JobName | CycleDesc | CycleStart | CycleEnd |
+-------+---------+----------------+------------+------------+
| 10003 | Run1 | January 2019 | 31/12/2018 | 31/12/2018 |
| 10005 | Run2 | December 2018 | 31/12/2017 | 31/11/2018 |
| 10006 | Run3 | March 2019 | 31/12/2018 | 31/02/2019 |
| 10007 | Run4 | September 2019 | 31/12/2018 | 31/09/2019 |
| 10008 | Run5 | November 2019 | 31/12/2018 | 31/10/2019 |
+-------+---------+----------------+------------+------------+
Table B consists of the following sample data and the code used to generate this data is below:
+-------+---------+---------+
| JobID | PeriodID | Entity |
+-------+---------+---------+
| 10003 | 202101 | XYZ1 |
| 10003 | 202112 | XYZ2 |
| 10007 | 202008 | XYZ3 |
| 10007 | 202003 | XYZ4 |
| 10008 | 201904 | XYZ5 |
+-------+----------+--------+
Declare #Counter3 INT
SELECT #Counter3=1
WHILE #Counter3 <= 1000
BEGIN
INSERT INTO [dbo].[TableB]
SELECT
FLOOR(RAND()*(33979-1+1))+1 [JobID]
,CAST(ROUND(((2021 - 2019 -1) * RAND() + 2020), 0) AS VARCHAR) + RIGHT('0'+CAST(FLOOR(RAND()*(12-1+1))+1 AS VARCHAR),2) [PeriodID]
,FLOOR(RAND()*(23396-1+1))+1 [Entity]
The issue lies within Table B column [PeriodID]. This column represents an ID generated from [CycleStart] in Table A e.g. 31/12/2018 = 201812 (YYYYMM).
What I want to show in Table B is a Period ID for each Job ID but show EACH month + 30 years ahead of the [CycleStart] date. Example table of what I am looking to achieve:
+-------+---------+---------+
| JobID | PeriodID | Entity |
+-------+---------+---------+
| 10006 | 201812 | XYZ1 |
| 10006 | 201901 | XYZ2 |
| 10006 | 201902 | XYZ3 |
| 10006 | 201903 | XYZ4 |
| 10006 | 201904 | XYZ5 |
| 10006 | 201905 | XYZ5 |
| 10006 | 201906 | XYZ5 |
| 10006 | 201907 | XYZ5 |
| ... | +30yrs | ... |
| 10006 | 204812 | XYZ5 |
+-------+----------+--------+
How can I achieve this? Currently I am just randomly generating IDs which is not related to the [CycleStart] date and therefore just skewing my data but this is the only way I can think of doing it.
The best way is to create a calendar table / date dimension. You can use this table to solve this issue, and reuse it for other problems later. (Search online for some examples on how to build one).
If you have this table then you only need to join this table and that's it.
e.g.
INSERT INTO TableB ( JobID , PeriodID)
SELECT DISTINCT A.JobID , D.TheYear * 100 + D.TheMonth
FROM tableA A
JOIN myDateTable D
ON D.TheDate BETWEEN CONVERT(date , A.CycleStart , 103) AND DATEADD(YEAR,30, CONVERT(date , A.CycleStart , 103));
This SQL Statement is not enough to add the right biweekly periods.
What i want is something like this:
Column 1 (period) / Column 2 (start period) / Column 3 (end period)
20160115 / 2016-01-01 / 2016-01-15
20160131 / 2016-01-15 / 2016-01-31
20160215 / 2016-02-01 / 2016-02-15
20160229 / 2016-02-16 / 2016-02-29
and so on...
This is what I have now, which is wrong / incomplete.
the value is being inserted correctly in the table columns but the gap is wrong since months don't have the same amount of days
Can someone help me? Thank you very much!
DECLARE #d date= '20020101'
WHILE #d<'20030101'
BEGIN
INSERT INTO [TABLE]
VALUES ((SELECT CONVERT(VARCHAR(8), #d, 112) AS [YYYYMMDD]), #d, #d, 'Fechado', '1')
SET #d=DATEADD(DAY,15,#d)
END
using a common table expression for an adhoc numbers table, and union all for two queries with some date math using dateadd():
declare #startdate date = '2016-01-01'
declare #enddate date = '2016-12-31'
;with cte as (
select top (datediff(month,#startdate,#enddate)+1)
Month = convert(date,dateadd(month,row_number() over (order by (select 1))-1,#startdate))
from master..spt_values
order by month
)
select
Period = convert(char(8), dateadd(day,14,month), 112)
, PeriodStart = month
, PeriodEnd = dateadd(day,14,month)
from cte
union all
select
Period = convert(char(8), eomonth(month), 112)
, PeriodStart = dateadd(day,14,month)
, PeriodEnd = eomonth(month)
from cte
order by period
rextester demo: http://rextester.com/BSYIXW7864
returns:
+----------+-------------+------------+
| Period | PeriodStart | PeriodEnd |
+----------+-------------+------------+
| 20160115 | 2016-01-01 | 2016-01-15 |
| 20160131 | 2016-01-15 | 2016-01-31 |
| 20160215 | 2016-02-01 | 2016-02-15 |
| 20160229 | 2016-02-15 | 2016-02-29 |
| 20160315 | 2016-03-01 | 2016-03-15 |
| 20160331 | 2016-03-15 | 2016-03-31 |
| 20160415 | 2016-04-01 | 2016-04-15 |
| 20160430 | 2016-04-15 | 2016-04-30 |
| 20160515 | 2016-05-01 | 2016-05-15 |
| 20160531 | 2016-05-15 | 2016-05-31 |
| 20160615 | 2016-06-01 | 2016-06-15 |
| 20160630 | 2016-06-15 | 2016-06-30 |
| 20160715 | 2016-07-01 | 2016-07-15 |
| 20160731 | 2016-07-15 | 2016-07-31 |
| 20160815 | 2016-08-01 | 2016-08-15 |
| 20160831 | 2016-08-15 | 2016-08-31 |
| 20160915 | 2016-09-01 | 2016-09-15 |
| 20160930 | 2016-09-15 | 2016-09-30 |
| 20161015 | 2016-10-01 | 2016-10-15 |
| 20161031 | 2016-10-15 | 2016-10-31 |
| 20161115 | 2016-11-01 | 2016-11-15 |
| 20161130 | 2016-11-15 | 2016-11-30 |
| 20161215 | 2016-12-01 | 2016-12-15 |
| 20161231 | 2016-12-15 | 2016-12-31 |
+----------+-------------+------------+
Create a function, then call in your query:
Function:
create FUNCTION advance_biweekly
(
#current date
)
RETURNS date
AS
BEGIN
--if it's the first day of month, advance two weeks
if (DAY(#current) = 1)
return DATEADD(WEEK, 2, #current)
else
--if its last day of month, advance one day
if (DAY(DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, #current) + 1, 0))) = DAY(#current))
return DATEADD(DAY, 1, #current)
else
--else, it's the middle of the month, go to end
return dateadd(month,((YEAR(#current)-1900)*12)+MONTH(#current)-1,DAY(DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, #current) + 1, 0)))-1)
return null
END
GO
code:
DECLARE #d date= '20020101'
WHILE #d<'20030101'
begin
set #d = dbo.advance_biweekly(#d)
print #d
end
result:
2002-01-15
2002-01-31
2002-02-01
2002-02-15
2002-02-28
2002-03-01
2002-03-15
2002-03-31
2002-04-01
2002-04-15
2002-04-30
2002-05-01
I have a table with start and end dates for team consultations with customers.
I need to merge certain consultations based on a number of days specified in another column (sometimes the consultations may overlap, sometimes they are contiguous, sometimes they arent), Team and Type.
Some example data is as follows:
DECLARE #TempTable TABLE([CUSTOMER_ID] INT
,[TEAM] VARCHAR(1)
,[TYPE] VARCHAR(1)
,[START_DATE] DATETIME
,[END_DATE] DATETIME
,[GROUP_DAYS_CRITERIA] INT)
INSERT INTO #TempTable VALUES (1,'A','A','2013-08-07','2013-12-31',28)
,(2,'B','A','2015-05-15','2015-05-28',28)
,(2,'B','A','2015-05-15','2016-05-12',28)
,(2,'B','A','2015-05-28','2015-05-28',28)
,(3,'C','A','2013-05-27','2014-07-23',28)
,(3,'C','A','2015-01-12','2015-05-28',28)
,(3,'B','A','2015-01-12','2015-05-28',28)
,(3,'C','A','2015-05-28','2015-05-28',28)
,(3,'C','A','2015-05-28','2015-12-17',28)
,(4,'A','B','2013-07-09','2014-04-21',7)
,(4,'A','B','2014-04-29','2014-08-01',7)
Which looks like this:
+-------------+------+------+------------+------------+---------------------+
| CUSTOMER_ID | TEAM | TYPE | START_DATE | END_DATE | GROUP_DAYS_CRITERIA |
+-------------+------+------+------------+------------+---------------------+
| 1 | A | A | 07/08/2013 | 31/12/2013 | 28 |
| 2 | B | A | 15/05/2015 | 28/05/2015 | 28 |
| 2 | B | A | 15/05/2015 | 12/05/2016 | 28 |
| 2 | B | A | 28/05/2015 | 28/05/2015 | 28 |
| 3 | C | A | 27/05/2013 | 23/07/2014 | 28 |
| 3 | C | A | 12/01/2015 | 28/05/2015 | 28 |
| 3 | B | A | 12/01/2015 | 28/05/2015 | 28 |
| 3 | C | A | 28/05/2015 | 28/05/2015 | 28 |
| 3 | C | A | 28/05/2015 | 17/12/2015 | 28 |
| 4 | A | B | 09/07/2013 | 21/04/2014 | 7 |
| 4 | A | B | 29/04/2014 | 01/08/2014 | 7 |
+-------------+------+------+------------+------------+---------------------+
My desired output is as follows:
+-------------+------+------+------------+------------+---------------------+
| CUSTOMER_ID | TEAM | TYPE | START_DATE | END_DATE | GROUP_DAYS_CRITERIA |
+-------------+------+------+------------+------------+---------------------+
| 1 | A | A | 07/08/2013 | 31/12/2013 | 28 |
| 2 | B | A | 15/05/2015 | 12/05/2016 | 28 |
| 3 | C | A | 27/05/2013 | 23/07/2014 | 28 |
| 3 | C | A | 12/01/2015 | 17/12/2015 | 28 |
| 3 | B | A | 12/01/2015 | 28/05/2015 | 28 |
| 4 | A | B | 09/07/2013 | 21/04/2014 | 7 |
| 4 | A | B | 29/04/2014 | 01/08/2014 | 7 |
+-------------+------+------+------------+------------+---------------------+
I am struggling to do this at all, let alone with any efficiency! Any ideas / code will be greatly received.
Server version is MS SQL Server 2014
Thanks,
Dan
If I am understanding your question correctly, we want to return rows only when a second, third, etc consultation has not occurred within group_days_criteria number of days after the previous consultation end date.
We can get the previous consultation end date and eliminate rows (since we are not concerned with the number of consultations) where a consultation occurred for the same customer by the same team and of the same consultation type within our date range.
DECLARE #TempTable TABLE([CUSTOMER_ID] INT
,[TEAM] VARCHAR(1)
,[TYPE] VARCHAR(1)
,[START_DATE] DATETIME
,[END_DATE] DATETIME
,[GROUP_DAYS_CRITERIA] INT)
INSERT INTO #TempTable VALUES (1,'A','A','2013-08-07','2013-12-31',28)
,(2,'B','A','2015-05-15','2015-05-28',28)
,(2,'B','A','2015-05-15','2016-05-12',28)
,(2,'B','A','2015-05-28','2015-05-28',28)
,(3,'C','A','2013-05-27','2014-07-23',28)
,(3,'C','A','2015-01-12','2015-05-28',28)
,(3,'B','A','2015-01-12','2015-05-28',28)
,(3,'C','A','2015-05-28','2015-05-28',28)
,(3,'C','A','2015-05-28','2015-12-17',28)
,(4,'A','B','2013-07-09','2014-04-21',7)
,(4,'A','B','2014-04-29','2014-08-01',7)
;with prep as (
select Customer_ID,
Team,
[Type],
[Start_Date],
[End_Date],
Group_Days_Criteria,
ROW_NUMBER() over (partition by customer_id, team, [type] order by [start_date] asc, [end_date] desc) as rn, -- earliest start date with latest end date
lag([End_Date] + Group_Days_Criteria, 1, 0) over (partition by customer_id, team, [type] order by [start_date] asc, [end_date] desc) as PreviousEndDate -- previous end date +
from #TempTable
)
select p.Customer_Id,
p.[Team],
p.[Type],
p.[Start_Date],
p.[End_Date],
p.Group_Days_Criteria
from prep p
where p.rn = 1
or (p.rn != 1 and p.[Start_date] > p.PreviousEndDate)
order by p.Customer_Id, p.[Team], p.[Start_Date], p.[Type]
This returned the desired result set.
I'm trying to achieve a report that will show all daily count as well weekly count in the same table. I've tried different techniques that I know but it seems that I wasn't able to get what I want.
I'm trying to show a similar table below.
+-----------+-----+-------+--------+--+--+--+
| August | | Count | | | | |
+-----------+-----+-------+--------+--+--+--+
| 8/1/2013 | Thu | 1,967 | | | | |
| 8/2/2013 | Fri | 1,871 | | | | |
| 8/3/2013 | Sat | 1,950 | | | | |
| 8/4/2013 | Sun | 2,013 | 7801 | | | |
| 8/5/2013 | Mon | 2,039 | | | | |
| 8/6/2013 | Tue | 1,871 | | | | |
| 8/7/2013 | Wed | 1,611 | | | | |
| 8/8/2013 | Thu | 1,680 | | | | |
| 8/9/2013 | Fri | 1,687 | | | | |
| 8/10/2013 | Sat | 1,649 | | | | |
| 8/11/2013 | Sun | 1,561 | 12,098 | | | |
+-----------+-----+-------+--------+--+--+--+
Please let me if there's an existing code or technique that I could to achieve something like this. Thanks.
Sherwin
Try something like this but make sure to check which WEEKDAY is Sunday on your server since this can be modified.
select T1.August, T1.[Count],
case DATEPART(WEEKDAY, O.Order_Date)
WHEN 1 THEN (SELECT CONVERT(varchar(10), SUM(T2.[Count]) FROM TableName T2 WHERE T2.August BETWEEN DATEADD(d,-7,T1.August) and T1.August))
ELSE ''
end as Weekly_Count
FROM TablleName T1
ORDER BY T.August
If you don't mind having those subtotal on a new row instead of on a new column, GROUP BY WITH ROLLUP could be the solution for you:
SET LANGUAGE GERMAN is used for setting monday as first day of the week and allowing us to sum up until sunday
SET LANGUAGE GERMAN;
WITH first AS
(
SELECT
date,
day,
DATEPART(dw, date) AS dayweek,
DATEPART(wk, date) AS week,
count
FROM example
)
SELECT
CASE WHEN (GROUPING(dayweek) = 1) THEN 'TOT' ELSE CAST(MAX(date) AS VARCHAR(20)) END AS date,
CASE WHEN (GROUPING(dayweek) = 1) THEN 'TOT' ELSE MAX(day) END AS day,
SUM(count) AS count
FROM first
GROUP BY week,dayweek WITH ROLLUP
see the complete example on sqlfiddle
If you use stored procedures, then make a temp table and loop it through with a cursor, and make the sums.
You could also do something like this:
SELECT CreatedDate, Amount, CASE WHEN DATENAME(dw , CreatedDate) = 'Sunday' THEN (SELECT SUM(Amount) FROM AmountTable at2 WHERE CreatedDate <= at1.CreatedDate AND CreatedDate > DATEADD(Day, -7, at1.CreatedDate)) ELSE 0 END AS 'WeekTotal'
from AmountTable at1
Would something along these lines work, the syntax may not be 100%
select
[Date],
DOW,
[Count],
Case When DOW = 'Mon' then 1 else 2 end as Partition_DOW,
SUM([Count]) OVER (PARTITION BY (Case When DOW = 'Mon' then 1 else 0 end) ORDER BY [Date]) AS 'Monthly_Total'
from My_table
where Month([Date]) = Month(Date()) AND Year([Date]) = Year(Date())