How to SUM rows with an outer join? - sql

Question:
I have the following tables that I'd like to sum on two fields: HOURS and RATE. I also want to retrieve the NAME from the third table, joining all 3 tables on the field LINE_NUM.
If the LINE_NUM and CODE are the same, sum the fields of A with B.
Table EARNINGS A:
| EMPLOYEE_ID | LINE_NUM | REG_CODE | REG_HOURS | REG_RATE |
------------------------------------------------------------
| 0001 | 1 | C | 20 | 200 |
| 0002 | 1 | H | 0 | 0 |
Table OTH_EARNINGS B:
| LINE_NUM | OTH_CODE | OTH_HOURS | OTH_RATE |
----------------------------------------------
| 1 | A | 0 | 0 |
| 1 | B | 0 | 0 |
| 1 | C | 10 | 100 |
| 2 | A | 50 | 50 |
Table PAYCHECK C:
| EMPLOYEE_ID | LINE_NUM | NAME |
---------------------------------
| 0001 | 1 | Tom |
| 0001 | 2 | Tom |
| 0002 | 1 | John |
The result I'm looking for should be:
| EMPLOYEE_ID | LINE_NUM | CODE | HOURS | RATE | NAME |
-------------------------------------------------------
| 0001 | 1 | A | 0 | 0 | Tom |
| 0001 | 1 | B | 0 | 0 | Tom |
| 0001 | 1 | C | 30 | 300 | Tom |
| 0001 | 2 | A | 50 | 50 | Tom |
| 0002 | 1 | H | 0 | 0 | John |
Any idea how I can achieve this?
What I tried:
I've tried (table A with C) UNION (table B with C), but I can't get the sums to work.
SELECT C.EMPLOYEE_ID, A.REG_CODE, A.REG_HRS, SUM(A.REG_RATE)
FROM EARNINGS A, PAYCHECK C
WHERE A.LINE_NUM = C.LINE_NUM
GROUP BY C.EMPLOYEE_ID, A.REG_CODE, A.REG_HRS
UNION
SELECT D.EMPLOYEE_ID, B.OTH_CODE, B.OTH_HRS, SUM(B.OTH_RATE)
FROM OTH_EARNINGS B, PAYCHECK D
WHERE B.LINE_NUM = D.LINE_NUM
GROUP BY D.EMPLOYEE_ID, B.OTH_CODE, B.OTH_HRS
But I couldn't get the sum to work and it returned:
| EMPLOYEE_ID | LINE_NUM | CODE | HOURS | RATE | NAME |
-------------------------------------------------------
| 0001 | 1 | A | 0 | 0 | Tom |
| 0001 | 1 | B | 0 | 0 | Tom |
| 0001 | 1 | C | 10 | 100 | Tom |
| 0001 | 1 | C | 20 | 200 | Tom |
| 0001 | 2 | A | 50 | 50 | Tom |
| 0002 | 1 | H | 0 | 0 | John |

Your approach wasn't bad and you were almost there.
You should make the GROUP BY on the results of the 2 UNIONed queries being nested:
SELECT EMPLOYEE_ID, NAME, CODE, SUM(HRS), SUM(RATE)
FROM
(
SELECT C.EMPLOYEE_ID, C.NAME, A.REG_CODE AS CODE, A.REG_HRS AS HRS, A.REG_RATE AS RATE
FROM EARNINGS A
INNER JOIN PAYCHECK C ON A.LINE_NUM = C.LINE_NUM
UNION ALL
SELECT D.EMPLOYEE_ID, C.NAME, B.OTH_CODE AS CODE, B.OTH_HRS AS HRS, B.OTH_RATE AS RATE
FROM OTH_EARNINGS B
INNER JOIN PAYCHECK D ON B.LINE_NUM = D.LINE_NUM
)
GROUP BY EMPLOYEE_ID, NAME, CODE
However this will return wrong results because the JOINs on the PAYCHECK table will returns duplicates.
There's obviously something missing somewhere.
To identify the employee, you should combine 2 columns : EMPLOYEE_ID and LINE_NUM. For the first query on EARNING, there's no issue as the EMPLOYEE_ID is present in the table. However for the second query on OTH_EARNINGS, the EMPLOYEE_ID is missing...
In theory you should have something like this (check the INNER JOIN...ON)
SELECT EMPLOYEE_ID, NAME, CODE, SUM(HRS), SUM(RATE)
FROM
(
SELECT C.EMPLOYEE_ID, C.NAME, A.REG_CODE AS CODE, A.REG_HRS AS HRS, A.REG_RATE AS RATE
FROM EARNINGS A
INNER JOIN PAYCHECK C ON A.LINE_NUM = C.LINE_NUM AND A.EMPLOYEE_ID = C.EMPLOYEE_ID
UNION ALL
SELECT D.EMPLOYEE_ID, C.NAME, B.OTH_CODE AS CODE, B.OTH_HRS AS HRS, B.OTH_RATE AS RATE
FROM OTH_EARNINGS B
INNER JOIN PAYCHECK D ON B.LINE_NUM = D.LINE_NUM AND B.EMPLOYEE_ID = D.EMPLOYEE_ID
)
GROUP BY EMPLOYEE_ID, NAME, CODE
I also changed from your initial query:
the JOINs from implicit to explicit syntax.
the UNION into an UNION ALL as there's no reason here to remove the duplicates (maybe I am wrong)

