SQL: insert total count row after joining table - sql-server

The first step is to join staff and customer together. The second step is to count the distinct product_id. My target is to add the total(sum) field under the result table.
Thanks.
staff
staff_ID Name cust_id
1 Tom 101
1 Tom 101
1 Tom 105
2 Peter 102
2 Peter 104
3 Billy 103
customer
cust_id product_id
101 A1
102 A2
103 A3
104 A4
105 A5
My work:
SELECT a.staff_name,COUNT(DISTINCT a.product_id)
FROM (SELECT distinct a.staff_id, a.staff_name, a.cust_id
FROM staff)a
LEFT JOIN customer b ON a.cust_id=b.cust_id
GROUPBY a.staff_name
What I want is to add the total column below the count.
Name count
Tom 2
Peter 2
Billy 1
Total 5

Update:
Regarding the "Total", as #MatBailie correctly pointed out in the comments:
The aggregate of multiple COUNT(DISTINCT) rows CAN NOT be guaranteed to be summable. If two staff members share the same product_id the summary value will be LESS THAN the sum of its members.
So for this sample data set:
db<>fiddle here
cust_id
product_id
101
A1
102
A2
103
A3
104
A4 <== Same product
105
A5
105
A4 <== Same product
Using GROUP BY ROLLUP yields a "Total" value of 5:
SELECT COALESCE(a.staff_name, 'Total') AS Staff_Name
, COUNT(DISTINCT b.product_id) AS [Count]
FROM staff a LEFT JOIN customer b ON a.cust_id=b.cust_id
GROUP BY ROLLUP (a.staff_name);
Results:
Staff_Name
Count
Billy
1
Peter
2
Tom
3
Total
5 **
Whereas calculating a simple sum of the totals, yields a "Total" value of 6. So just be aware of the difference.
Staff_Name
Count
Billy
1
Peter
2
Tom
3
Total
6 **
Original (Wrong Answer):
Can't remember where I saw this answer, but ... assuming Staff_Name is never null, you could use GROUP BY ROLLUP to obtain the total. That said calculating grand totals is usually more of a front-end job.
SELECT COALESCE(a.staff_name, 'Total') AS Staff_Name
, COUNT(DISTINCT b.product_id) AS [Count]
FROM staff a LEFT JOIN customer b ON a.cust_id=b.cust_id
GROUP BY ROLLUP (a.staff_name);

Try this one:
SELECT s.staff_name, COUNT(DISTINCT b.product_id), SUM(count) Total
FROM staff s
INNER JOIN customer b ON b.cust_id = s.cust_id
GROUP BY s.staff_name

Related

Joining multiple tables and applying aggegate giving wrong result

I have a business case where when a CountryId is passed to my proc, I need to get all the Regions where the Business is set up in that country, All the Active Sales Employees working in that Region, Total sales done by the current active sales employees in that region.
My Region table look like below.
RegionId | Name | CountryId
100 A 1
101 B 4
103 C 1
SalesEmployee Table
Id | EmployeeId | RegionId
1 250 100
2 255 101
3 289 101
Employee Table
EmployeeId | Active
250 1
255 1
289 0
314 1
Sales table
SaleId | EmployeeId| RegionId | Sale
1 100 2 3500
2 101 4 2000
3 100 2 1500
My below query is giving me the correct TotalSales value but the TotalUsers count doesn't match.
Select R.[RegionId], COUNT(SE.[UserId]) AS TotalUsers, SUM(S.[Sales]) AS TotalSales
FROM dbo.[Region] R
INNER JOIN [SalesEmployee] SE
ON R.[RegionId] = SE.[RegionId]
INNER JOIN dbo.[Employee] E
ON E.[EmployeeId] = SE.[EmployeeId]
LEFT JOIN dbo.[Sales] S
ON S.[EmployeeId] = E.[EmployeeId]
WHERE R.[CountryId] = 12 AND E.[Active] = 1
GROUP BY R.[RegionId]
For Ex RegionId 100 has only 7 Active sales employees currently working but the result gives me 89, in my Employee table there can be many more users but few of them can be inactive and few of them may be working in another department, to make sure that the employee is sales employee the employee needs to be present in SalesEmployee table and to check if the Employee is Active I need to check in Employee table.
The problem is 1 single user can have multiple entries on his name in sales table, so when i am joining with Sales table which has multiple entries on a single user then the TotalEmployees count is going up.
So its actually a easy small fix.
Select R.[RegionId], COUNT(DISTINCT(SE.[UserId])) AS TotalUsers, SUM(S.[Sales]) AS TotalSales
FROM dbo.[Region] R
INNER JOIN [SalesEmployee] SE
ON R.[RegionId] = SE.[RegionId]
INNER JOIN dbo.[Employee] E
ON E.[EmployeeId] = SE.[EmployeeId]
LEFT JOIN dbo.[Sales] S
ON S.[EmployeeId] = E.[EmployeeId]
WHERE R.[CountryId] = 12 AND E.[Active] = 1
GROUP BY R.[RegionId]
This small change will give you what you want.
Changing COUNT(SE.[UserId]) to COUNT(DISTINCT(SE.[UserId])) is all you need.

