SQL Server How to get Date Difference excluding Weekends and Holidays? - sql

I have this table:
tbl_Sales
----------------------------------------
|Item_Code|Sold|Date |
|---------+----+-----------------------|
|BBPen100 |30 |2017-04-17 00:00:00.000|
|BBPen100 |21 |2017-04-13 00:00:00.000|
|BBPen100 |13 |2017-04-12 00:00:00.000|
|XSHIRT80 |0 |2017-04-17 00:00:00.000|
|XSHIRT80 |24 |2017-04-14 00:00:00.000|
|XSHIRT80 |9 |2017-04-13 00:00:00.000|
|XSHIRT80 |5 |2017-04-12 00:00:00.000|
|YBSHADE7 |0 |2017-04-17 00:00:00.000|
|YBSHADE7 |6 |2017-04-15 00:00:00.000|
|YBSHADE7 |0 |2017-04-13 00:00:00.000|
|YBSHADE7 |11 |2017-04-12 00:00:00.000|
----------------------------------------
How can I get the last non-zero Sold value from the last 2 working days? This means that I need to exclude the Weekends and Holidays.
I have this table which consists holidays.
tbl_Holiday
-------------------------
|Holiday_Date |
|-----------------------|
|2017-04-14 00:00:00.000|
|2017-05-01 00:00:00.000|
|2017-10-18 00:00:00.000|
|2017-12-25 00:00:00.000|
-------------------------
So for example today is 2017-04-18, the output should be like this:
---------------------
|Item_Code|Last_Sold|
|---------+---------|
|BBPen100 |30 |
|XSHIRT80 |9 |
|YBSHADE7 |0 |
---------------------
The goal is to get the last Sold value from LAST 2 working days, so the counting start on 2017-04-17.
Output analysis:
BBPen100-since it has value from last 1 working day (2017-04-17), that value will be retrieved.
XSHIRT80-Zero value from last 1 working day (2017-04-17)
-2017-04-16 & 2017-04-15 are weekends
-2017-04-14 is holiday
-So value from 2017-04-13 will be retrieved.
YBSHADE7-Zero value from last 1 working day (2017-04-17)
-2017-04-16 & 2017-04-15 are weekends
-2017-04-14 is holiday
-2017-04-13 has Zero value
-2017-04-12 is beyond Last 2 working days
-So value retrived should be Zero
Currently, I have this query:
SELECT Item_Code, Sold AS 'Last_Sold'
FROM tbl_Sales
WHERE CONVERT(date, [DATE]) = CASE
WHEN CONVERT(date, [DATE]) = CONVERT(date, DATEADD(day, -1, GETDATE())) THEN CONVERT(date, DATEADD(day, -1, GETDATE()))
WHEN CONVERT(date, [DATE]) <> CONVERT(date, DATEADD(day, -1, GETDATE())) THEN CONVERT(date, DATEADD(day, -2, GETDATE()))
But of course, this would not meet the requirements.
Please help me resolve this.
IMPORTANT NOTE: Consider the holidays on weekends and what if I run the program on weekends or holidays.
Thank you in advance.

