SQL join simple problem - sql

This is simple SQL JOIN question and my solution works while trying with sample data but when i do the same with huge data, it fails.
I have two table
tbl1
a b
0 10
1 2
4 5
2 2
Another table tbl2
a c
1 22
2 18
10 9
98 8
Now i want final table like this
a b c
0 10
1 2 22
2 2 18
4 5
10 9
98 8
What i did is:
1) temptbl = select a from tbl1 UNION select a from tbl2;
2) valueA = temptbl left join tbl1 on a
3) valueB = temptbl left join tbl2 on a
4) inner join valueA and ValueB on a
My solution works on small data when i try it locally, but while running it on server, left join produces some random data (steps 1 works, but after step 2, it does not work). Can somebody help me on this? AM i doing wrong? Are there any other solution
Please note, value in column a is unique in both the table.

You can do this in pieces:
-- Pick out records whose "a" values are in T1
SELECT T1.a, T1. b, T2.c
FROM T1
LEFT OUTER JOIN T2
ON T1.a=T2.a
UNION
-- Add records whose "a" values are NOT in T1
SELECT T2.a, NULL 'b', T2.c
FROM T2
WHERE NOT EXISTS (SELECT 1 FROM T1 WHERE T1.a = T2.a)

You probably want something like this:
SELECT coalesce(tbl1.a, tbl2.a) as a, /* one of them will be non-null */
tbl1.b,
tbl2.c
FROM tbl1 FULL OUTER JOIN tbl2
ON tbl1.a = tbl2.a
If your database doesn't support the FULL JOIN, you can UNION together a LEFT JOIN for each of the tables. A LEFT JOIN with only tbl1 on the "left side" will not yield records where only tbl2 has an a value.
Edit: Per the OP's request here's the equivalent as a UNION -- since apparently this database doesn't support #DVK's good suggestion of an anti-join:
SELECT a,
b,
NULL as c
FROM tbl1
UNION
SELECT a,
NULL as b,
c
FROM tbl2

Try something like
SELECT all.a, b, c
FROM (SELECT DISTINCT a FROM tbl1 UNION SELECT a from tbl 2) all
LEFT OUTER JOIN tbl1 on tbl1.a = all.a
LEFT OUTER JOIN tbl2 on tbl2.a = all.a

Related

Count the number of common users in multiple tables

I have 4 tables as shown below
I basically want to get how many users from table1 are in tables 2, 3 and 4. Similarly for table2 I want to get how many users are present in table 1, 3 and 4. and same for tables 3 and 4
Basically all the possible combinations. The final result I want is something as below
One of the way I am trying to solve is by doing a left-join of table1 with other tables to followed by count to get first row of my output. But doing it for all the possible combinations is not optimized. I was looking for any other alternative that is possible
My code for the same
SELECT
COUNT(DISTINCT A.id) table1,
COUNT(DISTINCT B.id) table2,
COUNT(DISTINCT C.id) table3,
COUNT(DISTINCT D.id) table4
FROM table1 A
LEFT JOIN table2 B
ON A.id = B.id
LEFT JOIN table3 C
ON A.id = C.id
LEFT JOIN table4 D
ON A.id = D.id
db-fiddle (This fiddle is for mysql, I am looking for a generic SQL based approach than any db specific approach)
I would recommend:
with t as (
select 'table1' as which, id from table1 union all
select 'table2' as which, id from table2 union all
select 'table3' as which, id from table3 union all
select 'table4' as which, id from table4
)
select ta.which,
sum(case when tb.which = 'table1' then 1 else 0 end) as cnt_table1,
sum(case when tb.which = 'table2' then 1 else 0 end) as cnt_table2,
sum(case when tb.which = 'table3' then 1 else 0 end) as cnt_table3,
sum(case when tb.which = 'table4' then 1 else 0 end) as cnt_table4
from t ta left join
t tb
on ta.id = tb.id
group by ta.which;
Note: This assumes that id is unique in each of the tables. That is a reasonable assumption given the name of the column and the sample data. However, if there are duplicates, you can change the union all in the CTE to union.
This structure also readily generalizes to additional tables.
Use UNION ALL
DEMO
select 'table1' as col1,count(table1.id),count(table2.id),count(table3.id),count(table4.id)
from table1
left join table2 on table1.id=table2.id
left join table3 on table1.id=table3.id
left join table4 on table1.id=table4.id
union all
select 'table2' ,count(table1.id),count(table2.id),count(table3.id),count(table4.id)
from table2
left join table1 on table2.id=table1.id
left join table3 on table2.id=table3.id
left join table4 on table2.id=table4.id
union all
select 'table3' ,count(table1.id),count(table2.id),count(table3.id),count(table4.id)
from table3
left join table1 on table3.id=table1.id
left join table2 on table3.id=table2.id
left join table4 on table3.id=table4.id
union all
select 'table4' ,count(table1.id),count(table2.id),count(table3.id),count(table4.id)
from table4
left join table1 on table4.id=table1.id
left join table2 on table4.id=table2.id
left join table3 on table4.id=table3.id
OUTPUT:
col1 tbl1 tbl2 tbl3 tbl4
table1 8 3 2 2
table2 3 6 1 0
table3 2 1 5 0
table4 2 0 0 4

