SQL inner join with conditional selection - sql

I am new in SQL. Lets say I have 2 tables one is table_A and the other one is table_B. And I want to create a view with two of them which is view_1.
table_A:
id
foo
1
d
2
e
null
f
table_B
id
name
1
a
2
b
3
c
and when I use this query :
SELECT DISTINCT table_A.id, table_B.name
FROM table_A
INNER JOIN table_B ON table_B.id = table_A.id
the null value in table_A can't be seen in the view_1 since it is not found in table_B. I want view_1 to show also this null row like :
id
name
1
a
2
b
null
no entry
Should I create a 4. table? I couldn't find a way.

Try this Query:
SELECT DISTINCT a.id,(CASE When b.name IS NULL OR b.name = '' Then 'No Entry' else b.name end) name FROM table_A a
LEFT JOIN table_B b on a.id = b.id

You are looking for an outer join. Thus you keep all table_A rows and join table_B rows where they exist. If no match exists, the table_B columns in the joined row are NULL.
You replace NULLs with a value with COALESCE.
SELECT a.id, COALESCE(b.name, 'no entry') AS name
FROM table_a a
LEFT OUTER JOIN table_b b ON b.id = a.id
ORDER BY a.id NULLS LAST;
You haven't tagged your request with your DBMS. Not all DBMS support the NULLS LAST clause.
Please note that there is no DISTINCT in my query. It is not needed. And every time you think you must use DISTINCT, think twice. SELECT DISTINCT is very seldom needed. Most often it is used, because the query is kind of flawed and causes the undesired duplicates itself.

Related

SQL Get rows that doesn't appear in another table

I have this SQL problem: I have tables A and B. Table A has columns id and name, Table B amount and id which is a foreign key to table A.id.
I need to return all table A rows that don't have their id stored in table B. Any ideas?
So the complete opposite is:
SELECT *
FROM a
LEFT OUTER JOIN b ON a.id = b.id;
Here row what I need is left out of result
Just add a where clause:
SELECT a.*
FROM a LEFT OUTER JOIN
b
ON a.id = b.id
WHERE b.id IS NULL;
You can also use NOT EXISTS:
select a.*
from a
where not exists (select 1 from b where b.id = a.id);
In most databases, the two methods typically have similar performance.

Why does this query return values in the column

I have the following query:
SELECT
a.id,
c.c_date
FROM table_a a ,
table_c c
WHERE
a.id = c.id AND
a.id IN (SELECT id from table_c where c_date is null);
I have two tables, table_a and table_c.
I join these two tables, but then get use an IN statement to only show the id's for in which are in table_c AND have the c_date column set to null`.
This query though returns id, and c_date values, and some of the c_date values are not null, how is this possible?
I thought in my sub query I am only selecting id which have null c_dates?
This should work without the subquery assuming you don't want to return null dates. Please note the use of the join:
SELECT a.id,
c.c_date
FROM table_a a
JOIN table_c c ON a.id = c.id
WHERE c_date is null;
It's difficult to answer your specific question though without sample data and expected results. You probably have multiple records in table_c that match the id field in table_a.
It will be easier to explain with this example:
table_a
id col_x col_...
1
2
table_c
id c_date col_m col_...
1 null
1 03/14/2016
2 04/14/2016
You should consider review your intention on your result set. Change your query to #sgeddes answer is a way.

SQL Query Duplicating records

