如何在where子句中搜索串联值时提高SQL查询性能

时间:2019-06-21 16:02:04

标签: sql oracle performance

我有一个查询,在运行时运行得非常好(快速),但是当我尝试按连接值搜索时,性能就会下降。

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

示例1:快速运行

parquet-tools

示例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, RCD
    From MainTable MT Inner Join Exmp1 E1 ON MT.ID = E1.ID 

)
Where USERNAME = 'test1'

如果您想知道为什么将整个查询都包装在一个选择中,则使用此查询的应用程序希望查询以“选择”开头。我已经尝试过不使用“ with”子句重写它,但是得到的结果相同。

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'

============= 编辑以获取其他信息:

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

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

对于给定的USERNAME@domain.com,理想的结果是USERNAME@domain.com和RCD。

请注意,这只是一个具体示例,我们对此主题的一般帮助非常感谢。

1 个答案:

答案 0 :(得分:2)

连接字符串时,将阻止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');

这应该使查询速度更快,因为过滤先决条件将与索引完全匹配。