更好的MySQL嵌套选择解决方案

时间:2012-09-12 16:41:34

标签: mysql

目前我有两个MySQL表

属性

id    name 
1     Grove house
2     howard house
3     sunny side

高级选项

prop_id    name
1          Wifi
1          Enclosed garden
1          Swimming pool
2          Swimming pool

如您所见,表2包含有关属性的特定功能

当我只有最多3个选项时,下面的查询工作正常。 (可能有点慢,但确定)现在事情已经有所扩展,我有最多12个选项,可以搜索并导致我一些主要的速度问题。下面的查询是8个选项,你可以看到它非常混乱。有没有更好的方法来做我想要实现的目标?

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') 
                        ) 
                    ) 
                ) 
            ) 
        ) 
    ) 
)

4 个答案:

答案 0 :(得分:3)

就像Mike Brant建议我考虑将您的数据模型更改为限制,以便在properties表中为每个数据模型设置和创建列。但老板有时会来:“我们还需要'平板电视'”,然后你必须回到数据库并更新方案和你的数据访问层。

如果数据库使用按位比较,则以某种方式移动此逻辑的方法。这允许您进行简单的查询,但在进行查询之前需要进行一些预处理。

自己判断。

我已经把所有内容都放在了测试套件中sqlfiddle

基本思想是表中的每个属性都有一个2的幂。像这样:

INSERT INTO `advanced_options` (id, name)
VALUES
 (1, 'Wifi'),
 (2, 'Enclosing Garden'),
 (8, 'Swimming Pool'),
 (16, 'Grill');

然后,您可以在属性表中存储单个值,购买时添加选项:

Wifi + Swimming Pool = 1 + 8 = 9

如果你想找到所有带wifi和游泳池的房产,你可以这样做:

SELECT * FROM `properties` WHERE `advanced_options` & 9 = 9

如果你只是想要游泳池,那就是:

SELECT * FROM `properties` WHERE `advanced_options` & 8 = 8

试试fiddle

答案 1 :(得分:0)

您确实需要考虑对表进行架构更改。看起来高级选项本身没有任何属性,因此,为什么不只是advanced_options而不是试图成为多对多JOIN表的property_options表。表格,每个“选项”都有一个字段。像这样的东西

|prop_id  | wifi |  swimming_pool  |  etc..
-----------------------------------
|   1     |  0   |        1        |
|   2     |  1   |        0        |

此处每个字段都是一个简单的TINYINT字段,其中包含0/1布尔值。

您可以在哪里查询:

SELECT * FROM properties AS p
INNER JOIN property_options AS po ON p.id = po.prop.id
WHERE wifi = 1 AND swimming_pool = 1 ....

在这里,您只需根据您要查询的选项构建WHERE子句。

实际上根本不需要单独的表,因为这些记录与属性具有一对一的关系,因此如果您愿意,可以将这些字段规范化到属性表中。

答案 2 :(得分:0)

多次加入advanced_options表。这是一个2(泡沫,冲洗,重复)的样品。

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'

答案 3 :(得分:0)

你可以这样做吗?:

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

该查询将获得具有所有这些advanced_options的所有属性...

我还建议通过创建一个名为Advanced_option (id,name)的单独表格来规范化表格,您可以在其中存储唯一的选项值,然后创建一个像Property_x_AdvancedOption (fk_PropertyID, FK_AdvancedOptionID)这样的联结实体表,这样就可以减少资源使用并避免数据诚信问题。

相关问题