提高仅使用select查询的存储过程的性能

时间:2013-10-01 10:20:03

标签: performance sql-server-2005 select

在我们的环境中,一个程序需要很长时间才能执行。我已检查过程,以下是摘要 -

  1. 该过程仅包含select块(大约24个)。在每次选择之前,我们检查数据是否存在。如果是,请选择数据,否则执行其他操作。例如:

    -- Select block 1 --
    IF EXISTS (SELECT 1 FROM table1 t1
              INNER JOIN table2 t2
              ON t1.col1=t2.col1
              WHERE t1.col2='someValue' AND t2.col2='someValue'
              )
    BEGIN
       SELECT t1.col1,t2.col2,t2.col3 FROM table1 t1
       INNER JOIN table2 t2
       ON t1.col1=t2.col1
       WHERE t1.col2='someValue' AND t2.col2='someValue'
    END
    ELSE
    BEGIN
      SELECT 'DEFAULT1', 'DEFAULT2', 'DEFAULT3'
    END
    
    
    -- Select block 2 --
    IF EXISTS (SELECT 1 FROM table1 t1
              INNER JOIN table2 t2
              ON t1.col1=t2.col1
              WHERE t1.col5='someValue' AND t2.col5='someValue'
              )
    BEGIN
       SELECT t1.col5,t2.col6,t2.col7 FROM table1 t1
       INNER JOIN table2 t2
       ON t1.col1=t2.col1
       WHERE t1.col5='someValue' AND t2.col5='someValue'
    END
    ELSE
    BEGIN
      SELECT 'DEFAULT1', 'DEFAULT2', 'DEFAULT3'
    END        
    
  2. 我已经得出结论,如果我们可以将IF EXISTS块中使用的查询组合成一个查询,并为某些变量设置一些值,以便我们可以识别哪个条件返回true,那么可以减少执行时间,提高绩效。

    我的想法是否正确?有没有选择呢?你能建议其他选择吗?

    我们正在使用Microsoft SQL Server 2005。

    [编辑:已添加] - 所有select语句不返回相同的列类型,它们是不同的。并且所有选择语句都是必需的。如果有24个块,则程序应该返回24个结果集。

    [补充]

    我想再问一个问题,下面哪一项运行得更快 -

    1. SELECT 1 FROM table1 t1 INNER JOIN table2 t2 ON t1.col1 = t2.col1 在哪里t1.col2 ='someValue'AND t2.col2 ='someValue'
    2. SELECT COUNT(1)FROM table1 t1 INNER JOIN table2 t2 ON t1.col1 = t2.col1 在哪里t1.col2 ='someValue'AND t2.col2 ='someValue'
    3. SELECT TOP 1 1 FROM table1 t1 INNER JOIN table2 t2 ON t1.col1 = t2.col1 在哪里t1.col2 ='someValue'AND t2.col2 ='someValue'
    4. 感谢。 Kartic

3 个答案:

答案 0 :(得分:2)

为了提高选择查询的性能...在where子句中使用的列上创建“索引

就像你正在使用

WHERE t1.col2='someValue' AND t2.col2='someValue' 
WHERE t1.col5='someValue' AND t2.col5='someValue'

所以在 col2 col5

上创建数据库索引

临时表 您可以使用临时表来存储结果。由于您使用相同的查询24次,因此首先将以下查询的结果存储到临时表中(根据需要更正语法)

insert into temp_table (col2, col5)
SELECT col1, col5 FROM table1 t1
          INNER JOIN table2 t2
          ON t1.col1=t2.col1

现在使用临时表进行检查

-- Select block 1 --
IF EXISTS (SELECT 1 FROM temp_table
          WHERE t1.col2='someValue' AND t2.col2='someValue'
          )
BEGIN
   SELECT t1.col1,t2.col2,t2.col3 FROM table1 t1
   INNER JOIN table2 t2
   ON t1.col1=t2.col1
   WHERE t1.col2='someValue' AND t2.col2='someValue'
