MYSQL: Find rows where multiple ID match - php

I have a table setup similarly as below.
genre_id series_id
1 4
1 2
2 5
4 1
2 4
3 3
What I want to do is to be able to find all series based on the mix of genres selected.
For example finding all series that have a genre id of 1 and 2. Hypothetically the result I want is the series ID of 4.
If I use
SELECT series_id FROM table WHERE genre_id = 1 AND genre_id = 2
it returns nothing.
If I use
SELECT series_id FROM table WHERE genre_id in (1, 2)
it returns everything in 1 and 2. But I just want the rows where the genre_id's intersect.
Is there any way to do this?

This should do the trick:
SELECT series_id FROM table
WHERE genre_id IN (1, 2)
GROUP BY series_id
HAVING COUNT(*) = 2
Note this is assuming that the pair (genre_id, series_id) is unique. If it is not you will have to change the HAVING clause to
HAVING COUNT(DISTINCT genre_id) = 2
Also note that the number 2 in the HAVING clause must match the amount of items in the IN clause.

You can think of the IN() predicate as basically a series of OR terms; it's equivalent to
SELECT series_id
FROM MyTable
WHERE genre_id = 1 OR genre_id = 2
What you want is to turn the OR into AND, but that doesn't make any sense because a WHERE expression applies to one row at a time. There's no way genre_id can be both 1 and 2 on the same row.
So you need to compare genre_id from two different rows in one expression. You can do this by joining the two rows:
SELECT t1.series_id
FROM MyTable AS t1
INNER JOIN MyTable AS t2 USING (series_id)
WHERE t1.genre_id = 1 AND t2.genre_id = 2
There's also a solution using GROUP BY as shown in another answer, but the self-join can be orders of magnitude more efficient if you define the right indexes.
I describe more details for this solution in my presentation SQL Query Patterns, Optimized.

Related

How to select particular column from table if another column includes all the set of requested values?

I have a table in MySQL named table1 which has three columns id, user_id(foreign_key) and destination_id(foreign_key). One user can have multiple destinations.
E.g Table1
id user_id destination_id
1 10 2
2 5 3
3 10 4
4 10 5
5 9 10
6 5 12
7 8 2
I get a request from the client side in PHP script; the request includes destination ids in an array.
E.g. $request = array('destination_id' => [2,4,5]);
I just want to get all the user_id from table1 if and only if the particular user_id contains all requested destinations.
I tried to achieve this using 'IN' operator.
i.e.
SELECT user_id FROM table1 WHERE destination_id IN ($requestedDestinationsInCommaSeparatedString)
It gives row including user_id 8 along with user_id 10 but I just need user_id 10. I just wanted to know the concept regarding the solution to the following problem. I am a beginner in SQL, any help would be very appreciable. Thanks.
You can check that a user_id refers to all requested destination by grouping and counting the destinations.
SELECT user_id
FROM table1
WHERE
destination_id IN (2,4,5)
GROUP BY
user_id
HAVING count(*) = 3
-- count must be the number of elments in (2,4,5)
For doing so, the field combination of user_id and destination_id must be unique over all records.
The only thing I can think of is to use multiple subselects and build the query string in PHP.
So for your specific example the SQL-Query-String generated should be
SELECT user_id
FROM table1
WHERE user_id IN
(SELECT user_id FROM table1 WHERE destination_id = 2)
AND user_id IN
(SELECT user_id FROM table1 WHERE destination_id = 4)
AND user_id IN
(SELECT user_id FROM table1 WHERE destination_id = 5)
GROUP BY user_id
I think programming the function which generates the middle part for you shouldn't be too hard.

MySql: Get number of row from query where id matches

