SQL query to find ids where field1 = x and field1 = y - sql

I have this table (simplified)
ID | Field1
---------------------------------
2 | Cat
2 | Goat
6 | Cat
6 | Dog
I need to find the IDs where a record exists whose value for field1 is cat and for the same id, another record exists whose value is Goat. In this case, it would only return ID 2.
Doing something like below will not work.
where Field1='Cat' and Field1='Goat'
I'm guessing I need some sort of subquery here? I'm not entirely sure. (Using SQL Server 2008)

Use:
SELECT t.id
FROM YOUR_TABLE t
WHERE t.field1 IN ('Cat', 'Goat')
GROUP BY t.id
HAVING COUNT(DISTINCT t.field1) = 2
The key here is using COUNT (DISTINCT ...) to get a count of the field1 values. Doesn't matter if a user has Cat 3x, and dog 1x... unless you need to filter those out?
Another option is INTERSECT, which returns any distinct values that are returned by both the query on the left and right sides of the INTERSECT operand:
SELECT a.id
FROM YOUR_TABLE a
WHERE a.field1 = 'Cat'
INTERSECT
SELECT b.id
FROM YOUR_TABLE b
WHERE b.field1 = 'Goat'

Try this:
SELECT id FROM
(
SELECT id FROM <YOUR_TABLE> WHERE Field1 = 'Cat'
INTERSECT
SELECT id FROM <YOUR_TABLE> WHERE Field1 = 'Goat'
) a
Alternative:
SELECT a.ID
FROM <YOUR_TABLE> a INNER JOIN <YOUR_TABLE> b
ON a.ID = b.ID
WHERE a.Field1 = 'CAT'
AND b.Field1 = 'GOAT'

Use a query like this
SELECT ID FROM table INNER JOIN
(SELECT ID, COUNT(FILED1) AS Expr1
FROM table GROUP BY ID
HAVING COUNT(FIELD1) > 1) SR ON table.ID = SR.ID WHERE table.FIELD1 = 'Cat'
So you just have to put a variable with a DECLARE for the 'Cat' if you want to have a more dynamic query

SELECT DISTINCT t1.ID
FROM table t1, table t2
WHERE t1.ID=t2.ID AND t1.Field1 <> t2.Field1

Not tested but something like this might work
select t1.ID from tbl t1 inner join tbl t2 on t1.ID=t2.ID
where (t1.Field1='Cat' and t2.Field1='Goat')

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

Matching multiple columns in one join