END

-- Select block 2 --
IF EXISTS (SELECT 1 FROM temp_table1
          WHERE t1.col5='someValue' AND t2.col5='someValue'
          )
BEGIN
   SELECT t1.col5,t2.col6,t2.col7 FROM table1 t1
   INNER JOIN table2 t2
   ON t1.col1=t2.col1
   WHERE t1.col5='someValue' AND t2.col5='someValue'
END

答案 1 :(得分:0)

当前结构不是很有效 - 你实际上必须执行每个“if”语句(这将是昂贵的),然后如果“if”返回true,则重复相同的where子句(昂贵的位)。而你这样做了24次。最糟糕的情况(所有查询返回数据),您的查询时间加倍。

你说你已经检查了索引 - 假设每个查询看起来有些微妙的不同,那么值得仔细检查一下。

显而易见的是重构应用程序以执行24个select语句,并处理有时它们不返回任何数据的事实。这是一个相当大的重构,我假设你已经考虑过......

如果你不能这样做,可以考虑一个不那么雄心勃勃(虽然更糟糕)的重构。而不是检查数据是否存在,并返回它或等效的默认结果集,而是将其写为union:

   SELECT t1.col1,t2.col2,t2.col3 FROM table1 t1
   INNER JOIN table2 t2
   ON t1.col1=t2.col1
   WHERE t1.col2='someValue' AND t2.col2='someValue'
   UNION  
   SELECT 'DEFAULT1', 'DEFAULT2', 'DEFAULT3'

这会减少您访问where子句的次数,但这意味着您的客户端应用程序必须过滤掉“默认”数据。

要回答你的最后一个问题,我会通过查询优化器运行它并查看执行计划 - 但我想第一个版本最快 -​​ 查询可以在找到第一个记录后立即完成匹配where条件。第二个版本必须找到匹配并计算它们的所有记录;最终版本必须找到所有记录并选择第一个。

答案 2 :(得分:0)

您可以将查询结果外部连接到一行默认值,然后在查询结果为空时回退到默认值:

SELECT
  col1 = COALESCE(query.col1, defaults.col1),
  col2 = COALESCE(query.col2, defaults.col2),
  col3 = COALESCE(query.col3, defaults.col3)
FROM
  (SELECT 'DEFAULT1', 'DEFAULT2', 'DEFAULT3') AS defaults (col1, col2, col3)
LEFT JOIN
  (
    SELECT t1.col1, t2.col2, t2.col3
    FROM table1 t1
    INNER JOIN table2 t2
    ON t1.col1=t2.col1
    WHERE t1.col2='someValue' AND t2.col2='someValue'
  ) query
  ON 1=1  -- i.e. join all the rows unconditionally
;

如果子查询实际上可能返回NULL并且那些不能用默认值替换,那么该方法可能不适合您。在这种情况下,让子查询返回一个标志列(只是任何值)。如果该列在最终查询中的计算结果为NULL,则只能表示子查询未返回行。您可以在CASE表达式中使用该事实,如下所示:

SELECT
  col1 = CASE WHEN query.HasRows IS NULL THEN defaults.col1 ELSE query.col2 END,
  col2 = CASE WHEN query.HasRows IS NULL THEN defaults.col2 ELSE query.col2 END,
  col3 = CASE WHEN query.HasRows IS NULL THEN defaults.col3 ELSE query.col2 END
FROM
  (SELECT 'DEFAULT1', 'DEFAULT2', 'DEFAULT3') AS defaults (col1, col2, col3)
LEFT JOIN
  (
    SELECT HasRows = 1, t1.col1, t2.col2, t2.col3
    FROM table1 t1
    INNER JOIN table2 t2
    ON t1.col1=t2.col1
    WHERE t1.col2='someValue' AND t2.col2='someValue'
  ) query
  ON 1=1
;