I have a complicated select like:
select id from table
left join...
left join... (a lot of joins)
where ... (a lot of ANDs and ORs)
order by... (a lot of orders)
and I'm getting a result like:
1234
5565
7212
2212
etc.
I have an id which belongs to the result-set, like 7212, and want to know which row in the result-set matches the id (starting with row 0 this would be row 2 in my example).
Right now I'm reading all data and compare it in php, but I was wondering if there is a SQL-statement which does that for me and results 2 when entering 7212.
In fact I want to get the previous and next ids (row 1 and row 3 = 5565 and 2212), if thats somehow possible in 1 query that would be even better.
You can select the number row:
select #rownum:=#rownum+1 No, foo, bar from table, (SELECT #rownum:=0) r;
It's a possible duplicate of the question asked here: How to show sequential number in MySQL query result
Add an auto_increment index for each selected rows:
SELECT
#i:=#i+1 as index,
id
FROM table, (SELECT #i:= 0) AS i
LEFT JOIN...
LEFT JOIN...(a lot of joins)
WHERE ... (a lot of ANDs and ORs)
ORDER BY... (a lot of orders)
Give you this:
index id
1 1234
2 5565
3 7212
4 2212

SQL get count of all rows that have the same id

I have a table called story_comment with (integer) columns story_id, and comment_id. I want to know how many comments each story has but I'm not sure the best way to write the sql query.
The query should return a table with the columns story_id and num_comments (where num_comments is the number of rows in story_comment where story_id is the same as the story_id in that results row).
Desired Results (Example):
story_id | num_comments
4 | 17
6 | 0
7 | 4
I was able to do this for one particular story_id with the query:
SELECT story_id, Count(story_id) as num_comments FROM story_comment where story_id=20;
but I'm not sure how I can do this for every story_id in the table. Side note I'm going to be doing this query using mysqli in php.
Use GROUP BY
SELECT story_id, Count(story_id) as num_comments FROM story_comment
GROUP BY story_id
The GROUP BY statement is used in conjunction with the aggregate
functions to group the result-set by one or more columns.
To make aggregate functions like count() apply to each unique value of a column instead to the complete table, add a group by
SELECT story_id, Count(*) as num_comments
FROM story_comment
group by story_id

Search for all combinations of values in a column from a defined subset

I have a table with the following schema in MySQL
Recipe_Quantity_ID,Recipe_ID, Recipe_Quantity, Ingredients_ID, Ingredient_Measurement_ID
The sample data can be found in this SQL Fiddle http://sqlfiddle.com/#!2/d05fe .
I want to search the table for the given (one or many) Ingredients_ID and return the Recipe_ID that has this Ingredients_ID
I do this by using this SQL
select Recipe_ID
from recipe_quantity
group by Recipe_ID
having count(*) = {$ar_rows}
and sum(Ingredients_ID in ({$ids})) = {$ar_rows}
which may translate to
select Recipe_ID
from recipe_quantity
group by Recipe_ID
having count(*) = 4
and sum(Ingredients_ID in (8,5,45,88)) = 4
For searching for less Ingredients_ID I substract the last ID until I reach one Ingredient ID. By using this technique of course is not possible to search for all the combinations. Eg 8,5,45,85 | 8,45,85 | 45,85 | 5,45,85 etc.
Any ideas how I can search for all the combinations that may be true? Thanks in advance.
My understanding is that you want to get all recipes where you already have all the ingredients you need. you don't need to use all the ingredients you have but you don't want to have to go shopping.
Correct me if I am wrong but I don't think there is a recipe that fits your ingredients list so I have used other ingredients. note that ingredients 13,36 wont be used.
you should be able to put another select statement in the brackets that gets the ingredients that you have (select ingredients_id from ingredients_owned) it isn't good to specify them each time.
select distinct c.Recipe_id
from
(select Recipe_ID
from recipe_quantity
where Ingredients_ID in (5,6,1,11,8,12,13,36, 81,82,62,73,35)) c
left join (select Recipe_ID
from recipe_quantity
where Ingredients_ID not in (5,6,1,11,8,12,13,36, 81,82,62,73,35)) x
on c.Recipe_id = x.Recipe_id
where x.Recipe_id is null
How about something like this?
select Recipe_ID, group_concat(Ingredients_ID), count(*) as ingredients
from recipe_quantity
where Ingredients_ID IN (8,5,45,88)
group by Recipe_ID
having ingredients > 0
order by ingredients desc
Instead of grouping all recipe ingredients and then filtering out the ones that don't include the ingredients you're looking for, I match only the entries in recipe_quantity that match the ingredients in the first place. I use a group_concat so you can see the set of ingredients that match.
This orders by the number of ingredients that match, but still preserves partial matches on one or more ingredients. You change the 0 to the minimum number of ingredients to match.

MySQL Inner Join Returning Multiples of the Same Row

I have two MySql Tables as follows:
resource
-----------------------------------------------
id name group owner_id
-----------------------------------------------
1 MyResource1 hs 11
2 MyResource2 ms 24
3 MyResource3 ps 11
...
resource_access
-----------------------------------------------
id resource_id user_id
-----------------------------------------------
1 1 12
2 2 24
3 2 11
4 3 15
...
Now, the first table is a list of resources, of course, and their respective owners in the owner_id column. The second table is the result of "sharing" this resource with another user. The table resource_access may contain records with a user_id that is equivalent to the owner_id in a row of the resource_access as a result of messy cleanup from an owner exchange.
I simply want to get the id, name, and group of any resource that a user has access to, whether they are the owner or it has been shared with them. Here is my MySQL query for an example user (24):
SELECT resource.id, resource.name, resource.group
FROM `resource`
INNER JOIN resource_access ON (
resource.owner_id='24'
OR (
resource_access.user_id='24' AND
resource_access.resource_id=resource.id
)
)
Right now, it returns the id, name, and group for resource number 2 multiple times (like twelve). Is there a possible cause for this? I have tried LEFT and RIGHT joins and am getting the same result. There are many records in the resource table, but none with the id of 2. There are no duplicate rows in resource_access sharing the resource with the same user twice.
Thanks in advance.
Use:
SELECT DISTINCT resource.id, resource.name, resource.group
to remove duplicates.
The way an inner join conceptually works is that it produces a full cross-product between the two tables. This cross-product contains a row for each pair of rows in the input tables. Then it keeps the rows that match all the ON and WHERE conditions, and returns this as the result set. If there are multiple matching rows between the two tables, you'll get multiple rows in the result set.
If you were selecting columns from both tables, you would see that they're not actually the same row. They just have the same data from the resource table, but different data from the resource_access table. But you're not showing those latter columns in your result. Using DISTINCT merges all these rows in the result.
Because you are only selecting from the resource table, I would suggest putting the conditions in the where clause rather than using an explicit join:
SELECT r.id, r.name, r.group
FROM `resource` r
WHERE r.owner_id='24' or
EXISTS (select 1
from resource_access ra
where ra.resource_id = r.id and
ra.user_id = '24'
);
With this logic, the "join" cannot product duplicates.
Select the ownership of resources then union it to resources with access.
Resulting user_id column that is different from your WHERE RA.user_id value just means that resource was shared to them instead of them owning the resource. Hope this helps.
SELECT resource.name,resource.group,resource.owner_id AS user_id
FROM resource
WHERE resource.owner_id = '11'
UNION
SELECT R.name,R.group,R.owner_id AS user_id
FROM resource_access RA
LEFT JOIN resource R
ON (R.id=RA.resource_id)
WHERE RA.user_id = '11';

Categories