I have two tables:
Table 1
item_name | assocID_1 | assocID_2 | assocID_3
ball 123 456 789
Table 2
assoc_key assoc_value
123 red
456 white
789 blue
Am I able to create an output of:
ball red white blue
With only one join? I understand I can just join the tables multiple times to easily get this result, but in my actual tables there are much more than 3 columns, and the app I'm using can only support 4 joins per query apparently.
Many thanks for any help.
If you don't care about performance, you can do:
select t1.item_name,
max(case when t2.assoc_key = t1.assocID_1 then t2.assoc_value end),
max(case when t2.assoc_key = t1.assocID_2 then t2.assoc_value end),
max(case when t2.assoc_key = t1.assocID_3 then t2.assoc_value end)
from table1 t1 join
table2 t2
on t2.assoc_key in (t1.assocID_1, t1.assocID_2, t1.assocID_3)
group by t1.item_name;
You can also use subqueries. If we assume that there is only one matching row in table2:
select t1.item_name,
(select t2.assoc_value from table2 t2 where t2.assoc_key = t1.assocID_1),
(select t2.assoc_value from table2 t2 where t2.assoc_key = t1.assocID_2),
(select t2.assoc_value from table2 t2 where t2.assoc_key = t1.assocID_3)
from table1 t1;
If there can be more than one match, you can arbitrarily choose one of them using aggregation functions:
select t1.item_name,
(select max(t2.assoc_value) from table2 t2 where t2.assoc_key = t1.assocID_1),
(select max(t2.assoc_value) from table2 t2 where t2.assoc_key = t1.assocID_2),
(select max(t2.assoc_value) from table2 t2 where t2.assoc_key = t1.assocID_3)
from table1 t1;
I do not think you need a join here. You just need to look up which you can do in the SELECT statement directly. Here is an implementation in SQL Server (In Sample Data preparation code, if you are using version older than SQL Server 2016, please replace the DROP TABLE IF EXISTS with older way of doing the same)
DDL and Test Data:
DROP TABLE IF EXISTS Table1
SELECT item_name = 'ball'
,assocID_1 = 123
,assocID_2 = 456
,assocID_3 = 789
INTO Table1
DROP TABLE IF EXISTS Table2
SELECT assoc_key = 123
,assoc_value = 'red'
INTO Table2
UNION ALL
SELECT assoc_key = 456
,assoc_value = 'white'
UNION ALL
SELECT assoc_key = 789
,assoc_value = 'blue'
SELECT * FROM Table1
SELECT * FROM Table2
1. Brute Force Approach:
SELECT item_name = T1.item_name
,(SELECT TOP 1 assoc_value FROM Table2 WHERE assoc_key = T1.assocID_1)
,(SELECT TOP 1 assoc_value FROM Table2 WHERE assoc_key = T1.assocID_2)
,(SELECT TOP 1 assoc_value FROM Table2 WHERE assoc_key = T1.assocID_3)
FROM Table1 T1
2. Dynamically Building the Query For Ease And Then Executing It. With this approach Number of Columns Would Not Be a Concern:
DECLARE #SQL NVARCHAR(MAX) = 'SELECT item_name = T1.item_name '
SELECT #SQL += '
,(SELECT TOP 1 assoc_value FROM Table2 WHERE assoc_key = T1.'+COLUMN_NAME+')'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'dbo' -- provide your proper schema name here
AND TABLE_NAME = 'Table1'
AND COLUMN_NAME <> 'item_name' -- provide the columns you want to avoid doing lookups
ORDER BY ORDINAL_POSITION
SET #SQL+='
FROM Table1 T1 '
PRINT #SQL
EXEC sp_executesql #statement=#SQL
3. Combination of UNPIVOT, JOIN and PIVOT
SELECT item_name, [assocID_1], [assocID_2], [assocID_3] -- you can dynamically build the select list like above example if you need
FROM
(
SELECT IQ.item_name, IQ.assocId, T2.assoc_value
FROM (
SELECT UNP.item_name, UNP.assocId, UNP.Value
FROM Table1 T1
UNPIVOT
(
Value FOR assocId IN ([assocId_1], [assocId_2], [assocId_3]) -- you can dynamically build this column list like above example if you need
) UNP
) IQ
INNER JOIN Table2 T2
ON IQ.Value = T2.assoc_key
) OQ
PIVOT
(
MAX(assoc_value)
FOR associd IN ([assocID_1], [assocID_2], [assocID_3]) -- you can dynamically build this column list like above example if you need
) PV
select item_name, decode(ASSOCID_1,(select assocID_1 from t1 ), (select assoc from t2 where assoc_key =aa.assocID_1),null ) ,
decode(ASSOCID_2,(select assocID_2 from t1 ) , (select assoc from t2 where assoc_key =aa.assocID_1),null ),
decode(ASSOCID_3,(select assocID_3 from t1 ), (select assoc from t2 where assoc_key =aa.assocID_1),null ) from t1 aa

Oracle SQL: GROUP BY and HAVING clause

