SQL Join instead of UNION - sql

I have the following tables:
Table T1
--------------------------
ZID | NAME
------------------------
1 | Test
Table C1
--------------------------
ZID | VALUE1
------------------------
1 | A
1 | B
Table S1
--------------------------
ZID | VALUE2
------------------------
1 | C
1 | D
I would like a select query to fetch the values below
----------------------------------
ZID | NAME | VALUE1 | VALUE2
----------------------------------
1 | Test | A | null
1 | Test | B | null
1 | Test | null | C
1 | Test | null | D
I am able to achieve the above by using a UNION by faking a column in each select as below.
SELECT
ZID,
NAME,
VALUE1,
NULL AS VALUE2
FROM
T1
LEFT JOIN C1 ON (
T1.ZID = C1.ZID
)
UNION ALL
(
SELECT
ZID,
NAME,
NULL AS VALUE1,
VALUE2
FROM
T1
LEFT JOIN S1 ON (
T1.ZID = S1.ZID
)
);
Would it be possible to retrieve data as above in a single select using JOINS? I have tried using left join and however, I end up with a Cartesian product of data in the results.

You could use:
select t1.*, c1.value1, s1.value2
from c1 full join
s1
on false join
t1
on t1.id in (c1.id, s1.id)
A full join on false is a lot like union all.

Query will be something like:
SELECT ZID, NAME, VALUE1, VALUE2
FROM T1, C1, S1;

Union will give you more rows. Join will give you more columns. If you want to join them to get one set of rows with all columns, you need to specify which rows go together. For example, if you do not specify which C1 row goes with which S1 row, it matches each to all of them, and gives the product you do not want.
Define the relationship in the on clause, and it will do what you want.

You need the UNION to add rows. You can put the join on the outside of the UNION to avoid having to do it twice:
SELECT
ZID,
NAME,
VALUE1,
NULL AS VALUE2
FROM
T1
LEFT JOIN (
SELECT ZID, VALUE1, NULL AS VALUE2
FROM C1
UNION ALL
SELECT ZID, NULL AS VALUE1, VALUE2
FROM S1
) Merged ON T1.ZID = Merged.ZID

Related

How to join two tables and show source?

How to join values from two tables ...
Table_1:
ID | Value
----------
10 | Dog
27 | Cat
Table_2:
ID | Value
----------
27 | Cat
My SQL... (Microsoft Access 2016)
SELECT ID, VALUE , "YES" AS Table_1, NULL AS Table_2
FROM Table_1
UNION
SELECT ID, VALUE, NULL AS Table_1, "YES" AS Table_2
FROM Table_2
...returns this result:
ID | Value | Table_1 | Table_2
------------------------------
10 | Dog | YES |
27 | Cat | YES |
27 | Cat | | YES
But I would like to get a result like this:
ID | Value | Table_1 | Table_2
------------------------------
10 | Dog | YES |
27 | Cat | YES | YES
You can use aggregation and union all:
select id, value, max(table_1) as table_1, max(table_2) as table_2
from (select ID, VALUE , "YES" AS Table_1, NULL AS Table_2
from Table_1
union
select ID, VALUE, NULL AS Table_1, "YES" AS Table_2
from Table_2
) t
group by id, value;
The alternative in SQL is a FULL JOIN, but MS Access does not support full joins.
You can make a list of table1 + table2 values and then check if the values exists in table2 or 2 using simple jeft join.
select
base.*,
iif(isnull(t.value), null, 'YES') table1,
iif(isnull(t2.value), null, 'YES') table2
from
(
Select value from Table_1 -- if you have duplicate values add groupby here
union
select value from Table_2
) base -- Make a collection of values from table1 and 2
left join Table_1 T on base.value = T.value
left join Table_2 T2 on base.value = T2.value;
Not sure if you can open above query in design view but the query should work in access. to be design view friendly, you need to make a query object for the base and then you can simply do left joins and iifs

Select ALL records with fieldvalue1 if any records (with fieldvalue1) have fieldvalue3 have a specific value

