MySQL: Select multiple rows containing values from one column - mysql

I'd like to find the car_id's of the cars that have 'FORD' AND 'SILVER' AND the user input value of '200' in the value column:
table_cars
+----+--------+----------+-----------+
| id | car_id | name | value |
+----+--------+----------+-----------+
| 1 | 1 | MAKE | FORD |
| 2 | 1 | CARLINE | FIESTA |
| 3 | 1 | COLOR | SILVER |
| 4 | 1 | TOPSPEED | 210KM/H |
| 5 | 2 | MAKE | FORD |
| 6 | 2 | CARLINE | FOCUS |
| 7 | 2 | COLOR | SILVER |
| 8 | 2 | TOPSPEED | 200KM/H |
| 9 | 3 | MAKE | HOLDEN |
| 10 | 3 | CARLINE | ASTRA |
| 11 | 3 | COLOR | WHITE |
| 12 | 3 | TOPSPEED | 212KM/H |
+----+--------+----------+-----------+
Which in this case should return only one car_id: car_id = 2.
What would be the way to go to create the SQL query for this?

What you have is a properties table. When you want to test multiple properties at once you need to join the table to itself:
SELECT c0.car_id
FROM table_cars AS c0
JOIN table_cars AS c1 ON c1.car_id=c0.car_id
JOIN table_cars AS c2 ON c2.car_id=c1.car_id
WHERE c0.name='MAKE' AND c0.value='FORD'
AND c1.name='COLOR' AND c1.value='SILVER'
AND c2.name='TOPSPEED' AND c2.value='200KM/H'
Having the surrogate id present in a properties table is questionable. It doesn't seem to be doing anything; each property isn't an entity of its own. Unless the id is required by some other element, I'd get rid of it and make car_id, name the primary key (a composite primary key).

I assume that every car needs to have variable parameters, otherwise you wouldn't have gone with a setup like this. It would be much easier if MAKE, CARLINE, COLOR, and TOPSPEED each had their own column.
Using the table you've provided, however, you need to use subqueries. http://dev.mysql.com/doc/refman/5.0/en/subqueries.html
The query should look something like this (untested):
SELECT * FROM table_cars WHERE id IN (SELECT * FROM table_cars WHERE name="MAKE" AND value="FORD") AND id IN (SELECT * FROM table_cars WHERE name="COLOR" AND value="SILVER") AND id IN (SELECT * FROM table_cars WHERE name="TOPSPEED" AND value="200KM/H")

Related

Union tables into 1 with a flag indicating the respective table entries

i have 2 tables.
1.home_shop
+---+------------+
|id | product |
+---+------------+
| 1 | soap |
| 2 | cake |
| 3 | biscuit |
+---+------------+
2.office_shop
+---+------------+
|id | product |
+---+------------+
| 1 | key |
| 2 | lock |
| 3 | pen |
+---+------------+
what i want is union this two tables into a new table "complete_shop" with a flag indicating "home" and "office"
for example:
+---+------------+-------------+
|id | product | flag |
+---+------------+-------------+
| 1 | soap | home |
| 1 | key | office |
| 2 | cake | home |
| 2 | lock | office |
| 3 | biscuit | home |
| 3 | pen | office |
+---+------------+-------------+
how do i do this union in mysql please help me. i am a beginner
Do a UNION query and introduce the flag column using the appropriate values.
SELECT id, product, 'home' AS flag
FROM home_shop
UNION ALL
SELECT id, product, 'office' AS flag
FROM office_shop
ORDER BY id, flag
Note that you don't need to use a subquery to order here, you can just specify the columns you want to use.

Find unique/duplicated rows from has and belongs to many association

