我正在研究SQL Server 2019。
我有一个表dbo.AllDates其中包含从1990到2050的所有日期。我有另一个表dbo.ActualExchangeRates,其中我有在给定来源中找到汇率的日期某些货币的实际汇率。
我正在尝试编写一个查询来获取2010 年至2020 年之间所有日期的所有货币。如果找到比率,则写入比率,否则写入NULL。
鉴于这种情况和下面给出的代码,有人可以帮助我理解为什么SELECT查询没有生成任何结果,甚至无法看到估计的执行计划吗?
CREATE TABLE dbo.AllDates(Date date)
CREATE TABLE dbo.ActualExchangeRates(Date date, Currency char(3), Rate real)
--Query 1: Not generating any results or estimated plan
SELECT d.Date, m.Currency, c.Rate
FROM dbo.AllDates d
INNER JOIN (
select
currency,
'20100101' as mindate,
'20201231' as maxdate
from dbo.ActualExchangeRates
group by currency
) as m on d.date between m.mindate and m.maxdate
LEFT …Run Code Online (Sandbox Code Playgroud) 我在 SQL 服务器上有这个查询,一个合并复制查询:
SELECT DISTINCT
b.tablenick,
b.rowguid,
c.generation,
sys.fn_MSgeneration_downloadonly
(
c.generation,
c.tablenick
)
FROM #belong b
LEFT OUTER JOIN dbo.MSmerge_contents c ON
c.tablenick = b.tablenick
AND c.rowguid = b.rowguid;
Run Code Online (Sandbox Code Playgroud)
估计的查询计划包括 3 个查询的信息:
实际的查询计划仅包括以下信息:
没有关于功能。为什么实际方案中缺少功能信息?
我尝试了这些选项:
SET STATISTICS PROFILE ON
SET STATISTICS XML ON
Run Code Online (Sandbox Code Playgroud)
它创建了一个实际计划,但它缺少第 2 部分和第 3 部分,这与我在 Management Studio 中使用实际查询计划选项时相同。
例如,如果我要使用 Profiler 来捕获有关函数调用的信息,我会选择哪些事件?
没有找到与查询计划特别相关的答案,但我分析了 SP:StmtStarting 和 SP:StmtCompleted 并显示了函数调用。

今天我在 The Heap 上,正在查看我认为可以改进的查询计划。然而,它创造了一些东西,动摇了我对 SQL Server 查询优化器的信念。如果sql-server甚至不能计数到 100%,我还可以信任它吗?
表的特点:
date_entered列有没有人以前见过这个,是什么导致计划看起来如此扭曲?

