Only use data from TableA when TableB has no matching entry - sql

So i have:
Table A with (not only Name - Multiple Columns):
| IDA | Name |
|:-----------|------------:|
| 1 | John |
| 2 | Jonas |
| 3 | Carl |
| 4 | Foo |
| 5 | Bar |
| 6 | Dog |
Table B with (not only Name - Multiple Columns):
| IDB | Name |
|:-----------|------------:|
| 1 | Bob |
| 2 | Lisa |
Table MAIN with:
| ID | FKtoB | FKtoA |
|:-----------|------------:|------------:|
| 1 | 1 | 4 |
| 2 | | 3 |
| 3 | | 1 |
| 4 | 2 | 6 |
| 5 | | 2 |
| 6 | | 5 |
My goal is to select from MAIN and prefer to use the data of Table B but if not Available i will use the data of Table A. I tryed to use coalesce but that didn't help because i dont want to mix the data, so if for example Table B has the data for one row, then in this row there should be no data from Table A.
My Query looks like that:
select
coalesce(b.name, a.name) as surname, coalesce(b.surname, a.surname) as surname
from MAIN m
left join TableA a on a.IDA = m.FKtoA
left join TableB b on b.IDA = m.FKtoB
My Problem there is that if for example b.name is filled and b.surname isn't it would mix b.name with a.surname, thats not okay in my case.

Maybe you could add the non-existens of b as join criteria:
select
coalesce(b.name, a.name) as name, coalesce(b.surname, a.surname) as surname
from MAIN m
left join TableB b on b.IDA = m.FKtoB
left join TableA a on a.IDA = m.FKtoA and b.IDA is null
EDIT: corrected one surname to name

Try Left Join with Case -
SELECT
M.ID, (CASE WHEN B.IDB IS NOT NULL THEN B.Name ELSE A.Name END) AS NAME
FROM
MAIN AS M
LEFT JOIN TableB as B ON B.IDB = M.FKtoB
LEFT JOIN TableA as A ON A.IDA = M.FKtoA

From SQL Server 2012 (and higher) you can use IIF:
SELECT
IIF(m.FKtoB IS NULL, A.name, B.name) AS surname
FROM MAIN m
LEFT JOIN TableA a ON a.IDA = m.FKtoA
LEFT JOIN TableB b ON b.IDA = m.FKtoB

try this:
declare #tableA table(IDA INT,Name varchar(MAX))
declare #tableB table(IDB INT, Name VARCHAR(MAX))
declare #tableMain table(ID INT,FKtoB INT,FKtoA INT)
INSERT INTO #tableA
SELECT 1,'John'
union all
SELECT 2, 'Jonas'
union all
SELECT 3, 'Carl'
union all
SELECT 4,'Foo'
union all
SELECT 5 ,'Bar'
union all
SELECT 6,'Dog'
INSERT INTO #tableB
SELECT 1, 'Bob'
Union all
SELECT 2, 'Lisa'
INSERT INTO #tableMain
SELECT 1,1,4
union all
SELECT 2,null,3
union all
SELECT 3,null,1
union all
SELECT 4,2,6
union all
SELECT 5,null,2
union all
SELECT 6,null,5
Select tm.Id,ISNULL(tb.Name,ta.Name) As NAME from #tableMain tm
LEFT JOIN #tableB tb on tm.FktoB=tb.IDb and IDb is not NUll
LEFT JOIN #tableA tA on tm.FktoA=ta.IDA and FktoB is NUll

You want to select from table A only when there is no match in table B, so given foreign keys are intact:
select
coalesce(b.firstname, a.firstname) as firstname,
coalesce(b.surname, a.surname) as surname,
coalesce(b.birthday, a.birthday) as birthday,
...
from MAIN m
left join TableB b on b.IDB = m.FKtoB
left join TableA a on a.IDA = m.FKtoA and m.FKtoB is null;
Another approach would be union all:
select firstname, surname, birthday, ...
from TableB
where IDB in (select FKtoB from MAIN)
union all
select firstname, surname, birthday, ...
from TableA
where IDA in (select FKtoA from MAIN where FKtoB is null);

Try this. It will give result as you want.
(select ida as ID ,
case when ida is not null then ida end fkToIda,
case when idb is not null then idb end fkToIdB
from A
left outer join B on a.ida=b.idb)
union
(select idb ,
case when ida is not null then ida end,
case when idb is not null then idb end
from A
right outer join B on a.ida=b.idb)

Related

How to make a UNION (or JOIN) query between two tables where 1st has higher priority without duplicates?

