Oracle SQL intersect between select and simple collection - sql

I would like to write an SQL script like this:
select count(*) from table1 t1 where exists (
select t2.desired_col from table2 t2 where t1.ID = t2.reference_t1_id
intersect
(2, 5, 9, 10)
);
The goal is to tell me, how many entries in t1 have at least one common value between desired_col of t2 and a given collection (i.e. (2, 5, 9, 10)). t2 has a ManyToOne relationship to t1. However, it seems I cannot intersect between a select and a simple collection. As a workaround, I wrapped the given collection like this:
select count(*) from table1 t1 where exists (
select t2.desired_col from table2 t2 where t1.ID = t2.reference_t1_id
intersect
select desired_col from table t2 where desired_col in (2, 5, 9, 10)
);
I think this solution looks a little ugly and was wondering, if there is a better way to find the intersect between a select statement and a simple collection.

You need to turn your list of IDs into an actual set, which you can then use in SQL with a table() function. Something like this:
select count(*) from table1 t1
where exists (
select t2.desired_col from table2 t2
where t1.ID = t2.reference_t1_id
intersect
select * from table(sys.odcinumberlist (2, 5, 9, 10))
);
There are several variant solutions. For instance, instead of intersection you could extend the WHERE clause with an IN subquery:
and t2.desired_col in (select * from table(sys.odcinumberlist (2, 5, 9, 10)))
or indeed
and t2.desired_col in (2, 5, 9, 10)
This uses the Oracle supplied collection type, sys.odcinumberlist, which is an array of numbers.

Related

How to Left join and remove the common fields between two tables

I have following tables:
table1 (p, crc, rti, l1)
table2 ( p, crc, rti)
I want to find all records from table1 with equal values for p and crc with table2 but remove those records that with the same values for p and crc have different values for rti in two tables
I tried to
Select * FROM table1 t1
LEFT JOIN table2 t2
ON t1.p = t2.p and t1.crc = t2.crc
But don't know how to remove the common records
It is probably better to list the columns you actually want. But, if the only columns you want are the join keys, then you can use USING:
SELECT *
FROM table1 t1 LEFT JOIN
table2 t2
USING (p, crc);
It seems as if you want to exclude all records from table1, where table2 contains at least one record with same p and same crc but different rti.
This could be written as "Get all records from table1, where no record with same p and same crc but different rti exists in table2".
select * from table1 t1
where not exists (
select * from table2 t2
where t1.p = t2.p and
t1.crc = t2.crc and
t1.rti <> t2.rti)
Hope I got you right.
I'm not sure I completely understand your question. Assuming you have records in the tables like this:
Table 1:
(p, crc, rti, l1) values (1, 23, 6, 8)
(p, crc, rti, l1) values (1, 23, 7, 5)
(p, crc, rti, l1) values (1, 23, 9, 2)
Table 2:
(p, crc, rti) values (1, 23, 7)
(p, crc, rti) values (1, 23, 8)
(p, crc, rti) values (1, 23, 9)
If you want to show all the records from table 1 where table1.rti is not equal to table2.rti, then try the below. This would give you table 1 row 1 (above).
SELECT t1.*
FROM table1 t1 LEFT JOIN table2 t2 on
t1.p = t2.p AND t1.crc = t2.crc
WHERE
t1.rti <> t2.rti
Based on your comment below, maybe try the MINUS operator.
SELECT * FROM table1
MINUS
SELECT t1.*
FROM table1 t1 LEFT JOIN table2 t2 on
t1.p = t2.p AND t1.crc = t2.crc

Making SQL query with multiple where conditions and variables more efficient

I have this query which does get the results I require but is incredibly slow and surely there must be a better way of doing this as I would need to run this regularly.
Each where clause has two variables which will always be the same number but I need to use this with 50+ BigIDs, the example shows 3 but I would do it for BigID 1 to 50+.
I am unsure how to make this shorter because of the two variables (one of which being in a subquery) and group by which is required.
Any help or pointing in the right direction would be appreciated. Thanks.
SELECT BigID,count(LittleID)
FROM Table1
where ( (BigID=1 and LittleID not in (SELECT LittleID FROM Table2 where BigID=1)) or
(BigID=2 and LittleID not in (SELECT LittleID FROM Table2 where BigID=2)) or
(BigID=3 and LittleID not in (SELECT LittleID FROM Table2 where BigID=3)) )
group by BigID
One method is a correlated subquery:
SELECT t1.BigID, count(t1.LittleID)
FROM Table1 t1
WHERE t1.BigID IN (1, 2, 3) and
t1.LittleID not in (SELECT t2.LittleID
FROM Table2 t2
WHERE t2.BigID = t1.BigId
)
GROUP BY t1.BigID
SELECT t1.BigID, COUNT(t1.LittleID)
FROM Table1 t1
LEFT JOIN Table2 t2 ON t1.LittleID = t2.LittleID AND t1.BigID = t2.BigID
WHERE t1.BigID IN (1, 2, 3)
AND t2.LittleID IS NULL
GROUP BY t1.BigID
SELECT Table1.BigID,
COUNT(Table1.LittleID)
FROM Table1
LEFT JOIN Table2 ON Table1.LittleID = Table2.LittleID
AND Table1.BigID = Table2.BigID
WHERE Table2.LittleID IS NULL
AND Table1.BigID IN (1, 2, 3)
GROUP BY Table1.BigID

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

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

Can you define values in a SQL statement that you can join/union, but are not stored in a table outside of the statement?

I'm trying to create a query and need to join against something that I can define values in without creating a table.
I'll attempt to describe what I'm trying to do:
table1
-------
fieldA
is joined on fieldA with
table2
-------
(titles for FK in table 1)
Table1 has values outside of what exists in table2
I want to add an additional 'table' to be unioned with table2 and then joined with table 1
Thanks
Sure, you can use a UNION ALL inside a subselect and join with the result of that. Something like this might do the trick:
SELECT *
FROM table1 T1
JOIN (
SELECT titles, stuff
FROM table2
UNION ALL
SELECT 'foo' AS titles, 'foostuff' AS stuff
UNION ALL
SELECT 'bar' AS titles, 'barstuff' AS stuff
) T2
ON T1.id = T2.titles
Note that the columns in the UNION ALL must be of the same type and in the same order. The column names don't have to match though.
Looks like you want to add arbitrary results to your query?
select
id,
titles
from
table1 t1
inner join table2 t2
on t2.titles = t1.titles
union (
(select 100, 'Dogs' from dual)
union
(select 200, 'Pigs' from dual)
union
(select 300, 'Sheep' from dual)
)
That's an oracle flavour, for other RDBMS' there will be an equivalent to dual
If you're using a modern Oracle version, there is an even neater solution
WITH arbitrary_data AS (
SELECT 100 id, 'Dogs' titles FROM DUAL
UNION ALL SELECT 200, 'Pigs' FROM DUAL
UNION ALL SELECT 300, 'Sheep' FROM DUAL
)
SELECT
id,
titles
FROM
table1 t1
inner join table2 t2
on t2.titles = t1.titles
inner join arbitrary_data ad
on ad.titles = t1.titles