【问题标题】:SQL Server 2008 R2 stored procedure is slowSQL Server 2008 R2 存储过程很慢
【发布时间】:2015-10-22 09:04:07
【问题描述】:

我要加入 3 个不同的表格以实现自动完成功能,而且它必须很快。建议必须及时返回。我需要使用 LIKE 运算符查看 3 个不同的字段以获得答案。请参阅下面的详细信息。

DECLARE @Space AS CHAR(1) = ' ';

 SELECT TOP (@QuantityToReturn) * 
 FROM
     (SELECT
          C.CMPID, C.CMPNAME, C.CMPTHEIRCODE, AD.ADTOWN
      FROM COMPANY C
      JOIN [dbo].[COMPADDRESS] CA ON CA.CMPID = C.CMPID
      JOIN [dbo].[ADDRESS] ad ON AD.ADID = CA.ADID
      JOIN [dbo].SUPPLIER SUP ON C.CMPID = SUP.CMPID
      WHERE 
          (C.CMPID = @LoggedInUserId
           OR @LoggedInUserId = dbo.fnIsAParentCompanyOf(@LoggedInUserId, C.CMPID) 
           OR @LoggedInUserId = 12345)

      UNION 

      SELECT
          C.CMPID, C.CMPNAME, C.CMP_THEIRCODE, AD.ADTOWN
      FROM COMPANY C
      JOIN [dbo].[COMPADDRESS] CA ON ca.CMPID = C.CMPID
      JOIN [dbo].[ADDRESS] AD ON AD.ADID = CA.ADID
      JOIN [dbo].CUSTOMER CUS ON C.CMPID = CUS.CMPID AND CUS.CUSTISTHIS = 1
      WHERE 
          (C.CMPID = @LoggedInUserId
           OR @LoggedInUserId = dbo.fnIsAParentCompanyOf(@LoggedInUserId, C.CMPID) 
           OR @LoggedInUserId = 12345)
      ) AS Results
      WHERE 
          (CMPNAME + @Space + ADTOWN + @Space + CMPTHEIRCODE) LIKE '%' + @Query + '%'

上面的代码很慢(~9sec)

  • 如果我使用:WHERE CMPNAME LIKE '%' + @Query + '%' = 快速;
  • 如果我使用:WHERE ADTOWN LIKE '%' + @Query + '%' = 快速;
  • 如果我使用:WHERE CMPTHEIRCODE LIKE '%' + @Query + '%' = 快速;

只有当我连接它们时才会变慢?

【问题讨论】:

  • 你没有sargable 谓词。您也正在使用函数调用。这就是为什么它可能很慢。如果可能,您也可以考虑将union 更改为union all
  • 您可以将OR 连接在一起,而不是串联吗?例如:CMPNAME LIKE '%' + @Query + '%' OR ADTOWN LIKE '%' + @Query + '%' OR CMPTHEIRCODE LIKE '%' + @Query + '%'
  • 我尝试了 {OR} 但它仍然很慢。它也不会真正符合要求,因为他们可以按公司名称和城镇进行搜索,然后不会返回任何内容。 UNION ALL 没有帮助。函数调用没问题。请解释一下 SARGABLE 谓词建议?
  • 但是您的搜索依赖于用户搜索由单个空格分隔的确切公司名称和城镇,如果他们搜索 Bob co, London
  • 您应该传入查询拆分部分。在单独的文本框中询问您的用户或使用应用程序逻辑来拆分用户的输入。在我看来,用这种方法找到一些东西或多或少是运气的问题。您依靠用户完全按照您期望的方式填写他们的查询...

标签: sql-server stored-procedures sql-server-2008-r2


【解决方案1】:

我会尝试这样:

WHERE 
    LEN(@Query) <> LEN(REPLACE(@Query, CMPNAME, ''))

    AND (LEN(@Query) <> LEN(REPLACE(@Query, ADTOWN, ''))
         AND CHARINDEX(CMPNAME, @Query) = (CHARINDEX(ADTOWN, @Query) - 1 - LEN(CMPNAME)) 
        ) -- making sure that the value of ADTOWN comes after CMPNAME

    AND (LEN(@Query) <> LEN(REPLACE(@Query, CMPTHEIRCODE, ''))
         AND CHARINDEX(ADTOWN, @Query) = (CHARINDEX(CMPTHEIRCODE, @Query) - 1 - LEN(ADTOWN))
        ) -- making sure that the value of CMPTHEIRCODE comes after ADTOWN

