Oracle - Compare two sets of columns are equal - sql

I have an output of a table of a single column with values similar to
OUTPUT
A
B.
And I have a table with a set consisting of an ID and values
ID
Data
1
A
1
B
1
C
2
A
2
B
3
B
3
C
3
D
I'm having trouble to build an oracle query that compares the output of the first table with the data column of the second table, and let me know which id matches the exact same data if I were to group them by id.
So for this scenario only ID 2 should be returned since it matches exactly, but ID 1 won't return because it has an extra value

Left join TableA to TableB.
Then the 100% match will have no unmatched OUTPUT.
And the matched will have the same count as what's in TableA.
SELECT b.ID
FROM TableB b
LEFT JOIN TableA a ON a.OUTPUT = b.Data
GROUP BY b.ID
HAVING COUNT(CASE WHEN a.OUTPUT IS NULL THEN 1 END) = 0
AND COUNT(DISTINCT a.OUTPUT) = (SELECT COUNT(*) FROM TableA)
ORDER BY b.ID;
ID
2
Demo on db<>fiddle here

The first select in the with clause is a join of tables.
The second select uses the first select and finds the id whose column "Data" takes all the values from the output column of table A.
The third select contains only the ids without the "Data" in table A.
The result contains only ids that have all outputs and no extra.
Unique values in both tables are prerequisites.
Oracle 11RG2.
DDL:
CREATE TABLE TableA
("OUTPUT" varchar2(1))
;
INSERT ALL
INTO TableA ("OUTPUT")
VALUES ('A')
INTO TableA ("OUTPUT")
VALUES ('B')
SELECT * FROM dual
;
CREATE TABLE TableB
("ID" int, "Data" varchar2(1))
;
INSERT ALL
INTO TableB ("ID", "Data")
VALUES (1, 'A')
INTO TableB ("ID", "Data")
VALUES (1, 'B')
INTO TableB ("ID", "Data")
VALUES (1, 'C')
INTO TableB ("ID", "Data")
VALUES (2, 'A')
INTO TableB ("ID", "Data")
VALUES (2, 'B')
INTO TableB ("ID", "Data")
VALUES (3, 'B')
INTO TableB ("ID", "Data")
VALUES (3, 'C')
INTO TableB ("ID", "Data")
VALUES (3, 'D')
SELECT * FROM dual
;
SQL:
with a (id, "Data", output) as (
select
id, "Data", output
from
tableb left join tablea on "Data"=output
)
select
id
from
a
where
output is not null
group by id
having count("Data") = (select count(output) from tablea)
minus
select id from a where output is null
;
Output:
ID
2

Related

Filtering out null values only if they exist in SQL select

Below I have sql select to retrieve values from a table. I want to retrieve the values from tableA regardless of whether or not there are matching rows in tableB. The below gives me both non-null values and null values. How do I filter out the null values if non-null rows exist, but otherwise keep the null values?
SELECT a.* FROM
(
SELECT
id,
col1,
coll2
FROM tableA a LEFT OUTER JOIN tableB b ON b.col1=a.col1 and b.col2='value'
WHERE a.id= #id
AND a.col2= #arg
) AS a
ORDER BY col1 ASC
You can do this by counting the number of matches using a window function. Then, either return all rows in A if there are no matching B rows, or only return the rows that do match:
select id, col1, col2
from (SELECT a.id, a.col1, a.coll2,
count(b.id) over () as numbs
FROM tableA a LEFT OUTER JOIN tableB b ON b.col1=a.col1 and b.col2='value'
WHERE a.id = #id AND a.col2= #arg
) ab
where numbs = 0 or b.id is not null;
Filter them out in WHERE clause
SELECT
id,
col1,
coll2
FROM tableA a LEFT OUTER JOIN tableB b ON b.col1=a.col1 and b.col2='value'
WHERE a.id= #id
AND a.col2= #arg
AND A.Col1 IS NOT NULL -- HERE
) AS a
ORDER BY col1 ASC
For some reason, people write code like Marko that puts a filter (b.col2 = 'value') in the JOIN clause. While this works, it is not good practice.
Also, you should get in the habit of having the ON clause in the right sequence. We are joining table A to table B, why write it as B.col1 = A.col1 which is backwards.
While the above statement works, it could definitely be improved.
I created the following test tables.
-- Just playing
use tempdb;
go
-- Table A
if object_id('A') > 0 drop table A
go
create table A
(
id1 int,
col1 int,
col2 varchar(16)
);
go
-- Add data
insert into A
values
(1, 1, 'Good data'),
(2, 2, 'Good data'),
(3, 3, 'Good data');
-- Table B
if object_id('B') > 0 drop table B
go
create table B
(
id1 int,
col1 int,
col2 varchar(16)
);
-- Add data
insert into B
values
(1, 1, 'Good data'),
(2, 2, 'Good data'),
(3, NULL, 'Null data');
Here is the improved statement. I choose literals instead of variables. However, you can change for your example.
-- Filter non matching records
SELECT
A.*
FROM A LEFT OUTER JOIN B ON
A.col1 = B.col1
WHERE
B.col1 IS NOT NULL AND
A.id1 in (1, 2) AND
A.col2 = 'Good data'
ORDER BY
A.id1 DESC
Here is an image of the output.