I have following DB structure:
Table cars:
+----+-----------------------+
| id | few other columns.... |
+----+-----------------------+
| 1 | ... |
| 2 | ... |
| 3 | ... |
+----+-----------------------+
Table properties:
+----+-------+
| id | name |
+----+-------+
| 1 | title |
| 2 | type |
| 3 | brand |
| 4 | color |
+----+-------+
Table cars_properties:
+----+--------+-------------+------------+
| id | car_id | property_id | txt |
+----+--------+-------------+------------+
| 1 | 1 | 1 | Volvo V70 |
| 2 | 1 | 2 | personal |
| 3 | 1 | 3 | Volvo |
| 4 | 1 | 4 | white |
| 5 | 2 | 1 | Volvo VV |
| 6 | 2 | 2 | personal |
| 7 | 2 | 3 | Volvo |
| 8 | 2 | 4 | blue |
| 9 | 3 | 1 | Volvo XXL |
| 10 | 3 | 2 | truck |
| 11 | 3 | 3 | Volvo |
| 12 | 3 | 4 | white |
+----+--------+-------------+------------+
I would like to get all cars that have unique/duplicated values in one or many properties. Currently I'm using this SQL pattern to get duplicates for car type and brand:
SELECT cars.id FROM cars
LEFT JOIN cars_properties AS cp_0 ON cp_0.car_id = cars.id AND cp_0.property_id = 2 # => type
LEFT JOIN cars_properties AS cp_1 ON cp_1.car_id = cars.id AND cp_1.property_id = 3 # => brand
INNER JOIN (
SELECT cp_0.txt AS type_txt, cp_1.txt AS brand_txt FROM cars
LEFT JOIN cars_properties AS cp_0 ON cp_0.car_id = cars.id AND cp_0.property_id = 2
LEFT JOIN cars_properties AS cp_1 ON cp_1.car_id = cars.id AND cp_1.property_id = 3
GROUP BY cp_0.txt, cp_1.txt
HAVING COUNT(cars.id) > 1
) dupes ON cp_0.txt=dupes.type_txt AND cp_1.txt=dupes.brand_txt;
And expected result is:
+----+
| id |
+----+
| 1 |
| 2 |
+----+
Explanation: Both cars with id = 1 and 2 has type and brand that is present in more than one car (multiple times).
As for unique cars, I'm just altering: HAVING COUNT(cars.id) = 1 and I want to find all rows where the combination of properties is present only in one car (once).
It works fine, but it's extremely slow with more than 2 properties I want to check.
I cannot change the DB structure, and I'm not sure how to optimize the query, or if there are better ways of achieving this.
It feels like I would need to implement counter table, where each property id and value (txt) would also store corresponding number of occurrences in cars, and update this counter on every insert/update/delete... But I still hope there is some better SQL, that could help. Do you know some? Any advice greatly appreciated, thanks!
PS: I tried to create fiddle for it, but after I build schema I cannot run any SQL on it. To quickly setup DB with data, you can check SQL Fiddle

ORDER BY - Identical values