I am trying to make a performant UNION (or JOIN) query between two tables:
tableA
+----+
| id |
+----+
| 1 |
| 2 |
| 7 |
+----+
tableB
+----+
| id |
+----+
| 1 |
| 2 |
| 9 |
+----+
Where it should produce the following output:
outputTable
+----+-----------+
| id | fromTable |
+----+-----------+
| 1 | TableA |
| 2 | TableA |
| 7 | TableA |
| 9 | TableB |
+----+-----------+
I want to reproduce the behavior of select tableA and then union (or join) the rows that aren't on tableA or by another words union tableA and tableB with fromTable priority on tableA. What's the best way to accomplish this (performance wise)?
You can use a full join. For example:
select
coalesce(a.id, b.id) as id,
case when a.id is null then 'TableB' else 'TableA' end as from_table
from a
full join b on a.id = b.id
Result:
id from_table
--- ----------
1 TableA
2 TableA
7 TableA
9 TableB
See fiddle.
you can try something like that:
select id, 'tableA'
from tableA
union all
select id, 'tableB'
from tableB
where tableB.id not in (select id from tableA)
You can get the ids of tableB that don't exist in tableA with EXCEPT:
SELECT id FROM tableB
EXCEPT
SELECT id FROM tableA
and your final query would be:
SELECT id, 'tableA' fromTable FROM tableA
UNION ALL
SELECT id, 'tableB'
FROM (
SELECT id FROM tableB
EXCEPT
SELECT id FROM tableA
) t
ORDER BY id;
or:
SELECT id, 'tableB' fromTable FROM tableB
EXCEPT
SELECT id, 'tableB' FROM tableA
UNION
SELECT id, 'tableA' FROM tableA
ORDER BY id;
See the demo.

Simple SQL Questions with join

i have some problems with join tables:
Table A -> ID,Col1,Col2,Col3
Table B -> Rank , ColX , A_ID (Relationship with A.ID)
I want to take higher Rank (each A_ID , like group by A_ID) of B table
my results must be something like A.ID , Col1 , Col2 , Col3 , ""ColX"" , how can i do that ?
and i want my result count equals to A.ID count.
TableA
+--------------------+
| ID|Col1|Col2|Col3| |
+--------------------+
| 1 | C1 | C2 | C3 |
| 2 | C1 | C2 | C3 |
+--------------------+
TABLE_B
+-----------------------------+
| ID| COL_X |RANK |A_ID| |
+-----------------------------+
| 1 | SomeValue | 1 | 1 |
| 2 | some22222 | 2 | 1 |
| 3 | SOMEXXXX | 3 | 1 |
| 4 | SOMEVAL | 1 | 2 |
| 5 | VALUE | 2 | 2 |
+-----------------------------+
Expected Output:
+--------------------------------------------------------------------+
| ID| Col1| Col2 | Col3| COLX |
+--------------------------------------------------------------------+
| 1 | C1 | C2 | C3 | SOMEXXXX (Higher Rank of TableB-> A_ID = 1) |
| 2 | C1 | C2 | C3 | VALUE (Higher Rank of TableB-> A_ID = 2) |
+--------------------------------------------------------------------+
You could easily do this using a subquery by first finding the max for each A_ID and then joining to tableA and TableB to get your desired rows:
SELECT a.ID,
a.col1,
a.Col2,
a.Col3,
b1.Col_X
FROM (
SELECT a_id
,max(rank) AS MaxRank
FROM tableb
GROUP BY a_id
) b
INNER JOIN tablea a ON a.id = b.a_id
INNER JOIN tableb b1 ON b.a_id = b1.a_id AND b1.rank = b.MaxRank
ORDER BY a.ID;
SQL Fiddle Demo
I'm thinking you want to take the max rank from your table b for each row in table a?
There's lots of different ways of approaching this. Here's one simple one:
with maxCTE as
(select
a_id,
max(rank) as MaxRank
from
tableb
group by
a_id
)
select
*
from
tablea a
inner join tableb b
on a.id = b.a_id
inner join maxcte c
on b.a_id = c.a_id
and b.rank = c.MaxRank
SQLFiddle
Basically, the CTE identifies the max rank for each a_id, then we join that back to tableb to get the details about that row.
with x as
(select a_id, max(rank) as mxrnk
from tableB
group by a_id)
select a.id, a.col1, a.col2, a.col3, b.col_x
from tableA a join x
on a.id = x.a_id
join tableB b
on x.mxrnk = b.rank
You can select max rank per a_id in the cte and use it to select the corresponding columns.
One is to INNER JOIN Table B onto Table A by ID's. You will have 3 records returned from Table B. If you ORDER those records by the COLX
SELECT
,a.ID
,a.Col1
,a.Col2
,a.Col3
,b.COLX
FROM TableA AS a
INNER JOIN TABLE_B AS b on b.A_ID = a.id
ORDER BY b.COLX DESC
Then another way is joining a sub query of Table B that also has a sub query that filters Table B records to only the records with the highest RANK.
That way you can bring in COLX values from the highest RANK records from Table B that match the records of Table A.
I think at least...
SELECT
a.ID
,a.Col1
,a.Col2
,a.Col3
,b.COLX
FROM TableA a
INNER JOIN (
SELECT
a.A_ID
,a.RANK
,a.COLX
FROM TABLE_B a
INNER JOIN (
SELECT
A_ID,
,MAX(RANK) AS [RANK] -- Highest Rank
FROM TABLE_B
GROUP BY A_ID
) AS b ON b.A_ID = a.A_ID AND b.RANK = a.RANK
) AS b on b.A_ID = a.id
ORDER BY a.ID ASC
Select A.*,D.Col_X
from
(Select C.COL_X,B.A_ID
from
(Select A_ID,MAX(rank) as MAX_rank
from TABLE_B
group by A_ID) B ----- gets the highest rank and ID of the highest rank
inner join TABLE_B c
on
concat(C.A_ID,C.RANK)= concat(B.A_ID,B.MAX_rank)) D ---- Gets the highest rank name
inner join TABLE_A A
on D.A_ID=A.ID
OUTPUT:
ID Col1 Col2 Col3 Col_X
1 c1 c2 c3 SOMEXXXX
2 c1 c2 c3 VALUE

