Natural full outer join? - sql

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.

Related

SQL: CROSS JOIN over table partitions

I have the following table
session_id | page_viewed
1 | A
1 | B
1 | C
2 | B
2 | E
What I would like to do is a cross join of the page_viewed column with itself but where the cross join is done on the partitions from session_id. So, from the table above the query would return:
session_id | page_1 | page_2
1 | A | A
1 | A | B
1 | A | C
1 | B | A
1 | B | B
1 | B | C
1 | C | A
1 | C | B
1 | C | C
2 | B | B
2 | B | E
2 | E | B
2 | E | E
I have looked into window functions today trying to find a way around it but it seems join functions cannot be used. Can anyone help?
You may join giving only the session_id as the join criteria:
SELECT
t1.session_id,
t1.page_viewed AS page_1,
t2.page_viewed AS page_2
FROM yourTable t1
INNER JOIN yourTable t2
ON t1.session_id = t2.session_id;
-- ORDER BY clause optional, if you need it here
Demo
Hmmm . . . you seem to want a self-join:
select t1.session_id, t1.page_viewed as page_1, t2.page_viewed as page_2
from t t1 join
t t2
on t1.session_id = t2.session_id
order by t1.session_id, t1.page_viewed, t2.page_viewed;

Access SQL: Self join without cross duplicates

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;

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

Join three tables with counts

I have these three tables (Soknad, Prognose and Did) in the SQL Server database:
Table Soknad has columns: S_ID (key), S_REFNR
Table Prognose has columns: P_ID (key), P_S_ID
Table Did has columns: D_ID (key), D_S_ID, Did_Something
Prognose.P_S_ID is foreign key to Soknad.S_ID.
Did.D_S_ID is foreign key to Soknad.S_ID.
The tables are like this:
SOKNAD
S_ID | S_REFNR |
1 | abc |
2 | cbc |
3 | sdf |
PROGNOSE
P_ID | P_S_ID |
10 | 1 |
11 | 2 |
DID
D_ID | D_S_ID | D_Did_Something |
100 | 1 | 1 |
101 | 1 | 1 |
102 | 1 | 0 |
103 | 2 | 1 |
104 | 2 | 1 |
I want to join these tables (like a view or select statement). From the Did table a count of column Did_Something should be returned, as well as a count of the same column where the value is 1 (one).
The result should be:
S_ID | S_REFNR | P_ID | Count_D_Did_Something | Count_D_Did_Something_Is_One |
1 | abc | 10 | 3 | 2 |
2 | cbc | 11 | 2 | 2 |
3 | sdf | | | |
Any help would be appreciated!
I believe what you want to do is join two tables and put the counts where the rows match the left table in separate columns for each table.
This would accomplish that.
select t1.id, count(t2.id) t2_count , count(t3.id) t3_count
from table1 as t1
left outer join table2 as t2 on t2.table1_id = t1.id
left outer join table3 as t3 on t3.table1_id = t1.id
group by t1.id;
To accomplish the counts you want based on criteria from one of the outer joined tables, you can do that this way, using a derived table...
select t1.id, count(t2.id) t2_count, count(tt2.mCount) Did_SomethingCount, count(t3.id) t3_count
from table1 as t1
left outer join table2 as t2 on t2.table1_id = t1.id
left outer join (select count(*), table1_id mCount from table2 where Did_Something = 1 group by table1_id) as tt2 on tt2.table1_id = t1.id
left outer join table3 as t3 on t3.table1_id = t1.id
group by t1.id;
Here you go:
select s.s_id,
p.p_id,
count(d.Did_Something) as Count_D_Did_Something, -- nulls won't be counted
sum(CASE WHEN d.Did_Something = 1 THEN 1 ELSE 0 END) as Count_D_Did_Something_is_one
from Soknad as s
left join Prognose as p on p.P_S_ID = s.s_id
left join Did as d on d.D_S_ID = s.s_id
This should be simply give the results you need. Note, I simply summed up when you want count to be for values having one,
SELECT S.S_ID, S.REFNR, P.P_ID, COUNT(D.D_DID_SOMETHING) AS COUNT_D_DID_SOMETHING,
SUM(D_DID_SOMETHING) AS COUNT_D_DID_SOMETHING_IS_ONE
FROM SOKNAD AS S
INNER JOIN PROGNOSE AS P
ON P.P_S_ID = S.S_ID
INNER JOIN DID AS D
ON D.D_S_ID = S.S_ID
GROUP BY S.S_ID, S.REFNR, P.P_ID

full outer join by hand

I am trying to understand full outer join by using these simple tables
t1
-----------
a | b
===========
1 | 2
-----------
t2
------------
b | c
===========
2 | 3
------------
t3
-------------
a | c
=============
4 | 5
-------------
To understand select * from t1 natural full outer join (t2 natural full outer join t3);
I tried first select * from t2 natural full outer join t3
Let me call this as result
-----------------
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
I cannot quite understand how the sql query can give
a | b | c
---+---+---
4 | | 5
| 2 | 3
1 | 2 |
You can do that using UNION ALL (ALL was specified to keep duplicates).
SELECT a, b, NULL as c FROM t1
UNION ALL
SELECT NULL as a, b, c FROM t2
UNION ALL
SELECT a, NULL as b, c FROM t3
SQLFiddle Demo