CTE function with insert statement - sql

I have 2 queries and I need to combine them into one query with an insert statement.
This is my first query that already has an insert statement:
with q as (
select s.department
,s.months
,s.years
,count(case when s.sum_lost_time >='10:00:00' then NAME end) as RTOTALLOSTTIME
,count(case when s.sum_ot >='20' then NAME end) as ROT
from (select MONTH(STATUSIN) as [months]
,YEAR(STATUSIN) as [years]
,NIP
,NAME
,DEPARTMENT
,convert(varchar,dateadd(second,sum(datediff(second,'00:00:00',cast(TOTALLT as time))),0),108) as sum_lost_time
,SUM(CAST(OT AS FLOAT)) as sum_ot
from SUMMARYDATA b
group by MONTH(STATUSIN)
,YEAR(STATUSIN)
,NIP
,NAME
,DEPARTMENT
)s
group by s.department
,s.months
,s.years
)
INSERT INTO REPORTDATA(DEPARTMENT,MONTHS,YEARS,RTOTALLOSTTIME,ROT)
SELECT DEPARTMENT,MONTHS,YEARS,RTOTALLOSTTIME,ROT
FROM q
This is the result from first query in table REPORTDATA:
And this is my second query.
WITH cte AS
(
SELECT DISTINCT [NAME], DEPARTMENT, MONTH(STATUSIN) [MONTH], YEAR(STATUSIN) [YEAR],
SUM(CASE WHEN LATECOME = '00:00:00' THEN 0 ELSE 1 END) OVER(PARTITION BY [NAME], DEPARTMENT, MONTH(STATUSIN), YEAR(STATUSIN)) Total
,SUM(CASE WHEN EARLYLEAVE = '00:00:00' THEN 0 ELSE 1 END) OVER(PARTITION BY [NAME], DEPARTMENT, MONTH(STATUSIN), YEAR(STATUSIN)) TotalEarlyLeave
FROM SUMMARYDATA
)
SELECT SUM(CASE WHEN TOTAL > 2 THEN 1 ELSE 0 END) LATECOME,
SUM(CASE WHEN TotalEarlyLeave > 1 THEN 1 ELSE 0 END) EARLYLEAVE
FROM cte
GROUP BY DEPARTMENT, [MONTH], [YEAR]
And this is the result from second query:
I want to place it into my first query but I don't know how to combine it into one in insert statement. Can anyone solve my problems?
This is the sample to my first query: Count summary records per month with conditional SQL
and this is the sample to second query: Count records per month with condition in SQL Server

It's easy if you concatenate your queries as multiple CTEs, and finally JOIN them.
Like this :
;
with cte1 as (
select s.department
,s.months
,s.years
,count(case when s.sum_lost_time >='10:00:00' then NAME end) as RTOTALLOSTTIME
,count(case when s.sum_ot >='20' then NAME end) as ROT
from (select MONTH(STATUSIN) as [months]
,YEAR(STATUSIN) as [years]
,NIP
,NAME
,DEPARTMENT
,convert(varchar,dateadd(second,sum(datediff(second,'00:00:00',cast(TOTALLT as time))),0),108) as sum_lost_time
,SUM(CAST(OT AS FLOAT)) as sum_ot
from SUMMARYDATA b
group by MONTH(STATUSIN)
,YEAR(STATUSIN)
,NIP
,NAME
,DEPARTMENT
)s
group by s.department
,s.months
,s.years
),
cte2 as (
SELECT DISTINCT [NAME], DEPARTMENT, MONTH(STATUSIN) [MONTH], YEAR(STATUSIN) [YEAR],
SUM(CASE WHEN LATECOME = '00:00:00' THEN 0 ELSE 1 END) OVER(PARTITION BY [NAME], DEPARTMENT, MONTH(STATUSIN), YEAR(STATUSIN)) Total
,SUM(CASE WHEN EARLYLEAVE = '00:00:00' THEN 0 ELSE 1 END) OVER(PARTITION BY [NAME], DEPARTMENT, MONTH(STATUSIN), YEAR(STATUSIN)) TotalEarlyLeave
FROM SUMMARYDATA
),
cte3 as (
SELECT DEPARTMENT, [MONTH], [YEAR], SUM(CASE WHEN TOTAL > 2 THEN 1 ELSE 0 END) LATECOME,
SUM(CASE WHEN TotalEarlyLeave > 1 THEN 1 ELSE 0 END) EARLYLEAVE
FROM cte2
GROUP BY DEPARTMENT, [MONTH], [YEAR]
)
INSERT INTO REPORTDATA (DEPARTMENT, MONTHS, YEARS, RTOTALLOSTTIME, ROT, RLATECOME, REARLYLEAVE)
SELECT cte1.DEPARTMENT, cte1.MONTHS, cte1.YEARS, cte1.RTOTALLOSTTIME, cte1.ROT,
cte3.LATECOME, cte3.EARLYLEAVE
FROM cte1
LEFT JOIN cte3 ON cte3.DEPARTMENT = cte1.DEPARTMENT and cte3.[MONTH] = cte1.[MONTH] and cte3.[YEAR] = cte1.[YEAR]

