Query two tables by a condition from a third table - sql

Here is my case:
table a
table b
table c (type int)
if c.type = 1 select all rows in table a
if c.type = 2 select all rows in table b
Currently my solution is find all rows in 3 tables and handle result to get values but it's really bad.

You don't specify what the relationship is between the tables. The expression c.type refers to a rows, not to the entire table. So, let me assume that c.type = 1 means "there exists a row where c.type = 1".
The solution to this problem is then conditional union all:
select a.*
from tablea a
where exists (select 1 from tablec c where c.type = 1)
union all
select b.*
from tableb b
where exists (select 1 from tablec c where c.type = 2)
This assumes that the columns are the same in a and b. Otherwise, you need to specify the correct set of columns.

Related

Select from table where another value doesn't exist in any of the joined tables

I am trying to retrieve the IDs from a table in Oracle only IF another column value doesn't exist in any of the joined tables. Let me give you an example:
example
As you can see in the sketch, Table A is joined to tables B via the ID. I would like to get the IDs from Table A only if all statuses in any joined Table B DO NOT contain the value 2.
Here is my SQL statement:
SELECT ID FROM TABLE A
LEFT JOIN TABLE B
ON A.ID = B.REF_ID
WHERE B.STATUS NOT IN (2)
Unfortunately, I still get all IDs (which makes sense) and am not able to come up with a method to retrieve only the IDs without a certain value in the Status column of a joined table. Hence, I would only like to get ID 1, since all joined tables do not contain the value 2 in their Status.
Many thanks for any inputs.
Use aggregation
SELECT ID
FROM TABLE A LEFT JOIN
TABLE B
ON A.ID = B.REF_ID
GROUP BY A.ID
HAVING SUM(CASE WHEN B.STATUS IN (2) THEN 1 ELSE 0 END) = 0;
Or, simply use NOT EXISTS:
SELECT A.ID
FROM TABLE A
WHERE NOT EXISTS (SELECT 1
FROM B
WHERE A.ID = B.REF_ID AND B.STATUS IN (2)
);

Value present in more than one table

I have 3 tables. All of them have a column - id. I want to find if there is any value that is common across the tables. Assuming that the tables are named a.b and c, if id value 3 is present is a and b, there is a problem. The query can/should exit at the first such occurrence. There is no need to probe further. What I have now is something like
( select id from a intersect select id from b )
union
( select id from b intersect select id from c )
union
( select id from a intersect select id from c )
Obviously, this is not very efficient. Database is PostgreSQL, version 9.0
id is not unique in the individual tables. It is OK to have duplicates in the same table. But if a value is present in just 2 of the 3 tables, that also needs to be flagged and there is no need to check for existence in he third table, or check if there are more such values. One value, present in more than one table, and I can stop.
Although id is not unique within any given table, it should be unique across the tables; a union of distinct id should be unique, so:
select id from (
select distinct id from a
union all
select distinct id from b
union all
select distinct id from c) x
group by id
having count(*) > 1
Note the use of union all, which preserves duplicates (plain union removes duplicates).
I would suggest a simple join:
select a.id
from a join
b
on a.id = b.id join
c
on a.id = c.id
limit 1;
If you have a query that uses union or group by (or order by, but that is not relevant here), then you need to process all the data before returning a single row. A join can start returning rows as soon as the first values are found.
An alternative, but similar method is:
select a.id
from a
where exists (select 1 from b where a.id = b.id) and
exists (select 1 from c where a.id = c.id);
If a is the smallest table and id is indexes in b and c, then this could be quite fast.
Try this
select id from
(
select distinct id, 1 as t from a
union all
select distinct id, 2 as t from b
union all
select distinct id, 3 as t from c
) as t
group by id having count(t)=3
It is OK to have duplicates in the same table.
The query can/should exit at the first such occurrence.
SELECT 'OMG!' AS danger_bill_robinson
WHERE EXISTS (SELECT 1
FROM a,b,c -- maybe there is a place for old-style joins ...
WHERE a.id = b.id
OR a.id = c.id
OR c.id = b.id
);
Update: it appears the optimiser does not like carthesian joins with 3 OR conditions. The below query is a bit faster:
SELECT 'WTF!' AS danger_bill_robinson
WHERE exists (select 1 from a JOIN b USING (id))
OR exists (select 1 from a JOIN c USING (id))
OR exists (select 1 from c JOIN b USING (id))
;

Combining four tables in SQL Server

