在一个表和另一个表之间的一对多关系中,通过“多”侧的值过滤“一”侧的有效方法?

问题描述 投票:0回答:2

我有一个表,比方说,

users
,它与另一个名为
users_attributes
的表具有一对多关系。
users_attributes
表是一个简单的键值类型表,具有指向
users
表的外键列。像这样的东西:

create table users(
  id: integer primary key, 
  name: varchar
);

create table users_attributes(
  attribute_id: integer primary key,
  user_id: integer references users(id),
  attribute_name: varchar, 
  attribute_value: varchar
);

现在我需要根据

users
表中的 attribute_name 和 attribute_value 过滤
users_attributes
。我已经尝试过这个查询(Postgresql),它有效,但执行时间更长:

select * from users u
left join users_attributes ua1 on u.id = ua1.user_id and ua1.attribute_name = 'dog_name'
left join users_attributes ua2 on u.id = ua2.user_id and ua2.attribute_name = 'cat_name'
where ua1.attribute_value = 'Spot' and ua2.attribute_value = 'Mittens';

在这里,我需要根据其过滤用户的每个属性的连接数量都会增加。这会导致查询变慢(4-10 秒之间,具体取决于连接数量),因为存在 appx.十万用户。查询的解释计划似乎支持这一理论。

有没有一种方法可以以返回更快的方式查询用户?

我正在使用 Postgresql。

sql postgresql performance sqlperformance
2个回答
0
投票

LEFT JOIN
WHERE
条件的混合从逻辑上讲是没有意义的。参见:

基本重写:

SELECT *
FROM   users u
JOIN   users_attributes ua1 ON u.id = ua1.user_id
JOIN   users_attributes ua2 ON u.id = ua2.user_id
WHERE  ua1.attribute_name = 'dog_name'
AND    ua1.attribute_value = 'Spot'
AND    ua2.attribute_name = 'cat_name'
AND    ua2.attribute_value = 'Mittens';

基本上,这是一个的情况。

有很多方法可以做到这一点。最佳查询样式取决于您的基数、典型过滤器以及您要优化的内容。这是整个武器库:


0
投票

如果您正在尝试查找同时拥有一只名为 Mittens 的猫和一只名为 Spot 的狗的不同用户,您可能会对 intersect

 感兴趣。这有点像 union
 但只保留出现在 
both 集中的值,默认删除重复项。

select u.* from users as u where u.id in ( select ua.user_id from users_attributes as ua where ua.attribute_name = 'dog_name' and ua.attribute_value = 'Spot' intersect select ua.user_id from users_attributes as ua where ua.attribute_name = 'cat_name' and ua.attribute_value = 'Mittens' )
我不太喜欢 postgres;所以,我不能说作为连接子查询或 

in()

 表达式是否会更高效。

© www.soinside.com 2019 - 2024. All rights reserved.