I need to show all records for a specific value if ANY one of those records have another specific value. Essentially, if field3 = 'b', what is field1? Show all records with value of field1 regardless of their field3 value.
Record Number External Id Letter
1 123456 a
2 123456 b
3 123456 c
4 456852 t
5 456852 b
record 2 has a letter value of 'b' - so I want to look at externalid, which is 123456, now I want to pull all records for external id regardless if the other records have a letter value of 'b'
Use EXISTS and a correlated subquery:
SELECT *
FROM mytable t
WHERE
t.letter = 'b'
OR EXISTS (
SELECT 1
FROM mytable t1
WHERE
t1.record_number != t.record_number
AND t1.external_id = t.external_id
AND t1.letter = 'b'
)
Another option is to use a window function:
SELECT record_number, external_id, letter
FROM (
SELECT
t.*,
MAX(CASE WHEN letter = 'b' THEN 1 END) OVER(PARTITION BY external_id) mx
FROM mytable t
) x WHERE mx = 1
Demo on DB Fiddle:
record_number | external_id | letter
------------: | ----------: | :-----
1 | 123456 | a
2 | 123456 | b
3 | 123456 | c
4 | 456852 | t
5 | 456852 | b
Use exists, but don't worry about filtering in the outer query:
select t.*
from t
where exists (select 1
from t t2
where t2.external_id = t.external_id and t2.letter = 'b'
);
With an index on (external_id, letter), I would expect this to have very good performance.

How to do an outer join with full result between two tables

I have two tables:
TABLE1
id_attr
-------
1
2
3
TABLE2
id | id_attr | val
----------------------
10 | 1 | A
10 | 2 | B
As a result I want a table that show:
RESULT
id | id_attr | val
----------------------
10 | 1 | A
10 | 2 | B
10 | 3 | NULL
So I want the row with id=10 and id_attr=3 also when id_Attr=3 is missing in TABLE2 (and I know that because I have a NULL value (or something else) in the val column of RESULT.
NB: I could have others ids in table2. For example, after insert this row on table2: {11,1,A}, as RESULT I want:
id | id_attr | val
----------------------
10 | 1 | A
10 | 2 | B
10 | 3 | NULL
11 | 1 | A
11 | 2 | NULL
11 | 3 | NULL
So, for every id, I want always the match with all id_attr.
Your specific example only has one id, so you can use the following:
select t2.id, t2.id_attr, t2.val
from table2 t2
union all
select 10, t1.id_attr, NULL
from table1 t1
where not exists (select 1 from table2 t2 where t2.id_attr = t1.id_attr);
EDIT:
You can get all combinations of attributes and ids in the following way. Use a cross join to create all the rows you want and then a left join to bring in the data you want:
select i.id, t1.id_attr, t2.val
from (select distinct id from table2) i cross join
table1 t1 left join
table2 t2
on t2.id = i.id and t2.id_attr = t1.id_attr;
It sounds like you want to do just an outer join on id_attr instead of id.
select * from table2 t2
left outer join table1 t1 on t2.id_attr = t1.id_attr;

How to add a column to a row in a select

Say I have this table
| Col |
-------
| ABC |
| DEF |
What query should I write to obtain this result (not literally this result, but a general way to do that)?
| Col | Col2 |
--------------
| ABC | 0 |
| ABC | 1 |
| DEF | 0 |
| DEF | 1 |
Unless I'm missing something, this should give you the results you're looking for:
Select Col, Col2
From YourTable
Cross Join (Select 0 As Col2 Union Select 1 As Col2) X
Order By Col, Col2
I would guess that you want to pair two columns, for each combination. Your question is vague and not specific to a problem. That's my assumption.
I guess this query could do:
Select Table1.Col1, Table2.Col2 from Table1 LEFT JOIN Table2 on 1=1
This way, you pair up every row from table1 with every row from table2.
Edit, without table2:
Select Table1.Col1, Constructed.Col1 from Table1 LEFT JOIN
(Select 1 as Col1 UNION Select 2 as Col1 UNION
Select 7 as Col1 UNION Select 14 as Col1) Constructed on 1=1
Can you test query, is this what you want?
select * from
(select col1, 0 b from table) table1
union all (select col1, 1 b from table) order by 1;

dynamic selection in SQL aggregate function

Imagine I have a column with row data A, B, and C. When aggregating the column, I want B to show up if any row contains B, else C, else A.
Is this possible? I'm using vertica but would be curious to hear solutions for other data stores.
+------+-------+
| Col1 | Col 2 |
+------+-------+
| A | X |
| B | X |
| C | X |
+------+-------+
SELECT ???(Col1), Col2
FROM my_two_column_table
GROUP BY Col2
expected result: one row - B, X
Something like this could work:
SELECT COALESCE( MAX(t2.value), MAX(t3.value), MAX(t4.value) )
FROM my_table t1
LEFT OUTER JOIN my_table t2 ON t2.value = 'B'
LEFT OUTER JOIN my_table t3 ON t3.value = 'C'
LEFT OUTER JOIN my_table t4 ON t4.value = 'A'
WHERE t1.value in ( 'A', 'B', 'C' )
The t1 table can also be replaced by dual or whatever dummy table with at least one row you have.