Access SQL: Self join without cross duplicates - sql

Table:
Bag | Weight
------------
A | 2
B | 2
C | 3
I want to know which bags have similar weights; normally I would self-join on the Weight column; but I don't want cross duplications; that is if I have A-B, I don't want B-A.
Query:
SELECT lhs.bag, rhs.bag
FROM myTable AS lhs INNER JOIN myTable AS rhs ON lhs.weight = rhs.weight;
Result:
lhs.bag | rhs.bag
-----------------
A | A
A | B
B | B
B | A
C | C
I don't want the row B|A in the results, because similarly it has been in A|B.
Expected Table:
lhs.bag | rhs.bag
-----------------
A | A
A | B
B | B
C | C

Extend the join condition
SELECT lhs.bag, rhs.bag
FROM myTable AS lhs
INNER JOIN myTable AS rhs ON lhs.weight = rhs.weight AND
lhs.bag <= rhs.bag;

Related

Join two tables, get all results from first table

So I have two tables. Let's say this is the structure of the tables:
TABLE A | TABLE B |
-----------------------------------------|
ID | div | ID | date | val |
-----------------------------------------|
A | a | A | d1 | 22 |
B | b | B | d2 | 10 |
C | c | C | d3 | 9 |
F | l | F | d1 | 10 |
What I want is to have this result:
row 1: A, a, null, null
row 2: B, b, d2, 10
row 3: C, c, null, null
row 4: F, l, d1, 10
It's a left JOIN but I want to retrieve only the values that have val = 10.
I am using SQLite. I have tried many answers from different threads without any success.
You are looking for left join:
select a.*, b.date, b.val
from a left join
b
on a.id = b.id and b.val = 10
order by a.id;

Get left table data completely even when there is no reference in right joined table

Database used: SQL Server
I have three tables A,B,C.
TABLE A:
------------------
| ID | Name |
------------------
| 1 | X |
------------------
| 2 | Y |
------------------
TABLE B:
----------------------
| ID | Date |
----------------------
| 1 | 2019-11-06 |
----------------------
| 2 | 2019-11-05 |
----------------------
TABLE C:
----------------------------------
| ID | B.ID | A.ID | Amount |
----------------------------------
| 1 | 1 | 1 | 500 |
----------------------------------
| 2 | 2 | 2 | 1000 |
----------------------------------
The result I would like to get is all entries of table A.Name with their amount in table C.amount where table B.Date = 2019-11-06. The result set should include all A.name entries even it have no reference in Table C.
Required result is:
-----------------------
| A.Name | C.Amount |
-----------------------
| X | 500 |
-----------------------
| Y | NULL |
-----------------------
Code I tried with :
SELECT A.Name,C.Amount
FROM A
LEFT OUTER JOIN C ON C.A_ID=A.ID
LEFT OUTER JOIN B ON B.ID = C.B_ID ON
WHERE B.Date='2019-11-06'
The result I obtained with above code is :
------------------
| Name | Amount |
------------------
| X | 500 |
------------------
There is no Y in the result, its because there is no entry for Y on that particular date. I just want to show Y and amount as null or zero.
SQL Fiddle with my query
Please help me with this.
There's is no relationship between your A and B, so we need to group B and C using a subquery to filter with date before doing the left join.
SELECT A.Name, t1.Amount
FROM A
LEFT JOIN
(SELECT C.A_ID, C.Amount FROM C
INNER JOIN B ON B.ID = C.B_ID
WHERE B.Date='2019-11-06') t1
ON t1.A_ID=A.ID
see dbfiddle
Try this-
Fiddle Here
SELECT A.Name,C.Amount
FROM A
LEFT JOIN B ON A.ID = B.ID AND B.Date = '2019-11-06'
LEFT JOIN C ON B.ID = C.ID
Output is-
Name Amount
X 500
Y (null)

join table in sqlalchemy with filtering by groups