How do show a result table from two 'select'? SQL

I have 2 queries:
First query:
select TableB.name, count(TableB.name) from TableB group by TableB.name
result:
name | count
Jack | 2
Marry| 1
and
Second query:
select DISTINCT TableA.kName, TableA.Value from TableA inner join TableB
ON
TableA.kName=TableB.name
result:
kName | Value
Jack | 1
Marry | 3
But I need result table:
kName | Value | newColumn
Jack | 1 | 2
Marry | 3 | 1
where the newColumn is result of first query, How can to do it? help, please.
maybe:
select DISTINCT TableA.kName, TableA.value,
(select TableB.name, count(TableB.name) from TableB group by TableB.name)
AS
newColumn from TableA inner join TableB ON TableA.kName=TableB.name
but this is not work :(
Try this query
SELECT tableB.name, tableA.value, count(tableB.name) as newColum FROM tableB
JOIN tableA ON tableB.name = tableA.kname
GROUP BY tableB.name,tableA.value
select kname, value, "newColumn"
from
(
select name, count(name) as "newColumn"
from tb
group by name
) tb
inner join
(
select distinct kname, value
from ta
) ta ta.kname = tb.name

TSQL - retrieve results from table A that contains exact data contained in table B

I have TableA (id bigint, name varchar) and TableB (name varchar) that contains the following data:
Table A: Table B: Results:
------------- --------- ---------------
| 1 | "A" | | "A" | | 1 | "A" |
| 1 | "B" | | "B" | | 1 | "B" |
| 2 | "A" | --------- | 4 | "A" |
| 3 | "B" | | 4 | "B" |
| 4 | "A" | ---------------
| 4 | "B" |
-------------
I want to return results from TableA that contains an EXACT match of what's in table B.
Using the 'IN' clause only retrieves back an occurrence.
Also, another example, if TableB has only "A", I want it to return back: 2-"A"
I understand your question but it is a tricky one as not exactly in line with the relational logic. You are looking for id's for which SELECT name FROM TableA WHERE id IN ... ORDER BY name; is identical to SELECT name FROM B order by name;.
Can you assume that A(id,name) is unique and B(name) is unique? Better said, are there constraints like that or can you set them up?
If yes, here is a solution:
1. Get rid of ids in A with rows not matching the rows in B
SELECT id, A.name FROM A WHERE id NOT IN
(SELECT id FROM A LEFT JOIN B ON A.name = B.name WHERE B.name IS NULL);
2. Count rows per each id (this is why the unique constraints are necessary)
SELECT id, COUNT(*) FROM
(
SELECT id, A.name FROM A WHERE id NOT IN
(SELECT id FROM A LEFT JOIN B ON A.name = B.name WHERE B.name IS NULL)
) t
GROUP BY id;
3. Only retain those that match the number of rows of B.
SELECT id, COUNT(*) FROM
(
SELECT id, A.name FROM A WHERE id NOT IN
(SELECT id FROM A LEFT JOIN B ON A.name = B.name WHERE B.name IS NULL)
) t
GROUP BY id
HAVING COUNT(*) = SELECT COUNT(*) FROM B;
This works in SQL Server
select * from TableA a
where
(select count(*) from TableB) = (select count(*) from TableA where id = a.id) and
(select count(*) from TableB) =
(
select count(*) from
(
select name from TableA where id = a.id
intersect
select name from TableB
) as b
)

Select first record in a One-to-Many relation using left join

I'm trying to join two tables using a left-join. And the result set has to include only the first record from the "right" joined table.
Lets say I have two tables A and B as below;
Table "A"
code | emp_no
101 | 12222
102 | 23333
103 | 34444
104 | 45555
105 | 56666
Table "B"
code | city | county
101 | Glen Oaks | Queens
101 | Astoria | Queens
101 | Flushing | Queens
102 | Ridgewood | Brooklyn
103 | Bayside | New York
Expected Output:
code | emp_no | city | county
101 | 12222 | Glen Oaks | Queens
102 | 23333 | Ridgewood | Brooklyn
103 | 34444 | Bayside | New York
104 | 45555 | NULL | NULL
105 | 56666 | NULL | NULL
If you notice my result has only the one matched record from table "B"(doesn't matter what record is matched) after left join (and it is a one to many mapping)
I need to pick the first matched record from table B and ignore all other rows.
Please help!
Thanks
After playing around a bit, this turns out to be trickier than I'd expected! Assuming that table_b has some single column that is unique (say, a single-field primary key), it looks like you can do this:
SELECT table_a.code,
table_a.emp_no,
table_b.city,
table_b.county
FROM table_a
LEFT
JOIN table_b
ON table_b.code = table_a.code
AND table_b.field_that_is_unique =
( SELECT TOP 1
field_that_is_unique
FROM table_b
WHERE table_b.code = table_a.code
)
;
Another option: OUTER APPLY
If supported by the database, OUTER APPLY is an efficient and terse option.
SELECT *
FROM
Table_A a
OUTER APPLY
(SELECT TOP 1 *
FROM Table_B b_1
WHERE b_1.code = a.code
) b
;
This results in a left join to the indeterminate first matched record. My tests show it to be quicker than any other posted solution (on MS SQL Server 2012).
The highest voted answer does not seem correct to me, and seems overcomplicated.
Just group by the code field on table B in your subquery and select the maximum Id per grouping.
SELECT
table_a.code,
table_a.emp_no,
table_b.city,
table_b.county
FROM
table_a
LEFT JOIN
table_b
ON table_b.code = table_a.code
AND table_b.field_that_is_unique IN
(SELECT MAX(field_that_is_unique)
FROM table_b
GROUP BY table_b.code)
If you are on SQL Server 2005 or later version, you could use ranking to achieve what you want. In particular, ROW_NUMBER() seems to suit your needs nicely:
WITH B_ranked AS (
SELECT
*,
rnk = ROW_NUMBER() OVER (PARTITION BY code ORDER BY city)
FROM B
)
SELECT
A.code,
A.emp_no,
B.city,
B.county
FROM A
LEFT JOIN B_ranked AS B ON A.code = B.code AND b.rnk = 1
OR
WITH B_unique_code AS (
select * from(
SELECT
*,
rnk = ROW_NUMBER() OVER (PARTITION BY code ORDER BY city)
FROM B
) AS s
where rnk = 1
)
SELECT
A.code,
A.emp_no,
B.city,
B.county
FROM A
LEFT JOIN B_unique_code AS B ON A.code = B.code
I modified the answer from ruakh and this seem to work perfectly with mysql.
SELECT
table_a.code,
table_a.emp_no,
table_b.city,
table_b.county
FROM table_a a
LEFT JOIN table_b b
ON b.code = a.code
AND b.id = ( SELECT id FROM table_b
WHERE table_b.code = table_a.code
LIMIT 1
)
;
this is how:
Select * From TableA a
Left Join TableB b
On b.Code = a.Code
And [Here put criteria predicate that 'defines' what the first record is]
Hey, if the city and county are unique, then use them
Select * From TableA a
Left Join TableB b
On b.Code = a.Code
And b.City + b.county =
(Select Min(city + county)
From TableB
Where Code = b.Code)
But the point is you have to put some expression in there to tell the query processor what it means to be first.
In Oracle you can do:
WITH first_b AS (SELECT code, min(rowid) AS rid FROM b GROUP BY code))
SELECT a.code, a.emp_no, b.city, b.county
FROM a
INNER JOIN first_b
ON first_b.code = a.code
INNER JOIN b
ON b.rowid = first_b.rid