这是 Oracle 11g。
我有两个表,其相关列如下所示(我必须采用给定的表 - 我无法更改列数据类型):
CREATE TABLE USERS
(
UUID VARCHAR2(36),
DATA VARCHAR2(128),
ENABLED NUMBER(1)
);
CREATE TABLE FEATURES
(
USER_UUID VARCHAR2(36),
FEATURE_TYPE NUMBER(4)
);
这些表表达了可以为用户分配多个功能的概念。 (USER_UUID, FEATURE_TYPE)
组合是唯一的。
我有两个非常相似的查询,我对此很感兴趣。第一个查询用英语表示,是“返回分配了功能 X 的启用用户的 UUID”。第二个是“返回分配了功能 X 的启用用户的 UUID 和数据”。 USERS
表大约有 5,000 条记录,FEATURES
表大约有 40,000 条记录。
我最初将第一个查询天真地写为:
SELECT u.UUID FROM USERS u
JOIN FEATURES f ON f.USER_UUID=u.UUID
WHERE f.FEATURE_TYPE=X and u.ENABLED=1
而且性能很差。作为一个实验,我试图看看如果我不关心用户是否启用会发生什么,这激发了我的尝试:
SELECT USER_UUID FROM FEATURES WHERE TYPE=X
而且运行得非常快。这反过来又激励我尝试
(SELECT USER_UUID FROM FEATURES WHERE TYPE=X)
INTERSECT
(SELECT UUID FROM USERS WHERE ENABLED=1)
它的运行速度不如第二个查询,但比第一个查询运行得快得多。
经过更多思考,我意识到,在当前的情况下,每个用户或几乎每个用户都被分配了至少一个功能,这意味着连接条件始终或几乎始终为真,这意味着内部连接完全或大部分退化进入交叉连接。因为 5,000 x 40,000 = 200,000,000 这不是一件好事。显然,INTERSECT
版本将处理更少的行,这可能就是它明显更快的原因。
问题:在这种情况下,INTERSECT
真的是正确的选择吗?还是我应该考虑其他类型的连接?
我编写了与第一个查询类似的查询,该查询也需要返回DATA
:
SELECT u.UUID, u.DATA FROM USERS u
JOIN FEATURES f ON f.USER_UUID=u.UUID
WHERE f.FEATURE_TYPE=X and u.ENABLED=1
但我似乎无法在这里执行 INTERSECT
技巧,因为 FEATURES
中没有与 DATA
列匹配的列。
问题:如何重写它以避免退化连接问题并像不返回 DATA
的查询一样执行?
最佳答案
我会直观地使用EXISTS
子句:
SELECT u.UUID
FROM USERS u
WHERE u.ENABLED=1
AND EXISTS (SELECT 1 FROM FEATURES f where f.FEATURE_TYPE=X and f.USER_UUID=u.UUID)
或类似:
SELECT u.UUID, u.DATA
FROM USERS u
WHERE u.ENABLED=1
AND EXISTS (SELECT 1 FROM FEATURES f where f.FEATURE_TYPE=X and f.USER_UUID=u.UUID)
这样您就可以从USERS
中选择每个字段,因为不再需要INTERSECT
(对于第一种情况,这是一个相当不错的选择,恕我直言)。
关于sql - 如何改进 moSTLy "degenerate"内部联接?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25903294/