Query table with composite primary key, excluding specific rows - mysql

product_id | category
---------------------
1 | A
1 | B
2 | A
3 | B
I would like to know which unique ids belong to category A but NOT category B.
In the example above, only id: 2 would be a valid result
I came up with this which does not work as intended:
SELECT id
FROM table
WHERE category = 'A' AND category <> 'B'
GROUP BY id
A valid approach would be to use a subquery
SELECT id
FROM table
WHERE category = 'A'
AND id NOT IN (SELECT id
FROM table
WHERE category = 'B')
Is it possible to do this with a single query?

Use a self-join:
SELECT t1.id
FROM table AS t1
LEFT JOIN table AS t2 ON t1.id = t2.id AND t2.category = 'B'
WHERE t1.category = 'A' and t2.id IS NULL
Another way is with grouping:
SELECT id
FROM table
WHERE category IN ('A', 'B')
GROUP BY id
HAVING MAX(category) = 'A'

Use aggregation and the conditions in the HAVING clause:
SELECT id
FROM tablename
GROUP BY id
HAVING SUM(category = 'A') > 0
AND SUM(category = 'B') = 0;
or:
SELECT id
FROM tablename
WHERE category IN ('A', 'B')
GROUP BY id
HAVING SUM(category = 'B') = 0;

Related

FETCH not associated field_name from mysql table

