Reputation: 1436
I have a Postgres database with the following table layout:
Let's assume the contents are as follows:
cars properties
| id | name | desc | | id | name | type |
------------------------------ ----------------------------
| 1 | 'Toyota' | 'Japanese'| | 1 | 'length' | 'dim' |
------------------------------ ----------------------------
| 2 | 'Ford' | 'American'| | 2 | 'vin' | 'reg' |
------------------------------ ----------------------------
float_properties string_properties
| car_id | property_id | min | max | | car_id | property_id | value |
---------------------------------------- ----------------------------------
| 1 | 1 | 10.5 | 12.5 | | 1 | 2 | 'abc' |
---------------------------------------- ----------------------------------
| 2 | 1 | 9.5 | 14 | | 2 | 2 | 'def' |
---------------------------------------- ----------------------------------
How do I write a query to get the car name given values from both float and string properties tables:
For ex: Give car name and description for 'length' min > 10, max < 13 and vin = 'abc'(I understand the VIN is unique for each car, but let's ignore that for now).
I tried
SELECT
C.name,
C.description,
P.name,
P.type
FROM
properties P
INNER JOIN float_properties FP
ON FP.property_id = P.id
INNER JOIN string_properties SP
ON SP.property_id = P.id
INNER JOIN cars C
ON C.id = FP.car_id AND C.id = SP.car_id
WHERE
P.name = 'length' AND FP.min > 10 AND FP.max < 13
AND
P.name = 'vin' AND SP.value = 'abc';
but this returns zero rows. What's the correct query here?
EDIT: After reading @ginkul's comment it made me think of 2 use cases - my original question is for use case 1 below:
Find a car(s) that must satisfy conditions from both tables: For example:
Condition 1: Car that has a property named 'length' and their limits min > 10 AND FP.max < 13
Condition 2: Car that has a property named 'vin' with value 'abc'
Find cars that need to satisfy only 1 of the conditions; in other words, all cars with 10 < 'length' < 13 union all cars with property 'vin' = 'abc'.
Upvotes: 1
Views: 736
Reputation: 1269553
Basically, you need to JOIN
to properties
twice, once for the string table and once for the float table:
SELECT C.name, C.description, PF.name, PF.type, PS.name, PS.type
FROM Cars C JOIN
float_properties FP
ON C.id = FP.car_id JOIN
properties PF
ON FP.property_id = PF.id JOIN
string_properties SP
ON c.id = SP.car_id JOIN
properties PS
ON SP.property_id = PS.id
WHERE (PF.name = 'length' AND FP.min > 10 AND FP.max < 13) AND
(PS.name = 'vin' AND SP.value = 'abc');
Upvotes: 1
Reputation: 14259
You start from inside outwards - first you find the ID of the property, then you search for the needed values of that property, then you find the cars which have such values:
SELECT name, description
FROM cars
WHERE id IN
(SELECT car_id
FROM string_properties AS sp
WHERE car_id =
(SELECT car_id
FROM float_properties AS fp
WHERE property_id =
(SELECT id AS propID
FROM properties AS p1
WHERE p1.name = 'length'
)
AND fp.min > 10
AND fp.max < 13
)
AND property_id = (SELECT id AS propID FROM properties AS p2 WHERE p2.name = 'vin')
AND sp.value = 'abc'
)
ORDER BY name, description
For case 2 you will slightly modify the same query:
SELECT name, description
FROM cars
WHERE id IN
(SELECT car_id
FROM string_properties AS sp
WHERE property_id =
(SELECT id AS propID
FROM properties AS p2
WHERE p2.name = 'vin'
)
AND sp.value = 'abc'
UNION ALL
SELECT car_id
FROM float_properties AS fp
WHERE property_id =
(SELECT id AS propID
FROM properties AS p1
WHERE p1.name = 'length'
)
AND fp.min > 10
AND fp.max < 13
)
)
ORDER BY name, description
Upvotes: 1
Reputation: 1518
You can't inner-join from both detail tables. Try this.
SELECT
C.name,
C.description,
coalesce(Pfp.name, Psp.name) pname,
coalesce(Pfp.type, psp.type) ptype
FROM cars C
LEFT JOIN float_properties FP
ON FP.car_id = c.id
LEFT JOIN properties Pfp
ON pfp.id = fp.property_id
LEFT JOIN string_properties SP
ON SP.car_id = c.id
LEFT JOIN properties Psp
ON psp.id = sp.property_id
WHERE
Pfp.name = 'length' AND FP.min > 10 AND FP.max < 13
OR
Psp.name = 'vin' AND SP.value = 'abc';
The above query hasn't been tested.
Take into account that in your current design there might be multiple rows returned. You can't ask for a single property description and filter for two properties, all at the same time. If you want then you can use something like string_agg()
to aggregate all property descriptions and thus have one result row per car.
Upvotes: 0