How do I link the subquery to the main query? - sql

I am having an issue getting blast_seconds to show up as the correct value. In my select subquery I need to get the blast_seconds to show up within the same time as in my main Select statement. When the query is executed Year, Month, ScheduledSeconds, TotalDays and Totaltrucks match up, but blast_seconds column shows the same value through out all the months.
Here is my query:
SELECT DATEPART(YEAR, [Time]) AS [Year]
, DATEPART(MONTH, [Time]) AS [Month]
, SUM(Total) AS ScheduledSeconds
, COUNT(DISTINCT([Time])) AS TotalDays
, COUNT(DISTINCT(Equipment)) AS TotalTrucks
, (
SELECT SUM(CASE
WHEN dbo.reasons.status_id = '298'
THEN (dbo.by_operator_reasons.seconds)
ELSE 0
END)
FROM dbo.reasons
INNER JOIN by_operator_reasons
ON dbo.reasons.id = by_operator_reasons.reason_id
INNER JOIN equipment
ON by_operator_reasons.equipment_id = equipment.id
WHERE reasons.descrip LIKE 'Blast'
AND [Time] BETWEEN '2011-01-01' AND '2012-05-15'
AND by_operator_reasons.deleted_at IS NULL
AND equipment.type = 'Truck'
) AS Blast_Seconds
FROM by_equipment_times
WHERE [Time] BETWEEN '2011-01-01' and '2012-05-15'
AND equipment_type = 'Truck'
GROUP BY DATEPART(YEAR, [Time])
, DATEPART(MONTH, [Time])
ORDER BY DATEPART(YEAR, [Time]) ASC
, DATEPART(MONTH, [Time]) ASC
Here is my current output:
Year Month SchedSec Days TotalTrucks Blast_Seconds
---- ----- -------- ---- ----------- -------------
2011 1 51340448 31 20 4931156
2011 2 51979509 28 22 4931156
2011 3 58845600 31 22 4931156
2011 4 59121967 30 24 4931156
2011 5 66857271 31 25 4931156
2011 6 67306766 30 28 4931156
2011 7 76976358 31 30 4931156
2011 8 80393145 31 30 4931156
2011 9 75556005 30 30 4931156
2011 10 77741205 31 29 4931156
2011 11 75272400 30 29 4931156
2011 12 77691044 31 29 4931156
2012 1 77683752 31 29 4931156
2012 2 72662400 29 29 4931156
2012 3 77574538 31 29 4931156
2012 4 75172177 30 29 4931156
2012 5 37584000 15 29 4931156

The subquery is not corelated to main query, that is it does not depend on it. You need to connect it, and I believe that you want to do it using Time column.
SELECT DATEPART(YEAR, [Time]) AS [Year]
, DATEPART(MONTH, [Time]) AS [Month]
, SUM(Total) AS ScheduledSeconds
, COUNT(DISTINCT([Time])) AS TotalDays
, COUNT(DISTINCT(Equipment)) AS TotalTrucks
, (
SELECT SUM(CASE
WHEN dbo.reasons.status_id = '298'
THEN (dbo.by_operator_reasons.seconds)
ELSE 0
END)
FROM dbo.reasons
INNER JOIN by_operator_reasons
ON dbo.reasons.id = by_operator_reasons.reason_id
INNER JOIN equipment
ON by_operator_reasons.equipment_id = equipment.id
WHERE reasons.descrip LIKE 'Blast'
AND DATEPART(YEAR, [Time]) = DATEPART(YEAR, by_equipment_times.[Time])
AND DATEPART(MONTH, [Time]) = DATEPART(MONTH, by_equipment_times.[Time])
AND by_operator_reasons.deleted_at IS NULL
AND equipment.type = 'Truck'
) AS Blast_Seconds
FROM by_equipment_times
WHERE [Time] BETWEEN '2011-01-01' and '2012-05-15'
AND equipment_type = 'Truck'
GROUP BY DATEPART(YEAR, [Time])
, DATEPART(MONTH, [Time])
ORDER BY DATEPART(YEAR, [Time]) ASC
, DATEPART(MONTH, [Time]) ASC
If you regularly take more than few months it would probably pay off to convert subquery to derived table, grouping by year/month and outer-joining to main query.

