Reputation: 301
Currently I have two MySQL tables
Properties
id name
1 Grove house
2 howard house
3 sunny side
Advanced options
prop_id name
1 Wifi
1 Enclosed garden
1 Swimming pool
2 Swimming pool
As you can see table two contains specific features about the properties
When I only have max 3 options the query below worked just fine. (maybe a little slow but ok) now things have expanded somewhat and i have a max of 12 options that it is possible to search by and its causing me some major speed issues. The query below is for 8 options and as you can see its very messy. Is there a better way of doing what I'm trying to achieve?
SELECT * FROM properties WHERE id in (
select prop_id from advanced_options where name = 'Within 2 miles of sea or river' and prop_id in (
select prop_id from advanced_options where name = 'WiFi' and prop_id in (
select prop_id from advanced_options where name = 'Walking distance to pub' and prop_id in (
select prop_id from advanced_options where name = 'Swimming pool' and prop_id in (
select prop_id from advanced_options where name = 'Sea or River views' and prop_id in (
select prop_id from advanced_options where name = 'Pet friendly' and prop_id in (
select prop_id from advanced_options where name = 'Open fire, wood burning stove or a real flame fire-place' and prop_id in (
select prop_id from advanced_options where name='Off road parking')
)
)
)
)
)
)
)
Upvotes: 0
Views: 103
Reputation: 2631
Like Mike Brant suggest I would consider altering your datamodel to a limit to set and creating a column for each of these in your properties
table. But some times the boss comes: "We also need 'flatscreen tv'" and then you have to go back to the DB and update the scheme and your data access layer.
A way to move this logic somehow out if the database it to use bitwise comparison. This allows you to make simple queries, but requires a bit of preprocessing before you make your query.
Judge for yourself.
I've put everything in a test suite for you here sqlfiddle
The basic idea is that each property in your table has an id that is the power of 2. Like this:
INSERT INTO `advanced_options` (id, name)
VALUES
(1, 'Wifi'),
(2, 'Enclosing Garden'),
(8, 'Swimming Pool'),
(16, 'Grill');
You can then store a single value in your properties table buy adding up the options:
Wifi + Swimming Pool = 1 + 8 = 9
If you want to find all properties with wifi and a swimming pool you then do like this:
SELECT * FROM `properties` WHERE `advanced_options` & 9 = 9
If you just wanted swimming pool this would be it:
SELECT * FROM `properties` WHERE `advanced_options` & 8 = 8
Go try out the fiddle
Upvotes: 3
Reputation: 2874
Could you do something like this?:
select p.*,count(a.prop_id) as cnt
from properties p
inner join advanced_options a on a.prop_id = p.id
where a.name in ('Enclosed garden','Swimming pool')
group by p.name
having cnt = 2
That query would get all the properties that have ALL of those advanced_options...
I would also suggest normalizing your tables by creating a separate table Called Advanced_option (id,name)
where you store your unique Option values and then create a junction entity table like Property_x_AdvancedOption (fk_PropertyID, FK_AdvancedOptionID)
that way you use less resources and avoid data integrity issues.
Upvotes: 0
Reputation: 16362
Join back to the advanced_options table multiple times. Here's a sample with 2 (lather, rinse, repeat).
select o1.prop_id
from advanced_options o1
inner join advanced_options o2 on o1.prop_id = o2.prop_id and o2.name = "WiFi"
where o1.name = 'Within 2 miles of sea or river'
Upvotes: 0
Reputation: 71384
You really need to consider a schema change to your table. It seems that advanced options in and of themselves don't have any properties, so instead of an advanced_options
table that is trying to be a many-to-many JOIN table, why not just have a property_options
table with a field for each "options". Something like this
|prop_id | wifi | swimming_pool | etc..
-----------------------------------
| 1 | 0 | 1 |
| 2 | 1 | 0 |
Here each field is a simple TINYINT field with 0/1 boolean representation.
To where you could query like:
SELECT * FROM properties AS p
INNER JOIN property_options AS po ON p.id = po.prop.id
WHERE wifi = 1 AND swimming_pool = 1 ....
Here you would just build your WHERE
clause based on which options you are querying for.
There actually wouldn't be any need to even have a separate table, as these records would have a one-to-one relationship with the properties, so you could normalize these fields onto you properties table if you like.
Upvotes: 0