Related

sql update statement in conditional aggregation

How to use update statement when there is a conditional aggregation?
because i need to update this query to my table REPORTDATA.
my column in table REPORTDATA is DEPARTMENT,MONTHS,YEARS,RTOTALLOSTTIME,RLATECOME,REARLYLEAVE,RST,ROT as i mentioned in this select query :
is it possible to use update statement in this query?
select s.department
,s.month
,s.year
,count(case when s.sum_lost_time >='02:00:00' then NAME end) as RTOTALLOSTTIME
,count(case when s.sum_late >='00:00:01' then NAME end) as RLATECOME
,count(case when s.sum_early >='00:00:01' then NAME end) as REARLYLEAVE
,count(case when s.sum_st <='8' then NAME end) as RST
,count(case when s.sum_ot >='1' then NAME end) as ROT
from (select MONTH(STATUSIN) as [month]
,YEAR(STATUSIN) as [year]
,NIP
,NAME
,DEPARTMENT
,convert(varchar,dateadd(second,sum(datediff(second,'00:00:00',cast(TOTALLT as time))),0),108) as sum_lost_time
,convert(varchar,dateadd(second,sum(datediff(second,'00:00:00',cast(LATECOME as time))),0),108) as sum_late
,convert(varchar,dateadd(second,sum(datediff(second,'00:00:00',cast(EARLYLEAVE as time))),0),108) as sum_early
,SUM(CAST(STRAIGHTTIME AS FLOAT)) as sum_st
,SUM(CAST(OT AS FLOAT)) as sum_ot
from SUMMARYDATA b
group by MONTH(STATUSIN)
,YEAR(STATUSIN)
,NIP
,NAME
,DEPARTMENT
)s
group by s.department
,s.month
,s.year
this is what i've tried so far,but appear error 'An aggregate may not appear in the set list of an UPDATE statement' and can't use group by too
merge
into REPORTDATA r
using (select MONTH(STATUSIN) as [month]
,YEAR(STATUSIN) as [year],NIP
,NAME
,DEPARTMENT
,convert(varchar,dateadd(second,sum(datediff(second,'00:00:00',cast(TOTALLT as time))),0),108) as sum_lost_time
,convert(varchar,dateadd(second,sum(datediff(second,'00:00:00',cast(LATECOME as time))),0),108) as sum_late
,convert(varchar,dateadd(second,sum(datediff(second,'00:00:00',cast(EARLYLEAVE as time))),0),108) as sum_early
from SUMMARYDATA b
group by MONTH(STATUSIN)
,YEAR(STATUSIN)
,NIP
,NAME
,DEPARTMENT
)s
on r.department=s.department
when matched then
update
set DEPARTMENT = s.DEPARTMENT,
RLOSTTIME = count(case when s.sum_lost_time >='02:00:00' then NAME end),
RLATECOME = count(case when s.sum_late >='00:00:01' then NAME end),
REARLYLEAVE = count(case when s.sum_early >='00:00:01' then NAME end);
group by s.department
,s.month
,s.year
You seem to want:
with q as (
<your query here>
)
update rd
set rtotallosttime = q.rtotallosttime,
. . . -- rest of columns
from reportdata rd join
q
on rd.department = q.department and
rd.month = q.month and
rd.year = q.year;
That said, I'm surprised you would approach this with update. I would think you would want to insert the rows instead.
Use MERGE:
merge
into REPORTDATA r
using ( select s.department as department
,s.month as month
,s.year as year
,count(case when s.sum_lost_time >='02:00:00' then NAME end) as RTOTALLOSTTIME
,count(case when s.sum_late >='00:00:01' then NAME end) as RLATECOME
,count(case when s.sum_early >='00:00:01' then NAME end) as REARLYLEAVE
,count(case when s.sum_st <='8' then NAME end) as RST
,count(case when s.sum_ot >='1' then NAME end) as ROT
from (select MONTH(STATUSIN) as [month]
,YEAR(STATUSIN) as [year]
,NIP
,NAME
,DEPARTMENT
,convert(varchar,dateadd(second,sum(datediff(second,'00:00:00',cast(TOTALLT as time))),0),108) as sum_lost_time
,convert(varchar,dateadd(second,sum(datediff(second,'00:00:00',cast(LATECOME as time))),0),108) as sum_late
,convert(varchar,dateadd(second,sum(datediff(second,'00:00:00',cast(EARLYLEAVE as time))),0),108) as sum_early
,SUM(CAST(STRAIGHTTIME AS FLOAT)) as sum_st
,SUM(CAST(OT AS FLOAT)) as sum_ot
from SUMMARYDATA b
group by MONTH(STATUSIN)
,YEAR(STATUSIN)
,NIP
,NAME
,DEPARTMENT
)s
group by s.department
,s.month
,s.year
)s
on r.department=s.department
when matched then
update
set r.month = s.month,
r.year=s.year,
r.RTOTALLOSTTIME=s.RTOTALLOSTTIME,
r.RLATECOME=s.RLATECOME,
r.REARLYLEAVE=s.REARLYLEAVE,
r.RST=s.RST,
r.ROT=s.ROT
Remember for update there should be common key between REPORTDATA table and Sub query output.

