Generic SQL questions - sql

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 ;)

Related

Oracle - Compare two sets of columns are equal

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

searching across column value pairs -- SQL server

i'm searching the database for matching values of a column pair.
say, the table T1 has these 3 columns:
(id, this, that)
and the query is
select id from T1 where
this = 'aValue' and that = 'bbb'
OR this = 'CCCC' and that = 'DDD'
OR this = 'EE' and that = 'EEE'.
is there a shortcut to this?
if i were searching on the values of one column only, say "this", it was easy:
select id from T1 where this in ('aa', 'bbbb', 'cccc')
i'm using SQL Server.
TIA.
You can use the VALUES clause to construct a table value, then inner join against this to get all matches:
SELECT id
FROM T1
INNER JOIN (
VALUES ('aValue', 'bbb'), ('CCCC', 'DDD'), ('EE', 'EEE')
) AS C(x,y)
ON this = C.x AND that = C.y

SQL: Returning columns from table 2 when using left join on table 2 is null

I'm trying to return results from a left join where a list of values was not found in the second table, but I need my results to return specifically which of the values was not found. I thought something like the following would work, but of course table2 is null, so I cannot pull a column out of it.
select table1.pidm, column1, column2, **column3** from
(select pidm, column1, column2 from aaa) table1
left join
(select pidm, column3 from bbb where **column3** in ('X','Y','Z')) table 2
on table1.pidm=table2.pidm where **table2.pidm is null**;
I've probably overlooked another simpler approach, but I cannot figure out what it is!
If the table structure of both the tables is exactly same, you can try this:
select * from Table1
except
select * from Table2
If they are different, you can select the exact columns you require in the SELECT clause
select table1.pidm, x.CountX, y.CountY, z.CountZ
from table1
left join
(select pidm, count(*) CountX from bbb where column3 = 'X' group by pidm) x on x.pidm = table1.pidm
left join
(select pidm, count(*) CountY from bbb where column3 = 'Y' group by pidm) y on y.pidm = table1.pidm
left join
(select pidm, count(*) CountZ from bbb where column3 = 'Z' group by pidm) x on z.pidm = table1.pidm
This will give you all pidm's in table1, together with the count of related X, Y, and Z values.
If you want only pidm's where one of X, Y, or Z is missing, add this:
where x.CountX = 0 or y.CountY = 0 or z.CountZ = 0
(Edited to add ' group by pidm')
Your query will select only entries from aaa where bbb does not contain any row by the same pidm (except rows where column3 is not one of 'X', 'Y' or 'Z'). If you need to see which entries are missing, I see two general approaches. Let's have a look at them with this dataset:
CREATE TABLE `aaa` (`pidm` BIGINT PRIMARY KEY AUTO_INCREMENT, `column1` VARCHAR(10), `column2` VARCHAR(10));
INSERT INTO `aaa` (`pidm`, `column1`, `column2`)
VALUES (1, '1a', '1b'), (2, '2a', '2b'), (3, '3a', '3b'), (4, '4a', '4b'), (5, '5a', '5b'), (6, '6a', '6b'),
(7, '7a', '7b'), (8, '8a', '8b');
CREATE TABLE `bbb` (`pidm` BIGINT, `column3` VARCHAR(10));
INSERT INTO `bbb` (`pidm`, `column3`)
VALUES (1, 'X'), (1, 'Y'), (1, 'Z'), (2, 'Y'), (2, 'Z'), (3, 'X'), (3, 'Z'), (4, 'Z'), (5, 'X'), (5, 'Y'), (6, 'Y'),
(7, 'X');
Join on a 'table' only containing 'X', 'Y' and 'Z' first
SELECT
table1.pidm,
table1.column1,
table1.column2,
temp1.column3
FROM
aaa AS table1
JOIN (SELECT
'X' AS column3
UNION SELECT
'Y'
UNION SELECT
'Z') temp1
LEFT JOIN
bbb AS table2 ON table2.pidm = table1.pidm AND table2.column3 = temp1.column3
WHERE table2.pidm IS NULL;
This will result in 12 rows where each value of column3 in the result will represent a pidm for which no row in bbb could be found. You can of course use this as a sub-query and create a similar result to approach 2.
Count values directly
SELECT
table1.pidm,
table1.column1,
table1.column2,
COALESCE(SUM(table2.column3 = 'X'), 0) AS X,
COALESCE(SUM(table2.column3 = 'Y'), 0) AS Y,
COALESCE(SUM(table2.column3 = 'Z'), 0) AS Z
FROM
aaa AS table1
LEFT JOIN bbb AS table2
ON table2.pidm = table1.pidm
GROUP BY table1.pidm;
This will result in 8 rows with the counts of each column3 value for each pidm attached to the data of aaa.

