SQL joining 2 tables without repeating values - sql

I have 2 tables with a 1:n relationship.
I want to join them without repeating (duplicating) the values from the one table.
First, I have a table with budgets:
id name budget
1 John 1000
2 Kim 3000
And second I have a table of spendings:
id amount
1 112
1 145
1 211
The result should look like this:
id name budget amount
1 John 1000 112
1 null null 145
1 null null 211
2 Kim 3000 null
Output could also be: (this is not important)
id name budget amount
1 null null 112
1 John 1000 145
1 null null 211
2 Kim 3000 null
Is this possible with SQL?
Here a join that repeats the values:
create temporary table a (id1 int,name varchar(10),budget int);
insert into a (id1,name,budget) values(1,'Maier',1000),(2,'Mueller',2000);
create temporary table if not exists b (id2 int,betrag int);
insert into b (id2,betrag) values(1,100),(1,133),(1,234);
select * from a left join b
on a.id1=b.id2
;

The keyword DISTINCT is used to eliminate duplicate rows from a query result:
select distinct b.id, b.name, b.budget, s.amount
from budgets b left join spendings s
on b.id = s.id;
You can also use Group By clause which works similarly like Distinct.In that case,
select b.id, b.name, b.budget, s.amount
from budgets b left join spendings s
on b.id = s.id
group by b.id, b.name, b.budget, s.amount;

create table a (id1 int,name varchar(10),budget int)
insert into a (id1,name,budget) values(1,'Maier',1000)
insert into a (id1,name,budget) values(2,'Mueller',2000)
create table b (id2 int,betrag int)
insert into b (id2,betrag) values(1,100)
insert into b (id2,betrag) values(1,133)
insert into b (id2,betrag) values(1,234)
insert into b (id2,betrag) values(2,300)
insert into b (id2,betrag) values(2,400)
select a.id1, CASE WHEN c.themin IS NOT NULL THEN a.name ELSE NULL END AS [name],
CASE WHEN c.themin IS NOT NULL THEN a.budget ELSE NULL END AS [budget],
b.*
from a
LEFT join b on a.id1=b.id2
LEFT OUTER JOIN (SELECT MIN(betrag) AS [themin], id2 FROM b GROUP BY id2) c ON a.id1 = c.id2 AND b.betrag = c.themin

Related

How to identify non-existing keys with reference to a table that has all mandatory keys, SQL?

I have the table 'Table01' which contains the keys that should be mandatory:
id
1
2
3
4
And I also have the table 'Table02' which contains the records to be filtered:
id
customer
weight
1
a
100
2
a
300
3
a
200
4
a
45
1
b
20
2
b
100
3
b
17
1
c
80
4
c
90
2
d
30
3
d
30
4
d
50
So I want to identify which are the mandatory id's that the table 'Table02' does not have, and in turn identify which is the 'customer' of each id's that the table 'Table02' does not have.
The resulting table should look like this:
customer
id
b
4
c
2
c
3
d
1
What I have tried so far is a 'rigth join'.
proc sql;
create table table03 as
select
b.id
from table02 a
right join table01 b
on a.id=b.id
where a.id is null;
run;
But that query is not identifying all the id's that should be mandatory.
I hope someone can help me, thank you very much.
here is one way:
select cl.customerid , a.id
from
Table1 a
cross join
( select customerid
from table2
group by customerid
) cl
where not exists ( select 1 from table2 b
where b.customerid = cl.customerid
and b.id = a.id
)
You can use an EXCEPT between two sub-selects. The first creates a matrix of all possibilities, and the except table is a selection of the extant customers.
Example:
data ids;
do id = 1 to 4; output; end;
run;
data have;
input id customer $ weight;
datalines;
1 a 100
2 a 300
3 a 200
4 a 45
1 b 20
2 b 100
3 b 17
1 c 80
4 c 90
2 d 30
3 d 30
4 d 50
run;
proc sql;
create table want(label='Customers missing some ids') as
select matrix.*
from
(select distinct have.customer, ids.id from have, ids) as matrix
except
(select customer, id from have)
;
quit;
If you are doing it in SQL server. Something like #eshirvana above posted, but also you can use with cte:
;with cte as
(
SELECT t1.id, t2.Customer
FROM Table01 t1
cross join (select distinct customer from Table02)
)
SELECT a.customer, a.id FROM cte a
LEFT JOIN Table02 b
ON a.id=b.id AND a.customer=b.customer
where b.id is null

How to join only latest date values from another table and prevent duplication

