Subqueries with Group By - sql

I have looked around on the internet for a while for a way to make this query work but am unable to work it out so far. I am trying to return the item descriptions and the quantity of items sold, This is what I have got at the moment:
SELECT itemdesc, quantity, (SELECT COUNT(quantity) FROM invoiceitem
WHERE invoiceitem.itemno = item.itemno
GROUP BY COUNT(invoiceitem.quantity)) Quantity
FROM item;
I am very lost at the moment, not sure if I am even linking the right tables together, can provide an ER Diagram if it helps, any help would be greatly appreciated, thankyou.
ANSWER:
SELECT item.itemdesc, (SELECT SUM(invoiceitem.quantity) FROM invoiceitem
WHERE invoiceitem.itemno = item.itemno
GROUP BY item.itemdesc) Quantity
FROM item
ORDER BY quantity;
Thankyou all!

Your outer query:
SELECT itemdesc,
quantity
/* ignoring the subquery */
FROM item;
Will not work as the item table does not have a quantity column.
If you intended to use the itemprice column then your query would be:
SELECT itemdesc,
itemprice,
( SELECT COUNT(quantity)
FROM invoiceitem
WHERE invoiceitem.itemno = item.itemno
) AS Quantity
FROM item
Which, for the sample data:
CREATE TABLE item (
itemno PRIMARY KEY,
itemdesc,
itemprice
) AS
SELECT 1, 'ItemA', 1 FROM DUAL UNION ALL
SELECT 2, 'ItemB', 2 FROM DUAL UNION ALL
SELECT 3, 'ItemC', 3 FROM DUAL UNION ALL
SELECT 4, 'ItemD', 4 FROM DUAL UNION ALL
SELECT 5, 'ItemE', 5 FROM DUAL;
CREATE TABLE invoiceitem( itemno, quantity ) AS
SELECT 1, 10 FROM DUAL UNION ALL
SELECT 1, 20 FROM DUAL UNION ALL
SELECT 1, 30 FROM DUAL UNION ALL
SELECT 3, 40 FROM DUAL UNION ALL
SELECT 4, 50 FROM DUAL UNION ALL
SELECT 5, NULL FROM DUAL;
Outputs:
ITEMDESC | ITEMPRICE | QUANTITY
:------- | --------: | -------:
ItemA | 1 | 3
ItemB | 2 | 0
ItemC | 3 | 1
ItemD | 4 | 1
ItemE | 5 | 0
And equivalent query using a join would be (assuming that item.itemno is a primary key):
SELECT MAX( i.itemdesc ) AS itemdesc,
MAX( i.itemprice ) AS itemprice,
COUNT(ii.quantity) AS Quantity
FROM item i
LEFT OUTER JOIN invoiceitem ii
ON ( ii.itemno = i.itemno )
GROUP BY i.itemno
You need to use LEFT OUTER JOIN rather than INNER JOIN to join the corresponding rows with zero items in the invoiceitem table.
db<>fiddle here

Group by like below should work. Please check.
select
item.itemdesc, count(invoiceitem.quantity) Quantity
from
item item
join invoiceitem invoiceitem on item.itemno = invoiceitem.itemno
group by
item.itemdesc
Thanks

Related

Complex Oracle SQL query