Join or Union or Subquery or something else?

I have two tables which I need to join/union for a distinct set - but every query I've tried so far results in too few/too many rows returned.
table one table two
ID Type
Type SubType
SubType
Percentage
Category
Table one is the main data set, table two is lookup values.
The data is something like this:
table one
1, A, ZZZ, 20.00, CAR
2, A, YYY, 80.00, CAR
3, B, YYY, 100.00, VAN
4, A, WWW, 100.00, BUS
table two
A, WWW
A, XXX
A, YYY
A, ZZZ
B, YYY
B, ZZZ
What I'm looking to end up with is the following data set
1, A, YYY, 80.00, CAR
2, A, ZZZ, 20.00, CAR
0, A, WWW, 0.00, CAR
0, A, XXX, 0.00, CAR
3, B, YYY, 100.00, VAN
0, B, ZZZ, 0.00, VAN
4, A, WWW, 100.00, BUS
0, A, XXX, 0.00, BUS
0, A, YYY, 0.00, BUS
0, A, ZZZ, 0.00, BUS
So, in other words I want all the data from table one PLUS any missing SubTypes from table two that match the Type of table one which would also include zero values for ID and Percentage, but keep the Category from the data set selected.
This works fine with an outer join if I only have one row in table one, but for each subsequent row my query adds more rows from table two. I think I need some sort of union or subquery that is aware of other rows selected from table one so will only select non-matching rows from table two.
Any help most appreciated!
Edit:
This is my current query (the more I think about this the more wrong it looks)
SELECT CASE WHEN t1.SubType = t2.SubType THEN t1.ID ELSE 0 END,
t1.Type, t2.SubType, CASE WHEN t1.SubType = t2.SubType THEN
t1.Percentage ELSE 0 END, t1.Category
FROM one t1
LEFT OUTER JOIN two t2
ON t2.Type = t1.Type
This gives me 8 rows - duplicated for each row selected from table one.
Another Edit:
Adding a couple more rows to table one I think explains this problem better. So the SQL to create tables and add rows looks like this:
create table one (ID int,Type nvarchar(1),SubType nvarchar(3),Percentage decimal(5,2),Category nvarchar(3))
create table two (Type nvarchar(1),SubType nvarchar(3))
insert into one values (1, 'A', 'ZZZ', 20.00, 'CAR')
insert into one values (2, 'A', 'YYY', 80.00, 'CAR')
insert into one values (3, 'B', 'YYY', 100.00, 'VAN')
insert into one values (4, 'A', 'WWW', 100.00, 'BUS')
insert into two values ('A', 'WWW')
insert into two values ('A', 'XXX')
insert into two values ('A', 'YYY')
insert into two values ('A', 'ZZZ')
insert into two values ('B', 'YYY')
insert into two values ('B', 'ZZZ')
I think this will do what you want:
SELECT ID, Type, SubType, Percentage, Category
FROM Table1
UNION ALL
SELECT 0, Type, SubType, 0.00, 'CAR'
FROM Table2 t2
LEFT JOIN t1 ON t1.Type = t2.Type AND t1.SubType = t2.SubType
WHERE t1.ID IS NULL
SELECT
ISNULL(t1.ID),
t2.Type,
t2.SubType,
ISNULL(t1.Percentage, 0),
ISNULL(t1.Category, 'CAR')
FROM Table2 t2
INNER JOIN (SELECT DISTINCT Type FROM Table1) f ON t2.Type = f.Type
LEFT JOIN Table1 t1 ON t2.Type = t1.Type AND t2.SubType = t1.SubType
You can do this without a UNION, just a LEFT JOIN:
UPDATED FOLLOWING COMMENT
SELECT ISNULL(t1.ID,0) Id, t2.Type, t2.SubType, ISNULL(t1.percentage,0) Percentage, ISNULL(t1.Category,'CAR') Category
FROM Table2 t2
LEFT JOIN Table1 t1
ON t1.Type = t2.Type AND t1.SubType = t2.SubType
WHERE t2.Type IN (SELECT DISTINCT Type FROM Table1)
Ok, this will give you the result you want, but its assigning the category as 'CAR', if you don't want that, then you should tell us what it should be.
Thanks for everyone's help on this, but I've worked it out myself finally...
SELECT ID, Type, SubType, Percentage, Category
FROM one
UNION
SELECT DISTINCT 0, t2.Type, t2.SubType, 0.00, t1.Category
FROM one t1
INNER JOIN two t2 ON t2.Type = t1.Type AND t2.SubType NOT IN
(SELECT t3.SubType FROM one t3 WHERE t3.Category = t1.Category and t3.Type = t1.Type)
ORDER BY Category, Type, SubType

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.