How to sum the value of 2 different SQL queries? - sql

I'm kinda new to using SQL. I have two different queries and I need to add or sum the value of one of the column in each queries. Sample code and screenshot of the outcome are provided below:
WITH tbl AS
(
SELECT
y.Location,
IIF(Status = 'Completed', 1,0) as Completed,
IIF(Status = 'Pending', 1, 0) as Pending,
IIF(Status = 'Scheduled', 1, 0) as Scheduled,
IIF(Satisfied = 'Satisfied', 1 , 0 ) As Satisfied,
IIF(Attrition IN ('Red', 'Amber'),1,0) As Attrition
FROM
order x
LEFT JOIN
roster y ON x.customerID = y.cID
LEFT JOIN
tbl_calendar z ON (x.starttime between z.datestart AND z.dateend)
WHERE
y.LOCATION IS NOT NULL
AND y.Location <> 'Ireland'
AND z.month ='2'
AND z.week IN (SELECT * FROM dbo.split('1,2,3',','))
AND z.year = '2017'
)
SELECT
Location,
SUM(Completed) AS Completed,
SUM(Pending) AS Pending,
SUM(Scheduled) AS Scheduled,
SUM(Satisfied) as Satisfied,
SUM(Attrition) as Attrition
FROM
tbl
GROUP BY
Location
And here is the second query:
SELECT
y.location, count (*) as qc
FROM
customer_quality x
LEFT JOIN
roster y ON x.customerEID = y.cEID
LEFT JOIN
tbl_calendar z ON (x.DateTimeDelivered BETWEEN z.datestart AND z.dateend)
WHERE
y.location IS NOT NULL
AND x.status = '4'
AND z.month = '2'
AND z.week IN (SELECT * FROM dbo.split('1,2,3',','))
AND z.year = '2017'
GROUP BY
y.Location
What I'm going to need to do is to add the value of the total completed orders from query 1 to the total quality customer on my query 2.
For example:
If there are 208 completed customer in Lisbon it would up the other 4 quality customer, the total should be 212.
It would show like this:
Location Completed Pending Scheduled Satisfied Attrition
Kuala Lumpur 388 76 9 388 3
Lisbon 212 92 29 207 1
Manila 3535 97 167 662 24
Mumbai 538 50 54 2100 6
Warsaw 147 38 4 145 9

I don't have data to test, but this should get you started.
With tbl as
( SELECT y.Location,
IIF(Status = 'Completed', 1,0) as Completed,
IIF(Status = 'Pending', 1, 0) as Pending,
IIF(Status = 'Scheduled', 1, 0) as Scheduled,
IIF(Satisfied = 'Satisfied', 1 , 0 ) As Satisfied,
IIF(Attrition IN ('Red', 'Amber'),1,0) As Attrition
FROM order x
LEFT JOIN roster y ON x.customerID=y.cID
LEFT JOIN tbl_calendar z ON (x.starttime between z.datestart AND z.dateend)
WHERE y.LOCATION IS NOT NULL
AND y.Location <> 'Ireland'
AND z.month ='2'
AND z.week IN (select * from dbo.split('1,2,3',','))
AND z.year='2017'
)
SELECT Location,
SUM(Completed) AS Completed ,
SUM (Pending) AS Pending,
SUM(Scheduled) AS Scheduled,
SUM(Satisfied) as Satisfied,
SUM(Attrition) as Attrition
INTO #TABLE1
FROM tbl
GROUP BY Location
SELECT y.location, count (*) as qc
INTO #TABLE2
FROM customer_quality x
LEFT JOIN roster y ON x.customerEID = y.cEID
LEFT JOIN tbl_calendar z ON (x.DateTimeDelivered between z.datestart
AND z.dateend)
WHERE y.location is not null
AND x.status='4' AND z.month = '2'
AND z.week IN (select * FROM dbo.split('1,2,3',',')) AND z.year='2017'
GROUP by y.Location
SELECT t1.Location
, (t1.Completed + t2.qc) AS Completed
, t1.Pending
, t1.Scheduled
, t1.Satisfied
, t1.Attrition
FROM #TABLE1 t1
JOIN #TABLE2 t2 ON
t1.Location = t2.Location
DROP TABLE #TABLE1
DROP TABLE #TABLE2