I'm trying to lookup a unique value from table b and get it into table a.
Table b stores multiple values that are changing by date.
I would like to join but only getting the values with the latest date from table b.
Table a
Unique ID
1
2
Table b
Date Unique ID Price
01/01/2019 1 100
01/02/2019 1 101
01/03/2019 1 102
01/01/2019 2 90
01/02/2019 2 91
01/03/2019 2 92
Expected result
Unique ID Price Date
1 102 01/03/2019
2 92 01/03/2019
Appreciate your help!
Have a sub-query that returns each UniqueID together with its max date. IN that result.
select * from tablename
where (UniqueID, date) in (select UniqueID, max(date)
from tablename
group by UniqueID)
You want correlated subquery :
select b.*
from tableb b
where b.date = (select max(b1.date) from tableb b1 where b1.UniqueID = b.UniqueID);
If you want to go with JOIN then you can do JOIN with subquery :
select a.UniqueID , b.Price, b.Date
from tablea a inner join
tableb b
on b.UniqueID = a.UniqueID
where b.date = (select max(b1.date) from tableb b1 where b1.UniqueID = a.UniqueID);
A correlated subquery?
select b.*
from b
where b.date = (select max(b2.date) from b b2 where b2.unique_id = b.unique_id);

SQL nested sums with multiple joins and group by

I'm not very familiar with SQLs. I'm using oracle. I met a question with over summing fields.
Here are the example tables:
A:
A_ID
A_NAME
B:
B_ID
A_ID
B_NAME
B_QTY
C:
C_ID
B_ID
C_QTY
So the data structure is like A -> *B -> *C
I need to get the total quantities of Bs and Cs grouped by B_NAME and A_ID. For example:
A:
A_ID A_NAME
1 A1
B:
B_ID A_ID B_NAME B_QTY
1 1 B1 20
2 1 B1 5
3 1 B1 5
4 1 B2 5
C:
C_ID B_ID C_QTY
1 1 3
2 1 4
4 2 2
5 2 1
6 3 1
7 4 1
The expected result is:
A_ID A_NAME B_NAME B_QTY C_QTY
1 A1 B1 30 11
1 A1 B2 5 1
The 30 of B_QTY in the 1st line is result of 20 + 5 + 5.
The 11 of C_QTY in the 1st line is result of 3 + 4 + 2 + 1 + 1.
Here is my sql:
select a.A_ID,
a.A_NAME,
b.B_NAME
sum(b.B_QTY),
sum(c.C_QTY)
from A a left outer join B b on b.A_ID = a.A_ID
left outer join C c on c.B_ID = b.B_ID
group by a.A_ID
order by a.A_ID, b.B_NAME
where a.XXXX = XXXXX;
So the problem is:
Since the B mapps to multiple Cs, the B_QTY will be summed multiple times. I'm not very familiar with SQL so I don't know if there is any simple way to distict the summing based on some fields (which is B_ID in my example). Thank you!
This can also be done like this:
WITH b2 AS
(SELECT b.*, sum(b.b_qty) over (partition BY b.a_id, b.b_name) b_qty_s
FROM b)
SELECT a.a_id, a.a_name, b2.b_name, b2.b_qty_s, sum(c.c_qty) c_qty_s
FROM a JOIN b2 ON a.a_id = b2.a_id
JOIN c ON b2.b_id = c.b_id
GROUP BY a.a_id,a.a_name, b2.b_name, b2.b_qty_s
Here is a sqlfiddle demo
You can also do like this:
SELECT DISTINCT A_ID,A_NAME,B_NAME,B_SUM,SUM(C_QTY) OVER(PARTITION BY A_NAME,B_NAME) C_SUM
FROM (
SELECT A.A_ID,A_NAME,B_NAME,B_ID,SUM(B_QTY) OVER(PARTITION BY A_NAME,B_NAME) B_SUM
FROM A JOIN B
ON A.A_ID=B.A_ID) T1
JOIN C
ON T1.B_ID=C.B_ID
I created an SQL fiddle for this problem. The trick is that the B_QTY was appearing in your results more than once. Summing on it was giving an artificially high value. So instead, run a sub select to use the B_NAME only once! Great question! :^D
A.B.Cade's answer is cool, but this solution will work for many databases. I've used this technique before with SQL Server, Oracle, and Informix.
Data/Schema:
create table a (A_ID int, A_NAME char(10));
create table b (B_ID int, A_ID int, B_NAME char(10), B_QTY int);
create table c (C_ID int, B_ID int, C_QTY int);
-- One dude
insert into a values (1,'Xiezi');
-- 2 orders? of 4 and 3
insert into b values (1,1,'B1',20);
insert into b values (2,1,'B1',5);
insert into b values (3,1,'B1',5);
insert into b values (4,1,'B2',5);
-- 2 order with 2 lines each.
insert into c values (1,1,3);
insert into c values (2,1,4);
insert into c values (4,2,2);
insert into c values (5,2,1);
insert into c values (6,3,1);
insert into c values (7,4,1);
SQL (The answer):
select a.A_ID,
a.A_NAME,
b.B_NAME,
(select sum(b2.B_QTY) from b b2 where b2.B_NAME = b.B_NAME)
as sum_b_qty,
sum(c.C_QTY)
from a left outer join b on b.A_ID = a.A_ID
left outer join c on c.B_ID = b.B_ID
group by a.A_ID,
a.A_NAME,
b.B_NAME
order by a.A_ID
;
Output:
A_ID A_NAME B_NAME SUM_B_QTY SUM(C.C_QTY)
1 Xiezi B1 30 11
1 Xiezi B2 5 1

