选择不同的...内部连接与选择...其中 id in (...)
Posted
技术标签:
【中文标题】选择不同的...内部连接与选择...其中 id in (...)【英文标题】:Select distinct ... inner join vs. select ... where id in (...) 【发布时间】:2011-02-08 00:00:52 【问题描述】:我正在尝试创建表的子集(作为物化视图),定义为在另一个物化视图中具有匹配记录的那些记录。
例如,假设我有一个包含 user_id 和 name 列的 Users 表,以及一个包含 entry_id、user_id、activity 和 timestamp 列的 Log 表。
首先,我创建了一个 Log 表的具体化视图,只选择那些时间戳 > some_date 的行。现在我想要一个在我的日志表快照中引用的用户的实体化视图。我可以将其创建为
select * from Users where user_id in (select user_id from Log_mview)
或者我可以做
select distinct u.* from Users u inner join Log_mview l on u.user_id = l.user_id
(需要 distinct 以避免来自具有多个日志条目的用户的多次点击)。
前者看起来更干净、更优雅,但耗时更长。我错过了什么吗?有没有更好的方法来做到这一点?
编辑:where exists
子句有很大帮助,除非条件使用OR
。例如,假设上面的 Log 表也有一个 user_name 列,将 Log 条目与 Users 记录匹配的正确方法是当任一列(用户 ID 或用户名)匹配时。我发现了
select distinct u.* from Users u
inner join Log_mview l
on u.user_id = l.user_id or u.name = l.user_name
比
快很多select * from Users u where exists
(select id from Log_mview l
where l.user_id = u.user_id or l.user_name = u.name)
有什么帮助吗?
(关于解释计划......让我来清理它,或者他们,更确切地说......我会在一段时间内发布它们。)
编辑:解释计划: 对于内连接查询:
计划哈希值:436698422 -------------------------------------------------- -------------------------------------------------- ------------ |身份证 |操作 |姓名 |行 |字节 |TempSpc|成本 (%CPU)|时间 | -------------------------------------------------- -------------------------------------------------- ------------ | 0 |选择声明 | | 4539K| 606M| | 637K (3)| 02:07:25 | | 1 |哈希唯一 | | 4539K| 606M| 3201M| 637K (3)| 02:07:25 | | 2 |串联 | | | | | | | |* 3 |哈希连接 | | 4206K| 561M| 33M| 181K (4)| 00:36:14 | | 4 |位图转换为行 | | 926K| 22M| | 2279 (1)| 00:00:28 | | 5 |位图索引快速全扫描| I_M_LOG_MVIEW_4 | | | | | | |* 6 |表访问完全 |用户 | 15M| 1630M| | 86638 (6)| 00:17:20 | |* 7 |哈希连接 | | 7646K| 1020M| 33M| 231K (4)| 00:46:13 | | 8 |位图转换为行 | | 926K| 22M| | 2279 (1)| 00:00:28 | | 9 |位图索引快速全扫描| I_M_LOG_MVIEW_4 | | | | | | | 10 |表访问完全 |用户 | 23M| 2515M| | 87546 (7)| 00:17:31 | -------------------------------------------------- -------------------------------------------------- ------------ 谓词信息(由操作 id 标识): -------------------------------------------------- - 3 - 访问("U"."NAME"="L"."USER_NAME") 6 - 过滤器(“U”。“名称”不为空) 7 - 访问("U"."USER_ID"=TO_NUMBER("L"."USER_ID")) filter(LNNVL("U"."NAME"="L"."USER_NAME") OR LNNVL("U"."NAME" IS NOT NULL)) 笔记 ----- - 此语句使用的动态采样
对于使用where exists
的人:
计划哈希值:2786958565 -------------------------------------------------- -------------------------------------------------- - |身份证 |操作 |姓名 |行 |字节 |成本 (%CPU)|时间 | -------------------------------------------------- -------------------------------------------------- - | 0 |选择声明 | | 1 | 114 | 21M (1)| 70:12:13 | |* 1 |过滤器 | | | | | | | 2 |表访问完全 |用户 | 23M| 2515M| 87681 (7)| 00:17:33 | | 3 |位图转换为行 | | 7062 | 179K| 1 (0)| 00:00:01 | |* 4 |位图索引快速全扫描| I_M_LOG_MVIEW_4 | | | | | -------------------------------------------------- -------------------------------------------------- - 谓词信息(由操作 id 标识): -------------------------------------------------- - 1 - 过滤器(存在(选择 /*+ */ 0 从“MYSCHEMA”。“LOG_MVIEW” "LOG_MVIEW" WHERE ("USER_NAME"=:B1 OR TO_NUMBER("USER_ID")=:B2) AND ("USER_NAME"=:B3 OR TO_NUMBER("USER_ID")=:B4) AND ("USER_NAME"=:B5 OR TO_NUMBER("USER_ID")=:B6))) 4 - 过滤器("USER_NAME"=:B1 OR TO_NUMBER("USER_ID")=:B2) 笔记 ----- - 此语句使用的动态采样
更改数据库对象名称以保护无辜者。 :p
【问题讨论】:
如果可能,请提供给我们EXPLAIN_PLAN
。很容易做出决定。
USER_ID和USER_NAME是什么关系?为什么 USER_ID 在表和 MVIEW 中是不同的数据类型?
【参考方案1】:
第二个查询可能比第一个查询(join+distinc)在硬盘上工作更多。
第一个查询可能会转换为:
对于表 Log 中的每一行,在表 User(在内存中)中找到相应的行。 数据库可能足够聪明,可以在内存结构中为可能比日志表小得多的用户表创建。 我相信查询一(join+distinct)只需要对表 Log 进行一次传递。 不同的可能在内存中执行。第二个查询可能会强制数据库对表 Log 进行多次全读。
所以在第二个查询中你可能会得到:
对于表中的每一行,用户读取表 Log 中的所有行(从磁盘)以匹配条件。
您还必须考虑,由于内存可用性、负载和表增加的变化,某些查询可能会遇到速度上的巨大差异。
【讨论】:
【参考方案2】:试试这个
select * from Users u
where exists
( select user_id
from Log_mview l
where l.user_id = u.user_id )
/
如果子查询返回大量行,WHERE EXISTS
可以大大快于WHERE ... IN
。
【讨论】:
【参考方案3】:这取决于您拥有的数据,但在联接中使用 Distinct
可以提高您的性能:
Select u.*
From Users u
Join ( Select Distinct user_id
From log_mview ) l On u.user_id = l.user_id
【讨论】:
这可能有帮助,但是第二个查询(使用内部连接,而不是where id in ...
)已经快得多了。这只会使更快的查询更快。我试图弄清楚为什么另一个看起来更干净,更优雅,却慢得多。我会在子查询中尝试distinct
:select * from Users where user_id in (select distinct user_id from Log_mview)
,看看是否更好。 (编辑:尝试了代码的四个空格缩进......似乎在 cmets 中不起作用。:(以上是关于选择不同的...内部连接与选择...其中 id in (...)的主要内容,如果未能解决你的问题,请参考以下文章