Sql Azure:WITH 语句查询的性能非常慢

Posted

技术标签:

【中文标题】Sql Azure:WITH 语句查询的性能非常慢【英文标题】:Sql Azure: Very slow performance on WITH statement query 【发布时间】:2014-10-13 05:32:49 【问题描述】:

我试图了解 SQL azure 中 WITH 语句的用法。 我有一个初始查询,然后需要过滤 2 次。 如果我只运行初始查询,它会运行得很快。但是,一旦我添加了额外的过滤器,查询就会运行得很慢,以至于永远不会完成,并且 azure force 会关闭连接。

所以,第一个 WITH 语句,它自己运行得非常快 ->

CREATE TYPE BrokerAccountAndTimeType as TABLE
(
    [BrokerAccountId] [bigint],
    [TimeUTC] [datetime]
);
GO

CREATE PROCEDURE [dbo].[GetLatestOpenBrokerAccountTradesByBrokerAccountAndTime]
@UserId [int],
@BrokerAccountAndTime BrokerAccountAndTimeType READONLY
AS
BEGIN
WITH trades as (SELECT bats.*
                FROM BrokerAccountTrades bats
                where bats.[TradeTimeUTC] = (SELECT MAX([TradeTimeUTC]) 
                           FROM BrokerAccountTrades bats2
                           inner join @BrokerAccountAndTime bat
                           on (bats.UserId = @UserId and bats2.BrokerAccountId = bat.BrokerAccountId)
                           WHERE bats2.UserId = bats.UserId
                           AND bats2.BrokerAccountId = bats.BrokerAccountId
                           AND bats2.SecurityId = bats.SecurityId
                           AND bats2.[TradeTimeUTC] < bat.TimeUTC))

    select *
    from trades

END

这不会导致问题 - 此查询在几秒钟内运行得足够快。 但是,如果我随后添加额外的过滤,以从“交易”结果中获取我真正想要的记录,那么一切都开始变得非常缓慢。 这似乎违反直觉。如果 sql server 只是按顺序运行查询,就不会有问题,而且结果会很快返回。

CREATE PROCEDURE [dbo].[GetLatestOpenBrokerAccountTradesByBrokerAccountAndTime]
@UserId [int],
@BrokerAccountAndTime BrokerAccountAndTimeType READONLY
AS
BEGIN

--initial query
WITH trades as (SELECT bats.*
                FROM BrokerAccountTrades bats
                where bats.[TradeTimeUTC] = (SELECT MAX([TradeTimeUTC]) 
                           FROM BrokerAccountTrades bats2
                           inner join @BrokerAccountAndTime bat
                           on (bats.UserId = @UserId and bats2.BrokerAccountId = bat.BrokerAccountId)
                           WHERE bats2.UserId = bats.UserId
                           AND bats2.BrokerAccountId = bats.BrokerAccountId
                           AND bats2.SecurityId = bats.SecurityId
                           AND bats2.[TradeTimeUTC] < bat.TimeUTC)),

    --filter the results from the 'trades' query    
    trades2 as (select t.*
    FROM trades t
    where t.ExternalId = (select max(ExternalId)
                        from trades t2
                        where t.userid = t2.userid
                        and t.brokeraccountid = t2.brokeraccountid
                        and t.securityid = t2.securityid))

    --filter the results from the 'trades2' query
    select t3.*
    from trades2 t3
    where t3.OpenClose = (select max(CONVERT(int,OpenClose))
                        from trades2 t4
                        where t4.userid = t3.userid
                        and t4.brokeraccountid = t3.brokeraccountid
                        and t4.securityid = t3.securityid)
    and t3.NewPosition <> 0

END

有谁知道这可能是什么问题?有没有办法强制查询按顺序运行?我可以将第一个查询返回到我的代码并过滤掉代码中不需要的行,但这似乎是一个非常丑陋的修复。

仅适用于那些试图理解查询的人。这只是获取指定时间之前的最新记录,其中可能为正在查询的每个帐户提供不同的时间(因此是表值参数)。 由于某些记录可能共享相同的时间戳,因此需要进一步过滤,因此有必要应用进一步过滤来确定共享相同时间戳的那些记录中的“最新”。

【问题讨论】:

WITH'ed (CTE) 查询没有“执行顺序” - 请注意如何阻止它们运行 DML。实际上,SQL 引擎只是将它们放在下面(它“构建一个可重复使用的中间结果集”)——现在,查询计划显示的是有问题吗? 具体化您的 CTE,这可能是您的解决方案,尽管反对者的说法不同。 HTH。 @TT 我已经尝试了物化黑客,但它在 Azure 上不起作用......我想这与它在 2012 年也不起作用的原因相同。 ***.com/questions/13090037/… @DaManJ 我不熟悉 SQL Server Azure,但是否可以使用表变量(因为您声称不能使用临时表)? @DaManJ 我遇到了this thread,关于 SQL Azure 中的临时表。我没有看到任何会禁止创建/使用临时表的东西。虽然没有使用 Azure 的个人经验,但我没有看到无法使用(本地)临时表的任何地方。 HTH。 【参考方案1】:

好的,所以我已经通过使用一个我觉得非常令人反感的表变量来“解决”这个问题。 这意味着我必须在存储过程中重新定义现有表的模式,这会产生维护负担,如果我需要修改它所引用的表(例如添加新列等),则必须更新此存储过程。呸……

CREATE PROCEDURE [dbo].[GetLatestOpenBrokerAccountTradesByBrokerAccountAndTime]
@UserId [int],
@BrokerAccountAndTime BrokerAccountAndTimeType READONLY
AS
BEGIN

DECLARE @tempTrades TABLE
(
[Id] [bigint] NOT NULL PRIMARY KEY,
[UserId] [int] NOT NULL,
[BrokerAccountId] [bigint] NOT NULL,
[SecurityId] [tinyint] NOT NULL,
[TradeTimeUTC] [datetime] NOT NULL,
[OpenClose] [bit] NOT NULL,
--many more columns...
)

insert into @tempTrades
SELECT bats.*
                FROM BrokerAccountTrades bats
                where bats.[TradeTimeUTC] = (SELECT MAX([TradeTimeUTC]) 
                           FROM BrokerAccountTrades bats2
                           inner join @BrokerAccountAndTime bat
                           on (bats.UserId = @UserId and bats2.BrokerAccountId = bat.BrokerAccountId)
                           WHERE bats2.UserId = bats.UserId
                           AND bats2.BrokerAccountId = bats.BrokerAccountId
                           AND bats2.SecurityId = bats.SecurityId
                           AND bats2.[TradeTimeUTC] < bat.TimeUTC);

    --filter the results from the 'trades' query    
    WITH trades2 as (select t.*
    FROM @tempTrades t
    where t.ExternalId = (select max(ExternalId)
                        from @tempTrades t2
                        where t.userid = t2.userid
                        and t.brokeraccountid = t2.brokeraccountid
                        and t.securityid = t2.securityid))

    --filter the results from the 'trades2' query
    select t3.*
    from trades2 t3
    where t3.OpenClose = (select max(CONVERT(int,OpenClose))
                        from trades2 t4
                        where t4.userid = t3.userid
                        and t4.brokeraccountid = t3.brokeraccountid
                        and t4.securityid = t3.securityid)
    and t3.NewPosition <> 0

END

【讨论】:

在您向表 BrokerAccountTrades 添加列的那一天,此过程将失败。 您可以使用我在 cmets 中针对您的问题概述的临时表。 @t-clausen.dk 这就是为什么我讨厌这个并说'YUK'。见feedback.azure.com/forums/217321-sql-database/suggestions/… 始终在 SELECT 语句中使用显式列而不是 *,这样就不会中断。 此外,临时表,特别是如果你有很多行,将优于表变量。 SQL Server 不会维护表变量的统计信息,它会维护临时表。如果您插入到表变量中的行数在某个时候增加,您可能会再次遇到性能下降的情况。但只要你把它排除在等式之外,你就已经过得更好了......

以上是关于Sql Azure:WITH 语句查询的性能非常慢的主要内容,如果未能解决你的问题,请参考以下文章

DB2 中 WITH 查询的 SQL 查询性能改进

性能测试四十一:慢sql和执行计划一

如何提高 SQL Azure 查询性能

Synapse Analytics sql 按需同步与火花池的查询速度非常慢

逆水行舟 —— SQL优化之慢查询和explain以及性能分析

MySQL性能优化---定位慢查询