full outer join by hand - sql

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

Related

How to fetch record from Table A which has not any approved status true on Table B

I have three table A , B and C and it structure is shown below
Table A
----------------------------
| id | Text_message_to_show|
----------------------------
| 1 | first demo message |
----------------------------
| 2 | second demo message |
----------------------------
Table B
------------------------------------
| id | request_id | approved_status |
------------------------------------
| 101 | 1 | 2 |
------------------------------------
| 102 | 1 | 1 |
------------------------------------
| 103 | 2 | 2 |
------------------------------------
| 104 | 2 | 2 |
------------------------------------
Table c
------------------------------------
| id | request_id | approved_status |
------------------------------------
| 501 | 1 | 2 |
------------------------------------
| 502 | 2 | 1 |
------------------------------------
Table B and Table C has foreign key request_id column which is reference id column of table A.Table Table A-> Table B has one to many relaionship and Table A->Table C has one to one relationship Now I have question is how to wrie sql query such that to fetch Table A record where no approved_status for request_id should be 2 in Table B And also Table C approved_status should not be 2
You can use not exists like following.
select *
from tablea ta
where not exists (
select 1
from tableb tb
where ta.id = tb.request_id
AND tb.approved_status = 2
)
You can use left join as follows:
Select a.*
From a
Left Join b on a.id = b.request_id and b.approved_status = 2
Left join c on a.id = c.request_id and c.approved_status = 2
Where coalesce(b.id,c.id) is null
select * from tablea ta
where not exists
(
select id from tableb tb where ta.id=tb.request_d and tb.approved_status = 2
)
This may help you out.

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)

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;

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.

I want to figure out a sql statement for a complex data structure

I am working on trying to access data from a table by using joins. I am given information for a primary key in A and want to retrieve information from X. There are two ways to get to X, one by going through B and one by going through C. Which path I choose is decided from a column in A. I tried to create a sql statement that uses CASE WHEN and joins the tables with a FROM statement in this manner:
X - Join - B - Right Outer Join - A - Left Outer Join - C - Join - X
but sql tells me I can't join the same table at both ends. I use outer join because A will either have a connection to B or C, but not both.
Basically, I need to retrieve data from X, but X is accessed using different keys depending on which path is deemed in a column in A.
You should use a query like this:
SELECT X.*
FROM A
LEFT JOIN B ON B.A_id = A.id
LEFT JOIN C ON C.A_id = A.id
LEFT JOIN X ON X.id = IF(A.path = 'B', B.X_id, C.X_id)
WHERE A.id = <numeric_id>;
I tested this query on MySQL with these data:
Table A:
------ ------
| id | path |
------ ------
| 1 | B |
| 2 | B |
| 3 | C |
| 4 | C |
------ ------
Table B:
------ ------
| A_id | X_id |
------ ------
| 1 | 9 |
| 2 | 8 |
------ ------
Table C:
------ ------
| A_id | X_id |
------ ------
| 3 | 7 |
| 4 | 6 |
------ ------
Table X:
------ -------
| id | value |
------ -------
| 1 | 10 |
| 2 | 20 |
| 3 | 30 |
| 4 | 40 |
| 5 | 50 |
| 6 | 60 |
| 7 | 70 |
| 8 | 80 |
| 9 | 90 |
------ -------
If you remove the WHERE clause and SELECT A.id, B.X_id, C.X_id, X.* you'll have the following result, which confirms what table is the actual path to X:
-------- -------- -------- -------- ---------
| A.id | B.X_id | C.X_id | X.id | X.value |
------ ------ ------ ------ ------- ---------
| 1 | 9 | NULL | 9 | 90 |
| 2 | 8 | NULL | 8 | 80 |
| 3 | NULL | 7 | 7 | 70 |
| 4 | NULL | 6 | 6 | 60 |
-------- -------- -------- -------- ---------
Hope it works for you!
You could also do a UNION or UNION ALL query (if teh two tables are mutuially exclusive)
Select a.field1, b.field2
from tableA a
join tableb b on a.table1id - b.table1id
Union all
Select a.field1, c.field2
from tableA a
join tablec c on a.table1id - c.table1id
This is doable if you choose both paths like this:
SELECT ....
FROM A
LEFT JOIN B ON A.bkey = B.bkey
LEFT JOIN X as XB ON B.xkey = XB.xkey
LEFT JOIN C ON A.ckey = C.ckey
LEFT JOIN X as XC ON C.xkey = XC.xkey
And then if you just want (for example) field 1 out of X, then you might use a function like:
SELECT ISNULL(XB.field1, XC.field1)...
Or some such