这个问题是对previous question的跟进。为了向您提供上下文,我想总结一下上一个问题:在我之前的问题中,我打算使用一种方法来执行选择而不将结果发送给客户端。目标是通过发送数百万个数据来衡量性能,而不会占用大量资源。我只对执行这些查询所需的时间感兴趣,而不是在他们将结果发送到客户端应用程序时,因为我打算优化查询,因此查询的结果根本不会改变,但方法将会改变,我打算能够比较方法。
在我的另一个问题中,提出了几个想法。一个想法是选择记录的计数并将其放入变量中。但是,这显着改变了查询计划,并且结果在性能方面不准确。还提出了使用临时表的想法,但是如果我们不知道什么查询将是我们要测量的输入并且还引入了大量白噪声,则创建临时表并插入其中是很困难的,因此,即使想法很有创意,对我的问题不太理想。最后,弗拉基米尔·巴拉诺夫(Vladimir Baranov)提出了一个想法,即创建尽可能多的变量列。这是个好主意,但我通过创建nvarchar(max)的单个变量并选择所有列来进一步优化它。除了一些问题,这个想法很有效。我有大多数问题的解决方案,但我想分享它们,所以,我会描述它们,但不要误解我,我只有一个问题。
如果我有一个@container
变量并且我在每个选项中都有@container = columnname
,那么我会遇到转换问题。
我需要做一个@container = columnname
@container = cast(columnname as nvarchar(max))
对于选择中的每一列,我需要将<whatever> as something
转换为@container = cast(<whatever> as nvarchar(max))
,但不是针对子选择,我需要有一个处理case when
和parantheses的通用解决方案,我这样做不希望任何地方@container =
的任何实例,除了主选择的左边。
由于我对正则表达式一无所知,我可以通过迭代查询字符串来解决这个问题,直到我找到主查询的from
,每次我找到一个parantheses,我都会做什么,直到这些parantheses关闭,找到应该放置@container =
并且应该取出as [customname]
的索引,并从右到左完成查询字符串中的所有操作。这将是一个漫长而不雅的代码。
是否可以确保我的所有主要内容,但没有其他内容以@container =
开头,而没有as [Customname]
结束?
答案 0 :(得分:2)
这对于评论来说太长了,但我想将我的$ .02添加到其他答案并分享我用来测试建议方法的脚本。
我喜欢@ MartinSmith的TOP 0解决方案,但我担心在某些情况下它会导致不同的执行计划形状。我在运行的测试中没有看到这个,但我认为您需要验证计划与您测试的每个查询的无故障查询大致相同。但是,我的测试结果表明,使用此方法,列和/或数据类型的数量可能会影响性能。
@ VladimirBaranov答案中的SQLCLR方法应该提供应用程序代码生成的确切计划(假设测试的SET选项相同),SQClLR中SqlClient消耗结果仍会有一些轻微的开销(YMMV)。与将结果返回给调用应用程序相比,此方法将减少服务器开销。
我在第一条评论中建议的SSMS丢弃结果方法会比其他方法产生更多的开销,但确实包括SQL Server不仅在运行查询时执行的服务器端工作,而且还为返回的结果填充缓冲区。是否应考虑此附加SQL Server是否有效取决于测试的目的。对于单元级性能测试,我更喜欢使用与应用程序代码相同的API执行测试。
我使用@ MartinSmith的原始查询使用这3种方法捕获了服务器端性能。我机器上平均1000次迭代:
test method cpu_time duration logical_reads
SSMS discard 53031.000000 55358.844000 7190.512000
TOP 0 52374.000000 52432.936000 7190.527000
SQLCLR 49110.000000 48838.532000 7190.578000
我使用一个简单的查询从用户表中返回10,000行和2列(int
和nvarchar(100)
):
test method cpu_time duration logical_reads
SSMS discard 4204.000000 9245.426000 402.004000
TOP 0 2641.000000 2752.695000 402.008000
SQLCLR 1921.000000 1878.579000 402.000000
重复相同的测试但使用varchar(100)
列而不是nvarchar(100)
:
test method cpu_time duration logical_reads
SSMS discard 3078.000000 5901.023000 402.004000
TOP 0 2672.000000 2616.359000 402.008000
SQLCLR 1750.000000 1798.098000 402.000000
以下是我用于测试的脚本:
像@VladimirBaranov这样的SQLCLR proc的源代码建议:
public static void ExecuteNonQuery(string sql)
{
using (var connection = new SqlConnection("Context Connection=true"))
{
connection.Open();
var command = new SqlCommand(sql, connection);
command.ExecuteNonQuery();
}
}
Xe跟踪以捕获实际的服务器端计时和资源使用情况:
CREATE EVENT SESSION [test] ON SERVER
ADD EVENT sqlserver.sql_batch_completed(SET collect_batch_text=(1))
ADD TARGET package0.event_file(SET filename=N'QueryTimes')
WITH (MAX_MEMORY=4096 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=30 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE,TRACK_CAUSALITY=OFF,STARTUP_STATE=OFF);
GO
用户表创建并加载:
CREATE TABLE dbo.Foo(
FooID int NOT NULL CONSTRAINT PK_Foo PRIMARY KEY
, Bar1 nvarchar(100)
, Bar2 varchar(100)
);
WITH
t10 AS (SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) t(n))
,t10k AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS num FROM t10 AS a CROSS JOIN t10 AS b CROSS JOIN t10 AS c CROSS JOIN t10 AS d)
INSERT INTO dbo.Foo WITH (TABLOCKX)
SELECT num, REPLICATE(N'X', 100), REPLICATE('X', 100)
FROM t10k;
GO
SQL脚本从SSMS运行,带有丢弃结果查询选项,使用3种不同的方法运行1000次迭代测试:
SET NOCOUNT ON;
GO
--return and discard results
SELECT v.*,
o.name
FROM master..spt_values AS v
JOIN sys.objects o
ON o.object_id % NULLIF(v.number, 0) = 0;
GO 1000
--TOP 0
DECLARE @X NVARCHAR(MAX);
SELECT @X = (SELECT TOP 0 v.*,
o.name
FOR XML PATH(''))
FROM master..spt_values AS v
JOIN sys.objects o
ON o.object_id % NULLIF(v.number, 0) = 0;
GO 1000
--SQLCLR ExecuteNonQuery
EXEC dbo.ExecuteNonQuery @sql = N'
SELECT v.*,
o.name
FROM master..spt_values AS v
JOIN sys.objects o
ON o.object_id % NULLIF(v.number, 0) = 0;
'
GO 1000
--return and discard results
SELECT FooID, Bar1
FROM dbo.Foo;
GO 1000
--TOP 0
DECLARE @X NVARCHAR(MAX);
SELECT @X = (SELECT TOP 0 FooID, Bar1
FOR XML PATH(''))
FROM dbo.Foo;
GO 1000
--SQLCLR ExecuteNonQuery
EXEC dbo.ExecuteNonQuery @sql = N'
SELECT FooID, Bar1
FROM dbo.Foo
';
GO 1000
--return and discard results
SELECT FooID, Bar1
FROM dbo.Foo;
GO 1000
--TOP 0
DECLARE @X NVARCHAR(MAX);
SELECT @X = (SELECT TOP 0 FooID, Bar2
FOR XML PATH(''))
FROM dbo.Foo;
GO 1000
--SQLCLR ExecuteNonQuery
EXEC dbo.ExecuteNonQuery @sql = N'
SELECT FooID, Bar2
FROM dbo.Foo
';
GO 1000
答案 1 :(得分:1)
我会尝试编写一个单独的CLR函数,该函数根据需要运行尽可能多的查询来进行测量。它可能有一个参数,包含要运行的查询文本或要运行的存储过程的名称。
您只有一个服务器请求。一切都在服务器上本地完成。没有网络开销。在不使用显式临时表的情况下,通过对需要测量的每个查询使用ExecuteNonQuery
,丢弃.NET CLR代码中的查询结果。
请勿更改您正在测量的查询。优化器很复杂,对查询的更改可能会对性能产生各种影响。
,使用SET STATISTICS TIME ON
和让服务器衡量您的时间。获取服务器要说的内容,解析它并以适合您的格式发回。
我认为SET STATISTICS TIME ON / OFF
的结果是最可靠,最准确,噪音最少的。