Sql 多对一联接

Sql 多对一联接,sql,query-optimization,Sql,Query Optimization,假设我有以下表格: 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

假设我有以下表格:

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由于表B中的外键,表a中必须至少有一个id为的条目,并且 2由于表a上的主键,表a中最多有一个id为的条目

然后我们知道表_a中正好有一个条目链接到join

现在考虑下面的SQL:

select id_b, name_b from join_X;
请注意,这不会从表_a中选择任何列,因为我们知道在这个连接中,表_b正好连接到一个列,所以在执行上述选择时,我们实际上不必查看表_a

那么,编写上述连接视图的最佳方法是什么

我是否应该只使用标准的join_1,并希望优化器发现不需要基于主键和外键访问表_a

还是像join_2或甚至join_3那样编写它更好,这使得表_b中的每一行只有一行来自join

编辑+附加问题

有没有时间我更喜欢join_3中的子选择而不是join_1中的普通联接?

直觉上,我认为join_1的执行速度会稍慢一些,因为您认为优化程序可以转换联接是错误的,因为您没有声明表\b.id\u a列为非空。事实上,这意味着1是错误的。表_b.id_a可以为空。即使你知道这不可能,乐观主义者也不知道

就join_2和join_3而言,根据您的数据库,可能会进行优化。最好的方法是运行Oracle语法

EXPLAIN select id_b, name_b from join_X;

研究执行计划。它将告诉您是否加入了表a。另一方面,如果您的视图应该是可重用的,那么我会选择普通连接,而不考虑预成熟的优化。使用适当的统计信息和索引可以获得更好的结果,因为连接操作并不总是那么昂贵。当然,这取决于您的统计数据。

1+2在SQL Server下实际上是相同的

我从未使用过[3],但它看起来很奇怪。我强烈怀疑优化器会使它等同于其他2

运行所有3条语句并比较生成的执行计划是一个很好的练习

因此,考虑到相同的性能,最清楚的一点就是我的投票-[2]是标准,否则它会得到支持[1]

在您的情况下,如果您不想从中得到任何列,那么为什么还要在语句中包含表A呢

如果它只是一个过滤器,即仅包含表B中的行,其中表a中存在一行,即使我不需要表a中的任何COL,那么所有3个语法都可以,尽管您可能会发现在某些数据库中使用If exists更有效:

 SELECT * from Table_B b WHERE EXISTS (SELECT 1 FROM Table_A a WHERE b.id_b = a.id_a)
尽管根据我的经验,这在性能上通常与其他任何一种都相当

您还会问,那么您会选择一个子查询而不是其他表达式吗。这可以归结为它是否是一个相关子查询

基本上-必须为outer语句中的每一行运行一次相关子查询-上述情况也是如此-对于表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

为什么要为此使用视图?如果要从表中获取数据,请从表中获取数据

或者,如果需要一个视图来转换表中的某些列,例如将空值合并为零,则只在B表上创建一个视图。如果某些DBA已经实施了一项策略,即所有选择都必须通过视图而不是表进行,则这也适用:-


在这两种情况下,您不必担心多表访问。

这取决于平台

SQLServer分析约束外键、主键等的逻辑含义,并以内联方式扩展视图。这意味着视图代码的“不相关”部分已被优化程序淘汰。SQL Server将为所有三种情况提供完全相同的执行计划。笔记乐观主义者能够处理的复杂性是有限的,但它肯定能够处理这一点

然而,并非所有的平台都是平等的。 -有些人可能不会以相同的方式分析约束,假设您对连接进行编码是有原因的 -有些可能会预编译视图的执行/解释计划

因此,要确定行为,您必须了解特定平台的能力。在绝大多数情况下,乐观主义者是一只复杂的野兽,因此最好的测试就是简单地去尝试和观察

编辑

作为回应 对于您的额外问题,是否优先选择相关子查询?没有简单的答案,因为这取决于您试图实现的数据和逻辑

在过去的一些案例中,我确实使用过它们,它们既可以简化查询的结构以便于维护,也可以启用特定的逻辑

如果字段表_b.id_a引用了表_a中的多个条目,则您可能只需要最新条目中的名称。您可以使用从表中选择前1名来实现,其中id\u a=table\u b.id\u a ORDER BY id\u a DESC

简言之,这要视情况而定。 -关于查询的逻辑 -论数据结构 -关于代码的最终布局

很多时候,我发现这是不必要的,但可以衡量的是,我发现这是一个积极的选择

注:

根据相关的子查询,它实际上并不总是“每个记录执行一次”。例如,SQLServer扩展了要与查询的其余部分一起执行的所需逻辑。需要注意的是,SQL代码在执行之前是经过处理/编译/任何处理的。SQL只是一种表达基于集合的逻辑的方法,然后使用优化人员可用的最佳算法将其转换为传统循环等


由于Optimizer的功能或局限性,其他RDBMS的性能可能会有所不同。有些RDBMS在使用SELECT FROM blah或EXISTS SELECT*FROM blah时表现良好,但有些表现糟糕。这同样适用于相关子查询。Sub在它们身上表现得异常出色,有些表现不太好,但根据我的经验,大多数都处理得非常好。

我想这是一个更复杂用例的简化示例。我不会过早地判断“想要成为”的,我们的应用程序中有一个类似的设置,这是有充分理由的。是真的吗,大约1+2是相同的?在Oracle中,外部联接与内部联接完全不同,特别是对于优化函数,由于外键设置的约束,它们在功能上是相同的。将表_b与表_a连接可以生成表_a的子集,但始终是表_b的完整集合。因此,从b内部联接a中选择b.*会产生与使用左联接相同的结果。将此与SQL Server的oprimiser结合使用,后者只需扫描表_b即可分析约束,并且在执行计划中不使用表_a。表_b.id_a可为空。因此,内部联接将应用表_b.id_a不为NULL,而外部联接将不为NULL…至少由于SQL Server中的主键,您不能将NULL放入表_a.id_a,并且表_b.id_a必须引用表_a.id_a的实例。因此,您的场景不能发生。@Dems:nottable\u A.id\u A。我还以为table_B.id_a可以为空。但是OP纠正了这个问题,所以这个想法已经过时了……表_b.id_a省略NOTNULL并不是故意的,我现在编辑了这个问题并添加了那个约束。编辑添加NOTNULL是多余的,因为FK约束在逻辑上不能绑定到表_a中的null记录。@Dems,幸运的是,在大多数RDBMS中,FK约束不是隐式的NOTNULL约束!这将是一个严重的功能损失!你会如何建立一个零或一的关系模型?看看这个问题,例如:@Dems。。。嗯,在谷歌搜索之后,SQL Server似乎确实有一个局限性。难以置信的