这是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和DATA。 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
的查询?
答案 0 :(得分:2)
我会直观地使用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
(对于第一种情况,恕我直言,这是一个相当不错的选择)。