I started learning SQL a few weeks back on my own and came upon an interesting problem that I can't seem to solve. Here's the database structure:
Material (name, price, origin_country)
Product (name, price)
Quantities (productName, materialName, quantity)
The query should show all data of the Material that's being used in the most Products that cost 20.000 or less.
This is what I have so far:
select max(count(m.name))
from Materials m
join Quantities q on (m.name=q.materialName)
join Product p on (p.name=q.productName)
where p.price < 20000
group by m.name
This, in theory, should show show the maximum number that a type of material appears in (and it indeed does so in practice). The problem is, I have no idea how to implement this in a way that it can show me the data from the Material table.
Join the tables together, filter out the products that are too expensive and then use the COUNT analytic function to find the number of products and ORDER BY that count in descending order and return the FIRST ROW WITH TIES:
SELECT M.*,
P.name AS ProductName,
p.Price,
Q.Quantity,
COUNT( DISTINCT p.ROWID ) OVER ( PARTITION BY m.ROWID ) AS num_products
FROM Material M
INNER JOIN Quantities Q
ON ( m.Name = Q.MaterialName )
INNER JOIN Product P
ON ( Q.ProductName = P.Name )
WHERE p.price <= 20000
ORDER BY num_products DESC
FETCH FIRST ROW WITH TIES;
(I used the ROWID pseudo-column in the count just in-case there were two products or materials with the same name then it would not group them together. If this is not going to be the case then you can just use p.Name and m.Name instead.)
Which, for the sample data:
CREATE TABLE Material( name, price, origin_country ) AS
SELECT 'M1', 1, 'Place1' FROM DUAL UNION ALL
SELECT 'M2', 2, 'Place2' FROM DUAL UNION ALL
SELECT 'M3', 3, 'Place3' FROM DUAL;
CREATE TABLE Product (name, price) AS
SELECT 'P123', 10 FROM DUAL UNION ALL
SELECT 'P13', 8 FROM DUAL UNION ALL
SELECT 'P223', 5 FROM DUAL;
CREATE TABLE Quantities (productName, materialName, quantity) AS
SELECT 'P123', 'M1', 1 FROM DUAL UNION ALL
SELECT 'P123', 'M2', 1 FROM DUAL UNION ALL
SELECT 'P123', 'M3', 1 FROM DUAL UNION ALL
SELECT 'P13', 'M1', 1 FROM DUAL UNION ALL
SELECT 'P13', 'M3', 1 FROM DUAL UNION ALL
SELECT 'P223', 'M2', 1 FROM DUAL UNION ALL
SELECT 'P223', 'M2', 1 FROM DUAL UNION ALL
SELECT 'P223', 'M3', 1 FROM DUAL;
Outputs:
NAME | PRICE | ORIGIN_COUNTRY | PRODUCTNAME | PRICE | QUANTITY | NUM_PRODUCTS
:--- | ----: | :------------- | :---------- | ----: | -------: | -----------:
M3 | 3 | Place3 | P123 | 10 | 1 | 3
M3 | 3 | Place3 | P223 | 5 | 1 | 3
M3 | 3 | Place3 | P13 | 8 | 1 | 3
db<>fiddle here

Find the row with the highest value in SQL

According to the schema, the Orders table has the following columns:
OrderId integer, CustomerId integer, RetailerId integer, ProductId integer, Count integer
I'm trying to figure out what product has the highest number of orders for each retailer.
So, to sum up all the orders per product, I have the following query:
SELECT RetailerId, ProductId, SUM(Count) AS ProductTotal
FROM Orders
GROUP BY RetailerId, ProductId
ORDER BY RetailerId, ProductTotal DESC;
This gives an output like this:
RETAILERID PRODUCTID PRODUCTTOTAL
---------- ---------- ------------
1 5 115
1 10 45
1 1 15
1 4 2
1 8 1
2 9 12
2 11 10
2 7 1
3 3 3
4 2 1
5 11 1
Now, all I want to do is find the product with the highest number of orders per retailer. Right now, it shows all the products; I want only one.
I have tried way too many things. Following is one particularly abhorrent example:
SELECT O.RetailerId, O.ProductId, O.ProductTotal
FROM (
SELECT Orders.ProductId, MAX(ProductTotal) AS MaxProductTotal
FROM (
SELECT Orders.ProductId AS PID, SUM(Orders.Count) AS ProductTotal
FROM Orders
GROUP BY Orders.ProductId
) AS O INNER JOIN Orders ON Orders.ProductId = PID
GROUP BY Orders.ProductId
) AS X INNER JOIN O ON O.RetailerId = X.RetailerId AND O.ProductTotal = X.MaxProductTotal;
The solution to this is probably the simplest thing ever, but I just can't right now. So, I'd like some help.
Select the maximum total per customer with a window function:
SELECT RetailerId, ProductId, ProductTotal
FROM
(
SELECT
RetailerId, ProductId, SUM(Count) AS ProductTotal,
MAX(SUM(Count)) OVER (PARTITION BY RetailerId) AS MaxProductTotal
FROM Orders
GROUP BY RetailerId, ProductId
)
WHERE ProductTotal = MaxProductTotal
ORDER BY RetailerId;
You can try using window function row_number()
select * from
(
select *,row_number() over(partition by RetailerId order by ProductTotal desc) as rn from
(
SELECT RetailerId, ProductId, SUM(Count) AS ProductTotal
FROM Orders
GROUP BY RetailerId, ProductId
)A
)X where rn=1
you can use row_number() with cte
with cte as
(
SELECT o.RetailerId,o.ProductId AS PID, SUM(o.Count) AS ProductTotal
FROM Orders o
GROUP BY o.ProductId,o.RetailerId
), cte2 as
(
select RetailerId,PID,ProductTotal,
row_number() over(partition by RetailerId order by ProductTotal desc) as rn
from cte
) select RetailerId,PID,ProductTotal from cte2 where rn=1
Have you tried using rank?
Try this query
select OrderC, retailer_id,product_id from ( select sum(count_order) as OrderC,retailer_id,product_id,row_number() over(order by OrderC desc) as RN from order_t group by retailer_id,product_id) as d where RN=1;
Here is a version without sub-query using FIRST/LAST:
WITH t(RETAILERID, PRODUCTID, PRODUCTTOTAL) AS (
SELECT 1, 5, 115 FROM dual UNION ALL
SELECT 1, 10, 45 FROM dual UNION ALL
SELECT 1, 1, 15 FROM dual UNION ALL
SELECT 1, 4, 2 FROM dual UNION ALL
SELECT 1, 8, 1 FROM dual UNION ALL
SELECT 2, 9, 12 FROM dual UNION ALL
SELECT 2, 11, 10 FROM dual UNION ALL
SELECT 2, 7, 1 FROM dual UNION ALL
SELECT 3, 3, 3 FROM dual UNION ALL
SELECT 4, 2, 1 FROM dual UNION ALL
SELECT 5,11, 1 FROM dual)
SELECT
RETAILERID,
MAX(PRODUCTID) KEEP (DENSE_RANK LAST ORDER BY PRODUCTTOTAL) AS PRODUCTID,
MAX(PRODUCTTOTAL)
FROM t
GROUP BY RETAILERID;
+--------------------------------------+
|RETAILERID|PRODUCTID|MAX(PRODUCTTOTAL)|
+--------------------------------------+
|1 |5 |115 |
|2 |9 |12 |
|3 |3 |3 |
|4 |2 |1 |
|5 |11 |1 |
+--------------------------------------+

