【问题标题】:How to Increase SQL Query Performance on where clauses searching concatenated values如何提高搜索连接值的 where 子句的 SQL 查询性能
【发布时间】:2019-06-21 16:02:04
【问题描述】:

我有一个查询在运行时运行得非常好(快速),但是一旦我尝试通过串联值进行搜索,性能就会下降。

为了保持高性能,我有哪些选择?我知道显而易见的解决方案是不通过连接的字符串进行搜索,但是在某些情况下我不得不这样做。我该如何处理这些情况。

示例 1:快速运行

Select * From
(
    With Exmp1 AS 
    (
    Select ID, RCD From Table1 a where EFFDT = (Select Max(b.EFFDT) 
    FROM Table1 b 
    Where a.ID = b.ID and a.RCD = b.RCD) and status = 'A'
    )

    Select USERNAME, RCD
    From MainTable MT Inner Join Exmp1 E1 ON MT.ID = E1.ID 

)
Where USERNAME = 'test1'

示例 2:运行缓慢

Select * From
(
    With Exmp1 AS 
    (
    Select ID, RCD From Table1 a where EFFDT = (Select Max(b.EFFDT) 
    FROM Table1 b 
    Where a.ID = b.ID and a.RCD = b.RCD) and status = 'A'
    )

    Select USERNAME || '@domain.com', RCD
    From MainTable MT Inner Join Exmp1 E1 ON MT.ID = E1.ID 

)
Where USERNAME = 'test1@domain.com'

如果您想知道为什么我将整个查询包装在一个 select 中,那么使用此查询的应用程序期望查询以“Select”开头。我已经尝试在没有“with”子句的情况下重新编写它,但我得到了相同的结果。

Select USERNAME || '@domain.com', RCD
From MainTable MT Inner Join 
(Select ID, RCD 
 From Table1 a 
 Where EFFDT = (Select Max(b.EFFDT) FROM Table1 b Where a.ID = b.ID and a.RCD = b.RCD) and status = 'A'
) E1 ON MT.ID = E1.ID 
Where USERNAME|| '@domain.com' = 'test1@domain.com'

============ 编辑附加信息:

Table1 有四列:ID、RCD、EFFDT、STATUS。重要的是,我只获取最新记录 (EFFDT) 和状态值为“A”的记录。

主表有两列:ID、USERNAME。我无法更改此表或创建其他表、数据库视图。我必须使用目前存在的这些数据,并且只能以 SQL 查询的形式进行交互。

所需的结果是 USERNAME@domain.com 和给定 USERNAME@domain.com 的 RCD。

请注意,这只是一个具体示例,我们非常感谢您提供有关该主题的一般帮助。

【问题讨论】:

  • 你能给出一些样本数据和他们想要的结果吗?
  • 我总是得到正确的数据输出,所以我并不担心,但如果有帮助,我会用一些额外的数据更新帖子。
  • 使用物化视图可能是一个不错的选择或生成列
  • 请列出Table1MainTable拥有的所有索引。
  • 那些搜索连接值的子句总是相同的吗?否则使用物化视图或生成的列也没有多大意义。理想情况下,请参阅 Why should I provide a Minimal Reproducible Example for a very simple SQL query? 以提供示例数据和预期结果。理想情况下,我们还需要有 CREATE TABLE 语句,以便我们知道数据类型和索引。

标签: sql oracle performance


【解决方案1】:

当您连接字符串时,您会阻止 SQL 优化器使用 MainTable (USERNAME) 上的现有索引。这迫使引擎遵循不同的 [较慢] 路径;可能是 HEAP [TABLE] 扫描。就这么简单。

如果您确实需要提供完整的电子邮件地址,我将在最后一步而不是之前计算串联,基本上回到您的第一个选项。例如:

Select USERNAME || '@domain.com', RCD From
(
    With Exmp1 AS 
    (
    Select ID, RCD From Table1 a where EFFDT = (Select Max(b.EFFDT) 
    FROM Table1 b 
    Where a.ID = b.ID and a.RCD = b.RCD) and status = 'A'
    )

    Select USERNAME, RCD
    From MainTable MT Inner Join Exmp1 E1 ON MT.ID = E1.ID 

)
Where USERNAME = 'test1'

编辑

更进一步,您可以重新表述整个查询,并找出简化查询后容易看到的优化:

  • 问题是MT.USERNAME 列可能比a.STATUS 更具选择性,因此您应该先对其进行过滤。
  • 那么,为了使关联子查询更快,您可能希望在其上使用“覆盖索引”,因此我建议添加ix2,如下所示。

例如:

Select
  MT.USERNAME || '@domain.com', a.RCD 
From MainTable MT
join Table1 a on a.ID = MT.ID
where MT.USERNAME = 'test1'
  and a.status = 'A'
  and a.EFFDT = (
    Select Max(b.EFFDT) FROM Table1 b Where a.ID = b.ID and a.RCD = b.RCD
  )

现在,为了让这个查询真正快速,您需要以下索引。看来你已经有了第一个:

create index ix1 on MainTable (USERNAME); -- You already have this one
create index ix2 on Table1 (ID, RCD, EFFDT);

二次编辑:如果您真的想使用完整的用户名进行搜索,您可以在表达式上添加索引。以您的“示例 2”为例,更改 WHERE 条件,如下所示:

Select * From
(
    With Exmp1 AS 
    (
    Select ID, RCD From Table1 a where EFFDT = (Select Max(b.EFFDT) 
    FROM Table1 b 
    Where a.ID = b.ID and a.RCD = b.RCD) and status = 'A'
    )

    Select USERNAME || '@domain.com', RCD
    From MainTable MT Inner Join Exmp1 E1 ON MT.ID = E1.ID 

)
Where USERNAME || '@domain.com' = 'test1@domain.com' -- changed here

然后添加以下索引:

create index ix3 on MainTable (USERNAME || '@domain.com');

这应该会加快查询速度,因为过滤谓词将与索引完全匹配。

【讨论】:

  • 感谢您抽出宝贵时间查看此内容。我同意你在这里所说的一切,但不幸的是它没有解决我的问题。我必须使用按连接值搜索的 where 子句。我没有选择。您的回复显示了所有尝试和避免这样做的方法,我同意这显然是最佳的,但出于我的目的,我不能。
  • @DrewAschenbrener 好的,知道了。您仍然可以选择使用表达式添加索引。让我检查一下。
  • 如果连接的值“总是”不同......它来自哪里?
  • @DrewAschenbrener 如果是这种情况,我只能看到实现视图的选项,正如 Raymond Nijland 之前提到的那样。
  • 我明白了。谢谢你们一直以来的反馈。我知道这不是一个定义明确的问题,它的用例本质上很奇怪。我会将你的答案标记为正确(任何看过这篇文章的人都应该听从你的建议)。
猜你喜欢
  • 2018-11-08
  • 1970-01-01
  • 1970-01-01
  • 2019-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-15
  • 1970-01-01
相关资源
最近更新 更多