سؤال

I have three tables products, properties and product_properties. The subset of table structures and values are given below:

products
---------------------------------------------
|  id  |  name  |   description   |  price  |
---------------------------------------------
|   1  | Camera |  Color Camera   |   100   |
|   2  | Lens 1 |  Camera Lens 1  |   20    |
|   3  | Lens 2 |  Camera Lens 2  |   30    |
---------------------------------------------

properties
------------------------------------------
|  id |  name          |  display        |
------------------------------------------
|  1  |  lens_mount    |   Lens Mount    |
|  2  |  image_circle  |   Image Circle  |
|  3  |  focal_length  |   Focal Length  |
|  4  |  lens_family   |   Lens Family   |
------------------------------------------

product_properties
------------------------------------------------
|  id  | value    | product_id |  property_id  |
------------------------------------------------
|  1   | F-Mount  | 2          |  1            |
|  2   | C-Mount  | 3          |  1            |
|  3   | 42.01 mm | 2          |  2            |
|  4   | 13.00 mm | 3          |  2            |
|  5   | 10.00    | 2          |  3            |
|  6   | 12.00    | 3          |  3            |
|  7   | Standard | 1          |  4            |
|  8   | Standard | 2          |  4            |
|  9   | Standard | 3          |  4            |
------------------------------------------------

Here is my output condition:

Find all the Lenses for Camera 1 (match camera and lens by lens_family, which is 'Standard' here) which have lens_mount = 'F-Mount' and image_circle >= 40 mm and focal_length > 5

I tried following query for this:

SELECT * FROM products 
  INNER JOIN product_properties ON products.id = product_properties.product_id
  INNER JOIN properties ON properties.id = product_properties.property_id 
  WHERE (product_properties.value = 'Standard') 
    AND (properties.name = 'lens_mount' AND product_properties.value = 'F-Mount') 
    AND (properties.name = 'image_circle' AND product_properties.value >= ABS('40 mm')) 
    AND (properties.name = 'focal_length' AND product_properties.value >= 5)

However this query only gives correct result if there is only one condition. With all the conditions it doesn't give any value. I tried with OR in place of AND in the where condition but that also didn't help to get the correct output.

Can anyone sortout this please. Thanks in advance.

هل كانت مفيدة؟

المحلول

You want to do the logic in the having clause rather than in the where clause:

SELECT products.id
FROM products 
  INNER JOIN product_properties ON products.id = product_properties.product_id
  INNER JOIN properties ON properties.id = product_properties.property_id 
group by products.id
having sum(properties.name = 'lens_family' AND product_properties.value = 'Standard') > 0 and
       sum(properties.name = 'lens_mount' AND product_properties.value = 'F-Mount') > 0 and
       sum(properties.name = 'image_circle' AND product_properties.value >= ABS('40 mm')) > 0 and
       sum(properties.name = 'focal_length' AND product_properties.value >= 5) > 0;

You are looking for a set of properties on a single product. No single row can match all the conditions -- they conflict with each other. Instead, use the group by clause to bring the rows together for a given product. Then count the number of rows that match each condition.

Each clause in the having corresponds to one of your original clauses in the where statement, enclosed by sum(). This counts the number of rows that match. The conditions ensure that there is at least one row for each property.

نصائح أخرى

Ah, the dreaded EVA schema. The first think you should do is create a view to normailze the data as per the following SQL.

You can then use two copies of that view (one for the camera, one for the lens) and join them together to get what you need.

SELECT  p.id
        ,p.name
        ,pp1.value as Lens_Mount
        ,pp2.value as Image_Circle
        ,pp3.value as Focal_Length
        ,pp4.value as Lens_Family
from    products p
left outer join
        product_properties pp1
on      pp1.product_id = p.id
and     pp1.property_id = 1 --lens Mount
left outer join
        product_properties pp2
on      pp2.product_id = p.id
and     pp2.property_id = 2 --Image Circle
left outer join
        product_properties pp3
on      pp3.product_id = p.id
and     pp3.property_id = 3 --Focal Length
left outer join
        product_properties pp4
on      pp4.product_id = p.id
and     pp4.property_id = 3 --Lens Family        
where   p.name like 'Lens%'

You should OR every condition and count how many of them are satisfied for each product_id. So you GROUP BY product_id and filter results using HAVING clause.

SELECT product_properties.product_id, count(*) as conditions_satisfied FROM products 
  INNER JOIN product_properties ON products.id = product_properties.product_id
  INNER JOIN properties ON properties.id = product_properties.property_id 
WHERE product_properties.value = 'Standard'
 OR properties.name = 'lens_mount' AND product_properties.value = 'F-Mount'
 OR properties.name = 'image_circle' AND product_properties.value >= ABS('40 mm')
 OR properties.name = 'focal_length' AND product_properties.value >= 5
GROUP BY product_properties.product_id
HAVING COUNT(*) = 4

Note it may be difficult to compare property values that are strings if you really need a number comparison.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top