Join 2 tables even with null values and sum each row - sql-server-2012

See pics below of what i want to do with my sql statement. I have used a left join and ISNULL to get all the results fine except for I don't get the total, which I want to sum the numbers for each customer. All table b values are integers.
Select a.CustomerId, a.FName, a.LName, b.mtg1, b.mtg2, b.mtg3, b.mtg4 From Customer a Left Join Hours b On a.CustomerID = b.CustomerID group by a.Lname

The GROUP BY will make it a bit difficult to captured all the necessary data. Also, the sum of different columns will require the use of COALESCE(column, 0) so as to use zero as the value if the column is null because if not done, your total will come back asNULL`.
One possible solution will is:
SELECT a.CustId, a.FName, a.LName, SUM(b.mtg1), SUM(b.mtg2), SUM(b.mtg3), SUM(b.mtg4), (COALESCE(SUM(b.mtg1), 0) + COALESCE(SUM(b.mtg2), 0) + COALESCE(SUM(b.mtg3), 0) + COALESCE(SUM(b.mtg4), 0)) AS total
FROM table_a a
LEFT JOIN table_b b ON(b.CustID = a.CustId)
GROUP BY a.CustID, a.FName, a.LName

with cte as
(
select flname+', '+fname as Name,
mtg1,mtg2,mtg3,mtg4,
isnull(((case when mtg1 is null then 0 else mtg1 end)+
(case when mtg2 is null then 0 else mtg2 end)+
(case when mtg3 is null then 0 else mtg3 end)+
(case when mtg4 is null then 0 else mtg4 end)),0) as total
from a left join b
on a.custid = b.custid
)
select name,
isnull(mtg1,0) as mtg1,
isnull(mtg2,0) as mtg2,
isnull(mtg3,0) as mtg3,
isnull(mtg4,0) as mtg4,
total
from cte
order by 1

Related

How to output a new column in a SELECT query based on a condition?

Currently, I have this SQL query:
SELECT AVG(ttbe.MarkGiven) FROM tblTestsTakenByEmployee ttbe
INNER JOIN tblCoursesTakenByEmployee ctbe ON ttbe.EmployeeId = ctbe.EmployeeId
LEFT JOIN tblCourse c ON ctbe.CourseId = c.CourseId
WHERE ctbe.HasCompletedCourse = 'Y'
GROUP BY ctbe.CourseId, c.CourseName, EXTRACT(YEAR FROM ctbe.DateOfCourseCompletion), ctbe.EmployeeId
At the moment, this returns the average mark of a single employee on a course which was completed on a certain year, calculated across each of the tests it contains (a course can have multiple tests).
I want to output an additional column to the SELECT query which specifies whether that employee has passed the course, based on a threshold. For example, if AVG(ttbe.MarkGiven) >= 40 then it would return 'Y' in the new column, otherwise it would return 'N'. What's the simplest and most efficient way of achieving this?
you could use the CASE expression like that:
SELECT AVG(ttbe.MarkGiven),
CASE WHEN AVG(ttbe.MarkGiven) >= 40 THEN 'Y' ELSE 'N' END as exam_passed
FROM tblTestsTakenByEmployee ttbe
INNER JOIN tblCoursesTakenByEmployee ctbe ON ttbe.EmployeeId = ctbe.EmployeeId
LEFT JOIN tblCourse c ON ctbe.CourseId = c.CourseId
WHERE ctbe.HasCompletedCourse = 'Y'
GROUP BY ctbe.CourseId, c.CourseName, EXTRACT(YEAR FROM ctbe.DateOfCourseCompletion),
ctbe.EmployeeId
https://www.oracletutorial.com/oracle-basics/oracle-case/#:~:text=Oracle%20CASE%20expression%20allows%20you,that%20accepts%20a%20valid%20expression.
You could use your current query as a CTE and then from the CTE use the reulst and compare it to your threshold
The advantag of the CTE is that you will not have to recalculate your AVG(ttbe.MarkGiven)
WITH _CTE as
(
SELECT AVG(ttbe.MarkGiven) as Col1
FROM tblTestsTakenByEmployee ttbe
INNER JOIN tblCoursesTakenByEmployee ctbe
ON ttbe.EmployeeId = ctbe.EmployeeId
LEFT JOIN tblCourse c
ON ctbe.CourseId = c.CourseId
WHERE ctbe.HasCompletedCourse = 'Y'
GROUP BY ctbe.CourseId, c.CourseName, EXTRACT(YEAR FROM ctbe.DateOfCourseCompletion), ctbe.EmployeeId
)
Select Col1
,CASE
WHEN Col1 >= 50 THEN 'Y'
ELSE 'N'
END AS Col1_Threshold

Avoid NULL value in CASE SQL SERVER

How to avoid NULL values in CASE and SUM by P.Id. Problem is that i have more than one DPB.ProductTypeId in DPB table
SELECT P.[Id],
CASE
WHEN DPB.ProductTypeId = 1 THEN SUM(DPB.BonusAmount)
END AS [CasinoBonus]
FROM Player P
JOIN PlayerBonus DPB ON P.[Id] = DPB.[PlayerId]
group by P.[Id],DPB.ProductTypeId
use case when inside sum
SELECT P.[Id],
sum(CASE
WHEN DPB.ProductTypeId = 1 THEN DPB.BonusAmount
else 0
END) AS [CasinoBonus]
FROM Player P
JOIN PlayerBonus DPB ON P.[Id] = DPB.[PlayerId]
where P.[Id] is not null and DPB.[PlayerId] is not null
group by P.[Id],DPB.ProductTypeId
The case should be the argument to the sum(). You query should look like this:
SELECT P.[Id],
SUM(CASE WHEN DPB.ProductTypeId = 1 THEN DPB.BonusAmount
END) AS [CasinoBonus]
FROM Player P JOIN
PlayerBonus DPB
ON P.[Id] = DPB.[PlayerId]
GROUP BY P.[Id];
Note that you don't want DPB.ProductTypeId in the GROUP BY.
That said, you may simply want:
SELECT P.[Id],
SUM(DPB.BonusAmount) AS [CasinoBonus]
FROM Player P LEFT JOIN
PlayerBonus DPB
ON P.[Id] = DPB.[PlayerId] AND
DPB.ProductTypeId = 1
GROUP BY P.[Id];
Moving the condition to the WHERE clause removes the need for the CASE entirely. The LEFT JOIN keeps all players, even those that don't have that product type.

Aggregate case when inside non aggregate query

I have a pretty massive query that in its simplest form looks like this:
select r.rep_id, u.user_id, u.signup_date, pi.application_date, pi.management_date, aum
from table1 r
left join table2 u on r.user_id=u.user_id
left join table3 pi on u.user_id=pi.user_id
I need to add one more condition that gives me count of users with non null application date per rep (like: rep 1 has 3 users with filled application dates), and assign it into categories (since 3 users, rep is a certain status category). This looks something like this:
case when sum(case when application_date is not null then 1 else 0 end) >=10 then 'status1'
when sum(case when application_date is not null then 1 else 0 end) >=5 then 'status2'
when sum(case when application_date is not null then 1 else 0 end) >=1 then 'status3'
else 'no_status' end as category
However, if I was to simply add it to the select statement, all reps will becomes of status1 because the sum() is done over all advisors with application dates filled:
select r.rep_id, u.user_id, u.signup_date, pi.application_date, pi.management_date, aum,
(
select case when sum(case when application_date is not null then 1 else 0 end) >=10 then 'status1'
when sum(case when application_date is not null then 1 else 0 end) >=5 then 'status2'
when sum(case when application_date is not null then 1 else 0 end) >=1 then 'status3'
else 'no_status' end as category
from table3
) as category
from table1 r
left join table2 u on r.user_id=u.user_id
left join table3 pi on u.user_id=pi.user_id
Can you assist with having the addition to my query to be across reps and not overall? Much appreciated!
Based on your description, I think you need a window function:
select r.rep_id, u.user_id, u.signup_date, pi.application_date, pi.management_date, aum,
count(pi.application_date) over (partition by r.rep_id) as newcol
from table1 r left join
table2 u
on r.user_id = u.user_id left join
table3 pi
on u.user_id = pi.user_id;
You can use the count() in a case to get ranges, if that is what you prefer.

How to get multiple counts in one sql query across multiple tables

I have 2 tables
Company & products
I need to get 2 counts. One is the total count of products and the secondly count of products for sale_flg=1
This SQL does not seem to work..Tried several other ways..not able to get the expected results
SELECT A.COMPANY_NAME, COUNT(B.PRODUCT_ID) AS TOTAL_COUNT_OF_PRODUCTS,
(CASE WHEN B.SALEFLG =1 THEN 1 END) AS COUNT_OF_SALES
FROM COMPANY A LEFT JOIN
PRODUCT B
ON B.COMPANY_ID = A.COMPANY_ID
GROUP BY A.COMPANY_NAME
I think you just need a sum for the case:
SELECT C.COMPANY_NAME, COUNT(P.PRODUCT_ID) AS TOTAL_COUNT_OF_PRODUCTS,
SUM(CASE WHEN P.SALEFLG = 1 THEN 1 ELSE 0 END) AS COUNT_OF_SALES
FROM COMPANY C LEFT JOIN
PRODUCT P
ON P.COMPANY_ID = C.COMPANY_ID
GROUP BY C.COMPANY_NAME ;
If you have B.SALEFLG = 1 or 0 for you may try
Sum(B.SALEFLG) AS COUNT_OF_SALES
Or use UNION
If you use count then in else you should consider null because null is not consider in count aggregation and if you have B.SALEFLG =1 or 0 then use sum aggregation.
You can try below code:
SELECT A.COMPANY_NAME, COUNT(B.PRODUCT_ID) AS TOTAL_COUNT_OF_PRODUCTS,
count(CASE WHEN B.SALEFLG =1 THEN 1 else null END) AS COUNT_OF_SALES
FROM COMPANY A LEFT JOIN
PRODUCT B
ON B.COMPANY_ID = A.COMPANY_ID
GROUP BY A.COMPANY_NAME
OR try this:
SELECT A.COMPANY_NAME, COUNT(B.PRODUCT_ID) AS TOTAL_COUNT_OF_PRODUCTS,
sum(B.SALEFLG) AS COUNT_OF_SALES
FROM COMPANY A LEFT JOIN
PRODUCT B
ON B.COMPANY_ID = A.COMPANY_ID
GROUP BY A.COMPANY_NAME
This SQL server query is working.
Company Table and Product Table
Company Table CompanyID is join with Product table and sales_flg add in product table .
sales_flg = 1 record display in CntSalesflg
select Comp.CompID as CompID, COUNT(Pro.ProductID) as CntProdustID,
SUM(CASE WHEN Pro.SalesflagID = 1 THEN 1 ELSE 0 END) as CntSalesflg
from Product as Pro
inner join Company as Comp on Pro.CompID = Comp.CompID
GROUP by Comp.CompID

SQL Server - Conditional Join to whatever table is not null

Badly phrased title my apologies.
I am trying to join a table to one of two other tables
MasterTable
SubTable
SubTableArchive
So, MasterTable contains an ID field.
SubTable and SubTableArchive contain a MasterTableId field for the join.
But, data will only ever exist in one of these SubTables. So I just want to join to whichever table has the data in it.
But the only way I know of doing it is by joining to both and using isnull's on all the fields im selecting, and its quickly becoming complicated to read (and write).
Especially because some of the fields are already wrapped in ISNULL's
SELECT M.Id, ISNULL(S.Field1, SA.field1), ISNULL(S.field2, SA.Field2),
SUM(CASE WHEN (ISNULL(S.Finished,SA.Finished)=1 AND ISNULL( ISNULL(S.ItemCode,SA.ItemCode),'')='') THEN 1 WHEN (ISNULL(S.Finished,SA.Finished)=0 AND ISNULL( ISNULL(S.AltItemCode,SA.AltItemCode),'')='') THEN 1 ELSE 0 END) AS SummaryField
FROM MAsterTable M
LEFT OUTER JOIN SubTable S ON S.MasterTableId = M.Id
LEFT OUTER JOIN SubTableArchive SA ON S.MasterTableId = M.Id
GROUP BY M.Id, ISNULL(S.Field1, SA.field1), ISNULL(S.field2, SA.Field2)
So that is working, but its not pretty.
Thats a sample, but the real queries are longer and more convoluted.
I was hoping SQL might have had some sort of conditional joining functionality built in.
Something to do what I am trying to do and leave the query itslef a little friendlier.
Another option is to use a UNION and then use INNER JOINs
SELECT M.x, S.x
FROM MAsterTable M INNER JOIN SubTable S ON S.MasterTableId = M.Id
UNION
SELECT M.x, STA.x
FROM MAsterTable M INNER JOIN SubTableArchive STA ON STA.MasterTableId = M.Id
From a maintenance point of view, if you make the above union a view, you can then apply where filters and sorts to the view, which should simplify matters.
Use sub-select or better WITH statement (code not tested):
WITH SubTable_WithArchive
AS (SELECT Field1, Field2, Finished, ItemCode, AltItemCode, MasterTableID
FROM SubTable
UNION ALL
SELECT Field1, Field2, Finished, ItemCode, AltItemCode, MasterTableID
FROM SubTableArchive
)
SELECT M.Id,
S.Field1,
S.Field2,
SUM(CASE
WHEN s.Finished = 1 AND ISNULL(s.ItemCode, '') == '' THEN 1
WHEN s.Finished = 0 AND ISNULL(s.AltItemCode, '') == '' THEN 1
ELSE 0
END)
AS SummaryField
FROM MasterTable M
LEFT OUTER JOIN SubTable_WithArchive S
ON S.MasterTableId = M.Id
GROUP BY M.Id, S.Field1, s.field2
No, unfortunately, there isn't.
Try this
SELECT M.Id, S.Field1,S.field2,
SUM(CASE WHEN S.Finished=1 AND ISNULL( S.ItemCode,'')='')
THEN 1
WHEN S.Finished=0 AND ISNULL( S.AltItemCode,'')='')
THEN 1 ELSE 0 END) AS SummaryField
FROM MAsterTable M
JOIN (
SELECT id,field1,field2,ItemCode,AltItemCode,finished
FROM subTable
UNION
SELECT id,field1,field2,ItemCode,AltItemCode,finished
FROM subTableArchive
) S ON S.id = M.Id
GROUP BY M.Id, S.Field1,S.field2
Because a ID value from MasterTable will exist in only one table (SubTable or SubTableArchive) you can use this query:
SELECT MasterTableId Id, Field1, Field2,
SUM(CASE
WHEN Finished=1 AND ItemCode IS NULL THEN 1 --OR ISNULL(ItemCode,'') = ''
WHEN Finished=0 AND AltItemCode IS NULL THEN 1 --OR ISNULL(AltItemCode,'') = ''
ELSE 0
END) AS SummaryField
FROM SubTable
GROUP BY 1, 2, 3
UNION ALL
SELECT MasterTableId Id, Field1, Field2,
SUM(CASE
WHEN Finished=1 AND ItemCode IS NULL THEN 1 --OR ISNULL(ItemCode,'') = ''
WHEN Finished=0 AND AltItemCode IS NULL THEN 1 --OR ISNULL(AltItemCode,'') = ''
ELSE 0
END) AS SummaryField
FROM SubTableArchive
GROUP BY 1, 2, 3