Selecting groups of rows where at least one row of each group meets a criteria

I'm trying to SELECT groups of rows having one row with a certain criteria.
I've tried it with CASE WHEN statements without any success. Keep in mind this table has hundred of records.
What I'm trying to accomplish is this:
One row of the group must have a subcategory equal to "GAMECONSOLE".
Rows having the same category, description and section form one group.
The ID is different so MIN and MAX does not work either.
ID SECTION DESCRIPTION CATEGORY SUBCATEGORY
21349 14010014 TODDLER TOY GAMECONSOLE
21278 14010014 TODDLER TOY BICYCLE
21431 15020021 TODDLER TOY CHESS
In this example the two first rows should be selected because they form one group and one row of the group is a "GAMECONSOLE".
CASE WHEN is used when you have to take a decision within a column expression. Filtering on row level must be done in a WHERE clause:
SELECT T.id, T.section, T.description, T.category, T.subcategory
FROM
myTable T
INNER JOIN myTable S
ON T.section = S.section AND
T.description = S.description AND
T.category = S.category
WHERE
S.subcategory = 'GAMECONSOLE'
You can join the table with itself on the columns that have to be equal. The table with alias S selects the right subategory. T selects all corresponding rows of the groups.
SELECT a1.ID
, a1.SECTION
, a1.DESCRIPTION
, a1.CATEGORY
, a1.SUBCATEGORY
FROM MyTable a1
INNER JOIN MyTable a2 ON a2.DESCRIPTION = a1.DESCRIPTION
AND a2.CATEGORY = a1.CATEGORY
AND a2.SECTION = a1.SECTION
WHERE a2.SUBCATEGORY = 'GAMECONSOLE'
-- you may want to further filter the Where clause and apply a group by or distinct to get the actual results you are wanting
Your description sounds like:
select
...
from
(
select
...
,sum(case when subcategory = 'GAMECONSOLE' then 1 else 0 end)
over (partition by category, description, section) as cnt
from tab
) dt
where cnt > 0
SELECT *
FROM myTable T
WHERE Section = (SELECT Section
FROM myTable Q
WHERE Q.subcategory = 'GAMECONSOLE')
Using an analytic function you can get the answer without using a self join.
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE TEST( ID, SECTION, DESCRIPTION, CATEGORY, SUBCATEGORY ) AS
SELECT 1, 1, 'TODDLER', 'TOY', 'GAMECONSOLE' FROM DUAL
UNION ALL SELECT 2, 1, 'TODDLER', 'TOY', 'BICYCLE' FROM DUAL
UNION ALL SELECT 3, 2, 'TODDLER', 'TOY', 'CHESS' FROM DUAL
UNION ALL SELECT 4, 3, 'COMPUTERS', 'SOFTWARE', 'BOOK' FROM DUAL
UNION ALL SELECT 5, 4, 'COMPUTERS', 'SOFTWARE', 'SOFTWARE' FROM DUAL
UNION ALL SELECT 6, 5, 'COMPUTERS', 'HARDWARE', 'MONITOR' FROM DUAL
UNION ALL SELECT 7, 6, 'COMPUTERS', 'HARDWARE', 'GAMECONSOLE' FROM DUAL
UNION ALL SELECT 8, 7, 'COMPUTERS', 'HARDWARE', 'KEYBOARD' FROM DUAL
UNION ALL SELECT 9, 8, 'TODDLER', 'BEDDING', 'BED' FROM DUAL
Query 1:
SELECT ID, SECTION, DESCRIPTION, CATEGORY, SUBCATEGORY
FROM (
SELECT t.*,
COUNT( CASE SUBCATEGORY WHEN 'GAMECONSOLE' THEN 1 END ) OVER ( PARTITION BY DESCRIPTION, CATEGORY ) AS HAS_SUBCATEGORY
FROM TEST t
)
WHERE HAS_SUBCATEGORY > 0
Results:
| ID | SECTION | DESCRIPTION | CATEGORY | SUBCATEGORY |
|----|---------|-------------|----------|-------------|
| 8 | 7 | COMPUTERS | HARDWARE | KEYBOARD |
| 7 | 6 | COMPUTERS | HARDWARE | GAMECONSOLE |
| 6 | 5 | COMPUTERS | HARDWARE | MONITOR |
| 3 | 2 | TODDLER | TOY | CHESS |
| 2 | 1 | TODDLER | TOY | BICYCLE |
| 1 | 1 | TODDLER | TOY | GAMECONSOLE |
Try
SELECT * FROM <TABLE_NAME> WHERE SUBCATEGORY like "GAMECONSOLE";
or
SELECT * FROM <TABLE_NAME> WHERE SUBCATEGORY = "GAMECONSOLE";
Replace <TABLE_NAME> with the actual table name.
Further readings:
https://dev.mysql.com/doc/refman/5.0/en/select.html