Create 2 CTEs as described in https://msdn.microsoft.com/en-us/library/ms175972.aspx#C-Using-multiple-CTE-definitions-in-a-single-query and then join them together and perform the desired calculations in your SELECT final clause

Or you could store you results from the first query in a temporary table using
SELECT * INTO #temp1 FROM (
SELECT Location,
SUM(Completed) AS Completed ,
SUM (Pending) AS Pending,
SUM(Scheduled) AS Scheduled,
SUM(Satisfied) as Satisfied,
SUM(Attrition) as Attrition
FROM tbl
GROUP BY Location
)
and
SELECT * INTO #temp2 FROM (
SELECT y.location, count (*) as qc
FROM customer_quality x
LEFT JOIN roster y ON x.customerEID = y.cEID
LEFT JOIN tbl_calendar z ON (x.DateTimeDelivered between z.datestart AND z.dateend)
WHERE y.location is not null
AND x.status='4' AND z.month = '2'
AND z.week IN (select * FROM dbo.split('1,2,3',',')) AND z.year='2017'
GROUP by y.Location
)
in the end the summarizing query could be:
SELECT t1.location
, t1.COMPLETED + t2.cq AS Completed
, t1.Pending
, t1.Scheduled
, t1.Satisfied
, t1.Attrition
FROM #temp1 t1
JOIN #temp2 t2 on t1.location = t2.location
or take the approach of stringing the CTEs together.

Related

Conditional join based on row value

I have department mapping table where I have specific status for specific department but different status for all the rest departments that are not in department table.
dep_table:
country
department
status
FIN
D1
C
FIN
D2
C
FIN
**
O
SWE
D1
C
act_table:
country
department
amt
FIN
D1
16
FIN
D3
45
SWE
D1
13
expected result:
country
department
amt
status
FIN
D1
16
C
FIN
D3
45
O
SWE
D1
13
C
I have this but it causes duplicated rows (because joins ** and also non-** rows):
SELECT t1.country, t1.department, t1.amt, t2.status
FROM act_table t1
LEFT OUTER JOIN dep_table t2
ON t1.country = t2.country
AND CASE WHEN t1.department = t2.department THEN 1 WHEN t2.department = '**' THEN 1 END = 1
(This is very simplified scenario - needs to be done in this manner.)
If I understand correctly, you want 'O' if any of the statuses are 'O'. If there are only two statuses, you can use a correlated subquery:
select a.*,
(select d.status
from dep_table d
where d.country = a.country
order by d.status desc
fetch first 1 row only
) as status
from act_table a;
I think two left joins make it more clear :
with
dep_table (country, department, status) as (
values
('FIN', 'D1', 'C'),
('FIN', 'D2', 'C'),
('FIN', '**', 'O'),
('SWE', 'D1', 'C')
),
act_table (country, department, amt) as (
values
('FIN', 'D2', 16),
('FIN', 'D3', 45),
('SWE', 'D1', 13)
)
select
act.country, act.department, act.amt, coalesce(specific.status, rest.status) status
from act_table act
left join dep_table specific using(country, department)
left join dep_table rest on specific.status is null and (rest.country, rest.department) = (act.country, '**')
order by country, department
Use olap function row_number to select first row
select country , department , amt , status
from
(
select a.country , a.department ,
a.amt , d.status ,
row_number() over( partition by a.country , a.department
order by case when d.department = '**' then 1 else 0 end ) as rn1
from dep_table d ,
act_table a
where d.country = a.country
and ( d.department = a.department or d.department = '**' )
) x
where rn1 = 1

Combine 3 UNIONed queries into one