SQL joining most recent event by criteria to missing value

I have a SQL table that records interactions and the changes that happen in an interaction by interactionkey, user, group, and skill. I want to find the duration of each of the actions (A,B,C) by the grouping variables. Whenever action C happens, the skillKey is left blank (not NULL) and I need it to take the value of the last Skill in that interaction by the user and group so it is grouped together. The first table is the raw SQL data for 1 interaction, and the second is how I need it to look. Edit: I'm using Microsoft SQL Server Management Studio.
Here's what I have so far but it doesn't account for the last skill the user used in the group and interactionkey so it remains blank and adds it up seperately
SELECT
[InteractionKey],
[User],
[StartTime],
[SkillKey],
[GroupKey],
SUM(CASE WHEN ActionKey = 'A' THEN ActionDuration ELSE 0 END) AS 'ActionADuration',
SUM(CASE WHEN ActionKey = 'B' THEN ActionDuration ELSE 0 END) AS 'ActionBDuration',
SUM(CASE WHEN ActionKey = 'C' THEN ActionDuration ELSE 0 END) AS 'ActionCDuration'
FROM
(SELECT
[ActionKey],
[InteractionKey],
[SkillKey],
[GroupKey],
SUM(ActionDuration) AS 'ActionDuration',
[User],
CAST(StartTime AS DATE)
FROM
[InteractionTable]
GROUP BY
InteractionKey, User, SkillKey, GroupKey, ActionKey,
CAST(StartTime AS DATE)) sub
GROUP BY
InteractionKey, User, Date, SkillKey, GroupKey
ORDER BY
InteractionKey
Raw SQL Server table:
Desired output:
All you need is to prepare another "table" with SkillKey already filled as you need, and then use this table in your query.
I will use CTE in my code,
and it is different for divverent versions of SQL Server.
The first one is preferable, but it's for servers starting with 2012.
If you are on lower version use the second query.
-- for ##version >= 2012
with cte as
(
select *,
case
when SkillKey <> ''
then SkillKey
else lag(SkillKey) over(partition by InteractionKey, [User], GroupKey order by [Date])
end as SkillKey
from InteractionTable
)
SELECT
[InteractionKey],
[User],
[StartTime],
[SkillKey],
[GroupKey],
SUM(CASE WHEN ActionKey = 'A' THEN ActionDuration ELSE 0 END) AS 'ActionADuration',
SUM(CASE WHEN ActionKey = 'B' THEN ActionDuration ELSE 0 END) AS 'ActionBDuration',
SUM(CASE WHEN ActionKey = 'C' THEN ActionDuration ELSE 0 END) AS 'ActionCDuration'
FROM
(SELECT
[ActionKey],
[InteractionKey],
[SkillKey],
[GroupKey],
SUM(ActionDuration) AS 'ActionDuration',
[User],
CAST(StartTime AS DATE)
FROM
cte
GROUP BY
InteractionKey, User, SkillKey, GroupKey, ActionKey,
CAST(StartTime AS DATE)) sub
GROUP BY
InteractionKey, User, Date, SkillKey, GroupKey
ORDER BY
InteractionKey
The second:
-- for ##version >= 2005
with r as
(
select *,
row_number() over(partition by InteractionKey, [User], GroupKey order by [Date]) as rn
from InteractionTable
)
,cte as
(
select r1.*,
case
when r1.SkillKey <> ''
then r1.SkillKey
else r2.SkillKey
end as SkillKey
from r r1
left join r r2
on r1.rn = r2.rn + 1
)
SELECT
[InteractionKey],
[User],
[StartTime],
[SkillKey],
[GroupKey],
SUM(CASE WHEN ActionKey = 'A' THEN ActionDuration ELSE 0 END) AS 'ActionADuration',
SUM(CASE WHEN ActionKey = 'B' THEN ActionDuration ELSE 0 END) AS 'ActionBDuration',
SUM(CASE WHEN ActionKey = 'C' THEN ActionDuration ELSE 0 END) AS 'ActionCDuration'
FROM
(SELECT
[ActionKey],
[InteractionKey],
[SkillKey],
[GroupKey],
SUM(ActionDuration) AS 'ActionDuration',
[User],
CAST(StartTime AS DATE)
FROM
cte
GROUP BY
InteractionKey, User, SkillKey, GroupKey, ActionKey,
CAST(StartTime AS DATE)) sub
GROUP BY
InteractionKey, User, Date, SkillKey, GroupKey
ORDER BY
InteractionKey