更新:这绝对是一个错误。有关完整详细信息,请参阅此连接项。
在测试sp_BlitzCache 的一些更改(完全公开,我是作者之一)时,我发现了我认为是我们代码中的错误。
在某一时刻,我们匹配查询计划哈希以获得查询成本。我们这样做:
statement.value('sum(/p:StmtSimple[xs:hexBinary(substring(@QueryHash, 3)) =
xs:hexBinary(sql:column("b.QueryHash"))]/@StatementSubTreeCost)', 'float')
Run Code Online (Sandbox Code Playgroud)
就我所见,这已经奏效了。然而,在一个奇怪的情况下,XML 中的子字符串抛出一个NULL值,并且计划显示成本为 0,尽管它相当高。
深入研究执行计划(完全公开,我为托管 Paste The Plan 的公司工作),我注意到一个问题哈希的查询计划哈希长度为 17 个字符,而其余为 18 个字符。以下是示例:
QueryPlanHash="0x4410B0CA640CDA89" QueryPlanHash="0x2262FEA4CE645569" QueryPlanHash="0xED4F225CC0E97E5" -- 问题! QueryPlanHash="0xBF878EEE6DB955EA" QueryPlanHash="0x263B53BC8C14A452" QueryPlanHash="0x89F5F146CF4B476F" QueryPlanHash="0xEF47EA40805C8961" QueryPlanHash="0xB7BE27D6E43677A5" QueryPlanHash="0x815C54EC43A6A6E9"
查询计划哈希上市为BINARY 8-想必这应该是相同的长度,但到底是什么像我这样的人了解二进制值?
稍微使用 XQuery,我发现通过将子字符串更改为从第二个位置开始,它会得出一个有效(尽管不正确)的哈希值。
WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
SELECT
QueryPlanCost = statement.value('sum(/p:StmtSimple/@StatementSubTreeCost)', 'float'),
**q.n.value('substring(@QueryPlanHash, 2)', 'BINARY(8)')**
FROM #statements s
CROSS APPLY s.statement.nodes('/p:StmtSimple') AS q(n)
OPTION(RECOMPILE);
WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
SELECT
QueryPlanCost = statement.value('sum(/p:StmtSimple/@StatementSubTreeCost)', 'float'),
**q.n.value('substring(@QueryPlanHash, …Run Code Online (Sandbox Code Playgroud) 我正在尝试生成一个示例查询计划来说明为什么联合两个结果集比在 JOIN 子句中使用 OR 更好。我写的一个查询计划让我很难过。我正在使用 StackOverflow 数据库和 Users.Reputation 上的非聚集索引。
CREATE NONCLUSTERED INDEX IX_NC_REPUTATION ON dbo.USERS(Reputation)
SELECT DISTINCT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts
ON Users.Id = Posts.OwnerUserId
OR Users.Id = Posts.LastEditorUserId
WHERE Users.Reputation = 5
Run Code Online (Sandbox Code Playgroud)
查询计划位于https://www.brentozar.com/pastetheplan/?id=BkpZU1MZE,我的查询持续时间为 4:37 分钟,返回 26612 行。
我以前从未见过从现有表创建这种类型的常量扫描 - 我不熟悉为什么对每一行都运行常量扫描,而常量扫描通常用于用户输入的单行例如 SELECT GETDATE()。为什么用在这里?我非常感谢阅读此查询计划的一些指导。
如果我将 OR 拆分为 UNION,它会生成一个标准计划,运行 12 秒,返回相同的 26612 行。
SELECT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts
ON Users.Id = Posts.OwnerUserId
WHERE Users.Reputation = 5
UNION
SELECT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts
ON …Run Code Online (Sandbox Code Playgroud) 我正在尝试对实际执行计划使用显示计划分析 (SSMS) 来解决执行缓慢的查询。分析工具指出,在计划中的几个地方,对行数的估计与返回的结果不一致,并进一步给了我一些隐式转换警告。
我不明白这些 int 到 Varchar 的隐式转换 - 引用的字段不是查询中任何参数/过滤器的一部分,并且在所有涉及的表中,列数据类型是相同的:
我收到以下 CardinalityEstimate 警告:
表达式中的类型转换 (CONVERT_IMPLICIT(varchar(12),[ccd].[profileid],0)) 可能会影响查询计划选择中的“CardinalityEstimate”——这个字段在我的数据库中到处都是整数
表达式中的类型转换 (CONVERT_IMPLICIT(varchar(6),[ccd].[nodeid],0)) 可能会影响查询计划选择中的“CardinalityEstimate”——这个字段在我的数据库中到处都是 smallint
表达式中的类型转换 (CONVERT_IMPLICIT(varchar(6),[ccd].[sessionseqnum],0)) 可能会影响查询计划选择中的“CardinalityEstimate”——这个字段在我的数据库中到处都是 smallint
表达式中的类型转换 (CONVERT_IMPLICIT(varchar(41),[ccd].[sessionid],0)) 可能会影响查询计划选择中的“CardinalityEstimate”——这个字段在我的数据库中到处都是小数
[编辑]这里是查询和实际执行计划供参考 https://www.brentozar.com/pastetheplan/?id=SysYt0NzN
和表定义..
/****** Object: Table [dbo].[agentconnectiondetail] Script Date: 1/10/2019 9:10:04 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[agentconnectiondetail](
[sessionid] [decimal](18, 0) NOT NULL,
[sessionseqnum] [smallint] NOT NULL,
[nodeid] [smallint] NOT NULL,
[profileid] [int] NOT NULL,
[resourceid] [int] NOT NULL,
[startdatetime] [datetime2](7) NOT NULL,
[enddatetime] [datetime2](7) NOT NULL, …Run Code Online (Sandbox Code Playgroud) sql-server execution-plan type-conversion cardinality-estimates sql-server-2017
我有特定的清理代码试图删除一些重复项。
这在许多客户站点上完美运行。日志告诉我此查询至少消耗了 1 秒到 45 秒:
DELETE FROM [tbl]
WHERE [Id] NOT IN
(
SELECT MIN([Id])
FROM [tbl]
GROUP BY [IdProject], [IdRepresentative], [TimeStart]
)
Run Code Online (Sandbox Code Playgroud)
但是我有一个客户,该查询运行了 4 个多小时(到现在为止还没有结束)!我检查了数据库 ( DBCC CHECKDB),我已经更新了统计信息 ( sp_updatestats),也UPDATE STATISTICS [tbl] WITH FULLSCAN显示没有变化。
我有来自客户的 DB 的原始备份。我在 SQL Server 14.0.2002.14 上运行它。我有标准版,客户用的是速成版。
我可以在活动监视器中看到没有其他人在使用数据库。没有等待,CPU 使用率为 25%(正好是我的 4 个 CPU 中的 1 个)。同样在我的测试用例中,没有其他人在使用数据库。
我重新查询并检查了以下语句:
DELETE FROM [tbl]
FROM [tbl] AS t
LEFT OUTER JOIN
(
SELECT MIN([Id]) AS [IdMin]
FROM [tbl]
GROUP BY [IdProject], [IdRepresentative], [TimeStart]
) …Run Code Online (Sandbox Code Playgroud) performance sql-server execution-plan cardinality-estimates sql-server-2017 query-performance
所以我确定我的 SQL Server 的不稳定行为是因为 .Net SqlClient 数据提供程序的默认设置SET ARITHABORT OFF. 话虽如此,我已经阅读了各种文章,这些文章讨论了实现这一点的最佳方式。对我来说,我只是想要一个简单的方法,因为 SQL Server 正在遭受痛苦,而且我的查询调优还没有完全超越整个应用程序(显然SET在 sp 中添加是行不通的)。
在 Erland Sommarskog关于该主题的精彩文章中,他基本上建议采取安全的方法,通过更改应用程序来发出SET ARITHABORT ON连接。但是,在 dba.stackexchange question 的这个答案中,Solomon Rutzky提供了实例范围和数据库范围的方法。
设置此实例范围内我在这里遗漏了什么后果?正如我所看到的......因为SSMSON默认设置了这个,我认为ON为所有连接设置这个服务器范围没有坏处。归根结底,我只需要这个 SQL Server 来执行高于一切。
DROP TABLE IF EXISTS #EmptyTable, #BigTable
CREATE TABLE #EmptyTable(A int);
CREATE TABLE #BigTable(A int);
INSERT INTO #BigTable
SELECT TOP 10000000 CRYPT_GEN_RANDOM(3)
FROM sys.all_objects o1,
sys.all_objects o2,
sys.all_objects o3;
Run Code Online (Sandbox Code Playgroud)
WITH agg
AS (SELECT DISTINCT a
FROM #BigTable)
SELECT *
FROM #EmptyTable E
INNER HASH JOIN agg B
ON B.A = E.A;
Run Code Online (Sandbox Code Playgroud)
这是我今天之前没有注意到的现象的简化再现。我对内部散列连接的期望是,如果构建输入为空,则不应执行探测端,因为连接不会返回任何行。上面的示例与此相反,并从表中读取了 1000 万行。这使查询的执行时间增加了 2.196 秒 (99.9%)。
OPTION (MAXDOP 1)执行计划从#BigTable. 该ActualExecutions是0对哈希连接内所有的运营商。SELECT * FROM #EmptyTable E INNER …在本演示中,我使用的是2013 版 Stack Overflow 数据库和 SQL Server 2022 CTP2,但回溯到 SQL Server 2017 也是有效的,这是我想检查的最早版本。
对于此函数,SQL Server 跟踪该函数所花费的执行时间:
CREATE OR ALTER FUNCTION
dbo.ScoreStats
(
@UserId int
)
RETURNS
@out table
(
TotalScore bigint
)
WITH SCHEMABINDING
AS
BEGIN
INSERT
@out
(
TotalScore
)
SELECT
TotalScore =
SUM(x.Score)
FROM
(
SELECT
Score =
SUM(p.Score)
FROM dbo.Posts AS p
WHERE p.OwnerUserId = @UserId
UNION ALL
SELECT
Score =
SUM(c.Score)
FROM dbo.Comments AS c
WHERE c.UserId = @UserId
) AS x; …Run Code Online (Sandbox Code Playgroud) execution-plan ×10
sql-server ×10
functions ×2
performance ×2
ado.net ×1
datatypes ×1
optimization ×1
parallelism ×1
ssms ×1
xml ×1