This is most likely because you do not tell the
Select SUM(CASE WHEN dbo.reasons.status_id = '298'
then (dbo.by_operator_reasons.seconds) ELSE 0 END....
sub query to get values for respective rows - it just sums everything it finds.
You have to bind sub query to main query - something like
[...]
AND equipment.equipemnt_id_or_something = T1.equipment_id_or_something
) AS Blast_Seconds
FROM by_equipment_times as T1
[...]
Or so I think .... :)
PS.
Names of fields are imaginary.

Related

Incremental and decremental count based on a date

Name Start_date end_date
aaa 01/02/2017 05/03/2017
bbb 03/05/2017 07/07/2017
ccc 02/01/2017 10/09/2017
I want to write a query that calculates the number of people who exist in the DB in a certain month/year.
Answer:
Jan 2017 1
Feb 2017 2
Mar 2017 3
Apr 2017 3
May 2017 2 (one person - aaa ,ended in May 2017)
Jun 2017 2
Jul 2017 1 (bbb ended in July 2017)
How do I write a PSQL query to get the desired output?
Thanks.
First, get the max and min dates in order to declare the dates range.
Second, with etc select all the month in the range.
Third, sum the number of the records in each dates.
Like:
declare #date date
declare #toDate date
select #date = min(Start_date),
#toDate = max(end_date)
from table_name
;With dt As
(
Select #date As [TheDate]
Union All
Select DateAdd(month, 1, TheDate) From dt Where [TheDate] < #toDate
)
select month(dt.TheDate),
year(dt.TheDate),
sum(case when table_name.Name is not null then 1 else 0 end)
from dt
left join table_name
on table_name.Start_date >= dt.TheDate
and table_name.end_date < dateadd(day,-1,dateAdd(month,1,dt.TheDate))

Table of totals for the last 12 months, both by month and running

I'd like to have a table that contains 12 rows, one for each of the last 12 months, and two additional columns:
That month's (and only that month's) total spend
Total spend to date, since the beginning of time
The table could look like:
Month MonthTotal RunningTotal
Apr 16 0 6710
May 16 0 6710
Jun 16 2000 8710
Jul 16 0 8710
Aug 16 0 8710
Sep 16 0 8710
Oct 16 1000 9710
Nov 16 0 9710
Dec 16 0 9710
Jan 17 500 10210
Feb 17 0 10210
Mar 17 0 10210
I'm almost there but I'm hitting an issue when I have months with no purchases in them - that column is being output as null. Here's my current query:
with Y(n) as
(
select 0
union all
select n + 1
from Y
where n < 11
)
select
convert(varchar, M.Month) + '/' + convert(varchar, M.Year % 100) as Month,
isnull(sum(A.Value), 0) as MonthAssetValue,
sum(A.Value) over (order by A.DateAquired rows between unbounded preceding and current row) as TotalAssetValue
from (
select
datepart(year, dateadd(month, -n, getdate())) as [Year],
datepart(month, dateadd(month, -n, getdate())) as [Month]
from
Y
) M
left outer join Asset A
on (datepart(year, A.DateAquired) = M.Year and datepart(month, A.DateAquired) = M.Month)
group by
M.Year,
M.Month,
A.DateAquired,
A.Value
order by
M.Year,
M.Month
Any ideas where I'm going wrong?
Edit:
This is the output I currently get:
Month MonthTotal RunningTotal
Apr 16 0 null
May 16 0 null
Jun 16 2000 8710
Jul 16 0 null
Aug 16 0 null
Sep 16 0 null
Oct 16 1000 9710
Nov 16 0 null
Dec 16 0 null
Jan 17 500 10210
Feb 17 0 null
Mar 17 0 null
You can use rolling windowing function
SELECT Month, MonthTotal, [Sum 12 Months] = SUM(MonthTotal) OVER (ORDER BY [Month] ROWS BETWEEN 11 PRECEDING AND CURRENT ROW)
FROM YourTable
Your problem is the group by. It should only have the year and the month:
group by M.Year, M.Month
In addition, don't use varchar() without a length. And, I think there is a syntax error in the windows function call. You need to mix the aggregation and window functions.
I would write the query as:
with Y(n) as (
select 0
union all
select n + 1
from Y
where n < 11
)
select left(m.mmm, 3) + '/' + right(yyyymm.yyyy, 2) as mmmyy,
coalesce(sum(A.Value), 0) as MonthAssetValue,
sum(coalesce(sum(A.Value), 0)) over (order by min(dte)) as TotalAssetValue
from (select datename(year, dateadd(month, -n, getdate())) as yyyy,
datename(month, dateadd(month, -n, getdate())) as mmm,
dateadd(month, -n, getdate()) as dte
from Y
) yyyymm left outer join
Asset A
on datename(year, A.DateAquired) = yyyymm.yyyy and
datename(month, A.DateAquired) = yyyymm.mmm
group by yyyymm.yyyy, yyyymm.mmm
order by min(dte);
Actually, to be honest, I would represent the year/month as YYYY-MM, but I kept your format.
Try replace sum(A.Value) as MonthAssetValue,
sum(A.Value) over (order by A.DateAquired) rows between unbounded preceding and current row) as TotalAssetValue
with below
SUM(COALESCE(A.Value, 0)) as MonthAssetValue,
SUM(COALESCE(A.Value, 0)) over (order by A.DateAquired) rows between unbounded preceding and current row) as TotalAssetValue