Here is a table structure example:
// tablename
+----+------+---------+
| id | numb | color |
+----+------+---------+
| 1 | 4 | green |
| 2 | 4 | yellow |
| 3 | 3 | red |
+----+------+---------+
Here is a query example:
SELECT id, numb, color FROM tablename ORDER BY numb asc
The result will be:
+----+------+---------+
| id | numb | color |
+----+------+---------+
| 3 | 3 | red |
| 1 | 4 | green |
| 2 | 4 | yellow |
+----+------+---------+
Now, my focus is on the order of these rows:
| 3 | 4 | green |
| 2 | 4 | yellow |
Because their numb values are equal, Now I want to know, for several executing that query, they will be constant? (Is order guaranteed for the identical values?) Or there isn't any guarantee and I should use another column name in the query like this ORDER BY numb, id asc ?
Short answer: No, there is no guarantee. (as #Strawberry wrote under the question)
Full answer: You can add a new column named sort_identical, And fill it whatever you like. And then use this:
... ORDER BY numb, sort_identical asc
(Also you can use id instead of creating a new column - But if you need to sort it differently than id, then create a new column)
+----+------+---------+----------------+
| id | numb | color | sort_identical |
+----+------+---------+----------------+
| 3 | 3 | red | 1 |
| 1 | 4 | green | 2 |
| 2 | 4 | yellow | 3 |
+----+------+---------+----------------+

Simplifying MySQL query - 2 queries into 1

I have a table that looks like this:
+----+--------+-------+
| id | entity | word |
+----+--------+-------+
| 1 | 1 | red |
| 2 | 1 | green |
| 3 | 1 | blue |
| 4 | 2 | car |
| 5 | 2 | truck |
| 6 | 2 | train |
| 7 | 3 | water |
| 8 | 3 | milk |
| 9 | 3 | soda |
+----+--------+-------+
If I do a search for blue I would like to get red, green and blue as an answer. Right now I am using 2 queries. One to find the 'entity' number and one to find all the words with the same 'entity' number.
Try this. Join is much faster than subquery
select distinct t2.word from Table t1
INNER JOIN Table t2 on t2.entity=t1.entity
where t1.word="blue";
SELECT *
FROM TABLE_NAME
WHERE entity IN
(SELECT entity
FROM TABLE_NAME
WHERE word='blue');

MySQL Database - Performance Design

I'm currently redesign a heavy loaded website, and I would appreciate any opinion about a specific database design issue.
The concept is to keep in the db a number of products (500K of them).
Every product can have a number of dynamic properties (around 1K), and every property a number of predefined but dynamic values (lets say 10 on average for every property, so around 10K)
At this point of time this is the simplified db structure:
Products (Products Table)
+--------+--------------+
| ProdID | Product Name |
+--------+--------------+
| 1 | T-Shirt XYZ |
+--------+--------------+
| 2 | Dress ABC |
+--------+--------------+
| ... | ... |
+--------+--------------+
| 500000 | Something |
+--------+--------------+
Properties Definition (Props Table) (it holds the Property Types)
+--------+--------------+
| PropID | Property Name|
+--------+--------------+
| 1 | color |
+--------+--------------+
| 2 | size |
+--------+--------------+
| ... | ... |
+--------+--------------+
| 100 | Some Prop |
+--------+--------------+
Properties Values Definition (Values Table)
+-----------+--------+-------+
| PropValID | PropID | Value |
+-----------+--------+-------+
| 1 | 1 | red |
+-----------+--------+-------+
| 2 | 1 | blue |
+-----------+--------+-------+
| 3 | 2 | m |
+-----------+--------+-------+
| 4 | 2 | xl |
+-----------+--------+-------+
| 5 | 2 | xxl |
+-----------+--------+-------+
| ... | ... | ... |
+-----------+--------+-------+
| 1000 | 100 | xyz |
+-----------+--------+-------+
This way we can add any number of properties and values in any product.
The table below holds this info.
Product Properties & Values (ProdPropVal Table)
+--------+--------+--------+-----------+
| InfoID | ProdID | PropID | PropValID |
+--------+--------+--------+-----------+
| 1 | 1 | 1 | 1 |
+--------+--------+--------+-----------+
| 2 | 1 | 2 | 3 |
+--------+--------+--------+-----------+
| 3 | 2 | 1 | 2 |
+--------+--------+--------+-----------+
| 4 | 2 | 2 | 5 |
+--------+--------+--------+-----------+
| ... | ... | ... | |
+--------+--------+--------+-----------+
In the example above we know that "T-Shirt XYZ" has blue color and its size is medium.
And now the tricky part...
if we want to find all products that have a common property values set (all products of blue color and medium size) which is the best approach?
My ideas:
Search one time the ProdPropVal Table for each PropValID and compare the results in code. This can be fine tuned by starting from the most rare PropValIDs and limiting ProdIDs using a WHERE ProdID IN (previous IDs) in the next queries.
Use an Inner Join in the ProdPropVal Table for each PropValID wanted. Something like: SELECT ProdID FROM ProdPropVal ppv1 INNER JOIN ProdPropVal ppv2 ON ppv1.ProdID = ppv2.ProdID INNER JOIN ProdPropVal ppv3 ON ppv1.ProdID = ppv3.ProdID INNER JOIN ProdPropVal ppv4 ON ppv1.ProdID = ppv4.ProdID WHERE ppv1.PropValID = 10 AND ppv2.PropValID = 20 AND ppv3.PropValID = 30 AND ppv4.PropValID = 150
These are my ideas so far. The fact that ProdPropVal tablet has some millions rows doesn't leave any room for error.
Any suggestion is most welcomed!
To find all products with blue colour and medium size I would do this:
SELECT ProdID
FROM ProdPropVal
WHERE (PropID = 1 AND PropValID = 2)
OR (PropID = 2 AND PropValID = 3)
GROUP BY ProdID
HAVING COUNT(*) = 2
Better still, if PropValID is unique in the Values table, then you would remove the PropID column from the ProdPropVal table, and simplify the query to this:
SELECT ProdID
FROM ProdPropVal
WHERE PropValID IN (2, 3)
GROUP BY ProdID
HAVING COUNT(*) = 2