我尝试重新实现您的 3 个条件的逻辑,以便在参数上运行,而不是在您拥有索引的列上运行。我的假设是在这种情况下将使用索引。

PS:查找/减去 charindex 的结果时可能需要进行一些小调整,以防我在计算中犯了一些错误,但希望你明白了。 - SQLFiddle to validate that the CHARINDEX() comparisons are correct

另外,我建议/尝试对查询的顶部进行一些额外的更改,以便最终您的脚本如下所示:

DECLARE @Space AS CHAR(1) = ' ';

SELECT TOP (@QuantityToReturn) *
FROM (
    SELECT C.CMPID
        ,C.CMPNAME
        ,C.CMPTHEIRCODE
        ,AD.ADTOWN
    FROM COMPANY C
    INNER JOIN [dbo].[COMPADDRESS] CA
        ON CA.CMPID = C.CMPID
    INNER JOIN [dbo].[ADDRESS] ad
        ON AD.ADID = CA.ADID
    WHERE (
            C.CMPID = @LoggedInUserId
            OR @LoggedInUserId = dbo.fnIsAParentCompanyOf(@LoggedInUserId, C.CMPID)
            OR @LoggedInUserId = 12345
            )
        AND EXISTS (
            SELECT 1
            FROM [dbo].SUPPLIER SUP
            WHERE C.CMPID = SUP.CMPID
            )

    UNION

    SELECT C.CMPID
        ,C.CMPNAME
        ,C.CMP_THEIRCODE
        ,AD.ADTOWN
    FROM COMPANY C
    INNER JOIN [dbo].[COMPADDRESS] CA
        ON ca.CMPID = C.CMPID
    INNER JOIN [dbo].[ADDRESS] AD
        ON AD.ADID = CA.ADID
    WHERE (
            C.CMPID = @LoggedInUserId
            OR @LoggedInUserId = dbo.fnIsAParentCompanyOf(@LoggedInUserId, C.CMPID)
            OR @LoggedInUserId = 12345
            )
        AND EXISTS (
            SELECT 1
            FROM [dbo].CUSTOMER CUS
            WHERE C.CMPID = CUS.CMPID
                AND CUS.CUSTISTHIS = 1
            )
    ) AS Results
WHERE LEN(@Query) <> LEN(REPLACE(@Query, CMPNAME, ''))
    AND (
        LEN(@Query) <> LEN(REPLACE(@Query, ADTOWN, ''))
        AND CHARINDEX(CMPNAME, @Query) = (CHARINDEX(ADTOWN, @Query) - 1 - LEN(CMPNAME))
        ) -- making sure that the value of ADTOWN comes after CMPNAME
    AND (
        LEN(@Query) <> LEN(REPLACE(@Query, CMPTHEIRCODE, ''))
        AND CHARINDEX(ADTOWN, @Query) = (CHARINDEX(CMPTHEIRCODE, @Query) - 1 - LEN(ADTOWN))
        ) -- making sure that the value of CMPTHEIRCODE comes after ADTOWN

希望对你有帮助。

【讨论】:

  • 谢谢拉杜。一有时间,我就会尝试您的建议。会让你知道的。
  • @cocojiambo 你有没有机会检查它是否运行得更快?
  • 它确实回来了[很快,但无论我输入什么,它都不会返回任何结果。尝试调试它,看看可能出了什么问题。如果我成功了,会告诉你的。感谢您的跟进。
  • @cocojiambo 请让我知道问题出在哪里,也许我可以调整我的查询。您最初可以尝试将查询中的WHERE 子句替换为我的建议。我希望我没有通过将 JOINs 移动到 EXISTS 来改变逻辑中的任何内容。
  • 否 @Radu 不起作用。我尝试只使用WHERE 子句,但它似乎没有带来任何东西。
猜你喜欢
  • 2015-01-26
  • 1970-01-01
  • 1970-01-01
  • 2018-09-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-23
  • 2018-09-21
  • 1970-01-01
相关资源
最近更新 更多