I have this table (say TABLE1):
ID1 | ID2 | NAME
where (ID1, ID2) is the composite PK.
And this another table (say TABLE2):
ID | COD1 | COD2 | DATA | INDEX
where ID is the PK.
I need to join this tables on ((TABLE1.ID1 = TABLE2.COD1) AND (TABLE1.ID2 = TABLE2.COD2))
My problem is that, for each ID of TABLE2, I have many tuples with different INDEX. I only want join the tuple that its INDEX is the MAX of its group (COD1, COD2).
For instance, if I have:
ID1|ID2|NAME
10 10 JOSH
ID|COD1|COD2|DATA|INDEX
1 10 10 YES 0
2 10 10 NO 1
3 11 10 OH 0
I want to get:
ID1|ID2|NAME|DATA
10 10 JOSH NO
I have tried this but it doesn't work:
SELECT ID1, ID2, NAME, DATA
FROM TABLE1 T1 JOIN TABLE2 T2 ON T1.ID1 = T2.COD1 AND T1.ID2 = T2.COD2
GROUP BY ID1, ID2, NAME, DATA HAVING INDEX = MAX(INDEX)
Thanks.
This is the generic construct.
select field1,field2, etc
from yourtables
join
(select field1, max(something) themax
from table1
where whatever
group by field1) temp on table1.something = themax
and table1.field1 = temp.field1
where whatever
The two "where whatevers" should be the same. You should be able to take it from here.
A slightly different solution:
select t1.id1, t1.id2, t1."NAME", t3."DATA"
from table1 t1
left join
(
select max("INDEX") as maxindex, cod1, cod2
from table2
group by cod1, cod2
) tt on tt.cod1 = t1.id1 and tt.cod2 = t1.id2
left join table2 t2 on t2."INDEX" = tt.maxindex;
If all tuples have different and unique values INDEX, these example is OK. But if some tuples have the same value, it is necessary to write an additional subquery (e.g. select max(ID) from table2) to determine appropriate lines.
P.S. It's best not to use any keyword for your own tables or columns (e.g. INDEX, DATA ...).
How To Handle Table Column Named With Reserved Sql Keyword?
Got an Oracle Table Named as Reserved Word, Which problems may arise?
try
SELECT ID1,ID2,NAME
FROM TABLE1
join
(select ID,DATA, max(Index) themax
FROM TABLE2
WHERE (your condition)
group by ID) temp on table1.Index = themax
WHERE (your condition)
I have solved it this way:
SELECT ... FROM TABLE1 JOIN
(SELECT ID1, ID2, NAME, DATA
FROM TABLE1 T1 JOIN TABLE2 T2 ON T1.ID1 = T2.COD1 AND T1.ID2 = T2.COD2
GROUP BY ID1, ID2, NAME, DATA HAVING INDEX = SELECT MAX(INDEX) FROM TABLE2 WHERE TABLE1.ID1 = TABLE2.COD1 AND TABLE1.ID2 = TABLE2.COD2
Thanks!

Tsql select from related table with AND condition

I've two related tables:
Table1
Id
-----
1
2
3
Table2
Id Feature
--------------
1 Car
1 Moto
1 Camper
2 Moto
2 Scooter
3 Apple
I want to select Ids which have, for example, both 'Car' AND 'Moto'.
So in the example i want to get only Id = 1.
Use the INTERSECT operator:
select id from table2 where feature = 'Car'
intersect
select id from table2 where feature = 'Moto'
This:
WITH features AS
(
SELECT feature
FROM (
VALUES
('Car'),
('Moto')
) q (feature)
)
SELECT *
FROM table1 t1
WHERE NOT EXISTS
(
SELECT feature
FROM features
EXCEPT
SELECT feature
FROM table2 t2
WHERE t2.id = t1.id
)
or this:
SELECT *
FROM table t1
WHERE (
SELECT COUNT(*)
FROM table2 t2
WHERE t2.id = t1.id
AND t2.feature IN ('Car', 'Moto')
) = 2
Which query is more efficient depends on how many records you have in both tables and how many matches there are.
This select does two LEFT OUTER JOINs to table2 (one based on 'Car' and the other based on 'Moto') and makes sure that each JOIN returned a result. The DISTINCT ensures that you get each ID only once.
SELECT DISTINCT t1.id
FROM table2 t2
LEFT OUTER JOIN table2 t2_2 ON t2.id = t2_2.id AND t2_2.feature = 'Moto'
WHERE t2.feature = 'Car'
AND t2_2.id IS NOT NULL
Edit: Removed join to table1 since it really isn't needed.

Subquery with multiple select statements

to check the subquery having multiple select statement inside 'not in' condition
Eg.
select id from tbl where
id not in (select id from table1) and
id not in (select id from table2) and
id not in (select id from table3)
instead of repeating the same id 'not in' condition , i need the subquery which will check in one shot from multiple tables..
pls help..
Your query is better expressed as:
SELECT id
FROM tbl t
LEFT JOIN table1 t1 on t1.id = t.id
LEFT JOIN table2 t2 on t2.id = t.id
LEFT JOIN table3 t3 on t3.id = t.id
WHERE t1.id IS NULL AND t2.id IS NULL AND t3.id IS NULL
You could use a union, so you just have one in:
select id
from tbl
where id not in
(
select id from table1
union all select id from table2
union all select id from table3
)
Note: not in does not work well with nullable columns, but I assume id is not nullable here.
use union all
like this -->
select f.FIRST_NAME from farmer f where f.ID in (select v.ID from Village v where v.ID in (1,2) union all select s.ID from state s where s.ID in (3,4) )