MySQL get all related IDs from a table matching an array - mysql

I'm facing a problem while trying to retrieve all productIDs from a table if they match all items in an array, in this case, return products only if they contain every ingredient the user searched for.
Table looks like this
ID produktID ingredientID
----------------------------
1 418 1
2 418 2
3 418 3
4 416 4
5 411 1
6 411 5
7 411 6
I join this table from a products table where the main information is stored. The aim of the query should be to retreive a productID only when all ingredientIDs match with the given array. I've tried using WHERE ingredientID IN(1,5,6) but it always turns out to be an OR statement, returning every ID where any of the ingredients are matched.
So for example, if I pass (1,5,6) or (5,6) the product ID 411 should be returned, but if I pass (2,5,6) it should not.
The query I tried looks like this (simplified, it's part of a 5 way join to other relations like brands and catgories)
SELECT productID FROM products_ingredients_mm WHERE ingredientID IN (1,5,6) GROUP BY productID
but the result contains 418 aswell. How do I get it to match?
I hope I was able to describe the problem in an understandable way, it's really hard for me to wrap my head around it to ask a question.

This is called Relational Division.
SELECT produktID
FROM tableName
WHERE ingredientID IN (1,5,6)
GROUP BY produktID
HAVING COUNT(*) = 3
SQLFiddle Demo
If a unique constraint was not enforce on ingredientID for every produktID, then you need to use DISTINCT
SELECT produktID
FROM tableName
WHERE ingredientID IN (1,5,6)
GROUP BY produktID
HAVING COUNT(DISTINCT ingredientID) = 3
SQLFiddle Demo
Other Source
Relational Division

Try this:
SELECT pi.productID, p.productName
FROM products_ingredients_mm pim
INNER JOIN products p ON pim.productID = p.productID
WHERE ingredientID IN (1,5,6)
GROUP BY productID
HAVING COUNT(DISTINCT ingredientID) = 3

Related

With SQL how to select rows that the joined table matches 2 values

If we have a table, orders:
Order ID
Order Table
1
100
1
50
And we have a table, OrderProducts:
OrderID
Product ID
ProductName
ProductType
1
1
ProductOne
Small
1
2
ProductTwo
Big
1
3
ProductThree
Small
2
4
ProductFour
Big
2
5
ProductFive
Big
How do I use SQL to return only the Orders that contain products of 2 specific types. In the scenario above I want to return only products with ProductType = 'Small' and 'Big' but ignore orders like "2" which only have products of two 'Big' on.
Results:
Order ID
1
I have tried creating a view to help, inner joins with multiple clauses but I am struggling. I am using MySQL for this and think my logic is simply "out".
Any advice is appreciated.
Aggregation is one method:
select order_id
from orderproducts op
where ProductType in ('Small', 'Big')
group by order_id
having count(distinct ProductType) = 2;

Joining multiple columns into one with union, exclude results with same id

I want to join columns from multiple tables to one column, in my case column 'battery_value' and 'technical_value' into column 'value'. I want to fetch data for only given category_ids, but because of UNION, I get data from other tables as well.
I have 4 tables:
Table: car
car_id model_name
1 e6
Table: battery
battery_category_id car_id battery_value
1 1 125 kW
Table: technical_data
technical_category_id car_id technical_value
1 1 5
3 1 2008
Table: categories
category_id category_name category_type
1 engine power battery
1 seats technical
3 release year technical
From searching, people are suggesting that I use union to join these columns. My query now looks like this:
SELECT CARS.car_id
category_id,
CATEGORIES.category_name,
value,
FROM CARS
left join (SELECT BATTERY.battery_category_id AS category_id,
BATTERY.car_id AS car_id,
BATTERY.value AS value
FROM BATTERY
WHERE `BATTERY`.`battery_category_id` IN (1)
UNION
SELECT TECHNICAL_DATA.technical_category_id AS category_id,
TECHNICAL_DATA.car_id AS car_id,
TECHNICAL_DATA.value AS value
FROM TECHNICAL_DATA
WHERE `TECHNICAL_DATA`.`technical_category_id` IN (3))
tt
ON CARS.car_id = tt.car_id
left join CATEGORIES
ON category_id = CATEGORIES.id
So the result I want is this, because I only want to get the data where category_id 1 is in battery table:
car_id category_id category_name technical_value
1 1 engine power 125 kW
1 3 release year 2008
but with the query above I get this, category_id 1 from technical table is included which is not something I want:
car_id category_id category_name value
1 1 engine power 125 kW
1 1 seats 125 kW
1 3 release year 2008
How can get exclude the 'seats' row?
For the results you want, I don't see why the cars table is needed. Then, you seem to need an additional key for the join to categories based on which table it is referring to.
So, I suggest:
SELECT tt.*, c.category_name
FROM ((SELECT b.battery_category_id AS category_id,
b.car_id AS car_id, b.value AS value,
'battery' as which
FROM BATTERY b
WHERE b.battery_category_id IN (1)
) UNION ALL
(SELECT td.technical_category_id AS category_id,
td.car_id AS car_id, td.value AS value,
'technical' as which
FROM TECHNICAL_DATA td
WHERE td.technical_category_id IN (3)
)
) tt LEFT JOIN
CATEGORIES c
ON c.id = tt.category_id AND
c.category_type = tt.which;
That said, you seem to have a problem with your data model, if the join to categories requires "hidden" data such as the type. However, that is outside the scope of the question.

MySQL related items query

I have related products table like this:
product_id | related_product_id
1 | 2
1 | 3
1 | 4
2 | 1
3 | 1
4 | 1
But instead I would like to insert new related product ids so they all match. I.E. If product 1 has 2,3,4 I wan't that products 2,3,4 also have the same related ids which are missing.
Not sure how it's called but is this possible? Many thanks.
You can use a SELECT query as the source of data in an INSERT
INSERT INTO related_products (product_id, related_product_id)
SELECT r1.product_id, r2.related_product_id
FROM related_products AS r1
CROSS JOIN related_products AS r2
WHERE r1.product_id != 1
AND r2.product_id = 1
This join will get all of product 1's related products and combine them with all the other product IDs.
You can give this query a try (untested, make a backup first!):
insert into related_products (product_id,related_product_id) (select related_product_id, product_id from related products);
I would suggest to user bidirectional condition to get the inter related products. For example if you apply condition on single column product_id, you will not get visa-versa result. But, if you check that condition on both column, you will get the result.
For example:
select related_product_id, product_id from products where related_product_id=1 OR product_id=1
This will give your related product id in either related_product_id or product_id.
Same you can get it for product 2 i.e.
select related_product_id, product_id from products where related_product_id=2 OR product_id=2
This will give all your related product id in either related_product_id or product_id.

Select From table name obtained dynamically from the query

I have 3 Tables
campaign1 (TABLE)
id campaign_details
1 'some detail'
campaign2 (TABLE)
id campaign_details
1 'some other detail'
campaign_list (TABLE)
id campaign_table_name
1 'campaign1'
2 'campaign2'
Campaign list table contains the table name of the two tables described above. I want to Select from the Campaign List table and get the record count using the table name i get from this select
For eg.
using select i get campaign1(Table name). Then i run select query on campaign1 to count number of records.
What i'm doing right now is .
-Select from campign_list
-loop through all campaign_table_names and run select query individually
Is there a way to do this using a single query
something like this
select campaign_name,(SELECT COUNT(*) FROM c.campaign_name) as campcount from campaign_list c
SQLFiddle : http://sqlfiddle.com/#!9/b766d/2
It's not possible inside a single query to build it dynamically but it's possible to cheat. Especially if there are only two linked tables.
I've listed two options
left outer join both tables
select campaign_name,
coalesce(c1.campaign_details, c2.campaign_details)
from campaign_list c
left join campaign1 c1 using (id)
left join campaign2 c2 using (id);
union all two different selects
select campaign_name,
campaign_details
from campaign_list c
join campaign1 c1 using (id)
union all
select campaign_name,
campaign_details
from campaign_list c
join campaign2 c2 using (id);
sqlfiddle
Combine your campaign tables to 1 table and add an column named 'type' (int).
campaign_items tables:
item_id item_details item_type
1 'some detail' 1
2 'some detail' 1
3 'some other detail' 2
4 'some other detail' 2
campaign_lists table
campaign_id campaign_name
1 'campaign1'
2 'campaign2'
Then you can use the following select statement:
SELECT campaign_name, (SELECT COUNT(*) FROM campaign_items WHERE item_type = campaign_id) as campaign_count
FROM campaign_lists
Oops, writing took me so long that you got this answered by Colin Raaijmakers already. Well, I'll post my answer anyway in spite of being more or less the same answer. Maybe my elaboration helps you see the problem.
Your problem stems from a bad database design. A database is made to order data and its relations. A CD database holds albums, songs, artists, etc. A business database may hold items, warehouses, sales and so on. Your database holds table names. [... time for thinking :-) ]
(When writing a DBMS you would want to store table names, column names, constraints etc., but I guess I am right supposing that you are not writing a new DBMS.)
So create tables that deal with your actual data. E.g.:
campain_type (id_campain_type, description, ...)
campain (id_campain, id_campain_type, campain_date, ...)
campain_type
id_campain_type description
1 Type A
2 Type B
3 Type C
campain
id_campain id_campain_type date
33 1 2015-06-03
85 2 2015-10-23
97 2 2015-12-01
query
select
ct.description,
(select count(*) from campain c where c.id_campain_type = ct.id_campain_type) as cnt
from campain_type ct;
result
description cnt
Type A 1
Type B 2
Type C 0