I've got two tables.
Let's call them table_A and table_B.
Table_B contains the ForeignKey of table_A.
Table_A
ID Name
1 A
2 B
3 C
Table_B
ID table_a_fk
1 2
2 3
Now I want to get all the names out of table_a IF table_b does not contain the ID of the record in table_a.
I've tried it with this query:
SELECT a.name
FROM table_a a, table_b b
WHERE a.id != b.table_a_fk
With this Query I'm getting the right result I just get this result like 5times and I don't know why.
Hope someone can explain me that.
Your query creates a cartesian product between your two tables A and B. It is the cartesian product that generates those duplicate values. Instead, you want to use an anti-join, which is most commonly written in SQL using NOT EXISTS
SELECT a.name
FROM table_a a
WHERE NOT EXISTS (
SELECT *
FROM table_b b
WHERE a.id = b.table_a_fk
)
Another way to express an anti-join with NOT IN (only if table_b.table_a_fk is NOT NULL):
SELECT a.name
FROM table_a a
WHERE a.id NOT IN (
SELECT b.table_a_fk
FROM table_b b
)
Another, less common way to express an anti-join:
SELECT a.name
FROM table_a a
LEFT OUTER JOIN table_b b ON a.id = b.table_a_fk
WHERE b.id IS NULL
use distinct
SELECT distinct a.name
FROM table_a a, table_b b
WHERE a.id != b.table_a_fk
or better is...
Select distinct name
from tableA a
Where not exists (Select * from tableB
Where table_a_fk = a.id)

Oracle SQL WITH clause select joined column

SQL:
WITH joined AS (
SELECT *
FROM table_a a
JOIN table_b b ON (a.a_id = b.a_id)
)
SELECT a_id
FROM joined
returns invalid identifier.
How can you select joined column when using WITH clause? I have tried aliases, prefixing and nothing worked. I know I can use:
WITH joined AS (
SELECT a.a_id
FROM table_a a
JOIN table_b b ON (a.a_id = b.a_id)
)
SELECT a_id
FROM joined
but I need this alias to cover all fields.
Only way I managed to meet this condition is using:
WITH joined AS (
SELECT a.a_id a_id_alias, a.*, b.*
FROM table_a a
JOIN table_b b ON (a.a_id = b.a_id)
)
SELECT a_id_alias
FROM joined
but it is not perfect solution...
You can use the effect of the USING clause when joining the tables.
When you join tables where the join columns have the same name (as it is the case with your example), the USING clause will return the join column only once, so the following works:
with joined as (
select *
from table_a a
join table_b b using (a_id)
)
select a_id
from joined;
SQLFiddle example: http://sqlfiddle.com/#!4/e7e099/2
I don't think you can do this without aliases. The result of the "joined" query has two fields, both named a_id. Unless you alias one (or both), as you did in your final query, the outer query has no idea which a_id you are referring to.
Why is your final query not a "perfect" solution?
You can probably use alias as below:
WITH JOINED AS (
SELECT A.A_ID A_A_ID, B.A_ID B_A_ID,
A.FIELD_NAME1 A_FIELDNAME1, A.FIELDNAME2 A_FIELDNAME2,A.FIELDNAME_N A_FIELDNAME_N,
B.FIELD_NAME1 B_FIELDNAME1, B.FIELDNAME2 B_FIELDNAME2,B.FIELDNAME_N B_FIELDNAME_N,
FROM TABLE_A A
JOIN TABLE_B B ON (A.A_ID = B.A_ID)
)
SELECT A_A_ID, B_A_ID
FROM JOINED
IT IS ALWAYS A GOOD PRACTICE TO AVOID USING SELECT *

SQL LEFT outer join with only some rows from the right?

