【问题标题】:Optimize SQL Stored Procedure优化 SQL 存储过程
【发布时间】:2009-05-06 19:47:41
【问题描述】:

在大型表上运行 SELECT 的存储过程超时。 where 子句导致超时,因为我只选择另一个表中的城市。

AND city IN (SELECT DISTINCT city from tOH where clientId = @clientId)
AND state IN (SELECT DISTINCT state from tOH where clientId = @clientId)

*注意几乎总是只返回一种状态

我正在尝试将城市放入表中,然后使用该表来填充城市,但我收到了一个错误,即未声明 @cities。

DECLARE @cities TABLE
(
city varchar(200)
);
INSERT INTO @cities (city) SELECT city FROM tOH WHERE clientId = @clientId GROUP BY city

然后我的 where 子句变为

AND city IN (SELECT city from @cities) 

谁能找到优化这个存储过程的好方法?

-------------- 更新 ------------------ ------------------

连接都太慢了。我认为带有临时表或表变量的解决方案会起作用。

【问题讨论】:

  • 联接通常比临时表快,或者至少有可能是因为它们更好地利用了索引。您需要将连接与适当的索引结合起来。

标签: sql optimization stored-procedures select


【解决方案1】:

不仅慢,而且不正确。

假设您的城市是“威斯康星州埃文斯维尔”,但您的 tOH 表只有“埃文斯维尔,印第安纳州”和“密尔沃基,威斯康星州”的条目。您目前分别检查城市和州部分,因此您现有的查询将找到“Evansville”和“WI”的匹配项。它会允许那个城市,即使它真的不应该。

改为这样做:

INNER JOIN 
  ( 
    SELECT DISTINCT City AS tOHCity, State AS tOHState 
    FROM tOH 
    WHERE ClientID= @ClientID 
  ) cs ON cs.tOHCity = city AND cs.tOHState = state

请注意,子查询基于这样的假设,即原始帖子中的 DISTINCT 是必要的,因为每个客户的该表中可能有多个相同的城市。如果不是这样,您可以直接加入tOH 表。

将此与适当的索引结合起来,您应该会很好。

【讨论】:

  • 好点。在这个例子中,两个州同一个城市的机会非常少。我写了一个星号模糊地试图说大多数时间只会返回一个状态。应该说客户端覆盖的区域比区号还小,跨州线的情况很少见。
【解决方案2】:

我会在 tOH 表上尝试 JOIN 并通过 clientid 过滤整个查询。您也可以使用 SELECT INTO 将其放入临时表中。

【讨论】:

    【解决方案3】:

    来自 SQL 黑客:

    当子查询不包含 聚合函数,机会是你 不需要子查询 - 你需要一个 加入。

    因此,您应该将您的第一个子查询 (AND CITY IN) 转换为 JOIN。除非您向我们提供查询的其余部分,否则我们将无法准确地向您展示如何操作,但其基础是将 City 添加为您在主查询中选择的表。

    【讨论】:

    • 我已经有一个相当复杂的过程,所以我试图远离另一个加入。
    【解决方案4】:

    我认为指出感知超时背后的原因可能是值得的。您的查询从原始表中选择每条记录,然后对于它选择的每条记录,它必须为每条记录一遍又一遍地子查询同一表中的 DISTINCT 城市列表。

    【讨论】:

    • 好点。这就是我几乎从不使用子查询的原因。我要么使用在这种情况下可以完成的普通连接,要么使用派生表。
    • 正是我想要创建一个临时表来存储城市的原因 - @cities
    • 好吧,即使使用@cities 临时表,您最终也会为主表的每一行查询一次临时表,您最终会看到同样的低效率。如果没有在此处发布整个存储过程,我不确定我们所有人可以提供多少帮助。出于好奇,您使用的是主键,对吧?
    • 表有150万行
    【解决方案5】:

    您可能已经尝试过,但我的第一反应是使用您的城市填充临时表。这可能是你正在做的,我只是不熟悉语法,但我一直使用:

    Create Table #Cities(City varchar(200))
    

    然后您将填充临时表并从中查询,如您的示例(INSERT INTO... 和 AND city IN (SELECT city from #Cities))

    【讨论】:

    • 表变量(声明@newTable...)是新的。它们比临时表更漂亮,因为它们会自动超出范围(您不必删除它们)。但是有一些操作它们不起作用-例如,如果您需要将 SQL 语句构建为字符串然后执行它,或者在子查询中使用新表,则不能使用表变量.然后,您只需使用旧式临时表即可。
    • Roland,表变量并不新鲜,它们是在 SQL Server 2000 中。但有时临时表的性能更好,所以你应该尝试两者,看看哪个最好。
    • 因为我的表太大(150 万个条目),所以这个解决方案是最优的。谢谢!
    【解决方案6】:

    您可能希望考虑在 State 列上放置一个索引,但您应该对此进行一些基准测试。插入新行时,您必须权衡索引的好处与成本。

    您可能还想对 City 列执行相同的操作。

    【讨论】:

      【解决方案7】:

      使用 EXISTS 代替 IN

      AND EXISTS(SELECT 1 FROM tOH WHERE tOH.city=main.city AND clientId=@clientId)
      

      您还需要确保该城市在两个表中都有索引。

      【讨论】:

      • 不错的建议。使用存在还是太慢了。
      【解决方案8】:

      我假设 tOH 上的 DISTINCT 的原因是一个城市名称可能存在于多个州,同样,一个州也有多次出现,因为每个州都有多个城市。

      如果每个城市和州的组合都是唯一的事件,那么删除 DISTINCT 并执行以下操作会更合适且更具成本效益:

      select mytable.* 
      from mytable m
      inner join tOH t on  t.clientid = @clientId 
          and t.city = m.city and t.state = m.state
      

      【讨论】:

      • 在这种情况下仍然很慢。
      【解决方案9】:
      select ....
      from ....
          inner join tOH ON ...city=tOH.city and ...state=tOH.state
      where ... and tOH.clientId = @clientId
      

      【讨论】:

        【解决方案10】:

        在回答您为什么@cities 作为表变量可能不起作用的问题时,您还没有展示 sp 的其余部分,但我会在某个地方构建动态 SQL 并执行它。这将超出先前存在的表变量的范围。

        【讨论】:

          【解决方案11】:

          您可以使用您的原始查询(没有连接),进行下一步更正:

          1. 按 clientID、城市、州在 TOH 上创建索引
          2. 删除 DISTINCT 关键字 - 这允许优化器有效利用该索引

          【讨论】:

          • 不,他不能。他现有的查询是错误的。请参阅我的答案以了解原因。
          【解决方案12】:

          将您的 IN 过滤器更改为存在。所以而不是:

          AND city IN (SELECT DISTINCT city from tOH where clientId = @clientId)
          AND state IN (SELECT DISTINCT state from tOH where clientId = @clientId)
          

          将其更改为:

          AND EXISTS ( 
                SELECT 0 FROM tOH t WHERE 
                ClientID = @clientId 
                and t.City = parent.ctiy 
                and t.state = parent.state 
          )
          

          我已经进行了性能测试,并且一直发现 EXISTS 的性能比 IN 快。此外,由于您在同一张桌子上做了两次,所以您受到了双重打击。

          如果可能,您还应该在 tOH 中索引城市和州。索引是一种让步,因此请确保您了解添加它们的含义。

          【讨论】:

          • 好主意。在这种情况下仍然太慢了。
          猜你喜欢
          • 2018-04-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-10-26
          • 2018-02-06
          • 2021-04-17
          相关资源
          最近更新 更多