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 语句查询的性能非常慢的主要内容,如果未能解决你的问题,请参考以下文章
Synapse Analytics sql 按需同步与火花池的查询速度非常慢