Get records using left outer join

I have two tables as given below
Table A Table B Table C
============= ============== =========
Id Name Id AId CId Id Name
1 A 1 1 1 1 x
2 B 2 1 1 2 y
3 C 3 2 1 3 z
4 D 4 2 3 4 w
5 E 5 3 2 5 v
Now I want all the records of Table A with matching Id column CId from Table B where CId = 1.
So the output should be like below :
Id Name CId
1 A 1
2 B 1
3 C 1
4 D Null
5 E Null
Can anyone help me please?
This does what you want:
SELECT
A.Id,
A.Name,
CASE B.CId WHEN 1 THEN 1 ELSE NULL END AS CId
FROM
A LEFT JOIN B ON A.Id = B.Id
This is not about LEFT JOINing. You could as well do it with an INNER JOIN. When you don't want the 3 and 2 of column CId to appear you would still have to filter with WHERE and therefore the rows with Id 4 and 5 would not appear, which is not what you want.
EDIT:
Given this test data:
create table A (Id int, Name varchar(5));
insert into A values
(1, 'A'),
(2, 'B'),
(3, 'C'),
(4, 'D'),
(5, 'E');
create table B (Id int, AId int, CId int);
insert into B values
(1,1,1),
(2,1,1),
(3,2,1),
(4,2,3),
(5,3,2);
my query does not give a cartesian product. Read and try before downvoting. Anyway, it was not clear to me what you want to achieve, now I've joined on AId column and with this query:
SELECT DISTINCT
A.Id,
A.Name
, CASE
WHEN B.CId > 1 THEN 1
WHEN B.CId = 1 THEN 1
ELSE NULL END AS CId
FROM
A LEFT JOIN B ON A.Id = B.AId
and it also gives the right output, like the first before. If this is still not what you want, your test data is wrong or I absolutely don't get it.
Try something like this:
SELECT TableA.Id, TableA.Name, TableB.CId
FROM TableA
LEFT OUTER JOIN TableB ON TableA.Id = TableB.CId
WHERE TableB.CId = 1
Hope this helps.
Edit:
The output you desired, can be achieved if you match TableA's ID column with TableB's ID column, NOT TableB's CId column. Try below which I tested in my pc and gives thee similar output you needed.
select TableA.Id, TableA.Name, TableB.CId
from TableA
left outer join TableB on TableA.Id = TableB.Id
and TableB.CId in
(
select TableB.CId
from TableB
left outer join TableC on TableB.CId = TableC.Id
WHERE TableB.CId = 1
)
group by TableA.Id, TableA.Name, TableB.CId
Please inform if I guess it right. Check the column names.

SQL Select Statement issue - returning rows conditionally on a 2nd table

I could really use some help with the following SQL Select statement scenario:
I need to select all rows from a table conditionally depending on whether a userID has already entered data into a second table with the same ID.
Example:
Select all rows from TABLE A for idNumber where idNumber not in
TABLE B
but for each idNumber that IS in TABLE B, still return row unless a
specific userID is in that row in TABLE B.
TABLE A
========
idNumber|type|Date
1 A 01/01/01
2 A 01/01/01
3 B 01/01/01
4 B 01/01/01
5 B 01/01/01
TABLE B
========
idNumber|type|userID
1 A 0000
3 B 0000
4 B 1111
userID to exclude records for = 1111
SQL Query should return:
idNumber|type|Date
1 A 01/01/01
2 A 01/01/01
3 B 01/01/01
5 B 01/01/01
Apologies for the long winded post but i hope it makes sense.
Many thanks in advance,
ukjezza.!!
Select idNumber, type, Date
From TableA
Where Not Exists (
Select 1
From TableB
Where TableB.idNumber = TableA.idNumber
And TableB.userID = 1111
)
Another choice:
Select TableA.idNumber, TableA.type, TableA.Date
From TableA
Left Join TableB
On TableB.idNumber = TableA.idNumber
And TableB.userId = 1111
Where TableB.idNumber Is Null
Looks like a LEFT JOIN and COALESCE could take care of it:
SELECT a.*
FROM TableA as a
LEFT JOIN TableB as b
ON a.idNumber = b.idNumber
WHERE COALESCE(b.userID, -1) != 1111
select A.*
from TableA as A
left outer join TableB as B
on A.idNumber = B.idNumber
where B.idNumber is null or
B.userID <> '1111'