Partial match on key composed of two columns

Let s say my two tables keys are comprised of column A and B:
Table 1
Column A Column B
1 1
1 X
2 2
3 3
Table 2
Column A Column B
1 1
2 2
3 3
How do I select only the rows in Table1 where the key only matches partially. My intended result would pulling out row:
Column A Column B
1 X
Basically finding all rows where Column A and B match but where following records were ´left out' in Table 2
select *
from tabl1
join tabl2
on (tabl1.cola == tabl1.cola and tabl1.colb <> tabl1.colb)
or (tabl1.cola <> tabl1.cola and tabl1.colb == tabl1.colb)
join the tables on "partial" match
select t1.*
from table1 t1
join table2 t2 on ((t1.a = t2.a and t1.b <> t2.b) or (t1.a <> t2.a and t1.b = t2.b))
You can solve this problem using NOT-EXISTS subquery:
SELECT
input1.ColumnA,input1.ColumnB
FROM
t1
WHERE
NOT EXIST (
SELECT *
FROM t2
WHERE t1.ColumnB = t2.ColumnB AND t1.ColumnA = t2.ColumnA)
Explanation: you select rows in table1 whose values are not contained in table2.

MS Access 2007 Inner Join on Same Table

I have a table t1. It has columns [id] and [id2].
Select count(*) from t1 where id=1;
returns 31,189 records
Select count(*) from t1 where id=2;
returns 31,173 records
I want to know the records where id2 is in id=1 but not in id=2.
So, I use the following:
Select * from t1 a left join t1 b on a.id2=b.id2
Where a.id=2 And b.id=1
And b.id2 Is Null;
It returns zero records.
Using an inner join to see how many records have id2 in common, I do...
Select * from t1 a inner join t1 b on a.id2=b.id2
Where a.id=2 And b.id=1;
And that returns 31,060. So where are the extra records in my first query that don't match?
I am sure I must be missing something obvious.
Sample Data
id id2
1 101
1 102
1 103
2 101
2 102
My expected results is to find the record with '103' in it. 'id2' not shared.
Thanks for any help.
Jeff
You are attempting to do what is generally called an exclude join. This involves doing a LEFT JOIN between two tables, then using a WHERE clause to only select rows where the right table is null, i.e. there was no record to join. In this way, you select everything from the left table except what exists in the right table.
With this data, it would look something like this:
SELECT
t1.id,
t1.id2
FROM test_table t1
LEFT JOIN
(SELECT
id,
id2
FROM test_table
WHERE id = 2) t2
ON t2.id2 = t1.id2
WHERE t1.id = 1
AND t2.id IS NULL --This is what makes the exclude join happen
And here is a SQLFiddle demonstrating this in MySQL 5.7 with the sample data you provided.
I think maybe Access changes the left join to an inner join when you add a where clause to filter rows (I know SQL Server does this), but if you do the filtering in derived tables it should work:
select
a.*
from
(select * from t1 where id = 1) a
left join
(select * from t1 where id = 2) b
on a.id2 = b.id2
where b.id2 is null

