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

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)

Related

Ignore SQL INNER JOIN if specific record exist?

i got two table joined like this
SELECT A.id,B.status
FROM TableA A
INNER JOIN TableB B on (A.TableA_ID=B.TableA_ID)
WHERE B.status not in ('CO','CL');
I want to display results of two table joined but with condition if the status of TableB has anything in ('CO','CL') the whole join with id of TableA would be ignored not just the rows with status not in ('CO','CL').
A.id and A.TableA_ID are different columns
Original result without where condition would be like this:
+------+-----------+
| id | status |
+------+-----------+
| 1000 | RE |
| 1000 | RE |
| 1000 | RE |
| 1000 | CO |
| 2000 | RE |
| 2000 | RE |
+------+-----------+
My Result:
+------+-----------+
| id | status |
+------+-----------+
| 1000 | RE |
| 1000 | RE |
| 1000 | RE |
| 2000 | RE |
| 2000 | RE |
+------+-----------+
What i want:
+------+-----------+
| id | status |
+------+-----------+
| 2000 | RE |
| 2000 | RE |
+------+-----------+
Couldn't figure out how to do eliminate the whole join if the record 'CO' exist.
You could use not exists:
SELECT A.id,B.status
FROM TableA A
INNER JOIN TableB B on (A.TableA_ID=B.TableA_ID)
WHERE NOT EXISTS (
SELECT null
FROM TableB B
WHERE B.TableA_ID=A.TableA_ID
AND B.status in ('CO','CL')
);
Or if you only want to hit the tables once you could use an analytic count of the of the statuses you don't want to see, and eliminate any IDs with a non-zero count:
SELECT id, status
FROM (
SELECT A.id,B.status,
COUNT(case when B.status in ('CO','CL') then 1 end)
OVER (partition by A.id) AS cnt
FROM TableA A
INNER JOIN TableB B on (A.TableA_ID=B.TableA_ID)
)
WHERE cnt = 0;
db<>fiddle
This assumes A.id and A.TableA_ID are different columns; if they're the same then you don't need to look at table A directly at all, if you only want those two columns anyway - all of the information you need is in table B anyway.
here is one way :
SELECT A.id,B.status
FROM TableA A
INNER JOIN TableB B on (A.TableA_ID=B.TableA_ID)
WHERE not exists ( select 1 from TableB B where A.TableA_ID=B.TableA_ID and B.status in ('CO','CL')

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.

SQL JOIN two table & show all rows for table A

I have a question about JOIN.
TABLE A | TABLE B |
-----------------------------------------|
PK | div | PK | div | val |
-----------------------------------------|
A | a | 1 | a | 10 |
B | b | 2 | a | 100 |
C | c | 3 | c | 9 |
------------------| 4 | c | 99 |
-----------------------
There are two tables something like above, and I have been trying to join two tables but I want to see all rows from TABLE A.
Something like
SELECT T1.PK, T1.div, T2.val
FROM A T1
LEFT OUTER JOIN B T2
ON T1.div = T2.div
and I want the result would look like this below.
PK | div | val |
-------------------------
A | a | 10 |
A | a | 100 |
B | null | null |
C | c | 9 |
C | c | 99 |
I have tried all JOINs I know but B doesn't appear because it doesn't exist. Is it possible to show all rows on TABLE A and just show null if it doesn't exists on TABLE B?
Thanks in advance!
If you change your query to
SELECT T1.PK, T2.div, T2.val
FROM A T1
LEFT OUTER JOIN B T2
ON T1.div = T2.div
(Note, that div comes from T2 here.), you'll get exactly the result posted (but maybe in a different order, add an ORDER BY clause if you want a specific order).
Your query as it stands will get you:
PK | div | val |
-------------------------
A | a | 10 |
A | a | 100 |
B | b | null |
C | c | 9 |
C | c | 99 |
(Note, that div is b for the row with the PK of B, not null.)
To get to your resultset, all you need to do is use T2.Div as that is the value that does not exist in the second table:
SELECT T1.PK, T2.div, T2.val
FROM A T1
LEFT OUTER JOIN B T2
ON T1.div = T2.div

Postgres group by columns and within group select other columns by max aggregate

This is probably a standard problem, and I've keyed off some other greatest-n-per-group answers, but so far been unable to resolve my current problem.
A B C
+----+-------+ +----+------+ +----+------+-------+
| id | start | | id | a_id | | id | b_id | name |
+----+-------+ +----+------+ +----+------+-------+
| 1 | 1 | | 1 | 1 | | 1 | 1 | aname |
| 2 | 2 | | 2 | 1 | | 2 | 2 | aname |
+----+-------+ | 3 | 2 | | 3 | 3 | aname |
+----+------+ | 4 | 3 | bname |
+----+------+-------+
In English what I'd like to accomplish is:
For each c.name, select its newest entry based on the start time in a.start
The SQL I've tried is the following:
SELECT a.id, a.start, c.id, c.name
FROM a
INNER JOIN (
SELECT id, MAX(start) as start
FROM a
GROUP BY id
) a2 ON a.id = a2.id AND a.start = a2.start
JOIN b
ON a.id = b.a_id
JOIN c
on b.id = c.b_id
GROUP BY c.name;
It fails with errors such as:
ERROR: column "a.id" must appear in the GROUP BY clause or be used in an aggregate function Position: 8
To be useful I really need the ids from the query, but cannot group on them since they are unique. Here is an example of output I'd love for the first case above:
+------+---------+------+--------+
| a.id | a.start | c.id | c.name |
+------+---------+------+--------+
| 2 | 2 | 3 | aname |
| 2 | 2 | 4 | bname |
+------+---------+------+--------+
Here is a Sqlfiddle
Edit - removed second case
Case 1
select distinct on (c.name)
a.id, a.start, c.id, c.name
from
a
inner join
b on a.id = b.a_id
inner join
c on b.id = c.b_id
order by c.name, a.start desc
;
id | start | id | name
----+-------+----+-------
2 | 2 | 3 | aname
2 | 2 | 4 | bname
Case 2
select distinct on (c.name)
a.id, a.start, c.id, c.name
from
a
inner join
b on a.id = b.a_id
inner join
c on b.id = c.b_id
where
b.a_id in (
select a_id
from b
group by a_id
having count(*) > 1
)
order by c.name, a.start desc
;
id | start | id | name
----+-------+----+-------
1 | 1 | 1 | aname

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