I have four tables Table A, Table B, Table C and Table D. The schema of all four tables are identical. I need to union these four tables in the following way:
If a record is present in Table A then that is considered in the output table.
If a record is present in Table B then it is considered in the output table ONLY if it is not present in Table A.
If a record is present in Table C then it is considered ONLY if it is not present in Table A and Table B.
If a record is present in Table D then it is considered ONLY if it is not present in Table A, Table B, and Table C.
Note -
Every table has a column which identifies the table itself for every record (I don't know if this is of any importance)
Records are identified based on a particular column - Column X which is not unique even within each table
You could do something like (only two cases shown but you should see how to extend this)
WITH CTE1 AS
(
SELECT 't1' as Source, X, Y
FROM t1
UNION ALL
SELECT 't2' as Source, X, Y
FROM t2
), CTE2 AS
(
SELECT *,
RANK() OVER (PARTITION BY X
ORDER BY CASE Source
WHEN 't1' THEN 1
WHEN 't2' THEN 2
END) As RN
FROM CTE1
)
SELECT X,Y
FROM CTE2
WHERE RN=1
I would be inclined to do this using not exists:
select a.*
from a
union all
select b.*
from b
where not exists (select 1 from a where a.x = b.x)
union all
select c.*
from c
where not exists (select 1 from a where a.x = c.x) and
not exists (select 1 from b where b.x = c.x)
union all
select d.*
from d
where not exists (select 1 from a where a.x = d.x) and
not exists (select 1 from b where b.x = d.x) and
not exists (select 1 from c where c.x = d.x);
If you have an index on the x column in each table, then this should be the fastest method.
This will work as long as there are no NULL columns, or if columns for a record that exists in table with higher precedence are NULL you can assume the same column will NULL in tables with lower precedence.
SELECT coalesce(a.column1, b.column1, c.column1, d.column1) column1
,coalesce(a.column2, b.column2, c.column2, d.column2) column2
,coalesce(a.column3, b.column3, c.column3, d.column3) column3
--...
,coalesce(a.columnN, b.columnN, c.columnN, d.columnN) columnN
FROM TableA a
FULL JOIN TableB b on b.ColumnX = a.ColumnX
FULL JOIN TableC c on c.ColumnX = a.ColumnX or c.ColumnX = b.ColumnX
FULL JOIN TableD d on d.ColumnX = a.ColumnX or d.ColumnX = b.ColumnX or d.ColumnX = c.ColumnX
If the NULL values matter, you can switch to a more-complicated (and likely slower) CASE version:
CASE WHEN a.columnX IS NOT NULL THEN a.column1
WHEN b.columnX IS NOT NULL THEN b.column1
WHEN c.columnX IS NOT NULL THEN c.column1
WHEN d.columnX IS NOT NULL THEN d.column1 END column1
Of course, you can mix and match, so columns that are not nullable can use the former syntax, and columns where NULL values matter use the latter.
Hopefully the purpose of this is to fix the broken schema and put this data all in the same table, where it belongs.
This might seem stupid, but if, by any chance, you can leave out the table-identifying column and you also want to eliminate duplicate records (from within one table) too then the most straightforward answer would be
select <all columns without table identifier> from tableA
union
select <all columns without table identifier> from tableB
union
select <all columns without table identifier> from tableC
...
This is exactly, what union was designed to do: add rows only if they do not already exist before.

SQL alter a column if exists

I have two tables. A and B. I'm trying to figure out a way to add a column in my select query that returns true or false as to whether or not there exists a record in B.
Table A
ID Title
1 A
2 B
3 C
4 D
5 E
Table B
ID Detail
3 foo
4 foo
4 bar
4 barfood
I want to basically "SELECT ID, Title, (Exists?) FROM A" to return
ID Title Exists
1 A False
2 B False
3 C True
4 D True
5 E False
Table A's ID column will always be unique. Table B's ID column can have zero, one, or many relating back to table A's ID. I don't care about the detail in table B, I just want to know if there is at least one record in table B that relates to table A's ID.
I'm new to SQL and I've been searching for ways to use 'if exists' or any other way to parse this out but I'm not really finding what I'm looking for.
If you're adding a column named 'Exists' temporary then try this
select a.id, a.title,case when a.id=b.id then 'True' else 'False' end as Exists
from A a left outer join B b
on a.id = b.id
If you've alredy added Exists column to table then
select a.id, a.title,Exists=(case when a.id=b.id then 'True' else 'False')
from A a left outer join B b
on a.id = b.id
There are probably more efficient ways to accomplish it, but a combination of count and a case statement will do the trick:
select ID, Title,
case when
(select count(1) from B where ID = A.ID) = 0 then 'False'
else 'True'
end as 'Exists'
from A
SQLFiddle link
if you left join table B then you'll get that information
select a.id, a.title,
case
when b.id is null then 'false'
else 'true'
end
from a
left outer join b on a.id = b.id
group by a.id, a.title

Hive SQL - Refining JOIN query to ignore Null values

I'm a little new with SQL so bear with me.
I have two tables, each with an ID column. Table A has a column titled role, Table B has a column titled outcome. I want to query these tables to find which rows based on the ID have role = 'PS' and outcome = 'DE'. Here is my code:
SELECT count(*)
FROM A JOIN B
ON (A.id = B.id
AND A.role = 'PS'
AND B.outcome = 'DE')
I've been searching the internet for a way to do this so that it doesn't include rows that have null values for either A.role or B.outcome.
The above code returns lets say 40,100, even though the total number of entries in B where B.outcome = 'DE' is only 40,000. So it is obviously including entries that do not fit my conditions. Is there a way to better refine my query?
Your query already excludes rows with a null value in A.role. After all, null = 'PS' is not true, and you're using an inner join.
There's an easy explanation of how you can retrieve more rows from the join than there are in B. Say you have these rows for A:
A.id A.role
1 'A'
1 'A'
And these rows for B:
B.id B.outcome
1 'A'
1 'A'
Then this query:
select *
from A
join B
on A.id = B.id and A.role = 'A' and B.role = 'A'
will return 4 rows. That's more than there are in table A or B!
So I'd investigate whether id is unique:
select count(*) from A group by id having count(*) > 1
select count(*) from B group by id having count(*) > 1
If these queries return a count greater than zero, id is not unique. Since a join repeats rows for each match, that would explain a large increase in the amount of returned records.