Say I had a MySQL table as pictured below, and I wanted to find any product_id's which have the filters 2 AND 5 (so in the table this would be the product_id of 30), is this possible in MySQL? Or would I have to get product id's where the filter_id is 2 OR 5, and sort it in PHP?
SELECT product_id, COUNT(*) AS i FROM mytable WHERE filter_id IN (2,5) GROUP BY product_id HAVING i = 2;
This will match any rows containing any of the required filter_ids and group together those with the same product_id. The name i has been given to a column giving the number of rows in the group. Assuming that every combination of product_id to filter_id will be unique then you know that any group with two rows will have matched both of the filter_ids, so the HAVING clause will finally filter out the rows that only matched one of the filter_ids.
Related
Want to find the total of two or more table columns
Use GROUP BY and then SUM(condition) to obtain your counts:
SELECT id AS ID,
SUM(total = 'total' ) AS Total,
FROM table
GROUP BY ID
Something like this should work.
I have 2 Tables:
1 - cat
Id|category
2 - groups
Id|groupname|category
I want to sort the data from the cat table by the number of rows in the category (group) table.
For example in groups table, category row have the following data:
Education - 20
Fun - 50
Nature - 30
I wanna show cat as:
Fun
Nature
Education
How can I do it?
Can't you just count the amount of rows in the one table and order your results by the resulting count?
SELECT
*,
(SELECT COUNT(*) FROM category WHERE category.cat_id=cat.id) as count
FROM cat
ORDER BY count DESC;
Note To get a better answer, you'll need to be more specific about your table structure and what you would like to achieve.
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.
Hi guy's I've been trying to figure this out for hours, but I can't seem to wrap my head around it..
Take a look at this sql table..
I'm trying to search for a product_id that exists in all filter_cat_id's, so in this example it would be product_id 30, because it exists with filter_cat_id 1 2 and 3.. Product_id 30 exists in the same filter_cat_id twice, otherwise I'd just count the product id's, then the distinct cat id's and make sure the count's match.
Any ideas? Is this possible in mysql? another option would be to use PHP.
Thanks!
I'm trying to search for a product_id that exists in all filter_cat_id's, so in this example it would be product_id 30, because it exists with filter_cat_id 1 2 and 3.
In general, this can be done by grouping by product_id and using HAVING COUNT(product_id) >= x – where x is the number of categories,
SELECT product_id FROM table GROUP BY product_id HAVING COUNT(product_id) >= 3
If you don’t know the number of categories upfront (so that you can insert the actual number into the query), you can get that value with a subquery,
SELECT product_id FROM table GROUP BY product_id
HAVING COUNT(product_id) >= (SELECT COUNT(DISTINCT category_id) FROM table)
Edit: OK, so apparently the category_ids to search for come from some kind of search form/filter mechanism.
In that case, the filtering of items has to be added – and the number to compare to must be embedded implicitly, but that should be no problem.
SELECT product_id FROM table
WHERE category_id IN(1,2,3)
GROUP BY product_id
HAVING COUNT(product_id) >= 3
SELECT
product_id, count(i.product_id) as t
FROM
(SELECT
*
FROM
tablename
GROUP BY product_id , filter_cat_id) as i
GROUP BY product_id
HAVING t = 3
This should be smart enough to determine the number of distinct categories, and select a product_id that has that number of distinct categories.
SELECT product_id
FROM
(SELECT DISTINCT product_id, filter_cat_id
FROM test.play) a
GROUP BY product_id
HAVING count(product_id) = (SELECT count(DISTINCT filter_cat_id) FROM test.play)
;
Test Data:
Query Result:
30
40
I'm not really sure how to phrase the question, so let me just give an example of the problem:
Suppose there's a table which maps items to categories. Each item can have any number of categories, and each category can of course hold any number of items. So you have a table that looks like this:
items_categories
id item_id category_id
The problem is, I want to select all item id's which have specific category id's. For example, select all item_id's with category_id's of 1 and 2: I want to find all items that are associated with categories both 1 and 2. Obviously I can't use an AND statement, and an OR statement would return all item_id's with either category, but not necessarily both.
Here is my solution and the best thing I can think of: select all item_ids with category_id equal to 1 OR 2; iterate through the results in PHP and keep track of how many item_ids are associated with a category_id; and then unset all item_ids in the results that don't have the specified number of categories. Here's a snippet of my code:
// assume $results is an array of rows from the db
// query: SELECT * FROM items_categories WHERE category_id = 1 OR category_id = 2;
$out = array();
foreach ($results as $result)
{
if (isset($out[$result['item_id']]))
$out[$result['item_id']] ++;
else
$out[$result['item_id']] = 1;
}
foreach ($out as $key=>$value)
{
if ($value != 2)
unset($out($key));
}
return array_keys($out); // returns array of item_ids
Obviously if you have lots of different categories, you're selecting and processing way more information than you should theoretically need to. Any ideas?
Thanks!
Edit: Here's an example of a table and the information I want from it:
id item_id category_id
1 1 1
2 1 2
3 2 1
4 3 2
So say I'm interested in getting all of the items with categories 1 and 2. How do I get item #1 from my example table, given that I want only items with categories #1 and #2? If I select everything with categories 1 or 2 (as in my example above), I have to select the whole table in this case and "manually" remove item_id's 2 and 3, since they aren't associated with both category 1 and category 2. Hope this helps clarify a little.
Final edit: I figured it out, despite my apparent inability to describe what I'm trying to do, heh. Here's the query I came up with, for the record:
SELECT *
FROM
(
SELECT item_id, COUNT(*) as count
FROM items_categories
WHERE category_id IN (1, 2)
GROUP BY item_id
) table_count
WHERE count = 2;
In this case, the "(1, 2)" could be replaced with "(category_id1, category_id2, ...)", and the "2" at the end would be replaced with the number of categories I'm searching for.
So it finds out how many categories match the criteria for each item, and since I only want items where ALL the categories match, it only selects those where the number of categories equals the number of categories I'm looking for. This is of course assuming there are no duplicate categories or anything like that.
Thanks for the responses!
It seems that what is troubling you is that you are forced to do a linear search which of course takes O(n) time, but if you select elements from your database in sorted order, then can't you just use a binary search in O(lg n) time?
I hope this helps, If not, then maybe I misunderstood your question and I'd like you to clarify it a little bit.
SELECT
foo
FROM
bar
WHERE
foo IN (1,2)
is this what you are looking for?
This is something you should be getting the database to do rather than PHP.
SELECT item_id # We want a list of item ids
FROM cat_items # Gets the item ID list from the cat_items table
WHERE cat_id IN (1, 2, 7, 11) # List of categories you want to search in
GROUP BY item_id; # As the same item can appear in more than one category this line will eliminate duplicates
This query does assume that the data in cat_items is accurate, in other words that the category and item IDs point to valid entries in the categories and items tables respectively. If you're using a database with foreign key support (The InnoDB engine for MySQL, Postgres, etc) enforcing foreign keys is not difficult.
To get a list of IDs in each category in the format you want, that's easily done on the SQL side too.
SELECT *
FROM cat_items
WHERE cat_id IN (1, 2, 7, 11)
GROUP BY cat_id, item_id;
If you just want a count of how many items are in each category you can also do that in SQL
SELECT cat_id, COUNT(item_id) AS items
FROM cat_items
WHERE cat_id IN (1, 2, 7, 11)
GROUP BY cat_id;
If you need more data than just the ID then you can join against the table you need the data from.
SELECT items.*
FROM cat_items
JOIN items ON cat_items.item_id = items.id
WHERE cat_id IN (1, 2, 7, 11)
GROUP BY item_id;
SELECT item_id FROM items_categories WHERE category_id = 1 AND item_id IN (SELECT item_id FROM items_categories WHERE category_id = 2)