2 different condition on one SQL query

I have 2 tables. I need to pick some ids from table one and based one condition and insert into table 2. The second column again must come from tableA but based on a different condition
Table A
NC 1
NC 2
SC 3
SC 4
Table B
1 100
1 200
2 100
2 200
I want to insert rows to table B so it would look like this....
1 100
1 200
2 100
2 200
3 100
3 200
4 100
4 200
I am picking 3 and 4 from table A based on the state condtion = 'SC'and now I want to know how to pick the values of 100 and 200 which NC has...
Sorry if I havent worded it correctly
-- sample data
create table tbla (code char(2), id int);
insert into tbla values ('NC', 1);
insert into tbla values ('NC', 2);
insert into tbla values ('SC', 3);
insert into tbla values ('SC', 4);
create table tblb (id int, value int);
insert into tblb values (1, 100);
insert into tblb values (1, 200);
insert into tblb values (2, 100);
insert into tblb values (2, 200);
-- your query to INSERT the new rows into tblb
insert into tblb
select x.id, y.value
from
(
select distinct a.id
from tbla a
where a.code = 'SC'
) x
cross join
(
select distinct b.value
from tbla a
join tblb b on a.id = b.id
where a.code = 'NC'
) y
left join tblb b on b.id = x.id and b.value = y.value
where b.id is null;
You can do it by a query like :
Select a.id, b.value
from "Table A" a
join "Table B" b
on b.id=1 --This condition pick the two first rows on table B.
where a.condtion = 'SC'
This is not a elegant solution, but it work.

Parent Child Query without using SubQuery

Let say I have two tables,
Table A
ID Name
-- ----
1 A
2 B
Table B
AID Date
-- ----
1 1/1/2000
1 1/2/2000
2 1/1/2005
2 1/2/2005
Now I need this result without using sub query,
ID Name Date
-- ---- ----
1 A 1/2/2000
2 B 1/2/2005
I know how to do this using sub query but I want to avoid using sub query for some reason?
If I got your meaning right and you need the latest date from TableB, then the query below should do it:
select a.id,a.name,max(b.date)
from TableA a
join TableB b on b.aid = a.id
group by a.id,a.name
SELECT a.ID, a.Name, MAX(B.Date)
FROM TableA A
INNER JOIN TableB B
ON B.ID = A.ID
GROUP BY A.id, A.name
It's a simple aggregation. Looks like you want the highest date per id/name combo.
create table #t1 (id int, Name varchar(10))
create table #t2 (Aid int, Dt date)
insert #t1 values (1, 'A'), (2, 'B')
insert #t2 values (1, '1/1/2000'), (1, '1/2/2000'), (2, '1/1/2005'), (2, '1/2/2005')
;WITH cte (AId, MDt)
as
(
select Aid, MAX(Dt) from #t2 group by AiD
)
select #t1.Id, #t1.Name, cte.MDt
from #t1
join cte
on cte.AId = #t1.Id

write a query in sql

I have two tables that have following rows:
Table1
ID Name Number
=====================
1 a 100
2 b 200
3 c 300
Table2
ID Number Check
=====================
1 100 0
2 200 1
3 300 null
Now I want the following table:
table
---------------------
Name Number check
=====================
a 100 0
c 300 null
What query I must to write.
*You notice that the column of check in the row of 'c' in final table is null.
Thanks.
Left outer join is your friend:
select table1.name, table1.nmber, table2.check from table1 left outer join table2 on table1.nmber = table2.number
Assuming that the nmber column in table1 = number column in table2
SELECT a.name, a.nmber, b.check
FROM table1 a JOIN table2 b ON a.nmber = b.number
I believe your looking for
select a.Name, a.Nmber, b.Check
from Table1 a join table2 b
on a.Nmber = b.Number
can also add a where clase e.g.
where a.Name is not b
where b.check is not 1
etc
Hard to tell from your question how you want your results to exclude b
Set up test data.
create table table1(ID int, Name char(1), Number int)
create table table2(ID int, Number int, [Check] int)
insert into table1 values (1, 'a', 100)
insert into table1 values (2, 'b', 200)
insert into table1 values (3, 'c', 300)
insert into table2 values (1, 100, 0)
insert into table2 values (2, 200, 1)
insert into table2 values (3, 300, null)
The query
select
table1.Name,
table1.Number,
table2.[Check]
from table1
inner join table2
on table1.ID = table2.ID
where Name <> 'b'
Result
a 100 0
c 300 NULL
You must use square brackets around column name [check] because check is a reserved word. in SQL Server.
This question has been edited a lot. I believe that the answer to the original question differ from the answer to the current question.
Here is the setup for version 1 one of the question.
create table table1(ID int, Name char(1), Number int)
create table table2(ID int, Number int, [Check] int)
insert into table1 values (1, 'a', 100)
insert into table1 values (2, 'b', 200)
insert into table1 values (3, 'c', 300)
insert into table2 values (1, 100, 0)
insert into table2 values (2, 200, 1)
To get c from table1 when joined to table2 you need to use a left outer join.
select
table1.Name,
table1.Number,
table2.[Check]
from table1
left outer join table2
on table1.ID = table2.ID
where table1.Name <> 'b'
Even in this version you need the square brackets around check.