Related

Sum with 3 tables to join

I have 3 tables. The link between the first and the second table is REQ_ID and the link between the second and the third table is ENC_ID. There is no direct link between the first and the third table.
INS_RCPT
+----+--------+------+----------+
| ID | REQ_ID | CURR | RCPT_AMT |
+----+--------+------+----------+
| 1 | 1 | USD | 100 |
| 2 | 2 | USD | 200 |
| 3 | 3 | USD | 300 |
+----+--------+------+----------+
ENC_LOG
+----+--------+--------+-------------+
| ID | REQ_ID | ENC_ID | ENC_LOG_AMT |
+----+--------+--------+-------------+
| 1 | 1 | 1 | 20 |
| 2 | 1 | 2 | 50 |
| 3 | 1 | 3 | 30 |
| 4 | 2 | 4 | 20 |
+----+--------+--------+-------------+
ENC_RCPT
+----+--------+--------------+
| ID | ENC_ID | ENC_RCPT_AMT |
+----+--------+--------------+
| 1 | 1 | 10 |
| 2 | 1 | 10 |
| 3 | 2 | 15 |
| 4 | 2 | 25 |
| 5 | 2 | 10 |
| 6 | 3 | 12 |
| 7 | 3 | 18 |
| 8 | 4 | 10 |
+----+--------+--------------+
I would like to have output as follows:
+----+--------+------+----------+-------------+--------------+
| ID | REQ_ID | CURR | RCPT_AMT | ENC_LOG_AMT | ENC_RCPT_AMT |
+----+--------+------+----------+-------------+--------------+
| 1 | 1 | USD | 100 | 100 | 100 |
| 2 | 2 | USD | 200 | 20 | 10 |
| 3 | 3 | USD | 300 | 0 | 0 |
+----+--------+------+----------+-------------+--------------+
I am using SQL Server to write this query. Any help is appreciated.
One approach would be to join the first table to two subqueries which compute the sums separately:
SELECT
ir.ID,
ir.REQ_ID,
ir.CURR,
ir.RCPT_AMT,
el.ENC_LOG_AMT,
er.ENC_RCPT_AMT
FROM INS_RCPT ir
LEFT JOIN
(
SELECT REQ_ID, SUM(ENC_LOG_AMT) AS ENC_LOG_AMT
FROM ENC_LOG
GROUP BY REQ_ID
) el
ON ir.REQ_ID = el.REQ_ID
LEFT JOIN
(
SELECT t1.REQ_ID, SUM(t2.ENC_RCPT_AMT) AS ENC_RCPT_AMT
FROM ENC_LOG t1
INNER JOIN ENC_RCPT t2 ON t1.ENC_ID = t2.ENC_ID
GROUP BY t1.REQ_ID
) er
ON ir.REQ_ID = er.REQ_ID
Demo
Note that your question includes a curve ball. The second subquery needs to return aggregates of the receipt table by REQ_ID, even though this field does not appear in that table. As a result, we actually need to join ENC_LOG to ENC_RCPT in that subquery, and then aggregate by REQ_ID.
You can try the below query. Also change the join from left to inner as per your requirement.
select a.id,a.req_id,a.curr,sum(a.rcpt_amt) rcpt_amt,sum(a.enc_log_amt) enc_log_amt,sum(c.enc_rcpt_amt) enc_rcpt_amt
from
(
select a.id id ,a.req_id req_id ,a.curr curr,sum(rcpt_amt) as rcpt_amt,sum(enc_log_amt) as enc_log_amt
from ins_rcpt a
left join enc_log b
on a.req_id=b.req_id
group by id,req_id,curr
) a
left join enc_rcpt c
on a.enc_id = c.enc_id
group by id,req_id,curr;

