How to join two tables and show source? - sql

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

Related

SQL join 2 rows into 1 with conditions

I have a table with updated and inserted values. So, most IDs have 2 rows, one for insert, other for update (there is an "operator" column which has value UPDATED or value INSERTED). Sample data:
operator | ID | row2 | row3
===========================
updated | 01 | | 231
===========================
inserted | 01 | abc | 123
===========================
updated | 02 | khj | 567
===========================
inserted | 02 | klo | 567
===========================
inserted | 03 | nmb | 900
My task is to join these 2 rows in 1 grouping them by their ID. But, all values have to be from the "update" row AND if there are some NULL values in the "update", they must be taken from the "insert" row.
Desired results:
ID | row2 | row3
==================
01 | abc | 231
==================
02 | khj | 567
==================
03 | nmb | 900
==================
The goal is to have all distinct IDs with the newest data.
Does anyone have any idea how to do this?
I have tried implementing the following logic, but it doesn't return me the newest data:
SELECT
ID,
MAX(Field1) AS Field1,
MAX(Field2) AS Field2
FROM
table_name
GROUP BY
ID;
You may try this -
Select
u.ID,
NVL(u.row2, i.row2),
NVL(u.row3, i.row3) -- and so on for more columns
from
(Select * from tableName where operator = 'updated') u,
(Select * from tableName where operator = 'inserted') i
where u.ID = i.ID;
If your table has data such that there isn't any record with 'updated', you would need LEFT OUTER JOIN, so you may use below query then -
Select
i.ID,
NVL(u.row2, i.row2),
NVL(u.row3, i.row3) -- and so on for more columns
from
(Select * from tableName where operator = 'updated') u,
(Select * from tableName where operator = 'inserted') i
where u.ID(+) = i.ID;
If I understand correctly, you want to select rows, with update first and then the insert if there is no update. So one method is:
select t.*
from table_name t
where t.operator = 'UPDATED' or
not exists (select 1
from table_name t2
where t2.id = t.id and t2.operator = 'UPDATED'
);
you can do it this way:
SELECT
a.*
FROM
table_name a
WHERE a.operator = 'UPDATED'
UNION ALL
SELECT
b.*
FROM
table_name b
WHERE b.operator = 'INSERTED'
AND b.ID NOT IN
(SELECT
c.ID
FROM
table_name c
WHERE c.operator = 'UPDATED'
AND c.ID = b.ID)
Note: If you want or need to remove duplicates, you can replace: UNION ALL with UNION,
graciously.

SQL - How to pick the best available value for each column for each ID from multiple tables?

I have two tables with the same variables referring to attributes of a person.
How can I combine data from two such tables picking the best available value for each column from each table for each field?
Requirements:
For each field, I would like to fill it with a value from either one of the tables, giving a preference to table 1.
Values can be NULL in either table
In the combined table, the value for column 1 could come from table 2 (in case table 1 is missing a value for that person) and the value for column 2 could from table 1 (because both tables had a value, but the value from table 1 is preferred).
In my real example, I have many columns, so an elegant solution with less code duplication would be preferred.
Some users may exist in only one of the tables.
Example:
Table 1:
user_id | age | income
1 | NULL| 58000
2 | 22 | 60000
4 | 19 | 35000
Table 2:
user_id | age | income
1 | 55 | 55000
2 | 19 | NULL
3 | 22 | 33200
Desired output:
user_id | age | income
1 | 55 | 58000
2 | 22 | 60000
3 | 22 | 33200
4 | 19 | 35000
I think that's a full join and priorization logic with colaesce():
select user_id,
coalesce(t1.age, t2.age) as age,
coalesce(t1.income, t2.income) as income
from table1 t1
full join table2 t2 using(user_id)
Use full outer join if user_id in each table is unique.
SELECT
COALESCE(t1.user_id, t2.user_id) AS user_id,
GREATEST(t1.age, t2.age) AS age,
GREATEST(t1.income, t2.income) AS income
FROM t1
FULL OUTER JOIN t2 ON t1.user_id = t2.user_id
try like below using coalesce()
select t1.user_id, coalesce(t1.age,t2.age),
t1.income>t2.income then t1.income else t2.income end as income
table1 t1 join table2 t2 on t1.usesr_id=t2.user_id
You can use below code:
With TableA(Id,age,income) as
( --Select Common Data
select table_1.id,
--Select MAX AGE
case
when table_1.age> table_2.age or table_2.age is null then table_1.age else table_2.age
end,
--Select MAX Income
case
when table_1.income>table_2.income or table_2.income is null then table_1.income else table_2.income
end
from table_1 inner join table_2 on table_2.id=table_1.id
union all
-- Select Specific Data of Table 2
select table_2.id,table_2.age,table_2.income
from table_2
where table_2.id not in (select table_1.id from table_1)
union all
-- Select Specific Data of Table 1
select table_1.id,table_1.age,table_1.income
from table_1
where table_1.id not in (select table_2.id from table_2)
)select * from TableA

Join tables with same keys, second table has multiple values for key and rows of second table must have same column value

