我有一个表,比方说,
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。
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';
基本上,这是一个关系划分的情况。
有很多方法可以做到这一点。最佳查询样式取决于您的基数、典型过滤器以及您要优化的内容。这是整个武器库:
如果您正在尝试查找同时拥有一只名为 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()
表达式是否会更高效。