SSRS: how to get top 3 in order Z to A

I try to get in my diagram the top 3 of the worst value in SSRS:
my Code:
SELECT *
FROM (
Select top 3
intervaldate as Datum
,Name
,teamname as Team
,SUM(case when CounterName = 'Blown away' then calculationUnits else 0 end) as Blown
,Sum(case when CounterName = 'Thrown away' then calculationUnits else 0 end) as Thrown
,Sum(case when CounterName = 'total' then calculationUnits else 0 end) as Total
from Counting
where IntervalDate >= dateadd(day,datediff(day,1,GETDATE()),0)
AND IntervalDate < dateadd(day,datediff(day,0,GETDATE()),0)
and Name in (Select SystemID from tSystemView where SystemViewID = 2)
group by intervaldate, teamName, Name
) c
Expression of the diagram:
=Sum(Fields!Blown.Value + Fields!Thrown.Value) / Sum(Fields!Total.Value) * 100
And I sorted it from highest to lowest
But it does not show me the right order.
If I choose every "Name" then it shows me other value then the top 3:
all Names with value:
top 3:
It's because your top 3 statement is in the SQL while your sort is in the report. Without an order by SQL picks the top 3 random records. Also, unless there is more SQL you are not showing, the outer select is unnecessary. Add an order by <column> desc below your group by.
with Calcs as
(
select intervaldate as Datum,
Name,
TeamName,
SUM(case when CounterName = 'Blown away' then calculationUnits else 0 end) as Blown,
Sum(case when CounterName = 'Thrown away' then calculationUnits else 0 end) as Thrown,
Sum(case when CounterName = 'total' then calculationUnits else 0 end) as Total
from Counting
where IntervalDate >= dateadd(day,datediff(day,1,GETDATE()),0)
AND IntervalDate < dateadd(day,datediff(day,0,GETDATE()),0)
and Name in (Select SystemID from tSystemView where SystemViewID = 2)
group by intervaldate, teamName, Name
)
select b.*
from
(
select a.*, row_number() over (order by (Blown + Thrown)/Total desc) as R_Ord -- Change between ASC/DESC depending on needs
from Calcs a
) b
where R_Ord <=3

Get the Highest Value in different Select SUM

