在桌子上的多维搜索

时间:2011-07-02 02:33:03

标签: sql entity-attribute-value multivalue

我有下表存储用户在系统中的首选项

UserId | Product   | Brand     | City      |
-------------------------------------------
A      | Soap      | Tide      | NYC       |
A      | Cereal    | Dont-care | NYC       |
B      | Dont-Care | Tide      | Dont-care |
C      | Shampoo   | Dont-care | Dont-Care |

我想根据用户提供的搜索值进行搜索。所以,如果一个人搜索

City: NYC, Brand: Tide

输出应为:

    A    | Soap      | Tide      | NYC       |
    B    | Dont-Care | Tide      | Dont-care |

就好像他们搜索

一样
Brand: Tide, Product: Soap

结果应该是:

    A    | Soap      | Tide      | NYC       |

我当前的解决方案是针对MySQL表的以下查询(其中null表示'不关心'):

select *
from user_preferences
where (product is null or product = <user provided value>)
and (brand is null or brand = <user provided value>)
and (city is null or city = <user provided value>)

虽然它按预期工作,但[和+(或)]组合让我觉得这不是正确的方法。我也相当确定,一旦数据集增加,查询将无法正常运行。

存储和检索此类数据的最有效方法是什么?是否有任何no-sql类型的方法可用于提高效率?

更新

经过一些谷歌搜索后,我认为我的方法可能是最安全的选择。我仍然对这种方法感到矛盾的一个因素是添加另一个“可搜索”属性意味着添加一个新列。

关于EAV anti-pattern的博客提供了一些关于此类计划的优秀阅读材料。另请参阅friend-feed uses MySQL如何在表中存储变量属性。

3 个答案:

答案 0 :(得分:0)

您可以使用IFNULL()函数。

这将是这样的:

select *
from user_preferences
where ifnull(product, <user provided value>) = <user provided value>
and ifnull(brand, <user provided value>) = <user provided value>
and ifnull(city, <user provided value>) = <user provided value>

答案 1 :(得分:0)

  

基于@ Pompom6784的回答 -

     
    

但从逻辑上讲它有很大的不同     条件 -

  
select * from user_preferences 
where product = nvl(<user provided value> , product)
and brand = nvl(<user provided value> ,brand)
and city = nvl(<user provided value> ,city)

我是oracle pl sql开发人员所以请检查以上查询有oracle sql的syntex .. 如果您的数据库中ifnull是检查空值的函数,请将nvl()替换为ifnull()

@ Pompom6784你必须在&lt;上使用ifnull用户提供的值&gt;不是列值..

答案 2 :(得分:0)

既然你问如何存储这些数据,这就是我可能会做的事情:

            USERS
            userid  PK
            name
            etc


            PREFTYPES
            preftypeid  PK
            prefname



           PREFTYPEVALUES
           preftypeid  foreign key references PREFTYPES(preftypeid)
           value

           composite primary key (preftypeid, value)

上面的PREFTYPEVALUES表允许您约束USERPREFS表中可能的值条目:

            USERPREFS
            userid      foreign key references USERS(userid)
            preftypeid  
            value

           => composite  foreign key(preftypeid, value) references PREFTYPEVALUES(preftypeid, value)

要合并两组用户,第一组是那些喜欢Tide为他们的肥皂而第二组是那些喜欢NYC作为他们的城市的用户,你可以UNION两套用户:

             select U.userid, U.name, UP.value
             from USERS U 
             inner join USERPREFS UP on U.userid = UP.userid
             where 
             (UP.preftypeid = 'soap' and UP.value = 'Tide' )


              UNION

             select U.userid, U.name, UP.value
             from USERS U 
             inner join USERPREFS UP on U.userid = UP.userid
             where                  
             (UP.preftypeid = 'city' and UP.value = 'NYC')

但是这个结构当然也允许不使用UNION的查询。

             select U.userid, U.name, UP.value
             from USERS U 
             inner join USERPREFS UP on U.userid = UP.userid
             where 
             (UP.preftypeid = 'soap' and UP.value = 'Tide' )
              OR
             (UP.preftypeid = 'city' and UP.value = 'NYC')

我打赌你的下一个问题是:如何为每个用户获得一行,用户的偏好并排?

              user-A, Tide, NYC

这种非规范化最好留给表示层,尽管可以使用各种不优雅的方法在SQL中完成。这里的SQL键是复合的(user,preftype)。