问题
I have a problem in Oracle SQL that I'm trying to get my head around.
I'll illustrate with an example. I have three tables that I am querying:
Employees
__________________________________________
| EmployeeID | Name |
| 1 | John Smith |
| 2 | Douglas Hoppalot |
| 3 | Harry Holiday |
...
InternalCosts
________________________________
| IntID | Amount | EmployeeID |
| 1 | 10 | 1 |
| 2 | 20 | 2 |
| 3 | 30 | 1 |
...
ExternalCosts
________________________________
| ExtID | Amount | EmployeeID |
| 1 | 40 | 1 |
| 2 | 50 | 2 |
| 3 | 60 | 1 |
...
What I want to achieve is a result of one row per employee, with sums of each of their internal and external costs, i.e.
____________________________________________________________
| Name | InternalCostTotal | ExternalCostTotal |
| John Smith | 40 | 100 |
| Douglas Hoppalot | 20 | 50 |
...
The problem I have is that when I query both the InternalCosts and ExternalCosts tables, I get each permutation of costs, not just one per employee. When I group by employee Name and sum the amount fields the values are too high. What I have tried:
SELECT emp.Name, sum(int.Amount), sum(ext.Amount)
FROM Employees emp,
InternalCosts int,
ExternalCosts ext
WHERE emp.EmployeeId = int.EmployeeID
and emp.EmployeeID = ext.EmployeeID
GROUP BY emp.Name
The examples above would return:
____________________________________________________________
| Name | InternalCostTotal | ExternalCostTotal |
| John Smith | 80 | 200 | <- too high!
| Douglas Hoppalot | 20 | 50 |
...
Grateful for any help/advice/thoughts!
回答1:
You should use subqueries on int and ext to do the summing, and join to the subqueries.
I also suggest using explicit JOINs rather than table, table, table
e.g.
SELECT emp.Name, int.Amount AS InternalCostTotal, ext.Amount AS ExternalCostTotal
FROM Employees emp
JOIN (
SELECT EmployeeID, SUM(Amount) AS Amount
FROM InternalCosts
GROUP BY EmployeeID
) int ON emp.EmployeeId = int.EmployeeID
JOIN (
SELECT EmployeeID, SUM(Amount) AS Amount
FROM ExternalCosts
GROUP BY EmployeeID
) ext ON emp.EmployeeId = ext.EmployeeID
回答2:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE Employees ( EmployeeID, Name ) AS
SELECT 1, 'John Smith' FROM DUAL
UNION ALL SELECT 2, 'Douglas Hoppalot' FROM DUAL
UNION ALL SELECT 3, 'Harry Holiday' FROM DUAL;
CREATE TABLE InternalCosts ( IntID, Amount, EmployeeID ) AS
SELECT 1, 10, 1 FROM DUAL
UNION ALL SELECT 2, 20, 2 FROM DUAL
UNION ALL SELECT 3, 30, 1 FROM DUAL;
CREATE TABLE ExternalCosts ( ExtID, Amount, EmployeeID ) AS
SELECT 1, 40, 1 FROM DUAL
UNION ALL SELECT 2, 50, 2 FROM DUAL
UNION ALL SELECT 3, 60, 1 FROM DUAL;
Query 1:
SELECT e.*,
( SELECT SUM( Amount ) FROM InternalCosts i WHERE e.EmployeeID = i.EmployeeID ) AS InternalCostTotal,
( SELECT SUM( Amount ) FROM ExternalCosts x WHERE e.EmployeeID = x.EmployeeID ) AS ExternalCostTotal
FROM Employees e
Results:
| EMPLOYEEID | NAME | INTERNALCOSTTOTAL | EXTERNALCOSTTOTAL |
|------------|------------------|-------------------|-------------------|
| 1 | John Smith | 40 | 100 |
| 2 | Douglas Hoppalot | 20 | 50 |
| 3 | Harry Holiday | (null) | (null) |
Or (using joins):
WITH InternalTotals AS (
SELECT EmployeeID,
SUM( Amount ) AS InternalCostTotal
FROM InternalCosts
GROUP BY
EmployeeID
),
ExternalTotals AS (
SELECT EmployeeID,
SUM( Amount ) AS ExternalCostTotal
FROM ExternalCosts
GROUP BY
EmployeeID
)
SELECT e.EmployeeID,
i.InternalCostTotal,
x.ExternalCostTotal
FROM Employees e
LEFT OUTER JOIN
InternalTotals i
ON ( e.EmployeeID = i.EmployeeID )
LEFT OUTER JOIN
ExternalTotals x
ON ( e.EmployeeID = x.EmployeeID );
回答3:
This should do the trick:
SELECT emp.Name, NVL(IntAmount,0), NVL(ExtAmount,0)
FROM Employees emp
LEFT JOIN
(SELECT EmployeeID, sum(Amount) as IntAmount
FROM InternalCosts GROUP BY EmployeeID) int
ON emp.EmployeeID = int.EmployeeID
LEFT JOIN
(SELECT EmployeeID, sum(Amount) as ExtAmount
FROM ExternalCosts GROUP BY EmployeeID) ext
ON emp.EmployeeID = ext.EmployeeID
Please also note that normalizing your table structure could help you in this case and in the future probably (and by this I mean merging External and Internal costs into one table, provided that it is possible of course).
回答4:
I would suggest you try a group by using Employee ID instead and later do an INNER join to project the names from the Employee table.
Cheers!
回答5:
Try this
SELECT t1.emp_name,inernalAmount,sum(ext.Amount) as externalAmount from
(
SELECT emp.EmployeeID,emp.Name, sum(int.Amount) as internalAmount
FROM Employees emp inner join InternalCosts int on emp.EmployeeId = int.EmployeeID
) t1 inner join ExternalCosts ext on t1.EmployeeID = ext.EmployeeID
GROUP BY t1.Name,internalAmount
来源:https://stackoverflow.com/questions/31427071/sql-select-sum-values-without-including-duplicates