table A must be outer-joinned with B.
A
id | ...
---|-----
1 |
2 |
3 |
---|-----
B
id| a_id | b | ...
--|------|----------
1 |1 | special |
2 |1 | normal |
3 |2 | normal |
4 |2 | normal |
--------------------
so I want:
A.outerjoin(B)
like this:
a_id | b.id | ...
-----|------|--------
2 | 3 |normal |
2 | 4 |normal |
3 | None |
-----|------|
what should i use to filter entire group of B with the same a_id having at least one "special" value?
I dont want lose outerjoin - None for a.id = 3.
my first idea is to use nested select, but it's not optimal.
select distinct a.*
from a join b on a.id=b.a_id
where b.b='special';
excludes a groups with 'special'
select a.*
from a outer join
(select distinct a.*
from a join b on a.id=b.a_id
where b.b='special') excluded on a.id=excluded.id
having excluded.id is null;
leaves only valid a rows.
select *
from (select a.*
from a outer join
(select distinct a.*
from a join b on a.id=b.a_id
where b.b='special') excluded on a.id=excluded.id
having excluded.id is null) outer join b on a.id=b.a_id;
valid rows outer joined with b

Natural full outer join?

There are three relations (t1, t2, t3):
t1
-----------
a | b
1 | 2
-----------
t2
------------
b | c
2 | 3
------------
t3
-------------
a | c
4 | 5
-------------
The query is:
select * from t1 natural full outer join (t2 natural full outer join t3);
The result of select * from t2 natural full outer join t3 is:
-----------------
a | b | c
| 2 | 3
4 | | 5
------------------
then I tried:
select * from t1 natural full outer join (result)
t1 result
----------- -----------------
a | b a | b | c
1 | 2 | 2 | 3
4 | | 5
------------------
Shouldn't this be:
--------------------------
a | b | c
1 | 2 | 3
4 | | 5
But I don't know why the sql query give:
a | b | c
4 | | 5
| 2 | 3
1 | 2 |
Your query:
select *
from t1 natural full outer join
result
Is equivalent to:
select *
from t1 full outer join
result
on t1.a = result.a and t1.b = result.b;
The natural join looks at all the fields in common, not just one. There are no rows that match, which is why you are getting three rows of results.
You seem to want:
select *
from t1 full outer join
result
on t1.b = result.b;
In general, it is better to avoid natural join's, because they "hide" information about what the query is doing and can readily lead to mistakes/unexpected results as you experienced.
I don't know if you knows how FULL OUTER JOIN works.
The FULL OUTER JOIN combines the results of both left and right outer joins and returns all (matched or unmatched) rows from the tables on both sides of the join clause.
So, if you want this result:
--------------------------
a | b | c
1 | 2 | 3
4 | | 5
With these tables:
t1 result
----------- -----------------
a | b a | b | c
1 | 2 | 2 | 3
4 | | 5
You should do this query:
SELECT * FROM t1 natural RIGHT JOIN (result) res ON t1.b = res.b
because you must specify with which column have to be joined.

Join table 1 to either column 1 or 2 from table 2 without duplicates

