I have a table that maps products to categories. Each product can belong to multiple categories, so a product can have multiple rows with different category IDs. It looks something like this:
CREATE TABLE `products_mid` (
`id` int(11) UNSIGNED NOT NULL,
`product_id` int(11) UNSIGNED NOT NULL DEFAULT '0',
`category_id` int(11) UNSIGNED NOT NULL DEFAULT '0'
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
If I want to get a list of all product IDs that are in category A that's simple enough-- but is there a way to get all product_ids where they belong to categories A, B and C as simply as possible?
A better approach is to do it with HAVING clause, not a WHERE clause. Group by product_id, and select rows having non-zero count of each of your three categories, like this:
SELECT product_id
GROUP BY product_id
HAVING SUM(CASE WHEN category_id=1 THEN 1 ELSE 0 END) > 0
AND SUM(CASE WHEN category_id=2 THEN 1 ELSE 0 END) > 0
AND SUM(CASE WHEN category_id=3 THEN 1 ELSE 0 END) > 0
The above statement selects product_ids of products that belong to all three categories with IDs 1, 2, and 3.
It will be something like this:
select product_id from products_mid
where
category_id in (1,2,3)
group by product_id
having count(distinct category_id)=3
Use the intersect operator to find the common values of three separate queries.
select product_id from products_mid where category_id = 1
intersect
select product_id from products_mid where category_id = 2
intersect
select product_id from products_mid where category_id = 3
Another way:
SELECT product_id
FROM products_mid
WHERE category_id IN (1, 2, 3)
GROUP BY product_id
HAVING COUNT(DISTINCT category_id) = 3
This would find all products in categories 1 through 3, although a product could also belong to other categories. If you want products which exclusively belong to category 1 through 3, and no others, then we would have to rephrase the query.
Related
So here I have multiple table and joined them but here I have put simple example like:
here i am able to find all those items who has category_id 1 and 3 any one using in query, but now i need and condition here like where category 1 and 3 both matches.
here i need all those items who has category_id 1 and 3 both.
So how to do that?
mysql in query is matching anyone exist, so is there any other function which matches multiple values?
Using INNER JOIN with a subquery to get the items that have both category_id 1 and 3. The subquery would select all the item_id's that have category_id 1 or 3 and the main query would join with the subquery on the item_id.
SELECT p1.item_id, p1.product_id, p1.category_id
FROM PRODUCT p1
INNER JOIN (
SELECT item_id
FROM PRODUCT
WHERE category_id IN (1, 3)
GROUP BY item_id
HAVING COUNT(DISTINCT category_id) = 2
) p2 ON p1.item_id = p2.item_id
WHERE p1.category_id IN (1, 3);
Sample:
-- create
CREATE TABLE PRODUCT (
id INTEGER PRIMARY KEY,
item_id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
category_id INTEGER NOT NULL
);
-- insert
INSERT INTO PRODUCT VALUES (3862, 297,0, 1);
INSERT INTO PRODUCT VALUES (3861, 297,0, 3);
INSERT INTO PRODUCT VALUES (3860, 23, 0,1);
Output:
item_id product_id category_id
297 0 3
297 0 1
I have a database where I save information about my products. I use a query for getting those products from my table. The query looks like this:
SELECT * FROM products WHERE stock > 0 ORDER BY RAND();
This query returns all the products that have stock > 0 in a random order, and it works ok. However, now I want to get those products with stock = 0, but I want them to appear at the end of the query (also in a random way but always after products that have stock > 0). So I tried a new query which looks like this:
(SELECT * FROM products WHERE stock > 0 ORDER BY RAND())
UNION
(SELECT * FROM products WHERE stock = 0 ORDER BY RAND());
...this query returns the zero-stock products at the end, but it seems to ignore the ORDER BY RAND() statement and I always get them in the same order. So my question is: how can I get a random response from the query mantaining the condition of zero-stock products at the end?
You don't need UNION:
SELECT *
FROM products
ORDER BY stock = 0, RAND();
The condition stock = 0 in the ORDER BY clause makes sure that the zero-stock products are placed last and the 2nd level of sorting with RAND() randomizes the rows in each of the 2 groups.
SQL Fiddle
Use a case statement to create a field to order by
e.g.
CREATE TABLE IF NOT EXISTS `products` (
`id` int(6) unsigned NOT NULL,
`stock` int(3) unsigned NOT NULL,
`product` varchar(200) NOT NULL,
PRIMARY KEY (`id`,`product`)
) DEFAULT CHARSET=utf8;
INSERT INTO `products` (`id`, `stock`, `product`) VALUES
('1', '10', 'Timber'),
('2', '12', 'Nails'),
('1', '0', 'Glue'),
('1', '0', 'Left handed wrench.');
And run
SELECT stock, product, case when stock > 0 then 1 else 2 end as SetOrder
FROM products
ORDER BY SetOrder, RAND()
Gets you
stock product SetOrder
10 Timber 1
12 Nails 1
0 Glue 2
0 Left handed wrench. 2
SQL Fiddle
I have a mapping table that maps certain ID's to eachother (products and categories). Now I need to check whether or not in this mapping category there are multiple rows. For example every product has a standard mapping of category id of 1. Now there could be more rows that have other category id's. For example:
Product_id Category_id
1 1
1 2
2 1
3 1
4 1
4 2
I need to only select those rows that only have the product id and the category id of 1. So in this case I want to selct product with id 2,3. Because 1 and 4 have multiple category id's.
I have this query (it's a join because I want to add some other data in this query):
SELECT * FROM `products` as P
LEFT JOIN `product_categories` as PC
ON PC.`product_id` = P.`product_id`
WHERE PC.`category_id` = 1 AND
LIMIT 10
Now I don't know exacly what I need to do in the Where statement. Can anyone help me?
I thought about using count, but I don't think that is the best solution. Maybe check if there are other values other than 1 in the category id or something?
I have considered your table as a reference. You can add or perform join after that.
You can not add where here. The count is a group function (also called aggregate function). So you have to add having instead of where.
Creating the table:
CREATE TABLE IF NOT EXISTS `test` (
`product_id` int(6) unsigned NOT NULL,
`category_id` int(6) unsigned NOT NULL
) DEFAULT CHARSET=utf8;
INSERT INTO `test` (`product_id`, `category_id`) VALUES
(1,1),
(1,2),
(2,1),
(3,1),
(4,1),
(4,2)
Result generation SQL:
select product_id,category_id
from test group by product_id having count(product_id)<2
Output:
product_id category_id
---------- ------------
2 1
3 1
I am using four tables, I will call them "products", "attributes_categories", "attributes" and "product_has_attributes". Every product in "products" has many attributes in "product_has_attributes", like this:
[product_has_attributes] : product1
attribute_id=1 (Brands->Sony)
attribute_id=4 (Screen size -> 20")
attribute_id=7 (Colors -> Black)
attribute_id=8 (Colors -> White)
products and product_has_attributes are left joined on products.id = product_has_attributes.product_id
A simple SELECT correctly returns each product as many times as its attributes.
Now, I want to select all products that have:
product_has_attributes.attribute_id=1 AND
product_has_attributes.attribute_id=4 AND
(product_has_attributes.attribute_id=7 OR
product_has_attributes.attribute_id=8)
But, as it's expected, product_has_attributes.attribute_id can't be 1 AND 4 AND (7 OR 8) at the same time... So no records are returned.
How can I build the SQL so it returns the records in the logic I described?
Thank you,
George
You could use something like this. It's not massively efficient.
select * from products
where product_id in (
select product_id from product_has_attributes where attribute_id = 1
)
and product_id in (
select product_id from product_has_attributes where attribute_id = 4
)
and product_id in (
select product_id from product_has_attributes where attribute_id in (7, 8)
)
I have the following table setup in mysql:
CREATE TABLE `games_characters` (
`game_id` int(11) DEFAULT NULL,
`player_id` int(11) DEFAULT NULL,
`character_id` int(11) DEFAULT NULL,
KEY `game_id_key` (`game_id`),
KEY `character_id_key` (`character_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
My objective is to get a game_id where a list of character_ids are all present in this game_id.
An example set of data:
1, 1
1, 2
1, 3
2, 1
2, 2
3, 1
3, 4
Let's say i want to get the game_id where the character_id has 1, 2, and 3. How would I go about making an efficient query? Best idea I have had so far was joining the table to itself multiple times, but i assume there has to be a better way to do this.
Thanks
EDIT: for anyone curious this was the final solution I used as it proved the best query time:
SELECT game_ID
FROM (
SELECT DISTINCT character_ID, game_ID
FROM games_Characters
) AS T
WHERE character_ID
IN ( 1, 2, 3 )
GROUP BY game_ID
HAVING COUNT( * ) =3
Select game_ID from games_Characters
where character_ID in (1,2,3)
group by game_ID
having count(*) = 3
the above makes two assumptions
1) you know the characters your looking for
2) game_ID and character_ID are unique
I don't assume you can get the #3 for the count I knnow you can since you know the list of people you're looking for.
This ought to do it.
select game_id
from games_characters
where character_id in (1,2,3)
group by game_id
having count(*) = 3
If that's not dynamic enough for you you'll need to add a few more steps.
create temporary table character_ids(id int primary key);
insert into character_ids values (1),(2),(3);
select #count := count(*)
from character_ids;
select gc.game_id
from games_characters as gc
join character_ids as c
on (gc.character_id = c.id)
group by gc.game_id
having count(*) = #count;