i have tables like below,
Table 1 :
ID
FIELD_NAME
1
field_1
2
field_2
3
field_3
Table 2 :
ID
TAG_NAME
1
tag_1
2
tag_2
3
tag_3
4
tag_4
Table 3 :
FIELD_ID
TAG_ID
1
1
1
2
1
3
2
1
3
2
3
3
so table-3 shows :
field1(from table1) connected with all 3 tag id's of table-2
field2(from table1) connected with all tag id=1 of table-2
and
field3(from table1) connected with all tag id=2,3 of table-2
I like to write a MYSQL Query to fetch
for a given tag_name, find all field name which are NOT associated with this
Example :
input = tag_1
output = field_3
Explanation : as field_3 not have any relation with tag_1 id
input = tag_4
output = field_1, field_2, field_3
Explanation : as no field have any relation with tag_4 id
You could check for fields where certain link table entries do not exist...
SELECT
*
FROM
table1
WHERE
NOT EXISTS (
SELECT *
FROM table3
WHERE field_id = table1.id
AND tag_id = (SELECT id FROM table2 WHERE tag_name = 'tag_4')
)
(Please excuse typos, I'm on my phone.)
dbfiddle as borrowed from #nbk : here
You can do it with a CROSS join of table1 to table2 and a LEFT join yo table3 where you return the unmatched rows:
SELECT t2.TAG_NAME, t1.FIELD_NAME
FROM table1 t1 CROSS JOIN table2 t2
LEFT JOIN table3 t3 ON t3.FIELD_ID = t1.ID AND t3.TAG_ID = t2.ID
WHERE t2.TAG_NAME = ? AND t3.FIELD_ID IS NULL
If you want the results as comma separated list you can also group by TAG_NAME and use GROUP_CONCAT():
SELECT t2.TAG_NAME, GROUP_CONCAT(t1.FIELD_NAME) fields
FROM table1 t1 CROSS JOIN table2 t2
LEFT JOIN table3 t3 ON t3.FIELD_ID = t1.ID AND t3.TAG_ID = t2.ID
WHERE t2.TAG_NAME = ? AND t3.FIELD_ID IS NULL
GROUP BY t2.TAG_NAME
Replace ? with the tag that you search for.
See the demo.

GROUP BY and JOIN in HIVE

How can I do something like this in HIVE:
Table1:
ID Name Friends
1 Tom 5
Table2:
ID Name DOB
1 Jerry 10/10/1999
1 Kate Null
1 Peter 02/11/1983
1 Robert Null
1 Mitchell 09/09/2000
What I want to do is:
For each ID in table 1, find num of not null DOB and then divide by Friends
I wrote a query as:
SELECT t.ID, t.Friends, COUNT(s.DOB)/ t. Friends from Table1 t join Table2 s on (t.ID = s.ID) GROUP BY t.ID
When I do this, I get the error as FRIENDS is not part of the GROUP BY Key
The answer I am expecting is: 3/5
Just add the FRIENDS to your GROUP BY section:
SELECT t.ID, t.FRIENDS, COUNT(s.DOB)/ t. FRIENDS
from Table1 t
join Table2 s
on (t.ID = s.ID)
GROUP BY t.ID. t.FRIENDS
I prefer to write this kind of query like this:
SELECT t.ID, case when t.FIREND>0 then Cnt / t.FRIENDS ELSE 0 END
FROM Table t1
JOIN (Select ID, Count(*) AS Cnt from Table 1 GROUP BY Id) t2
ON t1.ID = t2.ID
if you have declare id,friends in table 1 as integer and id in table2 as integer, then below query will get you intended output
select a.name, concat(cast(b.cnt as string),'/',cast(a.friends as string))
from table1 a
join
(select id, count(DOB) as cnt from table2 where DOB is not null group by id) b
on (a.id = b.id)

How to select column values where all values of another column are equal to something?

I have a table with columns category_id and is_required (among others), and I wish to select all values of category_id where all records have is_required = 0.
Something like SELECT category_id FROM table WHERE ALL is_required = 0
Edit: Another way of phrasing it - I need to select only those category_id values which never have is_required = 1 in the entire table
You can readily do this with not exists:
select t.*
from table t
where not exists (select 1
from table t2
where t2.category_id = t.category_id and
(t2.is_required <> 0 or t2.is_required is null)
);
If you only want the category ids, then aggregation might produce more reasonable results:
select category_id
from table t
group by category_id
having sum(t.is_required <> 0 or t.is_required is null) = 0;
Is that what you need?
select t1.category_id from `table` t1 left join `table` t2 on t1.category_id =t2.category_id and t1.is_required = 0 and t2.is_required = 1 where t2.category_id is null.

Same id, different name, how to get name by another name?

This is my table:
id name
1 j
1 jack
1 john
So I have j and want find all names which id is equal to the id of j in single SQL?
select name
from your_table
where id in
(
select id from your_table where name = 'j'
)
Self join time
select t1.name
from your_table t1
inner join your_table t2 on t2.id = t1.id
where t2.name = 'j'

SQL Select Data with condition on multiple rows

Suppose that I have a table called "tblTemp" with the following data
ID Name
1 A
2 B
3 C
4 A
4 B
5 A
5 B
5 C
6 C
6 B
I want to get ID from name of A&B only not A&B&C like below:
4 A
or
4 B
How can I do like this in sql?
I try the following sql but it return row 5 as well:
SELECT tblTemp.ID, tblTemp.Name
FROM tblTemp INNER JOIN
tblTemp AS tbltemp_1 ON tblTemp.ID = tbltemp_1.ID
WHERE (tblTemp.Name = 'A') AND (tbltemp_1.Name = 'B')
One of the ways to compare sets is to take the count of group, filter groups by search set, and see if number of matches per group equals original number of group members:
select tblTemp.ID
from tblTemp
inner join
(
select ID,
count(*) GroupCount
from tblTemp
group by ID
having count(*) = 2
) g
on tblTemp.ID = g.ID
where tblTemp.Name in ('A', 'B')
group by tblTemp.Id, g.GroupCount
having count (*) = g.GroupCount
This should work on both MySql and Sql Server.
You can play with this code # Sql Fiddle.
try:
SELECT distinct ID
FROM tblTemp a
LEFT JOIN tblTemp b
ON a.ID = b.ID AND
b.name = 'C'
WHERE b.ID IS NULL;
select id from table
group by id
having min(Name)='A' and max(Nmae)='B'
SELECT ID, Name FROM tblTemp WHERE (Name = 'A' OR Name = 'B') and ID not in(SELECT ID FROM tblTemp WHERE Name = 'C')
Do you just want a list of distinct IDs that have A or B but not C?
SELECT distinct ID
FROM tblTemp
WHERE Name = 'A' or Name = 'B'
EXCEPT
SELECT distinct ID
FROM tblTemp
WHERE Name = 'C'
My above solution work for 'A' and/or 'B', but I notice you actually want 'A' and 'B' with no or. In that case:
SELECT distinct ID
FROM tblTemp as T1
INNER JOIN tblTemp as T2
ON T1.ID = T2.ID
WHERE T1.Name = 'A' and T2.Name = 'B'
EXCEPT
SELECT distinct ID
FROM tblTemp
WHERE Name = 'C'
this is an extension of your original code and is perhaps not as elegant as #Madhivanan's solution but it is more general should A B and C change to words for example.