Related
I have a list of members by week and I need to compare the current week to the previous in SQL Server.The first image is how the data is in table and the second image is what I want as a result. I thought maybe doing a CTE for each week and then comparing them. Thanks.
A very naive approach that counts all mem_id values that weren't in the previous week as new could look like this:
declare #t table(mem_id int,weeknum int, yearnum int);
insert into #t values(1,1,2020),(2,1,2020),(1,2,2020),(3,2,2020),(2,3,2020),(3,3,2020),(4,3,2020);
with p as
(
select yearnum
,weeknum
,case when lag(weeknum,1) over (partition by mem_id order by yearnum,weeknum) = weeknum-1 then 0 else 1 end as p
from #t
)
select yearnum
,weeknum
,sum(p) as new
,count(1) as total
from p
group by yearnum
,weeknum
order by yearnum
,weeknum;
Output
+---------+---------+-----+-------+
| yearnum | weeknum | new | total |
+---------+---------+-----+-------+
| 2020 | 1 | 2 | 2 |
| 2020 | 2 | 1 | 2 |
| 2020 | 3 | 2 | 3 |
+---------+---------+-----+-------+
I currently have the following table:
+-----+-----------------------------+------------------------------+
| ID | StartDate | EndDate |
+-----+-----------------------------+------------------------------|
| 1 | 2017-07-24 08:00:00.000 | 2017-07-29 08:00:00.000 |
| 2 | 2017-07-25 08:00:00.000 | 2017-07-28 08:00:00.000 |
| 3 | 2017-07-25 08:00:00.000 | 2017-07-26 08:00:00.000 |
+-----+-----------------------------+------------------------------+
I would like to know the count of the ID's that were not Closed on each date.
So for example, I wan't to know the count of open ID's on 2017-07-26 00:00:00.000. This would be all 3 in this case.
Another example: I wan't to know the count of open ID's on 2017-07-29 00:00:00.000. Which would be result to 1. Only ID=1 is Not yet closed at that date.
I have tried using another solution here on StackOverflow, but I can't quite figure why it is giving me false results.
declare #dt date, #dtEnd date
set #dt = getdate()-7
set #dtEnd = dateadd(day, 100, #dt);
WITH CTEt1 (SupportCallID, StartDate, EndDate, Onhold)
as
(SELECT SupportCallID
,OpenDate
,MAX(CASE WHEN StatusID IN('19381771-8E81-40C5-8E36-62A7DB0A2A99', '95C7A5FB-2389-4D14-9DAE-A08BFCC3B09A', 'D5429790-3B43-4462-9E1E-2466EA29AC74') then CONVERT(DATE, LastChangeDate) end) EndDate
,OnHold
FROM [ClienteleITSM_Prod_Application].[dbo].[SupportCall]
group by SupportCallID, OpenDate, OnHold
)
SELECT dates.myDate,
(SELECT COUNT(*)
FROM CTEt1
WHERE myDate BETWEEN StartDate and EndDate
)
FROM
(select dateadd(day, number, #dt) mydate
from
(select distinct number from master.dbo.spt_values
where name is null
) n
where dateadd(day, number, #dt) < #dtEnd) dates
If you use a cte to create a table of dates that span the range of dates in your source table, you can easily left join from that to your source table and count up the rows returned:
declare #t table(ID int,StartDate datetime,EndDate datetime);
insert into #t values (1,'2017-07-24 08:00:00.000','2017-07-29 08:00:00.000'),(2,'2017-07-25 08:00:00.000','2017-07-28 08:00:00.000'),(3,'2017-07-25 08:00:00.000','2017-07-26 08:00:00.000');
declare #StartDate datetime = (select min(StartDate) from #t);
declare #EndDate datetime = (select max(EndDate) from #t);
-- Table with 10 rows in to be joined together to create a large tally table (10 * 10 * 10 * etc)
with t(t) as (select t from (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(t))
-- Add the row_number of the tally table to your start date to generate all dates within your data range
,d(d) as (select top(datediff(d,#StartDate,#EndDate)+1) dateadd(d,row_number() over (order by (select null))-1,#StartDate) from t t1,t t2,t t3)
select d.d
,count(t.ID) as OpenIDs
from d
left join #t as t
on(d.d between cast(t.StartDate as date) and t.EndDate)
group by d.d
order by d.d;
Output:
+-------------------------+---------+
| d | OpenIDs |
+-------------------------+---------+
| 2017-07-24 08:00:00.000 | 1 |
| 2017-07-25 08:00:00.000 | 3 |
| 2017-07-26 08:00:00.000 | 3 |
| 2017-07-27 08:00:00.000 | 2 |
| 2017-07-28 08:00:00.000 | 2 |
| 2017-07-29 08:00:00.000 | 1 |
+-------------------------+---------+
I have searched high and low for weeks now trying to find a solution to my problem.
As far as I can ascertain, my SQL Server version (2008r2) is a limiting factor on this but, I am positive there is a solution out there.
My problem is as follows:
A have a table with potential contiguous dates in the form of Customer-Status-DateStart-DateEnd-EventID.
I need to merge contiguous dates by customer and status - the status field can shift up and down throughout a customers pathway.
Some example data is as follows:
DECLARE #Tbl TABLE([CustomerID] INT
,[Status] INT
,[DateStart] DATE
,[DateEnd] DATE
,[EventID] INT)
INSERT INTO #Tbl
VALUES (1,1,'20160101','20160104',1)
,(1,1,'20160104','20160108',3)
,(1,2,'20160108','20160110',4)
,(1,1,'20160110','20160113',7)
,(1,3,'20160113','20160113',9)
,(1,3,'20160113',NULL,10)
,(2,1,'20160101',NULL,2)
,(3,2,'20160109','20160110',5)
,(3,1,'20160110','20160112',6)
,(3,1,'20160112','20160114',8)
Desired output:
Customer | Status | DateStart | DateEnd
---------+--------+-----------+-----------
1 | 1 | 2016-01-01| 2016-01-08
1 | 2 | 2016-01-08| 2016-01-10
1 | 1 | 2016-01-10| 2016-01-13
1 | 3 | 2016-01-13| NULL
2 | 1 | 2016-01-01| NULL
3 | 2 | 2016-01-09| 2016-01-10
3 | 1 | 2016-01-10| 2016-01-14
Any ideas / code will be greatly received.
Thanks,
Dan
Try this
DECLARE #Tbl TABLE([CusomerID] INT
,[Status] INT
,[DateStart] DATE
,[DateEnd] DATE
,[EventID] INT)
INSERT INTO #Tbl
VALUES (1,1,'20160101','20160104',1)
,(1,1,'20160104','20160108',3)
,(1,2,'20160108','20160110',4)
,(1,1,'20160110','20160113',7)
,(1,3,'20160113','20160113',9)
,(1,3,'20160113',NULL,10)
,(2,1,'20160101',NULL,2)
,(3,2,'20160109','20160110',5)
,(3,1,'20160110','20160112',6)
,(3,1,'20160112','20160114',8)
;WITH CTE
AS
(
SELECT CusomerID ,
Status ,
DateStart ,
COALESCE(DateEnd, '9999-01-01') AS DateEnd,
EventID,
ROW_NUMBER() OVER (ORDER BY CusomerID, EventID) RowId,
ROW_NUMBER() OVER (PARTITION BY CusomerID, Status ORDER BY EventID) StatusRowId FROM #Tbl
)
SELECT
A.CusomerID ,
A.Status ,
A.DateStart ,
CASE WHEN A.DateEnd = '9999-01-01' THEN NULL
ELSE A.DateEnd END AS DateEnd
FROM
(
SELECT
CTE.CusomerID,
CTE.Status,
MIN(CTE.DateStart) AS DateStart,
MAX(CTE.DateEnd) AS DateEnd
FROM
CTE
GROUP BY
CTE.CusomerID,
CTE.Status,
CTE.StatusRowId -CTE.RowId
) A
ORDER BY A.CusomerID, A.DateStart
Output
CusomerID Status DateStart DateEnd
----------- ----------- ---------- ----------
1 1 2016-01-01 2016-01-08
1 2 2016-01-08 2016-01-10
1 1 2016-01-10 2016-01-13
1 3 2016-01-13 NULL
2 1 2016-01-01 NULL
3 2 2016-01-09 2016-01-10
3 1 2016-01-10 2016-01-14
I have a table with the following format
YEAR, MONTH, ITEM, REQ_QTY1, REQ_QTY2 , ....REQ_QTY31 ,CONVERTED1, CONVERTED2 ....CONVERTED31
Where the suffix of each column is the day of the month.
I need to convert it to the following format, where Day_of_month is the numeric suffix of each column
YEAR, MONTH, DAY_OF_MONTH, ITEM, REQ_QTY, CONVERTED
I thought of using CROSS APPLY to retrieve the data, but I can't use CROSS APPLY to get the "Day of Month"
SELECT A.YEAR, A.MONTH, A.ITEM, B.REQ_QTY, B.CONVERTED
FROM TEST A
CROSS APPLY
(VALUES
(REQ_QTY1, CONVERTED1),
(REQ_QTY2, CONVERTED2),
(REQ_QTY3, CONVERTED3),
......
(REQ_QTY31, CONVERTED31)
)B (REQ_QTY, CONVERTED)
The only way I found is to use a nested select with inner join
SELECT A.YEAR, A.MONTH, A.DAY_OF_MONTH, A.ITEM,A.REQ_QTY, D.CONVERTED FROM
(SELECT YEAR, MONTH, ITEM, SUBSTRING(DAY_OF_MONTH,8,2) AS DAY_OF_MONTH, REQ_QTY FROM TEST
UNPIVOT
(REQ_QTY FOR DAY_OF_MONTH IN ([REQ_QTY1],[REQ_QTY2],[REQ_QTY3],......[REQ_QTY30],[REQ_QTY31])
) B
) A
INNER JOIN (SELECT YEAR, MONTH, ITEM, SUBSTRING(DAY_OF_MONTH,10,2) AS DAY_OF_MONTH, CONVERTED FROM TEST
UNPIVOT
(CONVERTED FOR DAY_OF_MONTH IN ([CONVERTED1],[CONVERTED2],[CONVERTED3],....[CONVERTED30],[CONVERTED31])
) C
) D
ON D.YEAR = A.YEAR AND D.MONTH = A.MONTH AND D.ITEM = A.ITEM AND D.DAY_OF_MONTH = A.DAY_OF_MONTH
Is there a way to use CROSS APPLY and yet get the DAY_OF_MONTH out?
This is not a solution with CROSS APPLY but it will definitely make it a bit faster as it uses a bit simpler approach and simpler execution plan.
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE Test_Table([YEAR] INT, [MONTH] INT, [ITEM] INT, REQ_QTY1 INT
, REQ_QTY2 INT ,REQ_QTY3 INT , CONVERTED1 INT, CONVERTED2 INT, CONVERTED3 INT)
INSERT INTO Test_Table VALUES
( 2015 , 1 , 1 , 10 , 20 , 30 , 100 , 200 , 300),
( 2015 , 2 , 1 , 10 , 20 , 30 , 100 , 200 , 300),
( 2015 , 3 , 1 , 10 , 20 , 30 , 100 , 200 , 300)
Query 1:
SELECT *
FROM
(
SELECT [YEAR]
,[MONTH]
,ITEM
,Vals
,CASE WHEN LEFT(N,3) = 'REQ' THEN SUBSTRING(N,8 ,2)
WHEN LEFT(N,3) = 'CON' THEN SUBSTRING(N,10,2)
END AS Day_Of_Month
,CASE WHEN LEFT(N,3) = 'REQ' THEN LEFT(N,7)
WHEN LEFT(N,3) = 'CON' THEN LEFT(N,9)
END AS Tran_Type
FROM Test_Table t
UNPIVOT (Vals FOR N IN ([REQ_QTY1],[REQ_QTY2],[REQ_QTY3],
[CONVERTED1],[CONVERTED2],[CONVERTED3]))up
)t2
PIVOT (SUM(Vals)
FOR Tran_Type
IN (REQ_QTY, CONVERTED))p
Results:
| YEAR | MONTH | ITEM | Day_Of_Month | REQ_QTY | CONVERTED |
|------|-------|------|--------------|---------|-----------|
| 2015 | 1 | 1 | 1 | 10 | 100 |
| 2015 | 1 | 1 | 2 | 20 | 200 |
| 2015 | 1 | 1 | 3 | 30 | 300 |
| 2015 | 2 | 1 | 1 | 10 | 100 |
| 2015 | 2 | 1 | 2 | 20 | 200 |
| 2015 | 2 | 1 | 3 | 30 | 300 |
| 2015 | 3 | 1 | 1 | 10 | 100 |
| 2015 | 3 | 1 | 2 | 20 | 200 |
| 2015 | 3 | 1 | 3 | 30 | 300 |
Well, I found a way using CROSS APPLY, but instead of taking a substring, I'm basically hardcoding the days. Works well enough so...
SELECT A.YEAR, A.MONTH, A.ITEM, B.DAY_OF_MONTH, B.REQ_QTY, B.CONVERTED
FROM TEST A
CROSS APPLY
(
VALUES
('01', REQ_QTY1, CONVERTED1),
('02', REQ_QTY2, CONVERTED2),
('03', REQ_QTY3, CONVERTED3),
('04', REQ_QTY4, CONVERTED4),
......
('31', REQ_QTY31, CONVERTED31)
) B (DAY_OF_MONTH, REQ_QTY, CONVERTED)
assume I have a dataset:
rowID | dateStart | dateEnd | Year | Month
121 | 2013-10-03 | 2013-12-03 | NULL | NULL
143 | 2013-12-11 | 2014-03-11 | NULL | NULL
322 | 2014-01-02 | 2014-02-11 | NULL | NULL
And I want sql to generate the following datasource based on the dateStart and the dateEnd. Note the year and month grouping.
rowID | dateStart | dateEnd | Year | Month
121 | 2013-10-03 | 2013-12-03 | 2013 | 10
121 | 2013-10-03 | 2013-12-03 | 2013 | 11
121 | 2013-10-03 | 2013-12-03 | 2013 | 12
143 | 2013-12-11 | 2014-03-11 | 2013 | 12
143 | 2013-12-11 | 2014-03-11 | 2014 | 1
143 | 2013-12-11 | 2014-03-11 | 2014 | 2
143 | 2013-12-11 | 2014-03-11 | 2014 | 3
322 | 2014-01-02 | 2014-02-11 | 2014 | 1
322 | 2014-01-02 | 2014-02-11 | 2014 | 2
I'm having a hard time wrapping my head around this one. Any ideas?
I find it easiest to approach these problems by creating a list of integers and then using that to increment the dates. Here is an example:
with nums as (
select 0 as n
union all
select n + 1 as n
from nums
where n < 11
)
select rowid, datestart, dateend,
year(dateadd(month, n.n, datestart)) as yr,
month(dateadd(month, n.n, datestart)) as mon
from table t join
nums n
on dateadd(month, n.n - 1, datestart) <= dateend;
First, create a tabled-valued function that takes the 2 dates and returns the year and month as a table:
create function dbo.YearMonths(#StartDate DateTime, #EndDate DateTime)
returns #YearMonths table
([Year] int,
[Month] int)
as
begin
set #EndDate = DATEADD(month, 1, #EndDate)
while (#StartDate < #EndDate)
begin
insert into #YearMonths
select YEAR(#StartDate), MONTH(#StartDate)
set #StartDate = DATEADD(month, 1, #StartDate)
end
return
end
As an example the following:
select *
from dbo.YearMonths('1/1/2014', '5/1/2014')
returns:
Then you would join to it like this to get what you wanted:
select m.*, ym.Year, ym.Month
from myTable m
cross apply dbo.YearMonths(dateStart, dateEnd) ym
Try this:
declare #months table(mth int)
insert into #months values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
declare #calendar table(yr int,mth int)
insert into #calendar
select distinct year(datestart),mth
from tbl cross join #months
union
select distinct year(dateend),mth
from tbl cross join #months
select t.rowID, t.datestart, t.dateend, y.yr [Year], y.mth [Month]
from
yourtable t
inner join #calendar y on year(datestart) = yr or year(dateend) = yr
where
(mth >= month(datestart) and mth <= month(dateend) and year(datestart) = year(dateend))
or
(year(datestart) < year(dateend))
and
(year(datestart) = yr and mth >= month(datestart) --All months of start year
or
(year(dateend) = yr and mth <= month(dateend))) -- All months of end year
order by t.rowID, [Year],[Month]
We create a 'Calendar table' which lists all the month and year combinations present in the source table. Then, we join the source table to the calendar table based on the year, and filter as required.