You could try it
Sample data
DECLARE #SampleData as TABLE (Item_Code varchar(10), Sold int, Date datetime)
Insert into #SampleData VALUES
('BBPen100', 30,'2017-04-17 00:00:00.000'),
('BBPen100', 21,'2017-04-13 00:00:00.000'),
('BBPen100', 13,'2017-04-12 00:00:00.000'),
('XSHIRT80', 0 ,'2017-04-17 00:00:00.000'),
('XSHIRT80', 24,'2017-04-14 00:00:00.000'),
('XSHIRT80', 9 ,'2017-04-13 00:00:00.000'),
('XSHIRT80', 5 ,'2017-04-12 00:00:00.000'),
('YBSHADE7', 0 ,'2017-04-17 00:00:00.000'),
('YBSHADE7', 6 ,'2017-04-15 00:00:00.000'),
('YBSHADE7', 0 ,'2017-04-13 00:00:00.000'),
('YBSHADE7', 11,'2017-04-12 00:00:00.000')
DECLARE #TblHoliday AS TABLE
(
Holiday_Date date
)
INSERT INTO #TblHoliday
VALUES
('2017-04-14 00:00:00.000'),
('2017-05-01 00:00:00.000'),
('2017-10-18 00:00:00.000'),
('2017-12-25 00:00:00.000')
DECLARE #CurrentDate datetime = '2017-04-18 00:00:00'
You could calculate #2PreviousWorkingDays before #CurrentDate
-- 2 Previous Working Day with out Holiday
DECLARE #2PreviousWorkingDay date = CASE
WHEN datepart(dw,#CurrentDate) IN (2,3) THEN dateadd(day,-4, #CurrentDate) -- 2 previous working day before monday
WHEN datepart(dw,#CurrentDate) IN (1) THEN dateadd(day,-3, #CurrentDate) -- 2 previous working day before sunday
ELSE dateadd(day,-2, #CurrentDate) -- other day week
END
-- with holiday
SELECT #2PreviousWorkingDay = dateadd(day,0 - (SELECT count(1) FROM #TblHoliday th
WHERE th.Holiday_Date BETWEEN #2PreviousWorkingDay AND #CurrentDate
ANd datepart(dw,th.Holiday_Date) NOT IN (7,1) -- calculate only holiday that isn't weekend
)
, #2PreviousWorkingDay
And your desired result:
;with temps AS
(
SELECT *, row_number() over(PARTITION BY sd.Item_Code ORDER BY sd.[Date] DESC) AS Rn
FROM #SampleData sd
WHERE sd.[Date] >= #2PreviousWorkingDay -- 2 working days
AND NOT EXISTS (SELECT 1 FROM #TblHoliday th WHERE th.Holiday_Date = Cast(sd.[Date] AS date)) -- not holiday
AND datepart(dw,sd.[Date]) NOT IN (7,1) -- not weekend
AND sd.Sold <> 0 -- not zero sold
)
SELECT sd.Item_Code, ISNULL(t.Sold,0) AS Sold FROM
(
SELECT DISTINCT sd.Item_Code FROM #SampleData sd
) sd
LEFT JOIN temps t ON t.Item_Code = sd.Item_Code AND t.Rn = 1
Demo link: Rextester

WITH cte1 AS(
SELECT t.*
FROM tbl_Sales t
LEFT JOIN tbl_Holiday hd
ON t.Date = hd.Holiday_Date
WHERE hd.Holiday_Date IS NULL AND
DATENAME(WEEKDAY, t.Date) <> 'Saturday' AND
DATENAME(WEEKDAY, t.Date) <> 'Sunday'
),
WITH cte2 AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY Item_Code ORDER BY Date DESC) rn
FROM cte1
)
SELECT *
FROM cte2
WHERE rn=1

Related

Three months in a row

Please help. How to find users who have refueled 50 or more liters every month for 3 months in a row? Customers are counted on the last month of the three-month period.
I'm new to SQL and only guessed to filter out customers who refuel more than 50 liters per month.
SELECT [Client]
,SUM([Litr]) AS Volume
,DATEPART(mm, [Date_transaction]) AS Month_num
,DATEPART(yyyy, [Date_transaction]) AS Year_num
FROM [dbo].[Транзакции]
GROUP BY [Client], DATEPART(mm, [Date_transaction]), DATEPART(yyyy, [Date_transaction])
HAVING SUM([Litr]) >= 50
I received this table:
|Client|Volume |Month_num|Year_num|
|:-----|:---------------|:--------|:-------|
|33 |52,7497194163861|8 |2019 |
|33 |58,1573308846036|9 |2019 |
|33 |148,852157943067|10 |2019 |
|33 |61,2182430364249|12 |2019 |
|55 |73,0741761044791|1 |2019 |
|55 |136,802367105397|3 |2019 |
|58 |88,0522395673911|7 |2019 |
|58 |140,965207631874|8 |2019 |
|58 |130,20099989797 |9 |2019 |
|181 |507,009488827671|6 |2019 |
Desired result:
|Month_num|COUNT(Client)|
|:--------|-------------|
|9 | 1 | (Client 58)
|10 | 1 | (Client 33)
We can group by Client and the start of each month. Then filter out all rows with SUM(tr.Litr) < 50.
Now we can use the LAG window function to check if the previous two rows were the previous two months.
The final result is simply the count of clients who satisfy that condition
SELECT
MonthStart,
NumOfClients = COUNT(*)
FROM (
SELECT
tr.Client,
v.MonthStart,
PrevMonth1 = LAG(v.MonthStart, 1) OVER (PARTITION BY tr.Client ORDER v.MonthStart),
PrevMonth2 = LAG(v.MonthStart, 2) OVER (PARTITION BY tr.Client ORDER v.MonthStart)
FROM dbo.Транзакции tr
CROSS APPLY (VALUES (
DATEFROMPARTS(YEAR(tr.Date_transaction), MONTH(tr.Date_transaction), 1)
) ) v(MonthStart)
GROUP BY tr.Client, v.MonthStart
HAVING SUM(tr.Litr) >= 50
) trGrouped
WHERE DATEDIFF(month, PrevMonth1, MonthStart) = 1
AND DATEDIFF(month, PrevMonth2, PrevMonth1) = 1
GROUP BY MonthStart;
Try this
scripts for table and insert data. Although It was expected from #Andrey
create table #test(client int, litre decimal(10, 5), Date_transaction date)
insert into #test values (33 ,52.7497194163861,'8/1/2019')
insert into #test values (33 ,58.1573308846036,'9/1/2019')
insert into #test values (33 ,148.852157943067,'10/1/2019')
insert into #test values (33 ,61.2182430364249,'12/1/2019')
insert into #test values (55 ,73.0741761044791,'1/1/2019')
insert into #test values (55 ,136.802367105397,'3/1/2019')
insert into #test values (58 ,88.0522395673911,'7/1/2019')
insert into #test values (58 ,140.965207631874,'8/1/2019')
insert into #test values (58 ,130.20099989797 ,'9/1/2019')
insert into #test values (181 ,507.009488827671,'6/1/2019')
Solution Script
SELECT COUNT(distinct client)client, max(month)month
FROM
(
select t1.client, t1.litre, t1.Date_transaction,(DATEPART(mm, t1.Date_transaction)+2)month
from
#test t1
inner join #test t2 ON (t1.client = t2.client and (DATEPART(mm, t1.Date_transaction)+1) = DATEPART(mm, t2.Date_transaction))
inner join #test t3 ON (t1.client = t3.client and (DATEPART(mm, t1.Date_transaction)+2) = DATEPART(mm, t3.Date_transaction))
where t1.litre>50
)T
GROUP BY client
How to find users who have refueled 50 or more liters every month for 3 months in a row?
I would use aggregation and lag(). To get the last month where this occurs:
SELECT t.*
FROM (SELECT t.Client, v.yyyymm,
SUM(t.Litr) AS Volume,
LAG(v.yyyymm, 2) OVER (PARTITION BY t.Client ORDER BY v.yyyymm) as prev2_yyyymm
FROM [dbo].[Транзакции] t CROSS APPLY
(VALUES (DATEFROMPARTS(YEAR(t.Date_transaction), MONTH(t.Date_transaction), 1))
) v(yyyymm)
GROUP BY t.Client, v.yyyymm
HAVING SUM(Litr) >= 50
) t
WHERE prev2_yyyymm = DATEADD(MONTH, -2, yyyymm);
If you want to aggregate by month:
SELECT yyyymm, COUNT(*)
FROM (SELECT t.Client, v.yyyymm,
SUM(t.Litr) AS Volume,
LAG(v.yyyymm, 2) OVER (PARTITION BY t.Client ORDER BY v.yyyymm) as prev2_yyyymm
FROM [dbo].[Транзакции] t CROSS APPLY
(VALUES (DATEFROMPARTS(YEAR(t.Date_transaction), MONTH(t.Date_transaction), 1))
) v(yyyymm)
GROUP BY t.Client, v.yyyymm
HAVING SUM(Litr) >= 50
) t
WHERE prev2_yyyymm = DATEADD(MONTH, -2, yyyymm)
GROUP BY yyyymm;
What is this doing? The inner query is basically your query with some refinements:
yyyymm is a date, more convenient than storing the year and month in the same column.
The CROSS APPLY is just a convenient way of defining the yyyymm alias.
prev2_yyyymm gets the second time previously when the client met the condition.
The outer query implements the logic: If prev2_yyyymm is exactly two months ago, then we have three months in a row that meet the condition.
Here is a db<>fiddle.

Find out how many tickets were open each day historically

I have a table that looks like the below
ID | CreatedDate | CloseDate
271 | 01-Jan-2018 | 02-Jan-2018
278 | 03-Jan-2018 | 05-Jan-2018
333 | 03-Jan-2018 | NULL
I have been tasked to find out for each day in a month, how many tickets remained open going into the next day. A ticket would be classed as remaining open if the CloseDate was not on the same date as the created date, or the CloseDate is NULL.
My ideal output from the above would look like this
Day | Tickets Remaining
01-Jan-2018 | 1
02-Jan-2018 | 0
03-Jan-2018 | 2
04-Jan-2018 | 2
05-Jan-2018 | 1
Does this make sense? Using SQL Server 2016..
If you don't have a number/tally table handy, you can use a recursive CTE:
with cte as (
select id, createddate as dte, coalesce(closeddate, convert(date, getdate())) as closeddate
from t
union all
select id, dateadd(day, 1, dte), closeddate
from cte
where dte < closeddate
)
select dte, count(*)
from cte
group by dte;
Note: If any of the timespans can exceed 100 days, then you need to add option (maxrecursion 0).
If there are only a handful of dates, you can use a derived table and left join:
select v.dte, count(t.id)
from (values (convert(date, '2018-01-01')),
(convert(date, '2018-01-02')),
(convert(date, '2018-01-03')),
(convert(date, '2018-01-04')),
(convert(date, '2018-01-05')),
) v(dte) left join
t
on v.dte >= t.createddate and
(v.dte <= t.closeddate or t.closeddate is null)
group by v.dte
order by v.dte;

Duration overlap causing double counting

I'm using SQL Server Management Studio 2008 for query creation. Reporting Services 2008 for report creation.
I have been trying to work this out over a couple of weeks and I have hit a brick wall. I’m hoping someone will be able to come up with the solution as right now my brain has turned to mush.
I am currently developing an SQL query that will be feeding data through to a Reporting Services report. The aim of the report is to show the percentage of availability for first aid providers within locations around the county we are based in. The idea is that there should be only one first aider providing cover at a time at each of our 20 locations.
This has all been working fine apart from the first aiders at one location have been overlapping their cover at the start and end of each period of cover.
Example of cover overlap:
| Location | start_date | end_date |
+----------+---------------------+---------------------+
| Wick | 22/06/2015 09:00:00 | 22/06/2015 19:00:00 |
| Wick | 22/06/2015 18:30:00 | 23/06/2015 09:00:00 |
| Wick | 23/06/2015 09:00:00 | 23/06/2015 18:30:00 |
| Wick | 23/06/2015 18:00:00 | 24/06/2015 09:00:00 |
+----------+---------------------+---------------------+
In a perfect world the database that they set their cover in wouldn’t allow them to do this but it’s an externally developed database that doesn’t allow us to make changes like that to it. We also aren’t allowed to create functions, stored procedures, tally tables etc…
The query itself should return the number of minutes that each location has had first aid cover for, then broken down into hours of the day. Any overlap in cover shouldn’t end up adding additional cover and should be merged. One person can be on at a time, if they overlap then it should only count as one person lot of cover.
Example Output:
+----------+---------------------+---------------------+----------+--------------+--------+-------+------+----------+
| Location | fromDt | toDt | TimeDiff | Availability | DayN | DayNo | Hour | DayCount |
+----------+---------------------+---------------------+----------+--------------+--------+-------+------+----------+
| WicK | 22/06/2015 18:00:00 | 22/06/2015 18:59:59 | 59 | 100 | Monday | 1 | 18 | 0 |
| WicK | 22/06/2015 18:30:00 | 22/06/2015 18:59:59 | 29 | 50 | Monday | 1 | 18 | 0 |
| WicK | 22/06/2015 19:00:00 | 22/06/2015 19:59:59 | 59 | 100 | Monday | 1 | 19 | 0 |
+----------+---------------------+---------------------+----------+--------------+--------+-------+------+----------+
Example Code:
DECLARE
#StartTime datetime,
#EndTime datetime,
#GivenDate datetime;
SET #GivenDate = '2015-06-22';
SET #StartTime = #GivenDate + ' 00:00:00';
SET #EndTime = '2015-06-23' + ' 23:59:59';
Declare #Sample Table
(
Location Varchar(50),
StartDate Datetime,
EndDate Datetime
)
Insert #Sample
Select
sta.location,
act.Start,
act.END
from emp,
con,
sta,
act
where
emp.ID = con.ID
and con.location = sta.location
and SUBSTRING(sta.ident,3,2) in ('51','22')
and convert(varchar(10),act.start,111) between #GivenDate and #EndTime
and act.ACT= 18
group by sta.location,
act.Start,
act.END
order by 2
;WITH Yak (location, fromDt, toDt, maxDt,hourdiff)
AS (
SELECT location,
StartDate,
/*check if the period of cover rolls onto the next hour */
convert(datetime,convert(varchar(21),
CONVERT(varchar(10),StartDate,111)+' '
+convert(varchar(2),datepart(hour,StartDate))+':59'+':59'))
,
EndDate
,dateadd(hour,1,dateadd(hour, datediff(hour, 0, StartDate), 0))-StartDate
FROM #Sample
UNION ALL
SELECT location,
dateadd(second,1,toDt),
dateadd(hour, 1, toDt),
maxDt,
hourdiff
FROM Yak
WHERE toDt < maxDt
) ,
TAB1 (location, FROMDATE,TODATE1,TODATE) AS
(SELECT
location,
#StartTime,
convert(datetime,convert(varchar(21),
CONVERT(varchar(10),#StartTime,120)+' '
+convert(varchar(2),datepart(hour,#StartTime))+':59'+':59.999')),
#EndTime
from #Sample
UNION ALL
SELECT
location,
(DATEADD(hour, 1,(convert(datetime,convert(varchar(21),
CONVERT(varchar(10),FROMDATE,120)+' '
+convert(varchar(2),datepart(hour,FROMDATE))+':00'+':00.000')))))ToDate,
(DATEADD(hour, 1,(convert(datetime,convert(varchar(21),
CONVERT(varchar(10),TODATE1,120)+' '
+convert(varchar(2),datepart(hour,TODATE1))+':59'+':59.999'))))) Todate1,
TODATE
FROM TAB1 WHERE TODATE1 < TODATE
),
/*CTE Tab2 adds zero values to all possible hours between start and end dates */
TAB2 AS
(SELECT location, FROMDATE,
CASE WHEN TODATE1 > TODATE THEN TODATE ELSE TODATE1 END AS TODATE
FROM TAB1)
SELECT location,
fromDt,
/* Display MaxDT as start time if cover period goes into next dat */
CASE WHEN toDt > maxDt THEN maxDt ELSE toDt END AS toDt,
/* If the end date is on the next day find out the minutes between the start date and the end of the day or find out the minutes between the next day and the end date */
Case When ToDt > Maxdt then datediff(mi,fromDt,maxDt) else datediff(mi,FromDt,ToDt) end as TimeDiff,
Case When ToDt > Maxdt then round(datediff(S,fromDt,maxDt)/3600.0*100,0) else round(datediff(S,FromDt,ToDt)/3600.0*100.0,0) end as Availability,
/*Display the name of the day of the week*/
CASE WHEN toDt > maxDt THEN datename(dw,maxDt) ELSE datename(dw,fromDt) END AS DayN,
CASE WHEN toDt > maxDt THEN case when datepart(dw,maxDt)-1 = 0 then 7 else datepart(dw,maxDt)-1 end ELSE case when datepart(dw,fromDt)-1 = 0 then 7 else datepart(dw,fromDt)-1 END end AS DayNo
,DATEPART(hour, fromDt) as Hour,
'0' as DayCount
FROM Yak
where Case When ToDt > Maxdt then datediff(mi,fromDt,maxDt) else datediff(mi,FromDt,ToDt) end <> 0
group by location,fromDt,maxDt,toDt
Union all
SELECT
tab2.location,
convert(varchar(19),Tab2.FROMDATE,120),
convert(varchar(19),Tab2.TODATE,120),
'0',
'0',
datename(dw,FromDate) DayN,
case when datepart(dw,FromDate)-1 = 0 then 7 else datepart(dw,FromDate)-1 end AS DayNo,
DATEPART(hour, fromDate) as Hour,
COUNT(distinct datename(dw,fromDate))
FROM TAB2
Where datediff(MINUTE,convert(varchar(19),Tab2.FROMDATE,120),convert(varchar(19),Tab2.TODATE,120)) > 0
group by location, TODATE, FROMDATE
Order by 2
option (maxrecursion 0)
I have tried the following forum entries but they haven't worked in my case:
http://forums.teradata.com/forum/general/need-help-merging-consecutive-and-overlapping-date-spans
Checking for time range overlap, the watchman problem [SQL]
Calculate Actual Downtime ignoring overlap in dates/times
Sorry for being so lengthy but I thought I would try to give you as much detail as possible. Any help will be really appreciated. Thank you.
So the solution I came up with uses temp tables, which you can easily change to be CTEs so you can avoid using a stored procedure.
I tried working with window functions to find overlapping records and get the min and max times, the issue is where you have overlap chaining e.g. 09:00 - 09:10, 09:05 - 09:15, 09:11 - 09:20, so all minutes from 09:00 to 09:20 are covered, but it's almost impossible to tell that 09:00 - 09:10 is related to 09:11 - 09:20 without recursing through the results until you get to the bottom of the chain. (Hopefully that makes sense).
So I exploded out all of the date ranges into every minute between the StartDate and EndDate, then you can use the ROW_NUMBER() window function to catch any duplicates, which in turn you can use to see how many different people covered the same minute.
CREATE TABLE dbo.dates
(
Location VARCHAR(64),
StartDate DATETIME,
EndDate DATETIME
);
INSERT INTO dbo.dates VALUES
('Wick','20150622 09:00:00','20150622 19:00:00'),
('Wick','20150622 18:30:00','20150624 09:00:00'),
('Wick','20150623 09:00:00','20150623 18:30:00'),
('Wick','20150623 18:00:00','20150624 09:00:00'),
('Wick','20150630 09:00:00','20150630 09:30:00'),
('Wick','20150630 09:00:00','20150630 09:45:00'),
('Wick','20150630 09:10:00','20150630 09:25:00'),
('Wick','20150630 09:35:00','20150630 09:55:00'),
('Wick','20150630 09:57:00','20150630 10:10:00');
SELECT ROW_NUMBER() OVER (PARTITION BY Location ORDER BY StartDate) [Id],
Location,
StartDate,
EndDate
INTO dbo.overlaps
FROM dbo.dates;
SELECT TOP 10000 N=IDENTITY(INT, 1, 1)
INTO dbo.Num
FROM master.dbo.syscolumns a CROSS JOIN master.dbo.syscolumns b;
SELECT 0 [N] INTO dbo.Numbers;
INSERT INTO dbo.Numbers SELECT * FROM dbo.Num;
SELECT [Location] = raw.Location,
[WorkedDate] = CAST([MinuteWorked] AS DATE),
[DayN] = DATENAME(WEEKDAY, [MinuteWorked]),
[DayNo] = DATEPART(WEEKDAY, [MinuteWorked]) -1,
[Hour] = DATEPART(HOUR, [MinuteWorked]),
[MinutesWorked] = SUM(IIF(raw.[Minutes] = 1, 1, 0)),
[MaxWorkers] = MAX(raw.[Minutes])
FROM
(
SELECT
o.Location,
DATEADD(MINUTE, n.N, StartDate) [MinuteWorked],
ROW_NUMBER() OVER (PARTITION BY o.Location, DATEADD(MINUTE, n.N, StartDate) ORDER BY DATEADD(MINUTE, n.N, StartDate)) [Minutes]
FROM dbo.overlaps o
INNER JOIN dbo.Numbers n ON n.N < DATEDIFF(MINUTE, StartDate, EndDate)
) raw
GROUP BY
raw.Location,
CAST([MinuteWorked] AS DATE),
DATENAME(WEEKDAY, [MinuteWorked]),
DATEPART(WEEKDAY, [MinuteWorked]) - 1,
DATEPART(HOUR, [MinuteWorked])
Here's a subset of the results:
Location WorkedDate DayN DayNo Hour MinutesWorked MaxWorkers
Wick 2015-06-24 Wednesday 3 8 60 2
Wick 2015-06-30 Tuesday 2 9 58 3
Wick 2015-06-30 Tuesday 2 10 10 1
Here's the fiddle

SQL counting and summing records based on values in other columns and aggregating by week number

I am trying to measure my teams throughput in responding to queries. I need to sum up what has been raised in one week and closed in the same week. It is possible to close queries that have been raised last year as well and I want to log that in the week it has been closed.
I have a table below:
*note: when status = 3, it means it is "CLOSED" else "OPEN"
+---------+------+----------+----------+
|queryno_i|status|issue_date|mutation |
+---------+------+----------+----------+
|102 |1 |02/01/2014|02/01/2014|
+---------+------+----------+----------+
|103 |2 |03/01/2014|03/01/2014|
+---------+------+----------+----------+
|104 |3 |05/01/2014|07/01/2014|
+---------+------+----------+----------+
|105 |3 |06/01/2014|08/01/2014|
+---------+------+----------+----------+
|106 |4 |08/01/2014|08/01/2014|
+---------+------+----------+----------+
|204 |3 |02/04/2013|08/01/2014|
+---------+------+----------+----------+
My desired result is:
+-----------+---------------+---------------+
|Week Number|count_of_issued|count_of_closed|
+-----------+---------------+---------------+
|1 |2 |0 |
+-----------+---------------+---------------+
|2 |3 |3 |
+-----------+---------------+---------------+
I want to be able to count how many were queries were opened in a week and how many were closed in the same week.
Here is a sample of my code... but its output is not correct.
SELECT
Week_Number,
SUM(cnt_closed) AS 'Closed',
FROM
(
select
DATEPART(wk,issued_date) AS 'Week_Number',
DATEPART(wk,mutation) AS 'week_closed',
case /*count closed (else 0 so when we sum it will not affect our count)*/
when
status = '3' then count(queryno_i)
else '0'
end as 'cnt_closed',
issued_date as 'issued'
from t.tech_query
group by status, issued_date, mutation
) AS cnt
WHERE Week_Number <= DATEPART(wk,GETDATE()) OR week_closed <= DATEPART(wk, GETDATE())
GROUP BY issued, Week_Number, week_closed
ORDER BY issued DESC
Thanks!
This is a two-part query. The best way to start is to break down your question into parts.
First, I want to see a count of all the tickets that were opened by week:
SELECT datepart(wk, issued_date) as week_number
, count(queryno_i) as issued_requests
FROM t.tech_query
GROUP BY datepart(wk, issued_date)
And I also want to see all the tickets that were opened and closed in the same week
SELECT datepart(wk, issued_date) as week_number
, count(queryno_i)
FROM t.tech_query
where status=3
GROUP BY datepart(wk, issued_date)
Then, it should just be a matter of joining the results together.
WITH alltickets AS
(
SELECT datepart(wk, issued_date) as week_number
, count(queryno_i) as issued_requests
FROM t.tech_query
GROUP BY datepart(wk, issued_date)
), completedinsameweek AS
(
SELECT datepart(wk, issued_date) as week_number
, count(queryno_i) completed_requests
FROM t.tech_query
where status=3 and datepart(wk, issued_date) = datepart(wk, mutation_date)
GROUP BY datepart(wk, issued_date)
)
SELECT alltickets.week_number
, alltickets.issued_requests
, completedinsameweek.completed_requests
FROM alltickets JOIN completedinsameweek
ON (alltickets.week_number = completedinsameweek.week_number)
Note that completed tickets that were completed in a later week aren't counted in this query.
If you're just trying to determine the total count of opened and the count of closed for a given week, the query should be simpler:
WITH issuedtickets AS
(
SELECT datepart(wk, issued_date) as week_number
, count(queryno_i) as issued_requests
FROM t.tech_query
GROUP BY datepart(wk, issued_date)
), completedtickets AS
(
SELECT datepart(wk, mutation_date) as week_number
, count(queryno_i) completed_requests
FROM t.tech_query
where status=3
GROUP BY datepart(wk, mutation_date)
)
SELECT issuedtickets.week_number
, issuedtickets.issued_requests
, completedtickets.completed_requests
FROM issuedtickets JOIN completedtickets
ON (issuedtickets.week_number = completedtickets.week_number)
Also note that datepart(wk....) will be the same for all years. So you may need to add additional logic for years.

Looping with Summation of Fields and dateTime as a counter SQL

I have a 3 fields ( ID, Price, Date )
and it looks like this..
|ID| |Price| | Date |
--------------------------------------------
|001| |150.00| | 2007-01-01 11:48:18.000 |
|002| |150.00| | 2007-01-01 15:57:19.000 |
|003| |150.00| | 2007-01-02 13:26:12.000 |
|004| |150.00| | 2007-01-03 10:31:14.000 |
etc etc
and I need to display the TOTAL AMOUNT of sales for EACH DAY for a certain period of time.
So when I put January 1 to January 6...
it should be
| Days | Total Sales |
-------------------------------
| January 1 | --some amount |
| January 2 | --some amount |
| January 3 | --some amount |
| January 4 | --some amount |
| January 5 | --some amount |
| January 6 | --some amount |
I just cant figure it out and Im stuck with this code :) ...
DECLARE #StartDate dateTime,#EndDate dateTime, #TotalSales integer
SET #StartDate = '2007-01-02 11:41:19.000'
SET #EndDate = '2007-01-02 11:46:06.000'
SET #TotalSales = 0
while ( #StartDate = '2007-01-02 11:41:19.000' )
BEGIN
--Some codes
END
thanks :)
You don't need a loop, use set operations whenever possible:
DECLARE #StartDate dateTime,#EndDate dateTime
SET #StartDate = convert(DateTime,'2007-01-01 11:41:19.000',102)
SET #EndDate = convert(DateTime,'2007-01-04 11:46:06.000',102)
;WITH CTE AS (
SELECT ID,Price,[Date]
FROM Sales
WHERE [Date] Between #StartDate AND #EndDate
)
SELECT DATENAME( month ,[Date] ) + ' ' + DATENAME( day ,[Date] ) AS Days
, SUM(Price)AS 'Total Sales'
FROM CTE
GROUP BY DATENAME( month ,[Date] ) + ' ' + DATENAME( day ,[Date] )
SQL-Fiddle Demo
This is similar to Tim Schmelter's solution except that it handles for days that do not have any sales data:
DECLARE #StartDate DATETIME, #EndDate DATETIME
SELECT #StartDate = '01-01-2007', #EndDate = '01-06-2007'
;WITH DateRange ([Date]) AS
(
SELECT
#StartDate [Date]
UNION ALL
SELECT
DATEADD(DAY, 1, [Date]) [Date]
FROM
DateRange
WHERE
[Date] < #EndDate
)
SELECT
DATENAME(MONTH, d.[Date]) + ' ' + DATENAME(DAY, d.[Date]) AS Days,
SUM(ISNULL(Price, 0)) AS [Total Sales]
FROM
DateRange d
LEFT JOIN
Sales s
ON
d.[Date] = DATEADD(DAY, DATEDIFF(DAY, 0, s.[Date]), 0)
GROUP BY
DATENAME(MONTH, d.[Date]) + ' ' + DATENAME(DAY, d.[Date])
SQL Fiddle Example