Aggregation in Join and where

I have this Query for Invertory Balance and work well:
Select A.BATCH_ID ,
A.QTY_MOV - IsNull(B.QTY_USED,0) As BALANCE
From P_BATCH_PRODUC A
Left OUTER Join (Select MATERIAL_ID,
BATCH_MATERIAL_ID),
SUM(QTY_INS) QTY_USED
From CONSUMPTION
Group By MATERIAL_ID, BATCH_MATERIAL_ID) As B
On B.MATERIAL_ID= A.PRODUCT_ID
And A.BATCH_ID = B.BATCH_MATERIAL_ID"
Where A.QTY_MOV - IsNull(B.QTY_USED,0) > 0
AND A.PRODUCT_ID= 1
and A.BATCH_ID = 1
But now, it's possible to have more than one A.QTY_MOV for each A.BATCH_ID , so i need to Change A.QTY_MOV to Sum(A.QTY_MOV ). What do I need to change for that?
Sample:
Table A
+------------+------------+---------+
| Product_ID | Batch_ID | Qty_Mov |
+------------+------------+---------+
| 1 | 1 | 100 |
| 1 | 1 | 150 |
| 2 | 1 | 80 |
| 1 | 3 | 100 |
| 1 | 4 | 100 |
+------------+------------+---------+
Table B
+------------------+------------+------------+----------+--+
| BATCH_MATERIAL_ID| Product_ID | Batch_ID | Qty_USED | |
+------------------+------------+------------+----------+--+
| 1 | 1 | 1 | 80 | |
| 2 | 1 | 1 | 10 | |
| 3 | 1 | 2 | 150 | |
| 4 | 1 | 3 | 80 | |
+------------------+------------+------------+----------+--+
This is what I want
Batch_ID BALANCE
---------- ---------------
1 160
Based strictly on the question, it sounds like you want a window function:
Select A.BATCH_ID ,
SUM(A.QTY_MOV) OVER (PARTITION BY A.BATCH_ID) - IsNull(B.QTY_USED,0) As BALANCE
I don't know if this does anything useful. If it does not, you should ask a new question with sample data and an explanation of logic.

Need query for JOIN four tables with some conditions?

I have the following four tables:
1) mls_user
2) mls_category
3) bonus_point
4) mls_entry
In mls_user table values are like below:
*-------------------------*
| id | store_id | name |
*-------------------------*
| 1 | 101 | sandeep |
| 2 | 101 | gagan |
| 3 | 102 | santosh |
| 4 | 103 | manu |
| 5 | 101 | jagveer |
*-------------------------*
In mls_category table values are like below:
*---------------------------------*
| cat_no | store_id | cat_value |
*---------------------------------*
| 20 | 101 | 1 |
| 21 | 101 | 4 |
| 30 | 102 | 1 |
| 31 | 102 | 2 |
| 40 | 103 | 1 |
| 41 | 103 | 1 |
*---------------------------------*
In bonus_point table values are like below:
*-----------------------------------*
| user_id | store_id | bonus_point |
| 1 | 101 | 10 |
| 4 | 101 | 5 |
*-----------------------------------*
In mls_entry table values are like below:
*-------------------------------------------------------*
| user_id | store_id | category | distance | status |
*-------------------------------------------------------*
| 1 | 101 | 20 | 10 | Approved |
| 1 | 101 | 21 | 40 | Approved |
| 1 | 101 | 20 | 10 | Approved |
| 2 | 101 | 20 | 5 | Approved |
| 3 | 102 | 30 | 10 | Approved |
| 3 | 102 | 31 | 80 | Approved |
| 4 | 101 | 20 | 15 | Approved |
*-------------------------------------------------------*
And I want below output:
*--------------------------------------------------*
| user name | Points | bonus Point | Total Point |
*--------------------------------------------------*
| Sandeep | 30 | 10 | 40 |
| Santosh | 30 | 0 | 30 |
| Manu | 15 | 5 | 20 |
| Gagan | 5 | 0 | 5 |
| Jagveer | 0 | 0 | 0 |
*--------------------------------------------------*
I tell the calculation of how the points will come for user Sandeep.
Points = ((10+10)/1 + 40/4)=30
Here 1 and 4 is cat value which comes from mls_category.
I am using below code for a particular user but when i
SELECT sum(t1.totald/c.cat_value) as total_distance
FROM mls_category c
join (
select sum(distance) totald, user_id, category
FROM mls_entry
WHERE user_id=1 AND store_id='101' AND status='approved'
group by user_id, category) t1 on c.cat_no = t1.category
I have created tables in online for checking
DEMO
Computing the points (other than the bonus points) requires a separate join between the mls_entry and mls_category tables. I would do this in a separate subquery, and then join this to the larger query.
Here is one approach:
SELECT
u.name,
COALESCE(t1.points, 0) AS points,
COALESCE(b.bonus_point, 0) AS bonus_points,
COALESCE(t1.points, 0) + COALESCE(b.bonus_point, 0) AS total_points
FROM mls_user u
LEFT JOIN
(
SELECT e.user_id, SUM(e.distance / c.cat_value) AS points
FROM mls_entry e
INNER JOIN mls_category c
ON e.store_id = c.store_id AND e.category = c.cat_no
GROUP BY e.user_id
) t1
ON u.id = t1.user_id
LEFT JOIN bonus_point b
ON u.id = b.user_id
ORDER BY
total_points DESC;
This is the output I am getting from the above query in the demo you setup:
The output does not match exactly, because you have (perhaps) a typo in Santosh's data in your question, or otherwise the expected output in your question has a typo.