Find two rows of a column belongs to same row of another column

I have a table where I need to find list of subjects that have students from same department without using a subquery or Join
I tried to do the having count of department but it does not provide the output.
SELECT A.Subject,
B.StudentID,
B.DEPTID
FROM AUTHOR A , ACADEMIC B
WHERE A.StudentID = B.StudentID
GROUP BY B.DEPT,
A.Subject,
B.StudentID
Gives me the table output
Subject StudentID DEPT
1 100 100
1 101 100
2 102 100
3 103 100
3 104 100
I expect the output to give me the subject that has studentID from same department without using subquery or JOIN.

SQL query to get all the data from different tables with same id

Sorry if this is too elemental but I cannot work it out. Don’t know how to search information on it either:
I have three tables:
Provider
id_provider name
---------- -----------
100 John
101 Sam
102 Peter
Contact
id_contact RowNo Email
---------- ----------- ----------------
100 1 john#work.com
100 2 john#gmail.com
101 1 sam#work.com
101 2 sam#yahoo.com
Product
Id_product RowNo Product
---------- ----------- ------------------------
100 1 John’s 1st product
100 2 John’s 2nd product
101 1 Sam’s 1st product
101 2 Sam’s 2nd product
101 3 Sam’s 3rd product
I need a query to show all the data from the three tables like this:
Id name id_contact RowNo Email Id_Product RowNo Product
100 John 100 1 john#work.com 100 1 John’s 1st product
100 John 100 2 john#gmail.com 100 2 John’s 2st product
101 Sam 101 1 sam#work.com 101 1 Sam's 1st product
101 Sam 101 2 sam#yahoo.com 101 2 Sam's 2nd product
101 Sam null null null 101 3 Sam's 3rd product
102 Peter null null null null null null
I am trying all the joins I know but I cannot make it work.
Thanks a lot
You can use the following query:
SELECT t1.id_provider AS Id, t1.name,
t2.id_contact, t2.cRowNo, t2.Email,
t2.Id_product, t2.Product
FROM Provider AS t1
LEFT JOIN (
SELECT COALESCE(id_contact, id_product) AS id,
c.id_contact, c.RowNo AS cRowNo, c.Email,
p.Id_product, p.Product, p.RowNo AS pRowNo
FROM Contact AS c
FULL JOIN Product AS p ON p.id_product = c.id_contact AND p.RowNo = c.RowNo
) AS t2 ON t1.id_provider = t2.id
The query does a FULL JOIN between Contact and Product tables and joins the table derived from the FULL JOIN to Provider table.
A FULL JOIN is required because we cannot know beforehand which of the two tables, Contact or Product, contains the most rows for each id.
select *
from Provider P1
left join Contact C2
on C2.id_contact = P1.id_provider
left join Product P2
on P2.id_product = P1.id_provider
SELECT prov.*,
c.*,
prod.*
FROM PROVIDER prov
LEFT JOIN Product prod ON prod.id_product = prov.id_provider
LEFT JOIN Contact c ON prov.id_provider = c.id_contact
AND prod.RowNo = c.RowNo
use left joins but join provider to product first then to contact
SQL Fiddle Demo

Filter some records based on date range

