【问题标题】:What's faster: left join on NVL(key,'~') = otherkey or key = otherkey where is not null (or maybe inner query?)更快的是:左连接 NVL(key,'~') = otherkey 或 key = otherkey 不为空(或者可能是内部查询?)
【发布时间】:2021-07-17 12:05:56
【问题描述】:

我正在尝试将历史源表与新维度表的每日快照对齐。 我终于让我的查询做我想做的事,但我在两个等效版本之间的性能调整上苦苦挣扎。我在一个无法访问 Prod 的开发环境中,所以我的测试是不现实的(结果是立竿见影的)。 prod 环境将有数百万行和数十列,每个字段都有多个历史版本,因此性能很重要。当然,DBA 会审查性能,但这需要一些时间,我想了解幕后发生的事情。

对于下面的代码,请注意 A、B、C 和 X 共享一个键,X 仅具有键的子集(需要左连接),Y 具有 X 的描述信息(并且没有主键)。

我的查询运行如下:

    Select 
      A.field,
      B.field,
      C.field,
      nvl(X.field,valueIfNull),    --nvl is the oracle equivalent of coalesce
      nvl(Y.field,valueIfNull)
    from tableA A
      inner join tables BCD B, C, D
        on A.key = B.key 
        and A.date<= B.date < A.date

    ... do this 3 times for B, C & D...

现在我看到了两个选项(并且有效)

       left join tableX X
         on A.key = X.key and A.date = X.date -- this will yield null values -> left join -> ok
       left join tableY Y
         on Y.key = nvl(**X**.key,'~') and Y.date = X.date
       where Y.status in ('statusFlag1');

或者,这也可以:

        left join tableX X
         on A.key = X.key and A.date = X.date -- this will yield null values -> left join -> ok
       left join tableY Y
         on Y.key = X.key and Y.date = X.date
       where (Y.status in ('statusFlag1') or Y.status is null);

最后一种选择是使用内部查询来计算 Y 和 X 列并加入该列。我还没有添加它,因为它在 imo 中的可读性会降低,但如果它意味着性能改进,我可以添加它。

所以基本上是 nvl 和加入那些“空”值更快,还是在 where 子句中添加空值?还是使用内部查询?我们将有数百万行,mi

【问题讨论】:

  • 如问题中所述,因为我无权访问生产,而且我的测试数据是一个非常小的子集,其中两个查询给出完全相同的性能
  • @DawTheDataDawer 。 . .我不认为这些是等价的。很难看出你真正想要什么,但你应该选择正确的逻辑。
  • 是的,Martin schapendonk 在下面的答案中明确表示,我的情况不能转置为简单的等价物。我将使用最普遍适用的逻辑(没有 nvl)并等待 BDA 验证查询

标签: sql oracle query-optimization


【解决方案1】:
left join tableX X
  on A.key = X.key and A.date = X.date -- this will yield null values -> left join -> ok
left join tableY Y
  on Y.key = nvl(**X**.key,'~') and Y.date = X.date
where Y.status in ('statusFlag1');

这不是LEFT JOIN,因为WHERE 子句强制Y.status 中必须有一个值,因此这实际上是INNER JOIN,因为Y.status 不能是NULL;然后假设Y.dateNOT NULL 那么X.date 也不能是NULL 所以X 也必须是一个有效的行。

如果您想要LEFT OUTER JOIN,则将过滤条件放在ON 子句中,而不是WHERE 子句中:

left join tableX X
  on ( A.key = X.key and A.date = X.date )
left join tableY Y
  on ( Y.key = X.key and Y.date = X.date AND Y.status = 'statusFlag1' );

或者,如果您只想要 Y.status 具有所需标志的 X 和 Y 表的组合,则使用子查询:

LEFT OUTER JOIN (
  SELECT x.*,
         y.*
  FROM   tableX X
         INNER JOIN tableY Y
         ON (Y.key = X.key and Y.date = X.date)
  WHERE  Y.status = 'statusFlag1'
) XY
  on ( A.key = XY.key and A.date = XY.date );

注意:这并没有解决性能问题,因为这是您必须对表、分区、索引、数据、统计信息和硬件进行分析的东西,我们不能轻易提出建议。

【讨论】:

  • 这完全正确!只有在 A -> X 连接上才需要左连接,因为 X 中的每个值在 Y 中都有一个值!非常感谢!可怜的性能,我需要等待 DBA
【解决方案2】:

两种解决方案都需要or Y.status is null,因为左连接在这两种情况下都不会给出 Y 的结果(或者您会更改查询结果)。

添加后,最好不使用 nvl() 直接在键上进行连接,因为这对数据库来说是一项繁重的操作。

实际性能仍应在生产环境中衡量,因为硬件、索引、表大小(也相对于彼此)和数据库参数有很大影响。

【讨论】:

  • 如果“~”是 Y 中的有效键,并且您将 X 的记录与 Y 的不相关记录关联起来,而 Y 的记录恰好具有您不感兴趣的状态,那么您的结果更多的是运气而不是智慧.请不要那样做。
  • 谢谢!以性能着称,我将使用 where 子句。在这两种情况下都考虑 Null,这在我的模型中不正确,因为 Y.status 仅作为左连接的结果为空,它是一个不可为空的值,在键字段中具有“~”,因此 nvl(X. keyfield, '~') 产生完整的结果,因为 x.key 仅作为第一个左连接的结果为空。正如 MT0 指出的,第二个连接实际上是一个内部连接
  • 其实,是也不是。这是一个空替换,这意味着它将始终具有状态标志 1。在 100% 的键是“~”的情况下,值是默认值。当然你说的是对的,我不会再加入“〜”,因为它很危险。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-20
  • 2017-07-25
  • 2012-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多