I have two tables with shared key and I'm trying to join them to filter data based on few conditions
tbl1
id | OutPutValue |
1 | 2019 |
2 | 2018 |
tbl2
object_id | status | type |
1 | 22 | a |
1 | 22 | c |
1 | 33 | b |
2 | 33 | c |
2 | 33 | c |
2 | 33 | c |
What I'm trying to get is : it must select all 'OutPutValue' from tbl1 where, in tbl2 column 'type' should be c, and column 'status' must have same value for all rows i.e. 33. Note that Primary key (id) of tbl1 is foreign key (object_id) in tbl2.
Select column from tbl1 if, All rows in tbl2 (id of tbl1 have multiple rows (based on object_id) in tbl2) have same status value i.e. 33 and Type should be 'c'.
OutPutValue | Type | status |
2018 | c | 33 |
I have tried with following solution, but it's not returning desired output :
SELECT a.OutPutValue FROM tbl1 a JOIN tbl2 b ON a.id = b.object_id WHERE b.Type =c
GROUP BY a.OutPutValue, b.status HAVING b.STATUS IN(33)
You can try using correlated subquery
DEMO
select distinct OutPutValue,type, status
from t2 a inner join t1 b on a.object_id=b.id
where type='c' and not exists
(select 1 from t2 a1 where a.object_id=a1.object_id and status<>33 and type='c')
OUTPUT:
OutPutValue type status
2018 c 33
Another solution could be the following :
SELECT T1.id, T1.outputvalue FROM tbl1 T1
JOIN (
SELECT tbl2.*, MAX(type), MAX(status)
FROM tbl2
GROUP BY object_id
HAVING
MIN(status) = MAX(status) AND
MIN(type) = MAX(type)
) T2 ON T1.id = T2.object_id
WHERE T2.type = 'c'
EDIT: I have updated my query to match a particular case which make it quite similar to another answer.
FIND A DEMO HERE
Try a join combined with an aggregation:
SELECT
t1.OutPutValue,
MAX(t2.type) AS type,
MAX(t2.status) AS status
FROM tbl1 t1
INNER JOIN tbl2 t2
ON t1.id = t2.object_id
GROUP BY
t1.id,
t1.OutPutValue
HAVING
MIN(t2.status) = MAX(t2.status) AND
MAX(t2.status) = 33 AND
MIN(t2.type) = MAX(t2.type) AND
MAX(t2.type) = 'c';

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;

SQL right join, force return only one value from right hand side

table 1
---
id , name
table2
---
id , activity, datefield
table1 'right join' table2 will return more than 1 results from right table (table2) . how to make it return only "1" result from table2 with the highest date
You write poor information about your problem, But I'll try to make an example to help you.
You have a table "A" and a table "B" and you need to fetch the "top" date of table "B" that is related with table "A"
Example tables:
Table A:
AID| NAME
----|-----
1 | Foo
2 | Bar
Table B:
BID | AID | DateField
----| ----| ----
1 | 1 | 2000-01-01
2 | 1 | 2000-01-02
3 | 2 | 2000-01-01
If you do this sql:
SELECT * FROM A RIGHT JOIN B ON B.ID = A.ID
You get all information of A and B that is related by ID (that in this theoretical case is the field that is common for both tables to link the relation)
A.AID | A.NAME | B.BID | B.AID | B.DateField
------|--------|-------|-------|--------------
1 | Foo | 1 | 1 | 2000-01-01
1 | Foo | 2 | 1 | 2000-01-02
2 | Bar | 3 | 2 | 2000-01-01
But you require only the last date for each element of the Table A (the top date of B)
Next if you need to get only the top DATE you need to group your query by the B.AID and fetch only the top date
SELECT
B.AID, First(A.NAME), MAX(B.DateField)
FROM
A RIGHT JOIN B ON B.ID = A.ID
GROUP BY
B.AID
And The result of this operation is:
B.AID | A.NAME | B.DateField
------|--------|--------------
1 | Foo | 2000-01-02
2 | Bar | 2000-01-01
In this result I removed some fields that are duplicated (like A.AID and B.AID that is the relationship between the two tables) or are not required.
Tip: this also works if you have more tables into the sql. The sql "makes" the query and next applies a grouping for using the B to limit the repetitions of B to the top date.
right join table2 on on table1.id to to select id, max = max(date) from table2
Analytics!
Test data:
create table t1
(id number primary key,
name varchar2(20) not null
);
create table t2
(id number not null,
activity varchar2(20) not null,
datefield date not null
);
insert into t1 values (1, 'foo');
insert into t1 values (2, 'bar');
insert into t1 values (3, 'baz');
insert into t2 values (1, 'foo activity 1', date '2009-01-01');
insert into t2 values (2, 'bar activity 1', date '2009-01-01');
insert into t2 values (2, 'bar activity 2', date '2010-01-01');
Query:
select id, name, activity, datefield
from (select t1.id, t1.name, t2.id as t2_id, t2.activity, t2.datefield,
max(datefield) over (partition by t1.id) as max_datefield
from t1
left join t2
on t1.id = t2.id
)
where ( (t2_id is null) or (datefield = maxdatefield) )
The outer where clause will filter out all but the maximum date from t2 tuples, but leave in the null row where there was no matching row in t2.
Results:
ID NAME ACTIVITY DATEFIELD
---------- -------- ------------------- -------------------
1 foo foo activity 1 2009-01-01 00:00:00
2 bar bar activity 2 2010-01-01 00:00:00
3 baz
To retrieve the Top N records from a query, you can use the following syntax:
SELECT *
FROM (your ordered by datefield desc query with join) alias_name
WHERE rownum <= 1
ORDER BY rownum;
PS: I am not familiar with PL/SQL so maybe I'm wrong
my solution is
select from table1 right join table2 on (table1.id= table2.id and table2.datefiled= (select max(datefield) from table2 where table2.id= table1.id) )