I have below tables structures,
Trans Table:
Trans_Id(PK) User_Id(FK) Arroved_Date
________________________________________________
1 101 05-06-2016
2 101 12-06-2016
3 101 20-06-2016
4 102 06-06-2016
5 103 10-06-2016
6 103 25-06-2016
Table2:
Id(Pk) User_Id(Fk) Start_Date End_Date Is_Revoked
_________________________________________________________________________
1 101 01-06-2016 15-06-2016 1
2 101 10-06-2016 15-06-2016 0
3 103 05-06-2016 20-06-2016 0
I want to filter out the transaction, if the Approved_Date is not between the users Start_Date and End_Date of table2.
If is_revoked = 1 then it should not consider.
Expected Result:
Trans_Id
________
1
3
4
6
Try this
SELECT A.Trans_ID
FROM TransTable A JOIN Table2 B
ON (A.User_Id = B.User_Id)
WHERE B.Is_Revoked = 1 OR A.Approved_Date NOT BETWEEN B.Start_Date AND B.End_Date
This resolves you,
SELECT Trans_Id FROM TRANS AS A JOIN TABLE2 AS B ON A.[USER_ID]=B.[USER_ID]
WHERE B.IS_REVOKED=0 AND ARROVED_DATE NOT BETWEEN B.START_DATE AND B.END_DATE
there are basically two condition you want to meet.
if is_revoked is equal to 1 then ignore the row from the result..
if Approved_Date is between start_date and end_date then dont consider then also ignore this from the result.
So this can be easily done using join.
SELECT A.Trans_ID
FROM TransTable A, Table2 B
WHERE B.Is_Revoked != 1 AND A.Approved_Date NOT BETWEEN B.Start_Date AND
B.End_Date
As far as I understand from the expected result that you mentioned. you really dont' care if the User_id matches or not.

Joining two SQL tables based on the equality of few columns

I am trying to Create a SQL View by joining two SQL tables and return only the lowest value from second table and all the rows from first table similar to left join.
My problem can be clearly explained with the below example.
Table1
Id Product Grade Term Bid Offer
100 ABC A Q1 10 20
101 ABC A Q1 5 25
102 XYZ A Q2 25 30
103 XYZ B Q2 20 30
Table2
Id Product Grade Term TradeValue
1 ABC A Q1 100
2 ABC A Q1 95
3 XYZ B Q2 100
In the above data I want to join Table1 and Table2 when ever the columns Product,Grade and Term from both the tables are equal and return all the rows from Table1 while joining the lowest Value of the column TradeValue from Table2 to the first record of the match and making TradeValue as NULL for other rows of the resultant View and the resultant View should have the Id of Table2 as LTID
So the resultant SQL View should be
RESULT
Id Product Grade Term Bid Offer TradeValue LTID
100 ABC A Q1 10 20 95 2
101 ABC A Q1 5 25 NULL 2
102 XYZ A Q2 25 30 NULL NULL
103 XYZ B Q2 20 30 100 3
I tried using the following query
CREATE VIEW [dbo].[ViewCC]
AS
SELECT
a.Id,a.Product,a.Grade,a.Term,a.Bid,a.Offer,
b.TradeValue
FROM Table1 AS a
left JOIN (SELECT Product,Grade,Term,MIN(TradeValue) TradeValue from Table2 Group by Product,Grade,Term,) AS b
ON b.Product=a.Product
and b.Grade=a.Grade
and b.Term=a.Term
GO
The above Query returned the following data which is apt to the query I wrote but that is not what I was trying to get
Id Product Grade Term Bid Offer TradeValue
100 ABC A Q1 10 20 95
101 ABC A Q1 5 25 95 --This should be null
102 XYZ A Q2 25 30 NULL
103 XYZ B Q2 20 30 100
As we can see minimum value of TradeValue being assigned to all matching rows in Table1 and also I was not able to return Id As LTID from Table2 as I have issues with group by clause as I cannot group it by b.Id as it returns too many rows.
May I know a better way to deal with this?
You need a row number attached to each record from Table1, so that the requirement of only joining the first record from each group of Table1 can be fulfilled:
CREATE VIEW [dbo].[ViewCC]
AS
SELECT a.Id, a.Product, a.Grade, a.Term, a.Bid, a.Offer,
b.TradeValue, b.Id AS LTID
FROM (
SELECT *, ROW_NUMBER() OVER(PARTITION BY Product, Grade, Term ORDER BY Id) AS rn
FROM Table1
) a
OUTER APPLY (
SELECT TOP 1 CASE WHEN rn = 1 THEN TradeValue
ELSE NULL
END AS TradeValue, Id
FROM Table2
WHERE Product=a.Product AND Grade=a.Grade AND Term=a.Term
ORDER BY TradeValue) b
GO
OUTER APPLY returns a table expression containing either the matching record from Table2 with the lowest TradeValue, or NULL if no matching record exists.

Resources