SQL: add missing months from different years

SQL SERVER
[CreatedOn] - DATETIME
I get this table:
Year Month Count
2009 7 1
2009 9 1
2010 1 2
2010 3 13
From query:
SELECT
YEAR ([CreatedOn]) AS 'Year',
MONTH ([CreatedOn]) AS 'Month',
COUNT ([CreatedOn]) AS 'Count'
FROM xxx
GROUP BY YEAR ([CreatedOn]), MONTH ([CreatedOn])
How can I get table like this (with missed months and Count 0):
Year Month Count
2009 7 1
2009 8 0
2009 9 1
2009 10 0
2009 11 0
2009 12 0
2010 1 2
2010 2 0
2010 3 13
Syntax says you are using MSSQL. Use Recursive CTE to generate the calender table then do a Left outer join with XXX table
DECLARE #maxdate DATE = (SELECT Max([CreatedOn])
FROM xxx);
WITH calender
AS (SELECT Min([CreatedOn]) dates,
FROM xxx
UNION ALL
SELECT Dateadd(mm, 1, dates)
FROM cte
WHERE dates < #maxdate)
SELECT Year(dates) [YEAR],
Month(dates) [month],
Count ([CreatedOn]) AS 'Count'
FROM calender a
LEFT OUTER JOIN xxx b
ON Year(dates) = Year ([CreatedOn])
AND Month(dates) = Month ([CreatedOn])
GROUP BY Year(dates),
Month(dates)
Note : Instead of Recursive CTE create a physical calender table
This will use a build in table to create the calendar:
;WITH limits as
(
SELECT min([CreatedOn]) mi, max([CreatedOn]) ma
FROM xxx
), months as(
SELECT
dateadd(mm, number, mi) m
FROM
master..spt_values v
JOIN
limits l
ON
number between 0 and datediff(mm, l.mi, l.ma)
WHERE
v.type = 'P'
)
SELECT
year(months.m) year,
month(months.m) month,
count(qry.[CreatedOn]) cnt
FROM
xxx qry
RIGHT JOIN
months
ON
months.m = dateadd(mm, datediff(mm, 0, qry.[CreatedOn]), 0)
GROUP BY
year(months.m),
month(months.m)

How to get the date for the previous month which are closed in the current month

Iam using SSRS Report builder application to create BI Report for my System which is tracking the numbers of incidents logged and closed based on each month.
the below is the table which i need to create the query
Month Logged Received Closed Remaining
January 200 220 150 70
February 150 220 200 20
March 110 130 100 30
April 200 230 200 30
and each column define as follow:
Logged= Open Incident in the Current Month for example open from 1/1/2014 to 31/1/2014 (Contain only the current month data )
Received = Logged incident+ the remaining from the previous months which are still open not close for example the month febreuary will be 150 for the current moth+70 from previous month remaining will give me total 220 which is received.
Closed= incident which are opened in the current month and closed in the current month + the remaining from the previous month which closed in this month
Remaining= Received – closed
the code which i used is not giving me the close incident for the previous months also its only giving me which were closed in the current month
the below is the code which i used for my query:
SELECT group_id, YEAR(Opendate) AS Year, MONTH(Opendate) AS Month,
COUNT(CASE WHEN Month(Closedate) = Month(Opendate)
AND Month(closedate)> Month (opendate) THEN 1 ELSE NULL END) AS closed,
COUNT(*) AS Logged,
FROM Incidents
WHERE (Opendate >= #YearStart) AND (Opendate <= #YearEnd)
GROUP BY YEAR(Opendate), MONTH(Opendate), group_id
ORDER BY Year, Month,group_id
Logged is working fine the closed, Received and remaining i am stuck on it.
I tried to use Union and got the Logged and Closed Data
Select count(*) logged,year(opendate) as year1,MONTH(opendate) as
month1,'Logged' as status1
From Incidents
where opendate is not null
GROUP BY year(opendate),MONTH(opendate)
UNION
Select count(*) closed,year(Closedate) as year1,MONTH(Closedate) as
month1,'All_Closed' as status1
From Incidents
where Closedate is not null
GROUP BY year(Closedate),MONTH(Closedate)
UNION
Select count(*) Remaining,year(opendate) as year1,MONTH(opendate) as
month1,'Current_Month_Not_Closed' as status1
From Incidents
where Month(Closedate) > MONTH(Opendate)
GROUP BY year(opendate),MONTH(opendate)
UNION
Select count(*) Month_Closed,year(opendate) as year1,MONTH(opendate) as
month1,'Current_Month_Close' as status1
From Incidents
where MONTH(Closedate) = MONTH(Opendate)
GROUP BY year(opendate),MONTH(opendate)
order by year1,month1
the data which I received are as follow:
logged | year1 | month1 | status1
-------+-------+--------+-------------------------
1093 | 2014 | 1 | Logged
1089 | 2014 | 1 | All_Closed
997 | 2014 | 1 | Current_Month_Close
96 | 2014 | 1 | Current_Month_Not_Closed
1176 | 2014 | 2 | Logged
1176 | 2014 | 2 | All_Closed
91 | 2014 | 2 | Current_Month_Not_Closed
1085 | 2014 | 2 | Current_Month_Close
1340 | 2014 | 3 | Logged
1327 | 2014 | 3 | All_Closed
107 | 2014 | 3 | Current_Month_Not_Closed
1232 | 2014 | 3 | Current_Month_Close
116 | 2014 | 4 | Current_Month_Not_Closed
1320 | 2014 | 4 | Current_Month_Close
1424 | 2014 | 4 | All_Closed
1441 | 2014 | 4 | Logged
1167 | 2014 | 5 | Current_Month_Close
105 | 2014 | 5 | Current_Month_Not_Closed
1277 | 2014 | 5 | Logged
1283 | 2014 | 5 | All_Closed
To have a reliable data a calendar table as anchor can help, and is needed in case the tickets can be alive for months from their opening date or there can be a month without ticket created.
For example with the fake data
CREATE TABLE Incidents (
id int identity(1, 1)
, group_id nvarchar(100)
, Opendate Datetime
, Closedate Datetime
)
INSERT INTO Incidents
VALUES ('Service Desk', '20140107', '20140120')
, ('Service Desk', '20140117', '20140123')
, ('Service Desk', '20140127', '20140313')
, ('Service Desk', '20140310', '')
-- from an OP comment the open tickets have the Closedate '' (1900-01-01)
without a calendar table (or a temp, or a CTE) there is no way to add february in the resultset, even if the third record is both "Received" and "Remaining" in that month.
To create a calendar there are several way, in this case we need the some information about months but nothing about the days, so those are not generated.
declare #YearStart date = '20140101'
declare #YearEnd date = '20140430'
;WITH D(N) AS (
SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9
)
SELECT EOM
= DATEADD(D, -1, DATEADD(M, u.N + 10 * t.N + 1
, DATEADD(Y, DATEDIFF(Y, 0, #YearStart), 0)))
, pMonth = u.N + 10 * t.N
FROM D u
CROSS JOIN D t
WHERE u.N + 10 * t.N <= DATEDIFF(M, #YearStart, #YearEnd)
Here EOM is the date of the end of the month, it'll be used to check if the incidents are closed in the month and pMonth is the progressive month starting from #YearStart.
Now we need to prepare the data in the incident table to be used
SELECT ID
, OpenDate
, Closedate = COALESCE(NULLIF(Closedate, ''), '99991231')
, pOpenDate = DATEDIFF(M, #YearStart, OpenDate)
, pClosedate = DATEDIFF(M, #YearStart
, COALESCE(NULLIF(Closedate, ''), '99991231'))
FROM Incidents
the Closedate need to always have a value higher than the OpenDate, for this is used the constant date 9999-12-31, pOpenDate and pClosedate, as pMonth before, are the progressive month starting from #YearStart respectively of OpenDate and Closedate.
Putting those togheter it's possible to create the main query
declare #YearStart date = '20140101'
declare #YearEnd date = '20140430'
;WITH D(N) AS (
SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9
), CM AS (
SELECT EOM
= DATEADD(D, -1, DATEADD(M, u.N + 10 * t.N + 1
, DATEADD(Y, DATEDIFF(Y, 0, #YearStart), 0)))
, pMonth = u.N + 10 * t.N
FROM D u
CROSS JOIN D t
WHERE u.N + 10 * t.N <= DATEDIFF(M, #YearStart, #YearEnd)
), I AS (
SELECT ID
, OpenDate
, Closedate = COALESCE(NULLIF(Closedate, ''), '99991231')
, pOpenDate = DATEDIFF(M, #YearStart, OpenDate)
, pClosedate = DATEDIFF(M, #YearStart
, COALESCE(NULLIF(Closedate, ''), '99991231'))
FROM Incidents
)
SELECT MONTH(CM.EOM) [Month]
, Logged = SUM(CASE WHEN pOpenDate = pMonth
THEN 1
ELSE 0
END)
, Received = Count(i.id)
, Closed = SUM(CASE WHEN pClosedate = pMonth
AND i.Closedate < CM.EOM
THEN 1
ELSE 0
END)
, Remaining = SUM(CASE WHEN i.Closedate > CM.EOM
THEN 1
ELSE 0
END)
FROM CM
INNER JOIN I ON CM.pMonth
BETWEEN i.pOpenDate AND i.pClosedate
WHERE CM.EOM <= #YearEnd
GROUP BY CM.EOM
ORDER BY CM.EOM
SQLFiddle Demo
using a JOIN to get the month from the calendar table between #YearStart and #YearEnd and all the incident alive in the month. Their attribute are calculated with the CASE logic, in case of Received if a ticket is alive it's received so no logic is needed.
All the CASE can be transformed in BIT logic
declare #YearStart date = '20140101'
declare #YearEnd date = '20140430'
;WITH D(N) AS (
SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9
), CM AS (
SELECT EOM
= DATEADD(D, -1, DATEADD(M, u.N + 10 * t.N + 1
, DATEADD(Y, DATEDIFF(Y, 0, #YearStart), 0)))
, pMonth = u.N + 10 * t.N
FROM D u
CROSS JOIN D t
WHERE u.N + 10 * t.N <= DATEDIFF(M, #YearStart, #YearEnd)
), I AS (
SELECT ID
, OpenDate
, Closedate = COALESCE(NULLIF(Closedate, ''), '99991231')
, pOpenDate = DATEDIFF(M, #YearStart, OpenDate)
, pClosedate = DATEDIFF(M, #YearStart
, COALESCE(NULLIF(Closedate, ''), '99991231'))
FROM Incidents
)
SELECT MONTH(CM.EOM) [Month]
, Logged = SUM(1 - CAST(pOpenDate - pMonth AS BIT))
, Received = Count(i.id)
, Closed = SUM(1 - CAST(pClosedate - pMonth AS BIT))
, Remaining = SUM(0 + CAST(i.pClosedate / (CM.pMonth + 1) AS BIT))
FROM CM
INNER JOIN I ON CM.pMonth
BETWEEN i.pOpenDate AND i.pClosedate
WHERE CM.EOM <= #YearEnd
GROUP BY CM.EOM
ORDER BY CM.EOM;
SQLFiddle Demo
The bit logic is base on how the CAST to BIT works:
0 go to 0
everything else go to 1
based on that (with A and B integer):
1 - CAST(A - B AS BIT) is 1 when A = B
CAST(A / (B + 1) AS BIT) is 1 when A > B (the 0 + is to force an implicit cast to INT as BIT cannot be SUMmed)
Received would be the number of tickets that were opened before the end of the month, and not closed before the start of the month.
count(case when OpenDate <= #EndOfMonth and
(#StartOfMonth >= CloseDate or CloseDate is null) then 1 end)
as Received
Closed is straightforward:
count(case when CloseDate between #StartOfMonth and #EndOfMonth
then 1 end) as Closed
You should be able to figure out how to calculate the start and end of a month using Google.

sql server calculate cumulative number per month for different year

I have a table with "date" column. Each row represents a survey.
date
11/19/2013 5:51:41 PM
11/22/2013 1:30:38 PM
11/23/2013 3:09:17 PM
12/2/2014 5:24:17 PM
12/25/2014 11:42:56 AM
1/6/2014 2:24:49 PM
I want to count the number of survey per month cumulatively. As you see from the above table, there are 3 surveys for Nov 2013, 2 surveys for Dec 2013, 1 survey for Jan 2014. The cumulative number of survey per month would be:
month | year | number_of_survey
11 | 2013 | 3
12 | 2013 | 5
1 | 2014 | 6
I have this query which shows correct number of surveys for 2013, and number of survey for 2014 is not cumulative.
with SurveyPerMonth as -- no of Survey per month
(
select datepart(month, s.date) as month,
datepart(year, s.date) as year,
count(*) as no_of_surveys
from myTable s
group by datepart(year, s.date), datepart(month, s.date)
)
select p1.month, p1.year, sum(p2.no_of_surveys) as surveys -- cumulatively
from SurveyPerMonth p1
inner join SurveyPerMonth p2 on p1.month >= p2.month and p1.year>=p2.year **-- the problem is probably comes from this line of code**
group by p1.month, p1.year
order by p1.year, p1.month;
This query returns:
month | year | surveys
11 | 2013 | 3
12 | 2013 | 5
1 | 2014 | 1 // 2014 is not cumulative
How can I calculate cumulative number of surveys per month for 2014 as well?
Something like this ?
SELECT date = create_date INTO #myTable FROM master.sys.objects
;WITH perMonth ( [year], [month], [no_of_surveys])
AS (SELECT DatePart(year, s.date) ,
DatePart(month, s.date),
COUNT(*)
FROM #myTable s
GROUP BY datepart(year, s.date),
datepart(month, s.date))
SELECT [year],
[month],
[no_of_surveys] = ( SELECT SUM([no_of_surveys])
FROM perMonth agg
WHERE (agg.[year] < pm.[year])
OR (agg.[year] = pm.[year] AND agg.[month] <= pm.[month]))
FROM perMonth pm
ORDER BY [year], [month]
Edit: seems I missed the ball with < and >, fixed it and added small example
'--This should work.I have added a new column 'monthyear'
with surveypermonth as -- no of survey per month
(
select datepart(month, s.date) as month,
datepart(year, s.date) as year,
datepart(year, s.date) *100 + datepart(month, s.date) as monthyear,
count(*) as no_of_surveys
from test s
group by datepart(year, s.date), datepart(month, s.date),datepart(year, s.date)*100 + datepart(month, s.date)
)
select a.month,substring(cast(monthyear as varchar(6)),1,4) as year,surveys from
(
select p1.month, p1.monthyear as monthyear, sum(p2.no_of_surveys) as surveys
from surveypermonth p1
inner join surveypermonth p2 on p1.monthyear>=p2.monthyear
group by p1.month, p1.monthyear
--order by p1.monthyear, p1.month
)a