Partial match on key composed of two columns - sql

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.

Related

SQL Select all first items in a list of rows identified by Ids and filtered by a specific Type in another table

I need to create a table keyed by an ID where the values of one of the columns in the new table are the earliest values entered into the column of another table where the rows share the same ID and have a specific type label.
For example, say I want the Name and first Value entered for each fruit with an entry type A:
These are the tables I have:
TABLE1
Key
ID
Name
1
1
Cherry
2
2
Grape
TABLE2
Key
ID
Value
EntryNum
EntryType
1
1
21
1
A
2
1
32
2
B
3
1
4
3
B
4
1
15
4
A
5
2
3
1
B
6
2
8
2
A
7
2
16
3
B
And this is the result that I want:
TABLE3
ID
Name
EarliestEntry
1
Cherry
21
2
Grape
8
I've attempted the following query but it just returns the same value for all EarliestEntry:
SELECT TABLE1.ID, TABLE2.Name,
(SELECT Value FROM (SELECT ROW_NUMBER() OVER (ORDER BY TABLE2.EntryNum)
as row_num, Value FROM TABLE2
WHERE TABLE2.ID = TABLE1.ID AND TABLE2.EntryType = 'A')
AS sub
WHERE row_num = 1) AS EarliestEntry
INTO TABLE3
FROM TABLE2
INNER JOIN TABLE1 ON TABLE1.ID = TABLE2.ID
GROUP BY TABLE1.ID, TABLE2.Type, TABLE2.EntryNum
I would greatly appreciate help on this. Thank you
If you wanted to use the ROW_NUMBER function then you would need to put that on TABLE1 and add a partition by like so:
WITH rn AS(
SELECT a.Key, ROW_NUMBER() OVER(PARTITION BY a.ID ORDER BY a.EntryNum) AS rn
FROM TABLE2 AS a
)
SELECT b.Name, a.Value AS EarliestValue
FROM TABLE2 AS a
INNER JOIN TABLE1 AS b ON b.ID = a.ID
INNER JOIN rn AS rn ON rn.key = a.key
WHERE rn.rn = 1
In your example you skipped the PARTITION BY clause so you just get a number for all values in TABLE2. Instead of a number per ID in ascending order for Value.
Based on your description of the three tables TABLE1, TABLE2 and TABLE3.
I modified a little bit your script. Thank of Dale K remark, I explain in some words the solution : the field TABLE2.Name shown in the first select was wrong, because [name] belongs to TABLE1, so the right syntax for this is TABLE1.name. And in the GROUP BY clause the field TABLE2.Type might be replaced by TABLE1.name to repect aggregation criteria. So the script becomes :
SELECT DISTINCT table1.id, table1.name,
(SELECT Value FROM (SELECT ROW_NUMBER() OVER (ORDER BY table2.EntryNum)
as row_num, Value FROM table2
WHERE table2.id = table1.id AND table2.EntryType = 'A')
AS sub
WHERE row_num = 1) AS EarliestEntry
INTO table3
FROM table2
INNER JOIN table1 ON table1.id = table2.id
GROUP BY table1.id, table1.name, table2.entrynum;
Here, you can verify the output with fiddle
You are hugely over-complicating this.
Just partition Table2 and take a row-number, then join that to Table1 and filter on only row-number 1
SELECT
t1.Id,
t1.Name,
EarliestEntry = t2.Value
FROM Table1 t1
JOIN (
SELECT *,
rn = ROW_NUMBER() OVER (PARTITION BY t2.ID ORDER BY t2.EntryNum)
FROM Table2 t2
WHERE t2.EntryType = 'A'
) t2 ON t2.ID = t1.ID AND t2.rn = 1;
db<>fiddle

Ignore a column while doing MINUS in SQL query

I have written the query:
Select distinct a,b from t1 minus Select distinct a,b from t2.
Here t1 and t2 are two tables. I want distinct values of a and b that occur in t1 but not in t2. So I'm using minus operator. I want values of both a and b but I know that in some cases the value of b in t1 and t2 maybe different. This would result in values of a and b that are present in both t1 and t2 as minus would not happen if values of b do not match in both the tables. How can I do this successfully?
How can I get values of a and b that are present in table t1 but not in table t2 even though in some cases values of b might not match in both the tables?
table1: table2:
column1 column2 column1 column2
1 a 1 c
2 b 3 d
In this case I would want values (2,b) only. I would not want (1,a) as 1 is also present in table2.
Start with not exists:
select distinct. . .
from t1
where not exists (select 1 from t2 where t2.a = t1.a and t2.b = t1.b);
From you describe, you might want the comparison only on a:
select distinct a, b
from t1
where not exists (select 1 from t2 where t2.a = t1.a);
Another option is to use sub query in the WHERE condition as below-
SELECT A.*
FROM table1 A
WHERE A.column1 NOT IN
(SELECT DISTINCT column1 FROM table2)
You can also use LEFT JOIN as below which will provide you the same output as below-
SELECT A.*
FROM table1 A
LEFT JOIN table2 B ON A.column1 = B.column1
WHERE B.column1 IS NULL
For the data not include in t2, you can either go for the NOT EXISTS or LEFT OUTER JOIN.
Here is the solution.
Using NOT EXISTS
SELECT DISTINCT A,B FROM T1 WHERE NOT EXISTS (SELECT 1 FROM T2 WHERE T2.A = T1.A AND T2.B = T1.B);
Using Left Join
SELECT DISTINCT a,b,c FROM T1 LEFT JOIN T2 ON T1.a = T2.a and T1.b = T2.b WHERE T2.a IS NULL AND T2.b IS NULL
Hope it helps.

Oracle Sql - I have two tables and need to filter table1 with results from table2. I need to return all of table1 if table2 is empty

I'm currently turning a comma-separated string into a number table with the field name of ID. I'm then trying to do an nvl to select all if the generated table is null.
table1.ID = NVL(table2.ID, table1.ID)
I have two tables and need to filter table1 with results from table2. I need to return all of table1 if table2 is empty.
Scenario I
Table1
ID
1
2
3
4
Table2 (Empty)
ID
Return rows 1, 2, 3, 4
Scenario II
Table1
ID
1
2
3
4
Table2
ID
2
3
Return rows 2, 3
You can use filtering in the where clause:
select t1.id
from table1 t1
where not exists (select 1 from table2) or
exists (select 1 from table2 t2 where t2.id = t1.id);
I don't think join is the right way to express this logic.
You can also use UNION
select t1.id
from table1 t1
where not exists (select 1 from table2 where id = t1.id) union all
select t2.id
from table2 t2
where exist (select 1 from table1 where id = t2.id);

SQL join simple problem

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

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