Finding number of types of accounts from each customer

I am having a lot of trouble with trying to construct a query that will give me the name of each customer and the number of different types of accounts each has. The three types are Checkings, Savings, and CD.
customers:
+--------+--------+
| cid | name |
+--------+--------+
| 1 | a |
| 2 | b |
| 3 | c |
+--------+--------+
accounts:
+-----------+-----------+
| aid | type |
+-----------+-----------+
| 1 | Checkings |
| 2 | Savings |
| 3 | Checkings |
| 4 | CD |
| 5 | CD |
| 6 | Checkings |
+-----------+-----------+
transactions:
+--------+--------+--------+
| tid | cid | aid |
+--------+--------+--------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 3 |
| 4 | 3 | 4 |
| 5 | 1 | 5 |
| 6 | 3 | 4 |
| 7 | 1 | 6 |
+--------+--------+--------+
The expected answer would be:
a, 3
b, 1
c, 1
Getting the names is simple enough, but how can I keep count of each individual's account as well as compare the accounts to make sure that it is not the same type?
just add DISTINCT inside the COUNT
SELECT a.cid, a.name, COUNT(DISTINCT c.type) totalCount
FROM customers a
INNER JOIN transactions b
ON a.cis = b.cid
INNER JOIN accounts c
ON b,aid = c.aid
GROUP BY a.cid, a.name
Query:
SQLFiddleExample
SELECT
a."name",
COUNT(DISTINCT c."type") totalCount
FROM customers a
INNER JOIN transactions b
ON a."cid" = b."cid"
INNER JOIN accounts c
ON b."aid" = c."aid"
GROUP BY a."cid", a."name"
ORDER BY totalCount DESC
Result:
| NAME | TOTALCOUNT |
---------------------
| a | 3 |
| b | 1 |
| c | 1 |

Generate report using SUM

I've got two SQL Server 2005 tables: MainTable and Orders.
MainTable:
| MainID | Serial |
-------------------
| 1 | A00001 |
| 2 | B00002 |
Orders:
| OrderID | MainID | Name | Value |
-----------------------------------
| 1 | 2 | John | 100 |
| 2 | 2 | Mike | 200 |
| 3 | 1 | John | 150 |
| 4 | 1 | Mike | 350 |
| 5 | 1 | John | 200 |
| 6 | 2 | John | 500 |
| 7 | 1 | Mike | 50 |
I want to get something like this:
|Serial | Name | Total |
-----------------------
| A00001 | John | 350 |
| A00002 | John | 600 |
| A00001 | Mike | 400 |
| A00002 | Mike | 200 |
SELECT
m.serial,
o.name,
SUM(o.value)
FROM
main m
INNER JOIN order o ON m.mainid = o.mainid
GROUP BY
o.name,
m.serial
select serial, name, sum(value) as total
from maintable m inner join orders o on
m.mainID = o.mainID
group by
serial, name
SELECT
M.SERIAL, O.NAME, SUM(VALUE) AS TOTAL
FROM MAINTABLE M JOIN ORDERS O ON O.MAINID=M.MAINID
GROUP BY M.SERIAL, O.NAME
select Serial , name , Total
from MainTable m,
( select MainID ,name, SUM(value) Total from Orders o group by MainID , Name ) O
where m.MainID= O.MainID