Generic SQL questions

I have a table A
ID Term
10 A
10 B
10 C
20 A
20 B
20 E
what's the best way to write a sql to
get ID 10, if I try to find (A,B,C)
get NOTHING if I try to find (A,B)
get ID 20, if I try to find NOT in (C,D)
.
Select distinct ID from TableA where Term in (A,B,C) will return both 10 and 20
Select distinct ID from TableA where Term in (A,B) will also return both 10 and 20
Select distinct ID from TableA where Term NOT in (C,D) will also return both 10 and 20
Thanks!
1.
SELECT ID
FROM TableA
WHERE Term IN ('A','B','C')
GROUP BY ID
HAVING COUNT(ID)=3
LIMIT 1
Here 3 would be the length of the set (A,B,C in this case). 2 & 3 could probably be some variation of the above.
I assume you want one form of query that can be used to answer all three questions, not a different kind of query for each question.
These solutions take advantage of COUNT() not counting NULLs. When the OUTER JOIN does not match a row in t2, it results in NULL for all columns from t2.
get ID 10, if I try to find (A,B,C)
ID 10 has three distinct term values, and we're searching for all three.
SELECT t1.ID
FROM TableA t1 LEFT OUTER JOIN TableA t2
ON (t1.ID = t2.ID AND t1.term = t2.term AND t2.term IN ('A', 'B', 'C'))
GROUP BY t1.ID
HAVING COUNT(t1.term) = COUNT(t2.term);
get NOTHING if I try to find (A,B)
Both ID 10 and ID 20 have three distinct term values, but our search is only for two. The counts are 3 = 2 for both IDs, so neither have equal counts.
SELECT t1.ID
FROM TableA t1 LEFT OUTER JOIN TableA t2
ON (t1.ID = t2.ID AND t1.term = t2.term AND t2.term IN ('A', 'B'))
GROUP BY t1.ID
HAVING COUNT(t1.term) = COUNT(t2.term);
get ID 20, if I try to find NOT in (C,D)
ID 20 has three distinct term values, and all three of them are NOT 'C' or 'D'. So the counts are equal.
SELECT t1.ID
FROM TableA t1 LEFT OUTER JOIN TableA t2
ON (t1.ID = t2.ID AND t1.term = t2.term AND t2.term NOT IN ('C', 'D'))
GROUP BY t1.ID
HAVING COUNT(t1.term) = COUNT(t2.term);
Q1
SQLite version 3.6.10
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> create table A(ID smallint, term varchar(1));
sqlite> insert into A values(10, 'A');
sqlite> insert into A values(10, 'B');
sqlite> insert into A values(10, 'C');
sqlite> insert into A values(20, 'A');
sqlite> insert into A values(20, 'B');
sqlite> insert into A values(20, 'E');
sqlite> SELECT ID FROM A WHERE TERM = 'A' INTERSECT SELECT ID FROM A WHERE TERM
= 'B' INTERSECT SELECT ID FROM A WHERE TERM = 'C';
10
Q2
sqlite> SELECT ID FROM A WHERE TERM = 'A' EXCEPT SELECT ID FROM A WHERE TERM = '
B';
returns no results. EXCEPT can also be called MINUS in some versions of SQL
Q3
sqlite> SELECT ID FROM A EXCEPT SELECT ID FROM A WHERE TERM = 'C' UNION SELECT I
D FROM A WHERE TERM = 'D';
20
CREATE TABLE mySearch (
search_id INT,
val CHAR(1),
is_required INT,
is_excluded INT
)
INSERT INTO mySearch VALUES (1, 'A', 1, 0) -- Search1 : A is required
INSERT INTO mySearch VALUES (1, 'B', 1, 0) -- Search1 : B is required
INSERT INTO mySearch VALUES (1, 'C', 1, 0) -- Search1 : C is required
INSERT INTO mySearch VALUES (2, 'A', 1, 0) -- Search2 : A is required
INSERT INTO mySearch VALUES (2, 'B', 1, 0) -- Search2 : B is required
INSERT INTO mySearch VALUES (3, 'C', 0, 1) -- Search3 : C is excluded
INSERT INTO mySearch VALUES (3, 'D', 0, 1) -- Search3 : D is excluded
SELECT
[search].search_id,
[data].id
FROM
TableA AS [data]
LEFT JOIN
my_search AS [search]
ON [search].val = [data].Term
GROUP BY
[search].search_id,
[data].ID
HAVING
ISNULL(SUM([search].is_included),0) = (SELECT SUM(is_included) FROM mySearch WHERE search_id = [search].search_id)
AND MAX([search].is_excluded) IS NULL
Should satisfy all three seaches in one query. Unfortunately I can't test it as I'm at the in-laws house and they don't have geek toys to test on ;)