假设我有以下表格:
create table table_a
(
id_a,
name_a,
primary_key (id_a)
);
create table table_b
(
id_b,
id_a is not null, -- (Edit)
name_b,
primary_key (id_b),
foreign_key (id_a) references table_a (id_a)
);
我们可以通过多种方式在这些表上创建连接视图:
create view join_1 as
(
select
b.id_b,
b.id_a,
b.name_b,
a.name_a
from table_a a, table_b b
where a.id_a = b.id_a
);
create view join_2 as
(
select
b.id_b,
b.id_a,
b.name_b,
a.name_a
from table_b b left outer join table_a a
on a.id_a = b.id_a
);
create view join_3 as
(
select
b.id_b,
b.id_a,
b.name_b,
(select a.name_a from table_a a where b.id_b = a.id_a) as name_a
from table_b b;
);
我们知道:
(1)table_a
与id_a
必须至少有一个条目(由于表B中的外键)和
(2)table_a
与id_a
的最多一个条目(由于表A中的主键)
然后我们知道table_a
中只有一个条目与联接链接。
现在考虑以下SQL:
select id_b, name_b from join_X;
请注意,这不会从table_a
中选择任何列,并且因为我们知道在此联接中table_b
加入了一个我们真正不应该在执行时table_a
以上选择。
那么编写上述连接视图的最佳方法是什么?
我应该只使用标准join_1
,并希望优化器根据主键和外键确定不需要访问table_a
吗?
或者最好像join_2
甚至是join_3
那样编写它,这样可以更明确地说明来自table_b
的每一行的连接只有一行?
编辑+其他问题
我是否应该在正常联接(例如join_3
)中选择子选择(如join_1
)?
答案 0 :(得分:0)
直观地说,我认为 join_1
执行速度稍慢,因为您假设优化器可以转换连接是错误的,因为您没有声明table_b.id_a
列是NOT NULL
。实际上,这意味着(1)是错误的。 table_b.id_a
可以是NULL
。即使你知道它不可能,优化者也不知道。
就join_2
和join_3
而言,根据您的数据库,可能会进行优化。找出的最好方法是运行(Oracle语法)
EXPLAIN select id_b, name_b from join_X;
研究执行计划。它会告诉您table_a
是否已加入。另一方面,如果您的观点应该是可重用的,那么我会选择普通join
并忘记早熟优化。使用适当的统计信息和索引可以获得更好的结果,因为join
操作并不总是那么昂贵。但这当然取决于你的统计数据。
答案 1 :(得分:0)
1 + 2实际上是相同的。
我从未使用[3],但看起来很奇怪。我强烈怀疑优化器会使它等同于其他2。
运行所有3个语句并比较生成的执行计划是一个很好的练习。
因此,给定相同的性能,最清楚的阅读得到我的投票 - [2]是支持它的标准,否则[1]。
在您的情况下,如果您不想要A中的任何列,为什么还要在语句中包含Table_A?
如果这是一个简单的过滤器 - 即只包括从那里排在表A中存在,即使我不想从表A的任何的cols表B行,那么所有3个语法都很好,虽然你可能会发现,使用如果某些dbs中的EXISTS更高效:
SELECT * from Table_B b WHERE EXISTS (SELECT 1 FROM Table_A a WHERE b.id_b = a.id_a)
虽然根据我的经验,这通常与其他任何人的表现相当。
您也会问,然后您会选择子查询而不是其他表达式。这归结为它是否是一个相关的子查询。
基本上 - 相关子查询必须被用于在外部语句的每行执行一次 - 这是上述的真的 - 。在表B中的每一行必须运行对表A的子查询
如果子查询只能运行一次
SELECT * from Table_B b WHERE b.id_a IN (SELECT a.id_a FROM Table_A a WHERE a.id_a > 10)
然后,子查询通常比更高性能的联接 - 尽管我怀疑某些优化仍然能够发现这两者减少到相同的执行计划
同样,最好的办法是运行两个语句,并比较执行计划。
最后也是最简单的 - 给你FK你可以写:
SELECT * From Table_B b WHERE b.id_a IS NOT NULL
答案 2 :(得分:0)
为什么你为此目的使用视图?如果您想从表中获取数据,请从表中获取数据。
或者,如果您需要一个视图来转换表中的某些列(例如将NULL合并为零),则只在B
表上创建一个视图。如果某个DBA想要实现一个所有选择必须通过视图而不是表格的策略,这也适用: - )
在这两种情况下,您都不必担心多表访问。
答案 3 :(得分:0)
这将取决于平台。
SQL Server既分析了约束(外键,主键等)的逻辑含义,又扩展了内联的VIEW。这意味着'无关紧要' VIEW代码的一部分被优化器废弃。 SQL Server将为所有三种情况提供完全相同的执行计划。 (注意;优化器可以处理的复杂性有限制,但它当然可以处理这个问题。)
然而,并非所有平台都是平等的 - 有些人可能不会以相同的方式分析约束,假设您出于某种原因编码了联接 - 有些人可能预编译VIEW的执行/解释计划
因此,要确定行为,您必须了解特定平台的功能。在绝大多数情况下,优化器都是一个复杂的野兽,因此最好的测试只是尝试并看到它。
修改强>
在回答您的额外问题时,每个首选的相关子查询是什么?没有简单的答案,因为它取决于您尝试实施的数据和逻辑。
在我过去使用它们的过程中肯定会出现这种情况,既可以简化查询的结构(可维护性),也可以启用特定的逻辑。
如果字段table_b.id_a
引用了table_a中的许多条目,则可能只需要最新名称。您可以使用(SELECT TOP 1 name_a FROM table_a WHERE id_a = table_b.id_a ORDER BY id_a DESC)
实现该功能。
简而言之,取决于
- 关于查询的逻辑
- 关于数据的结构
- 关于代码的最终布局
我经常发现它不是必需的,但通常我发现这是一个积极的选择。
注意:
根据相关的子查询,它实际上并不总是为每个记录执行一次'例如,SQL Server将所需的逻辑扩展为与查询的其余部分一起执行。重要的是要注意在执行之前处理/编译SQL代码。 SQL只是一种表达基于集合的逻辑的方法,然后使用优化器可用的最优算法将其转换为传统的循环等。
由于优化器的功能或限制,其他RDBMS的执行方式可能不同。使用IN (SELECT blah FROM blah)
或使用EXISTS (SELECT * FROM blah)
时,某些RDBMS表现良好,但有些表现非常糟糕。这同样适用于相关子查询。 Sub表现得异常出色,有些表现不佳,但大部分表现都非常好。