I have two tables TABLE_A and TABLE_B having the joined column as the employee number EMPNO.
I want to do a normal left outer join. However, TABLE_B has certain records that are soft-deleted (status='D'), I want these to be included. Just to clarify, TABLE_B could have active records (status= null/a/anything) as well as deleted records, in this case i don't want that employee in my result. If however there are only deleted records of the employee in TABLE_B i want the employee to be included in the result.I hope i'm making my requirement clear. (I could do a lengthy qrslt kind of thingy and get what I want, but I figure there has to be a more optimized way of doing this using the join syntax). Would appreciate any suggestions(even without the join). His newbness is trying the following query without the desired result:
SELECT TABLE_A.EMPNO
FROM TABLE_A
LEFT OUTER JOIN TABLE_B ON TABLE_A.EMPNO = TABLE_B.EMPNO AND TABLE_B.STATUS<>'D'
Much appreciate any help.
Just to clarify -- all records from TABLE_A should appear, unless there are rows in table B with statues other than 'D'?
You'll need at least one non-null column on B (I'll use 'B.ID' as an example, and this approach should work):
SELECT TABLE_A.EMPNO
FROM TABLE_A
LEFT OUTER JOIN TABLE_B ON
(TABLE_A.EMPNO = TABLE_B.EMPNO)
AND (TABLE_B.STATUS <> 'D' OR TABLE_B.STATUS IS NULL)
WHERE
TABLE_B.ID IS NULL
That is, reverse the logic you might think -- join onto TABLE_B only where you have rows that would exclude TABLE_A entries, and then use the IS NULL at the end to exclude those. This means that only those which didn't match (those with no row in TABLE_B, or with only 'D' rows) get included.
An alternative might be
SELECT TABLE_A.EMPNO
FROM TABLE_A
WHERE NOT EXISTS (
SELECT * FROM TABLE_B
WHERE TABLE_B.EMPNO = TABLE_A.EMPNO
AND (TABLE_B.STATUS <> 'D' OR TABLE_B.STATUS IS NULL)
)
The following query will get you the employee records that aren't deleted, or only the employ only has deleted records.
select
a.*
from
table_a a
left join table_b b on
a.empno = b.empno
where
b.status <> 'D'
or (b.status = 'D' and
(select count(distinct status) from table_b where empno = a.empno) = 1)
This is in ANSI SQL, but if I knew your RDBMS, I could give a more specific solution that may be a bit more elegant.
ah crud, this apparently works ><
SELECT TABLE_A.EMPNO
FROM TABLE_A
LEFT OUTER JOIN TABLE_B ON TABLE_A.EMPNO = TABLE_B.EMPNO
where TABLE_B.STATUS<>'D'
If you guys have any extra info to chime in with though, please feel free.
UPDATE:
Saw this question after sometime and thought i'll add more helpful info: This link has good info regarding ANSI syntax - http://www.oracle-base.com/articles/9i/ANSIISOSQLSupport.php
In particular this part from the linked page is informative:
Extra filter conditions can be added to the join to using AND to form a complex join. These are often necessary when filter conditions are required to restrict an outer join. If these filter conditions are placed in the WHERE clause and the outer join returns a NULL value for the filter column the row would be thrown away. if the filter condition is coded as part of the join the situation can be avoided.
SELECT A.*, B.*
FROM
Table_A A
INNER JOIN Table_B B
ON A.EmpNo = B.EmpNo
WHERE
NOT EXISTS (
SELECT *
FROM Table_B X
WHERE
A.EmpNo = X.EmpNo
AND X.Status <> 'D'
)
I think this does the trick. The left join is not needed because you only want to include employees with all (and at least one) deleted rows.
This is how I understand the question. You need to include only those employees for which either of the following is true:
an employee has only (soft-)deleted rows in TABLE_B;
an employee has only non-deleted rows in TABLE_B;
an employee has no rows in TABLE_B at all.
In other words, if an employee has both deleted and non-deleted rows in TABLE_B, omit that employee, otherwise include them.
This is how I think it could be solved:
SELECT DISTINCT a.EMPNO
FROM TABLE_A a
LEFT JOIN TABLE_B b1 ON a.EMPNO = b1.EMPNO
LEFT JOIN TABLE_B b2 ON b1.EMPNO = b2.EMPNO
AND (b1.STATUS = 'D' AND (b2.STATUS <> 'D' OR b2 IS NULL) OR
b2.STATUS = 'D' AND (b1.STATUS <> 'D' OR b1 IS NULL))
WHERE b2.EMPNO /* or whatever non-nullable column there is */ IS NULL
Alternatively, though, you could use grouping:
SELECT a.EMPNO
FROM TABLE_A a
LEFT JOIN TABLE_B b ON a.EMPNO = b1.EMPNO
GROUP BY a.EMPNO
HAVING 0 IN (COUNT(CASE b.STATUS WHEN 'D' THEN 1 ELSE NULL END),
COUNT(CASE b.STATUS WHEN 'D' THEN NULL ELSE 1 END))