I have the following which I would like to do without UNIONs so that the string split is only happening once.
I would also like the results to be in one line per MemberId showing all 3 counts rather than 3 rows.
SELECT MemberKey, 'login' as countType, count(MemberKey) as total FROM [dbo].[CA_MembersAudit]
WHERE isSuccess = 1 and MemberKey IN (SELECT value FROM STRING_SPLIT( #userList, ','))
Group By MemberKey
UNION
SELECT MemberId as MemberKey, 'articles' as countType, count(MemberId) as total FROM [dbo].[CA_Activities]
WHERE StateId = 'Opened' and MemberId IN (SELECT value FROM STRING_SPLIT( #userList, ','))
Group By MemberId
UNION
SELECT MemberId as MemberKey,'assessments' as countType, count(MemberId) as total FROM [dbo].[CA_Activities]
WHERE PercentageComplete is not null AND MemberId IN (SELECT value FROM STRING_SPLIT( #userList, ','))
Group By MemberId
UNION
Can anyone suggest how I should amend the queries into one to be able to do this?
You could use a subquery for each total:
select m.MemberKey,
(select count(*) from CA_MembersAudit ma where m.MemberKey = ma.MemberKey and ma.isSuccess = 1) as 'login_total',
(select count(*) from CA_Activities a where m.MemberKey = a.MemberId and a.stateId = 'Opened') as 'articles_total',
(select count(*) from CA_Activities a where m.MemberKey = a.MemberId and a.PercentageComplete is not null) as 'assessments_total'
from (select value as MemberKey from STRING_SPLIT('1,2,3,4', ',')) m
If your tables have a primary key, you could also do something like this:
select m.MemberKey,
count(distinct ma.Id) 'login_total',
count(distinct a1.Id) 'articles_total',
count(distinct a2.Id) 'assessments_total'
from (select value as MemberKey from STRING_SPLIT('1,2,3,4', ',')) m
left outer join CA_MembersAudit ma on m.MemberKey = ma.MemberKey and ma.isSuccess = 1
left outer join CA_Activities a1 on m.MemberKey = a1.MemberId and a1.stateId = 'Opened'
left outer join CA_Activities a2 on m.MemberKey = a2.MemberId and a2.PercentageComplete is not null
group by m.MemberKey
I believe you can use a CTE and then JOIN to each of the UNION participants.
WITH MemberList AS (
SELECT value AS Member
FROM STRING_SPLIT(#userList, ',')
)
SELECT
MemberKey
,'login' AS countType
,count(MemberKey) AS total
FROM [dbo].[CA_MembersAudit]
JOIN MemberList
ON MemberList.Member = CA_MembersAudit.MemberKey
WHERE isSuccess = 1
GROUP BY MemberKey
UNION
SELECT
MemberId AS MemberKey
,'articles' AS countType
,count(MemberId) AS total
FROM [dbo].[CA_Activities]
JOIN MemberList
ON MemberList.Member = CA_Activities.MemberId
WHERE StateId = 'Opened'
GROUP BY MemberId
UNION
SELECT
MemberId AS MemberKey
,'assessments' AS countType
,count(MemberId) AS total
FROM [dbo].[CA_Activities]
JOIN MemberList
ON MemberList.Member = CA_Activities.MemberId
WHERE PercentageComplete IS NOT NULL
GROUP BY MemberId;
try this :
With MemberList as (
SELECT value as ID FROM STRING_SPLIT( #userList, ',')
),
Activities as (
select f1.MemberId, sum(case when f1.StateId = 'Opened' then 1 else 0 end) as TotalOpened,
sum(case when f1.PercentageComplete is not null then 1 else 0 end) as TotalPercentageComplete
FROM [dbo].[CA_Activities] f1 inner join MemberList f2 on f1.MemberId=f2.ID
where f1.StateId = 'Opened' or f1.PercentageComplete is not null
group by f1.MemberId
),
MemberAudit as (
SELECT f1.MemberKey, count(*) as TotalSuccess
FROM [dbo].[CA_MembersAudit] f1 inner join MemberList f2 on f1.MemberKey=f2.ID
WHERE f1.isSuccess = 1
Group By f1.MemberKey
)
select f1.*, isnull(f2.TotalOpened, 0) as TotalOpened, isnull(f2.TotalPercentageComplete, 0) as TotalPercentageComplete, isnull(f3.TotalSuccess, 0) as TotalSuccess
from MemberList f1
left outer join Activities f2 on f1.ID=f2.MemberId
left outer join MemberAudit f3 on f1.ID=f3.MemberKey
other solution :
SELECT f1.value as ID, isnull(f2.TotalOpened, 0) as TotalOpened, isnull(f2.TotalPercentageComplete, 0) as TotalPercentageComplete, isnull(f3.TotalSuccess, 0) as TotalSuccess
FROM STRING_SPLIT( #userList, ',') f1
outer apply
(
select sum(case when f1.StateId = 'Opened' then 1 else 0 end) as TotalOpened,
sum(case when f1.PercentageComplete is not null then 1 else 0 end) as TotalPercentageComplete
FROM [dbo].[CA_Activities] f1
where (f1.StateId = 'Opened' or f1.PercentageComplete is not null) and f1.MemberId=f1.value
) f2
outer apply
(
SELECT count(*) as TotalSuccess FROM [dbo].[CA_MembersAudit] f1 WHERE f1.isSuccess = 1 and f1.MemberKey=f1.value
) f3

SQL: How to create 2 seperate values in a column for a single isnull scenario?

I'm testing an SQL command as seen below:
with
AllEvents AS
(
SELECT E.EventName
FROM dbo.tblEvtDur E
GROUP BY E.EventName
),
EventDurations AS
(
SELECT macstring, E.EventName, SUM(DATEDIFF(SECOND, E.StartDT, E.EndDT))
AS DIFF
FROM dbo.tblEvtDur E
Inner Join
(
Select Distinct SUBSTRING(MacNm , 1,3) as MacString, MacID
From dbo.tblMachine)M on E.MacID = M.MacID
Where E.DayID BETWEEN '20180501'
And '20180531'
Group By macstring, E.EventName
)
Select MacString, AE.EventName, ISNULL(ED.DIFF,0) as DIFF
From AllEvents AE
Left Join EventDurations ED
On AE.EventName = ED.EventName
Order By EventName, MacString
The results I got can be seen here:
MacString EventName DIFF
NULL BLK 0
COR MDL 359880
FRA MDL 1805253
COR MTL 903783
FRA MTL 324831
COR OFF 6862901
FRA OFF 9905131
COR RPR 2986153
FRA RPR 5799318
COR RUN 7509506
FRA RUN 11250157
Null is present because the EventName - BLK is not used within the DayID range. Now I want to make it so that the MacString will show 2 rows for 'COR' and 'FRA' when there is NULL value.
Like so:
MacString EventName DIFF
NULL BLK 0
|
| (become)
v
COR BLK 0
FRA BLK 0
I tried using the ISNULL method to define the values if NULL is detected:
--modification in last row
Select isnull(MacString, 'COR')
As MacString, AE.EventName, ISNULL(ED.DIFF,0)
As DIFF
From AllEvents AE
Left Join EventDurations ED
On AE.EventName = ED.EventName
Order By EventName, macstring
But it seems to only allow for a single input. Is there any way to use ISNULL to create 2 rows with "COR' and 'FRA' in MacString column?
Try doing a LEFT JOIN on NULL value against a hard-coded list of MacString.
;with AllEvents AS
(
SELECT
E.EventName
FROM
dbo.tblEvtDur E
GROUP BY
E.EventName
),
EventDurations AS
(
SELECT
macstring,
E.EventName,
SUM(DATEDIFF(SECOND, E.StartDT, E.EndDT)) AS DIFF
FROM
dbo.tblEvtDur E
Inner Join
(
Select Distinct
SUBSTRING(MacNm , 1,3) as MacString,
MacID
From
dbo.tblMachine
)M on E.MacID = M.MacID
Where
E.DayID BETWEEN '20180501' And '20180531'
Group By
macstring,
E.EventName
),
NullMacStrings AS
(
SELECT
MacString = 'COR'
UNION ALL
SELECT
MacString = 'FRA'
)
Select
MacString = ISNULL(ED.MacString, NM.MacString),
AE.EventName,
ISNULL(ED.DIFF,0) as DIFF
From
AllEvents AE
Left Join EventDurations ED On AE.EventName = ED.EventName
LEFT JOIN NullMacStrings AS NM ON ED.MacString IS NULL
Order By
EventName,
MacString

Sql Loop - Other than that

I've read the threads about loops (pros & cons) in and around a SQL statements. I need to "Loop" through 12 hours, hr by hr and was wondering if there is a better way than "Do-While-Loop, etc.) Here is my SQL...
SELECT R.FinalProd
, MIN(r.seqnumber) Seq
, MIN(S.RollRecID) RID
, COUNT(DISTINCT S.RollRecID) Pieces
, (MIN(r.seqnumber) + COUNT(DISTINCT S.RollRecID) - 1) endd
FROM NYS1Reheat1 R INNER JOIN NYS1SawPieces S ON R.RecordID = S.RollRecID INNER JOIN TensileProducts T ON R.FinalProd = T.SQLProduct
where s.ShiftIdent = '05/22/15154D' and r.Location = 'HISTORY'
and datepart(hour,s.prodtime) > 17 and datepart(hour,s.prodtime) < 19
GROUP BY R.FinalProd, T.FootWeight
order by RID
I need to start at 1700 and go hr by hr to 0600.
My plan, as of now, is to "loop" 12 times through the code.
Thanks,
Doug
You can cross join your time intervals:
;with loop12 as (
select 17 as f, 19 as t union all
select 19 as f, 21 as t union all
...
)
SELECT R.FinalProd
, MIN(r.seqnumber) Seq
, MIN(S.RollRecID) RID
, COUNT(DISTINCT S.RollRecID) Pieces
, (MIN(r.seqnumber) + COUNT(DISTINCT S.RollRecID) - 1) endd
, loop12.f
FROM NYS1Reheat1 R
INNER JOIN NYS1SawPieces S ON R.RecordID = S.RollRecID
INNER JOIN TensileProducts T ON R.FinalProd = T.SQLProduct
CROSS JOIN loop12
WHERE s.ShiftIdent = '05/22/15154D' and r.Location = 'HISTORY'
and datepart(hour,s.prodtime) > loop12.f and datepart(hour,s.prodtime) < loop12.t
GROUP BY R.FinalProd, T.FootWeight
order by RID, loop12.f

Optimize EAV based SQL Query

I have a code that will return about 1 million records in about 15 seconds on a normal PC.
select joined.* , eset.PageNo from (select * from
(
select e.EntityTypeID , a.EntityID , a.AttributeValue , ea.AttributeName
from
(SELECT T1.EntityID,
T1.AttributeID,
STUFF(( SELECT ', ' + T22.OptionText
FROM [POC].[dbo].[EntityAttributeOptionValues] t2
inner join EntityAttributeOptions t22 on
t22.ID = t2.OptionValueID
WHERE T2.AttributeID = T1.AttributeID
AND T2.EntityID = T1.EntityID
-- ANd t2.EntityID between 5060 and 56598
FOR XML PATH('')
), 1, 2, '') [AttributeValue]
FROM [POC].[dbo].[EntityAttributeOptionValues] t1
inner join EntityAttributeOptions t11 on
t11.ID = t1.OptionValueID
--where t1.EntityID between 5060 and 56598
GROUP BY T1.EntityID, T1.AttributeID
) a
inner join Entities e on e.ID = a.EntityID
inner join EntityAttributes ea on ea.ID = a.AttributeID
--where e.EntityTypeID = 9
) options
union all
(select EntityAttributes.EntityTypeID, EntityAttributeValues.EntityID as 'EntityID' , EntityAttributeValues.AttributeValue,EntityAttributes.attributeName from EntityAttributeValues
inner join EntityAttributes on EntityAttributes.ID = EntityAttributeValues.AttributeID
)) joined
inner join Entity_Paged_View_With_Set eset on eset.ID = joined.EntityID
This query will return five columns called EntityTypeID, EntityID, AttributeValue, AttributeName, PageNo.
EntityTypeID EntityID AttributeValue AttributeName PageNo
1 5790 Musics, Paintings Hobbies 286
1 11673 Musics, Paintings Hobbies 580
1 30595 Males Gender 1527
1 31860 No Want Bus Facility 1590
1 39755 Males Gender 1985
1 41020 No Want Bus Facility 2048
1 47599 Males Gender 2377
1 48864 No Want Bus Facility 2440
Now when I just try to get about 100 rows it will take about 3 seconds. I want to filter this query by like
pageNo = 1 and EntityTypeID = 1
or
pageNo = 1 and EntityTypeID = 3
or
pageNo = 5632 and EntityTypeID = 1
and so on.
Can you please specify where should I apply where condition. Even order by clause drastically reduce performance.
The upper query before union all is slow. but the later is fast.
Where should I apply where conditions to optimize the query such that it will take less than a second to execute