[MS SQL 2008]
I have tables (all columns are string names):
A: two columns relating some datafield to an owning entity
B: three columns defining a hierarchy of entities
I need to create a singe table of the whole hierarchy (including all rows not existing in both tables), but the key column in table A (shown as Acol2) can be in either column 1 or 2 of table B...
A: B:
Acol1 | Acol2 Bcol1 | Bcol2 | Bcol3
-------+------ --------+-------+------
A | B B | X | Y
C | D Q | X | Y
E | F H | D | Z
G | H W | V | U
The output should be
Hierarchy:
Acol1 | Bcol1 | Bcol2 | Bcol3
-------+-------+-------+------
A | B | X | Y
Null | Q | X | Y
C | Null | D | Z
G | H | D | Z
E | Null | Null | Null
Null | W | V | U
Logic (also added to original):
If A has no record in B, show A with all Null
If A has record in Bcol1, show A with full row B
If A has record in Bcol2, show A with Null, Bcol2, Bcol3
If B has no record in A, show B with Null for Acol1
I have tried all sorts of UNIONs of two separate JOINs, but can't seem to get rid of extraneous rows...
B LEFT JOIN A ON Acol2=Bcol1 UNION B LEFT JOIN A ON Acol2=Bcol2;
gives duplicate rows, as the second part of the union has to set Bcol1 to NULL
(perhaps one solution is a way to remove this duplicate NULL row?)
B INNER JOIN A ON Acol2=Bcol1 UNION B INNER JOIN A ON Acol2=Bcol2;
Obviously removes all the rows from A and B that have no shared keys
(solution as to easy way to regain just those rows?)
Any idea appreciated!
To play:
[SQL removed - see fiddle in reply comments]
SELECT
Table1.ACol1,
CASE WHEN Table1.ACol1 = Table2.BCol1 THEN Table2.BCol1 ELSE NULL END AS BCol1
Table2.BCol2,
Table2.BCol3
FROM
Table1
FULL OUTER JOIN
Table2
ON Table1.ACol2 IN (Table2.BCol1, Table2.BCol2)
When you say no duplicates, this is only possible if ACol2 only ever appears in one field of one row in Table2. If it appears in multiple places, you'll get duplication.
- If that's possible, how would you want to chose which record from Table2?
Also, in general, however, this is a SQL-Anti-Pattern.
This is because the join would prefer an index on Table2. But, since you never know which field you're joining on, no single index will ever satsify the join condition.
EDIT:
What would make this significantly faster is to create a normalised TableB...
B_ID | B_Col | B_Val
------+-------+-------
1 | 1 | B
1 | 2 | X
1 | 3 | Y
2 | 1 | Q
2 | 2 | X
2 | 3 | Y
3 | 1 | H
3 | 2 | D
3 | 3 | Z
4 | 1 | W
4 | 2 | V
4 | 3 | U
Then index that table with (B_ID) and on (B_Val)...
Then include the B_ID field in the non_normalised table...
ID | Bcol1 | Bcol2 | Bcol3
------+-------+-------+-------
1 | B | X | Y
2 | Q | X | Y
3 | H | D | Z
4 | W | V | U
Then use the following query...
SELECT
Table1.ACol1,
CASE WHEN Table1.ACol1 = Table2.BCol1 THEN Table2.BCol1 ELSE NULL END AS BCol1
Table2.BCol2,
Table2.BCol3
FROM
(
Table1
LEFT JOIN
Table2Normalised
ON Table2Normalised.B_Val = Table1.ACol2
AND Table2Normalised.B_Col IN (1,2)
)
FULL OUTER JOIN
Table2
ON Table2Normalised.B_ID = Table2.ID
EDIT:
Without changing the schema, and instead having one index on BCol1 and a second index on Bcol2...
SELECT ACol1, BCol1, BCol2, BCol3 FROM Table1 a INNER JOIN Table2 b ON a.ACol2 = b.BCol1
UNION ALL
SELECT ACol1, NULL, BCol2, BCol3 FROM Table1 a INNER JOIN Table2 b ON a.ACol2 = b.BCol2
UNION ALL
SELECT ACol1, NULL, NULL, NULL FROM Table1 a WHERE NOT EXISTS (SELECT * FROM Table2 WHERE BCol1 = a.ACol2)
AND NOT EXISTS (SELECT * FROM Table2 WHERE BCol2 = a.ACol2)
UNION ALL
SELECT NULL, BCol1, BCol2, BCol3 FROM Table2 b WHERE NOT EXISTS (SELECT * FROM Table1 WHERE ACol2 = b.BCol1)
AND NOT EXISTS (SELECT * FROM Table1 WHERE ACol2 = b.BCol2)
But that's pretty messy...