需要帮助来提高使用IN子句的联合的性能

时间:2015-04-16 10:23:04

标签: sql oracle performance union

我有一个大型查询,我正在尝试提高性能,但不知道如何处理它。

以下是简化版本,但结构保持不变。

SELECT * FROM (
    SELECT A.Col1, SUM(B.Col2), SUM(C.Col3)
    FROM TableA A
       LEFT JOIN TableB B
           on A.Seq = B.Seq AND
              B.Nums in (1,2,3)
       LEFT JOIN TableC C
           on A.Seq = C.Seq AND
              C.Nums in (1,2,3)
     Where A.Location = 123456
     GROUP BY A.Col1
)

UNION ALL

SELECT * FROM (
    SELECT A.Col1, SUM(B.Col2), SUM(C.Col3)
    FROM TableA A
       LEFT JOIN TableB B
           on A.Seq = B.Seq AND
              B.Nums in (4,5,6)
       LEFT JOIN TableC C
           on A.Seq = C.Seq AND
              C.Nums in (4,5,6)
     Where A.Location = 123456
     GROUP BY A.Col1
)

现在每个联盟之间的唯一区别是联接的IN子句。它们包含不同的数字。 LEFT加入是故意的。

目前我有两个工会,但我有一个更改请求,将此增加到4个工会,因为我们有一些新的数字要检索哪些将在IN子句中使用。

此查询执行约300次,其中Where中的位置编号每次都会更改。 300次运行的总执行时间大约需要15分钟。这只会随着更多工会的增加而增加。

以前,每个单独的SELECT都在代码中执行,然后合并了各个DataTable。我创建了一个联盟,它提高了性能,但没有我希望的那么多。

有什么想法吗?

3 个答案:

答案 0 :(得分:1)

提高查询性能需要的是执行计划。这样你就可以看出瓶颈在哪里。

你还没有说过关于索引的内容,所以如果你还没有,请创建它们:

索引可以提前,但通常很简单:

CREATE INDEX idxTableASeq on TableA(Seq)
CREATE INDEX idxTableBSeq on TableB(Seq)
CREATE INDEX idxTableCSeq on TableC(Seq)
CREATE INDEX idxTableALocation on TableA(Location)
CREATE INDEX idxTableBNums on TableB(Nums)
CREATE INDEX idxTableCNums on TableC(Nums)

你必须衡量表现。 虽然索引可能会改进选择,但它可能会对插入和更新产生负面影响。

答案 1 :(得分:0)

您有一个奇怪的查询,因为您有来自“A”的相同值的多行,无法区分它们。但是,您可以使用case中的group byjoin中的某些逻辑来执行您想要的操作:

SELECT A.Col1, SUM(B.Col2), SUM(C.Col3)
FROM TableA A LEFT JOIN
     TableB B
     on A.Seq = B.Seq AND
        B.Nums in (1, 2, 3, 4, 5, 6) LEFT JOIN
     TableC C
     on A.Seq = C.Seq AND
        (coalesce(B.Nums, 1) IN (1, 2, 3) and C.Nums IN (1, 2, 3) or
         coalesce(B.Nums, 4) IN (4, 5, 6) and C.Nums IN (4, 5, 6)
        )
 Where A.Location = 123456 
 GROUP BY A.Col1,
          (CASE WHEN coalesce(B.Nums, C.Nums) in (1, 2, 3) then 1
                WHEN coalesce(B.Nums, C.Nums) in (4, 5, 6) then 2
           END);

注意:我认为您应该将case表达式放在select以及group by中,以便区分行。

如果我不得不猜测,c.num可能与b.num的内容相同。如果是这样,您可以将第二个on子句简化为:

     on A.Seq = C.Seq AND
        (B.Nums = C.Nums OR C.Nums IN (1, 2, 3 4, 5, 6) )

然后,不是多次执行查询,只需将所有位置放在查询中并运行一次:

SELECT A.location, A.Col1, SUM(B.Col2), SUM(C.Col3)
FROM TableA A LEFT JOIN
     TableB B
     on A.Seq = B.Seq AND
        B.Nums in (1, 2, 3, 4, 5, 6) LEFT JOIN
     TableC C
     on A.Seq = C.Seq AND
        (coalesce(B.Nums, 1) IN (1, 2, 3) and C.Nums IN (1, 2, 3) or
         coalesce(B.Nums, 4) IN (4, 5, 6) and C.Nums IN (4, 5, 6)
        )
 Where A.Location in (<list goes here>)
 GROUP BY A.location, A.Col1,
          (CASE WHEN coalesce(B.Nums, C.Nums) in (1, 2, 3) then 1
                WHEN coalesce(B.Nums, C.Nums) in (4, 5, 6) then 2
           END);

答案 2 :(得分:0)

加快查询速度的另一种方法是完全避免in条款。在我看来,B.Nums和C.Nums的可能值在某些类别中本质上是相关的(因此您对每组“Nums”使用一个查询)。如果是这样,您只需将“NumsGroup”列添加到预先填充的B和C,然后保持更新。 E.g:

update B set NumsGroup = 1 where Nums in (1,2,3)

然后在您的查询中,您可以这样做:

LEFT JOIN TableB B
  on A.Seq = B.Seq AND
  B.NumsGroup = 1

根据in条款中实际存在的数量,这可能会加快速度。显然,必须让NumsGroup列在插入上保持最新是一件麻烦事。将NumsGroup关系保存在一个单独的表中会更好,但是你必须使用子选择,并且可能会失去比你获得的更多的性能。