HAVING COUNT() not giving out the expected result

Attempting to get duplicate values of produce.prod_id using distinct, having clauses.but not giving out the expected result.
Here's my data:
PRODUCE
--------- -----------
PROD_ID PROD_NAME
--------- -----------
1 APPLES
2 PEARS
3 ORANGES
4 BANANAS
5 PEACHES
BUYERS
---------- ------------
BUYER_ID BUYER_NAME
---------- --------------
1 ADAM BARR
2 SEAN CHAI
3 EVA CORETS
4 ERIN O`MELIA
SALES
---------- --------- ------
BUYER_ID PROD_ID QTY
---------- --------- ------
1 2 15
1 3 5
4 1 37
3 5 11
4 3 1005
and here's my code:
select produce.prod_name,
buyer.BUYER_NAME,
SALES.PROD_ID
from produce
inner join SALES on produce.PROD_ID = SALES.PROD_ID
inner join buyer on SALES.BUYER_ID = buyer.BUYER_ID
group by produce.prod_name,
buyer.BUYER_NAME,
SALES.PROD_ID
having count(SALES.PROD_ID) > 1;
Expected result:
PROD_Name Buyer_Name
----------- --------------
Oranges ADAM BARR
Oranges ERIN O`MELIA
You need to use analytical function for your requirement
WITH PRODUCE(PROD_ID, PROD_NAME) AS (
SELECT 1, 'APPLES' from dual union all
select 2, 'PEARS' from dual union all
select 3, 'ORANGES' from dual union all
select 4, 'BANANAS' from dual union all
select 5, 'PEACHES' from dual),
BUYERS (BUYER_ID, BUYER_NAME) as (
select 1, 'ADAM BARR' from dual union all
select 2, 'SEAN CHAI' from dual union all
select 3, 'EVA CORETS' from dual union all
select 4, 'ERIN O`MELIA' from dual),
SALES(BUYER_ID, PROD_ID, QTY) as (
select 1, 2, 15 from dual union all
select 1, 3, 5 from dual union all
select 4, 1, 37 from dual union all
select 3, 5, 11 from dual union all
select 4, 3, 1005 from dual),
-- End of data preparation
TABLE_ AS (
SELECT produce.prod_name,
buyers.buyer_name,
sales.prod_id,
COUNT(1) OVER (PARTITION BY sales.prod_id) p_count
FROM produce
INNER JOIN sales
ON produce.prod_id = sales.prod_id
INNER JOIN buyers
ON sales.buyer_id = buyers.buyer_id)
SELECT prod_name, buyer_name, prod_id
FROM table_
WHERE p_count > 1;
Output:
| PROD_NAME | BUYER_NAME | PROD_ID |
|-----------|--------------|---------|
| ORANGES | ERIN O`MELIA | 3 |
| ORANGES | ADAM BARR | 3 |
Update: You simplified query would be:
With TABLE_ AS (
SELECT produce.prod_name,
buyers.buyer_name,
sales.prod_id,
COUNT(1) OVER (PARTITION BY sales.prod_id) p_count
FROM produce
INNER JOIN sales
ON produce.prod_id = sales.prod_id
INNER JOIN buyers
ON sales.buyer_id = buyers.buyer_id)
SELECT prod_name, buyer_name, prod_id
FROM table_
WHERE p_count > 1;
you don't need distinct. you only need group by and having. you also cant group by buyer_name to get a count > 1. that is where the issue lies, you can to do this as a set of nested queries
select dupes.prod_name,
buyers.buyer_name
from (
select produce.prod_name,
SALES.PROD_ID,
count(SALES.PROD_ID)
from produce
inner join SALES on produce.PROD_ID=SALES.PROD_ID
group by produce.prod_name,
SALES.PROD_ID
having count(*)>1
) as dupes
inner join sales on sales.prod_id = dupes.prod_id
inner join buyers on buyers.buyer_id = sales.buyer_id
Here's my, hopefully, simple answer.
Here's my SQL Fiddle scratch pad as well. http://sqlfiddle.com/#!9/d80f6/3
select P.Prod_Name, Buyer_Name
from Sales S
join
(select prod_id, count(prod_id) as ActSales
from Sales group by prod_id having count(prod_id) > 1) SQ
on S.Prod_Id = SQ.Prod_Id
join Produce P
on S.Prod_Id = P.Prod_Id
join Buyers B
on S.Buyer_Id = B.Buyer_Id

Left outer join on aggregate queries

So I have two payment tables that I want to compare in a Oracle SQL DB. I want to compare the the total payments using the location and invoice and total payments. It's more comlex then this but basically it is:
select
tbl1.location,
tbl1.invoice,
Sum(tbl1.payments),
Sum(tbl2.payments)
From
tbl1
left outer join tbl2 on
tbl1.location = tbl2.location
and tbl1.invoice = tbl2.invoice
group by
(tbl1.location,tbl1.invoice)
I want the left outer join because in addition to comparing payment amounts, I want see check all orders in tbl1 that may not exist in tbl2.
The issue is that there is that there is multiple records for each order (location & invoice) in both tables (not the same number of records necessarily ie 2 in tbl1 to 1 in tbl2 or vice versa) but the total payments for each order (location & invoice) should match. So just doing a direct join gives me a cartesian product.
So I am thinking I could do two queries, first aggregating the total payments by store & invoice for each and then do a join on those results because in the aggregate results, I would only have one record for each order (store & invoice). But I don't know how to do this. I've tried several subqueries but can't seem the shake the cartesian product. I'd like to be able to do this in one query as opposed to creating tables and joining on those as this will be ongoing.
Thanks in advance for any help.
You can use the With statement to create the two querys and join then as you said. I will put just the sintaxe and if you need more help just ask. Thats because you didn't provide full details on your tables. So I will just guess on my answer.
WITH tmpTableA as (
select
tbl1.location,
tbl1.invoice,
Sum(tbl1.payments) totalTblA
From
tbl1
group by
tbl1.location,
tbl1.invoice
),
tmpTableB as (
select
tbl2.location,
tbl2.invoice,
Sum(tbl2.payments) totalTblB
From
tbl2
group by
tbl2.location,
tbl2.invoice
)
Select tmpTableA.location, tmpTableA.invoice, tmpTableA.totalTblA,
tmpTableB.location, tmpTableB.invoice, tmpTableB.totalTblB
from tmpTableA, tmpTableB
where tmpTableA.location = tmpTableB.location (+)
and tmpTableA.invoice = tmpTableB.invoice (+)
The (+) operator is the left join operator for Oracle Database (Of course, you can use the LEFT JOIN statements if you prefer )
Two other options:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE tbl1 ( id, location, invoice, payments ) AS
SELECT 1, 'a', 1, 1 FROM DUAL
UNION ALL SELECT 2, 'a', 1, 1 FROM DUAL
UNION ALL SELECT 3, 'a', 1, 1 FROM DUAL
UNION ALL SELECT 4, 'a', 1, 1 FROM DUAL
UNION ALL SELECT 5, 'a', 1, 1 FROM DUAL
UNION ALL SELECT 6, 'a', 2, 1 FROM DUAL
UNION ALL SELECT 7, 'a', 2, 1 FROM DUAL
UNION ALL SELECT 8, 'a', 2, 1 FROM DUAL
UNION ALL SELECT 9, 'b', 1, 1 FROM DUAL
UNION ALL SELECT 10, 'b', 2, 1 FROM DUAL;
CREATE TABLE tbl2 ( id, location, invoice, payments ) AS
SELECT 1, 'a', 1, 1 FROM DUAL
UNION ALL SELECT 2, 'a', 1, 1 FROM DUAL
UNION ALL SELECT 3, 'a', 1, 1 FROM DUAL
UNION ALL SELECT 4, 'a', 2, 1 FROM DUAL
UNION ALL SELECT 5, 'a', 2, 1 FROM DUAL
UNION ALL SELECT 6, 'b', 1, 1 FROM DUAL
UNION ALL SELECT 7, 'b', 1, 1 FROM DUAL
UNION ALL SELECT 8, 'b', 1, 1 FROM DUAL
UNION ALL SELECT 9, 'b', 1, 1 FROM DUAL
UNION ALL SELECT 10, 'b', 1, 1 FROM DUAL;
Query 1:
This one uses a correlated sub-query to calculate the total for the second table:
SELECT location,
invoice,
SUM( payments ) AS total_payments_1,
COALESCE( (SELECT SUM( payments )
FROM tbl2 i
WHERE o.location = i.location
AND o.invoice = i.invoice),
0 ) AS total_payments_2
FROM tbl1 o
GROUP BY
location,
invoice
ORDER BY
location,
invoice
Results:
| LOCATION | INVOICE | TOTAL_PAYMENTS_1 | TOTAL_PAYMENTS_2 |
|----------|---------|------------------|------------------|
| a | 1 | 5 | 3 |
| a | 2 | 3 | 2 |
| b | 1 | 1 | 5 |
| b | 2 | 1 | 0 |
Query 2:
This one uses a named sub-query to pre-calculate the totals for table 1 then performs a LEFT OUTER JOIN with the second table and includes the total for table 1 in the group.
Without any indexes then, from the explain plans, Query 1 seems to be much more efficient but your indexes might mean the optimizer finds a better plan.
WITH tbl1_sums AS (
SELECT location,
invoice,
SUM( payments ) AS total_payments_1
FROM tbl1
GROUP BY
location,
invoice
)
SELECT t1.location,
t1.invoice,
t1.total_payments_1,
COALESCE( SUM( t2.payments ), 0 ) AS total_payments_2
FROM tbl1_sums t1
LEFT OUTER JOIN
tbl2 t2
ON ( t1.location = t2.location
AND t1.invoice = t2.invoice)
GROUP BY
t1.location,
t1.invoice,
t1.total_payments_1
ORDER BY
t1.location,
t1.invoice
Results:
| LOCATION | INVOICE | TOTAL_PAYMENTS_1 | TOTAL_PAYMENTS_2 |
|----------|---------|------------------|------------------|
| a | 1 | 5 | 3 |
| a | 2 | 3 | 2 |
| b | 1 | 1 | 5 |
| b | 2 | 1 | 0 |
Sorry, my first answer was wrong. Thank you for providing the sqlfiddle, MT0.
The point that i missed is that you need to sum up the payments on each table first, so there's only one line left in each, then join them. This is what MT0 does in his statements.
If you want a solution that looks more "symmetric", try:
select A.location, A.invoice, B.total sum1, C.total sum2
from (select distinct location, invoice from tbl1) A
left outer join (select location, invoice, sum(payments) as total from tbl1 group by location, invoice) B on A.location=B.location and A.invoice=B.invoice
left outer join (select location, invoice, sum(payments) as total from tbl2 group by location, invoice) C on A.location=C.location and A.invoice=C.invoice
which results in
LOCATION INVOICE SUM1 SUM2
a 2 3 2
a 1 5 3
b 1 1 5
b 2 1 (null)