I want to get the highest value in my query
Select SUM(CASE WHEN Day='Monday' THEN 1 END) AS'Total Monday',
SUM(CASE WHEN Day='Tuesday' THEN 1 END) AS'Total Tuesday'
FROM tbl_sched
WHERE teacherID='2014279384'
The Output would be TotalMonday ='1' and TotalTuesday ='2'
I need to get the highest value from the outputs which in this case is TotalTuesday=2
select max(daycnt) from
(Select SUM(CASE WHEN Day='Monday' THEN 1 END) AS daycnt
from tbl_sched WHERE teacherID='2014279384'
union all
Select SUM(CASE WHEN Day='Tuesday' THEN 1 END) AS daycnt
from tbl_sched WHERE teacherID='2014279384')
If you need the max between many columns:
Something interesting in SQLServer 2008 and above
SELECT (SELECT Max(v)
FROM (VALUES ([Total Monday]), ([Total Tuesday]), ...) AS value(v)) as [MaxDate]
From
(
Select SUM(CASE WHEN Day='Monday' THEN 1 END) AS'Total Monday',
SUM(CASE WHEN Day='Tuesday' THEN 1 END) AS'Total Tuesday'
..........
FROM tbl_sched
WHERE teacherID='2014279384'
)a
Another option:
SELECT Case When [Total Monday] > [Total Tuesday] then [Total Monday] else [Total Tuesday] End as maxvalue
FROM
(
Select SUM(CASE WHEN Day='Monday' THEN 1 END) AS'Total Monday',
SUM(CASE WHEN Day='Tuesday' THEN 1 END) AS'Total Tuesday'
FROM tbl_sched
WHERE teacherID='2014279384'
)a
I'd say the query below is better in terms of performance and highlights the intention better, because basically we are just GROUPing by days and COUNTing the groups, we don't need CASE's or SUM's (in which case SQL Server will have to go over all the records of the selected teacher).
SELECT TOP 1 Day, COUNT(*) AS Total
FROM tbl_sched
WHERE teacherID='2014279384'
AND Day IN ('Monday','Tuesday')
GROUP BY Day
ORDER BY Total DESC
You can just group by Day, sort by COUNT(*) DESC and get the top count:
SELECT TOP (1)
TotalCount = COUNT(*)
FROM
dbo.tbl_sched
WHERE
teacherID = '2014279384'
GROUP BY
Day
ORDER BY
TotalCount DESC
;
You can also include Day into the output to return the day that had the topmost result.
You can achieve this by using Max Function
Select MAX(SUM(CASE WHEN Day='Monday' THEN 1 END)) AS 'Total Monday',
MAX(SUM(CASE WHEN Day='Tuesday' THEN 1 END)) AS 'Total Tuesday'
FROM tbl_sched
WHERE teacherID='2014279384'

Group By Multiple Columns, SUM at the first Group By Column

I am writing a query that groups over two columns. this means, it will put Col1 and Col2 in the same group.
How can I do a SUM of records based on Group Col1 ?
I have this so far: http://sqlfiddle.com/#!3/eada2/1
Thank you
I was able to solve it as follows:
WITH cteActivityIds (activityId, allDocuByActivity)
AS
(SELECT activityId, COUNT(*) AS allDocuByActivity FROM #ER GROUP BY activityId)
SELECT
E.activityId AS [Actiivty ID],
docuType AS [Docu Type],
COUNT(docuType),
CONVERT(DECIMAL(16,2), (COUNT(docuType) * 100.00 / t.allDocuByActivity)) [Total DocuType in Activity],
SUM(CASE WHEN [sent] != '1-1-1900' THEN 1 END) AS [Sent],
SUM(CASE WHEN [approved] != '1-1-1900' THEN 1 END) AS [Approved]
FROM
#ER AS E
INNER JOIN cteActivityIds AS t
ON E.activityId = t.activityId
GROUP BY
E.activityId, docuType, t.allDocuByActivity
Thanks
Here is a solution that, in my opinion, looks a little cleaner - it does not require a join and uses 'partition by' to calculate the individual counts:
WITH activityCounts as (
select
activityId,
docuType,
count(activityId) OVER(PARTITION BY activityId) as activityIdCount,
count(docuType) OVER(PARTITION BY activityId, docuType) as docuTypeCount,
SUM(CASE WHEN [sent] != '1-1-1900' THEN 1 END) OVER(PARTITION BY activityId, docuType) AS [Sent],
SUM(CASE WHEN [approved] != '1-1-1900' THEN 1 END) OVER(PARTITION BY activityId, docuType) AS [Approved]
from ER
)
SELECT
activityId,
docuType,
docuTypeCount,
CONVERT(DECIMAL(16,2), (COUNT(docuTypeCount) * 100.00 / activityIdCount)) as [Total DocuType in Activity],
[Sent],
[Approved]
FROM activityCounts
GROUP BY
activityId,
docuType,
activityIdCount,
docuTypeCount,
[Sent],
[Approved]
Here is a link to sqlfiddle
You just need to put an aggregate function on t.allDocuByActivity
MIN(t.allDocuByActivity) AS [Total DocuType in Activity],
However, since your label is DocuType. Don't you need DISTINCT?