Selecting a value from multiple rows in MySQL

I have a little problem with my SQL sentence. I have a table with a product_id and a flag_id, now I want to get the product_id which matches all the flags specified. I know you have to inner join it self, to match more than one, but I don't know the exact SQL for it.
Table for flags
product_id | flag_id
1 1
1 51
1 23
2 1
2 51
3 1
I would like to get all products which have flag_id 1, 51 and 23.
get the product_id which matches all the flags specified
This problem is called Relational Division. One way to solve it, is to do this:
GROUP BY product_id .
Use the IN predicate to specify which flags to match.
Use the HAVING clause to ensure the flags each product have,
like this:
SELECT product_id
FROM flags
WHERE flag_id IN(1, 51, 23)
GROUP BY product_id
HAVING COUNT(DISTINCT flag_id) = 3
The HAVING clause will ensure that the selected product_id must have both the three flags, if it has only one or two of them it will be eliminated.
See it in action here:
SQL Fiddle Demo
This will give you only:
| PRODUCT_ID |
--------------
| 1 |
try this:
SELECT *
FROM your_table
WHERE flag_id IN(1,2,..);
Firstly it would help if you can specify what you have tried before, but as I understood you need to get products with certain flags, so you can just use WHERE:
SELECT product_id FROM Product WHERE flag_id IN (1,2,3,4,5)
Try:
SELECT *
FROM TABLE_NAME A INNER JOIN TABLE_NAME B ON A.product_id = B.product_id