sql query without outer join key word

Is it possible to write a sql query where you know you have to use the left outer join..but cannot or are not allowed to use the "outer join" Key Word
I have two table sand want to get rows with null vaues from the left table ...this is pretty simple ...but am not supposed to use the key word....outer join....I need to right the logic for outer join myself
SELECT Field1
FROM table1
WHERE id NOT IN (SELECT id FROM table2)
SELECT Field1
FROM table1
WHERE NOT EXISTS (SELECT * FROM table2 where table2.id = table1.id)
This is something people do but it is deprecated and it does not currently work correctly (it sometimes will return a cross join instead of a left join) so it should NOT be used. I'm telling this only so you avoid using this solution.
SELECT Field1
FROM table1, table2 where table1.id *= table2.id
;WITH t1(c,d) AS
(
SELECT 1,'A' UNION ALL
SELECT 2,'B'
),t2(c,e) AS
(
SELECT 1,'C' UNION ALL
SELECT 1,'D' UNION ALL
SELECT 3,'E'
)
SELECT t1.c, t1.d, t2.c, t2.e
FROM t1, t2
WHERE t1.c = t2.c
UNION ALL
SELECT t1.c, t1.d, NULL, NULL
FROM t1
WHERE c NOT IN (SELECT c
FROM t2
WHERE c IS NOT NULL)
Returns
c d c e
----------- ---- ----------- ----
1 A 1 C
1 A 1 D
2 B NULL NULL
(Equivalent to)
SELECT t1.c, t1.d, t2.c, t2.e
FROM t1
LEFT JOIN t2
ON t1.c = t2.c
For SQL Server, you can just use LEFT JOIN - the OUTER is optional, just like INTO in an INSERT statement.
This is the same for all OUTER JOINs.
For an INNER JOIN you can just specify JOIN with no qualifiers and it is interpreted as an INNER JOIN.
This will give you all the rows in table A that don't have a matching row in table B:
SELECT *
FROM A
WHERE NOT EXISTS (
SELECT 1
FROM B
WHERE A.id = B.id
);
Returns all the matching rows from both tables:
SELECT a.*,b.* FROM table_a a, table_b b
WHERE a.key_field = b.key_field
Potential drawback is non-matches will be skipped.

Changing IN to EXISTS in SQL

I have the following query:
select A,
B
from table1
where A in (select c
from table 2
)
But, now I need to change this query and use exists instead of in, and it should give the same results.
My tables look like the following:
table1 table2
A B c
------ -----
1 x 1
2 y 3
3 z 4
4 w 7
5 a
1 b
How do I use the exists function?
You need to match the two columns that will be used in the exists together:
select
t1.a, t1.b
from
table1 t1
where
exists (select 1 from table2 t2 where t2.c = t1.a)
The reason why you have to do that, is because exists performs a semi-join on the table, and therefore, needs to have a join condition.
Changing the expression:
FROM Table1 WHERE a IN( SELECT c FROM Table2 )
To an EXISTS is a simple matter of:
Add a WHERE on the end of the internal SELECT
FROM Table1 WHERE a IN( SELECT c FROM Table2 WHERE )
Move the external match column (a) into the internal SELECT's WHERE clause
FROM Table1 WHERE IN( SELECT c FROM Table2 WHERE a )
Move the internal match column (c) to the WHERE clause, leaving a column placeholder (a constant or *):
FROM Table1 WHERE IN( SELECT * FROM Table2 WHERE a = c )
Change the IN to EXISTS:
FROM Table1 WHERE EXISTS( SELECT * FROM Table2 WHERE a = c )
To be safe add the table name onto the external column:
FROM Table1 WHERE EXISTS( SELECT * FROM Table2 WHERE Table1.a = c )
This will do it via direct inner join.
select
t1.a, t1.b
from
table1 as t1
inner join table2 as t2 on t1.a=t2.c