我有一个表,其中有一列我不希望在其上创建或更新统计信息。如果我强制查询优化器使用主键上的统计密度而不是该列上的统计直方图,我会得到更好的连接基数估计。自动更新和自动创建统计信息在数据库级别启用,我无法更改。
如果您想建议防止创建统计信息的替代方法,请记住该表用于被数千个不同查询引用的视图中。我无法控制运行的查询。
我最初的策略是使用NOCOMPUTE和SAMPLE 0 ROWS选项在列上创建统计信息。我的印象是 SQL Server 不会在已经有统计对象的列上自动创建统计信息,但这已经发生在我们的开发和 QA 服务器上。
创建了新的统计信息COL_GROUP。我的NORECOMPUTE统计数据没有更新。我不知道为什么要创建统计信息,而且我自己也无法通过运行查询来触发它。
有没有办法阻止 SQL Server 自动为一列创建统计信息?我的表只有两列,因此防止在单个表上创建自动统计的解决方案也可以解决我的问题。
跟踪标志 4139 和 2371 处于打开状态,以防有所不同。
如果你想玩转表结构,我已经包含了它和下面的示例数据:
CREATE TABLE X_NO_COLUMN_STATS(
[COL_USER] [varchar](256) NOT NULL,
[COL_GROUP] [int] NOT NULL,
CONSTRAINT [PK_X_NO_COLUMN_STATS] PRIMARY KEY CLUSTERED
(
[COL_USER] ASC,
[COL_GROUP] ASC
)WITH (DATA_COMPRESSION = PAGE)
);
-- prevent stats from being updated on COL_GROUP
CREATE STATISTICS [X_NO_COLUMN_STATS__COL_GROUP] ON X_NO_COLUMN_STATS ([COL_GROUP]) WITH NORECOMPUTE, SAMPLE 0 ROWS;
BEGIN TRANSACTION;
INSERT INTO X_NO_COLUMN_STATS VALUES …Run Code Online (Sandbox Code Playgroud) 对于以下查询,我遇到了我认为不可能高的基数估计:
SELECT dm.PRIMARY_ID
FROM
(
SELECT COALESCE(d1.JOIN_ID, d2.JOIN_ID, d3.JOIN_ID) PRIMARY_ID
FROM X_DRIVING_TABLE dt
LEFT OUTER JOIN X_DETAIL_1 d1 ON dt.ID = d1.ID
LEFT OUTER JOIN X_DETAIL_LINK lnk ON d1.LINK_ID = lnk.LINK_ID
LEFT OUTER JOIN X_DETAIL_2 d2 ON dt.ID = d2.ID
LEFT OUTER JOIN X_DETAIL_3 d3 ON dt.ID = d3.ID
) dm
INNER JOIN X_LAST_TABLE lst ON dm.PRIMARY_ID = lst.JOIN_ID;
Run Code Online (Sandbox Code Playgroud)
估计的计划在这里。我正在处理表格的统计副本,因此无法包含实际计划。但是,我不认为它与这个问题非常相关。
SQL Server 估计将从“dm”派生表返回 481577 行。然后估计在连接到 X_LAST_TABLE 后将返回 4528030000 行,但 JOIN_ID 是 X_LAST_TIME 的主键。我希望连接基数估计在 0 到 481577 行之间。相反,当交叉连接外部表和内部表时,行估计值似乎是我得到的行数的 10%。计算结果为四舍五入:481577*94025*0.1 …
当我注意到我的一些插入花费的时间比预期的要长时,我正在做一个涉及 CCI 的演示。要重现的表定义:
DROP TABLE IF EXISTS dbo.STG_1048576;
CREATE TABLE dbo.STG_1048576 (ID BIGINT NOT NULL);
INSERT INTO dbo.STG_1048576
SELECT TOP (1048576) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
DROP TABLE IF EXISTS dbo.CCI_BIGINT;
CREATE TABLE dbo.CCI_BIGINT (ID BIGINT NOT NULL, INDEX CCI CLUSTERED COLUMNSTORE);
Run Code Online (Sandbox Code Playgroud)
对于测试,我将从临时表中插入所有 1048576 行。只要它没有因某种原因被修剪,这就足以填充一个压缩的行组。
如果我插入所有整数 mod 17000,它需要不到一秒钟的时间:
TRUNCATE TABLE dbo.CCI_BIGINT;
INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT ID % 17000
FROM dbo.STG_1048576
OPTION (MAXDOP 1);
Run Code Online (Sandbox Code Playgroud)
SQL Server 执行时间:CPU 时间 = 359 …
在工作原理:SQL Server 2012 数据库引擎任务计划中,Bob Dorr 解释了 SQL Server 2012 中工作调度程序分配的一些更改。他提到一些改进仅在企业版中可用。这些差异在 SQL Server 2019 中是否仍然存在?
如果这很重要,我会问这个问题,因为我发现 SQL Server 2017 标准版实例上可能存在调度程序效率低下的问题,该实例计划未来升级到企业版。如果升级到企业版可以解决问题,我不想尝试对标准版进行调查。
当我使用 sp_BlitzFirst 跟踪等待时,我得到以下详细信息:
<?ClickToSeeDetails --
For 20 seconds over the last 5 seconds, SQL Server was waiting on this
particular bottleneck.
-- ?>
Run Code Online (Sandbox Code Playgroud)
应该是“过去 5 秒内 20 次”吗?发现是 CLR_SEMAPHORE。
这有点偏离了真正的问题。如果提供上下文有帮助,则生成此数据对于处理字符串的性能测试方法、生成需要在游标内对其应用某些操作的字符串或为敏感数据生成唯一的匿名名称替换可能很有用。我只是对在 SQL Server 中生成数据的有效方法感兴趣,请不要问我为什么需要生成这些数据。
我将尝试从一个有点正式的定义开始。如果字符串仅由 A - Z 中的大写字母组成,则该字符串包含在该系列中。该系列的第一项是“A”。该系列由所有有效字符串组成,首先按长度排序,然后按典型的字母顺序排序。如果字符串位于名为 的列中的表中STRING_COL,则可以在 T-SQL 中将顺序定义为ORDER BY LEN(STRING_COL) ASC, STRING_COL ASC。
要给出一个不太正式的定义,请查看 Excel 中按字母顺序排列的列标题。该系列是相同的模式。考虑如何将整数转换为基数为 26 的数字:
1 -> A, 2 -> B, 3 -> C, ... , 25 -> Y, 26 -> Z, 27 -> AA, 28 -> AB, ...
这个类比并不完美,因为“A”的行为与基数 0 中的 0 不同。下面是一个选定值的表格,希望能让它更清楚:
???????????????????????
? ROW_NUMBER ? STRING ?
???????????????????????
? 1 ? A ?
? 2 ? B ?
? 25 ? Y ?
? 26 ? …Run Code Online (Sandbox Code Playgroud) 考虑以下查询,该查询仅在源表中不存在时才插入源表中的行:
INSERT INTO dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR WITH (TABLOCK)
SELECT maybe_new_rows.ID
FROM dbo.A_HEAP_OF_MOSTLY_NEW_ROWS maybe_new_rows
WHERE NOT EXISTS (
SELECT 1
FROM dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR halloween
WHERE maybe_new_rows.ID = halloween.ID
)
OPTION (MAXDOP 1, QUERYTRACEON 7470);
Run Code Online (Sandbox Code Playgroud)
一种可能的计划形状包括合并连接和急切线轴。Eager spool 运算符用于解决万圣节问题:
在我的机器上,上面的代码在大约 6900 毫秒内执行。用于创建表格的重现代码包含在问题的底部。如果我对性能不满意,我可能会尝试加载要插入到临时表中的行,而不是依赖于 Eager spool。这是一种可能的实现:
DROP TABLE IF EXISTS #CONSULTANT_RECOMMENDED_TEMP_TABLE;
CREATE TABLE #CONSULTANT_RECOMMENDED_TEMP_TABLE (
ID BIGINT,
PRIMARY KEY (ID)
);
INSERT INTO #CONSULTANT_RECOMMENDED_TEMP_TABLE WITH (TABLOCK)
SELECT maybe_new_rows.ID
FROM dbo.A_HEAP_OF_MOSTLY_NEW_ROWS maybe_new_rows
WHERE NOT EXISTS (
SELECT 1
FROM dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR halloween
WHERE maybe_new_rows.ID = halloween.ID
)
OPTION …Run Code Online (Sandbox Code Playgroud) 我的部分工作量使用了一个CLR 函数,该函数实现了诡异的哈希算法来比较行以查看是否有任何列值发生了变化。CLR 函数将二进制字符串作为输入,因此我需要一种快速的方法将行转换为二进制字符串。我希望在整个工作负载期间散列大约 100 亿行,所以我希望这段代码尽可能快。
我有大约 300 个不同架构的表。出于这个问题的目的,请假设一个简单的表结构,包含 32 个可空INT列。我在这个问题的底部提供了示例数据以及一种对结果进行基准测试的方法。
如果所有列值都相同,则行必须转换为相同的二进制字符串。如果任何列值不同,则必须将行转换为不同的二进制字符串。例如,像下面这样简单的代码将不起作用:
CAST(COL1 AS BINARY(4)) + CAST(COL2 AS BINARY(4)) + ..
Run Code Online (Sandbox Code Playgroud)
它不能正确处理 NULL。如果COL1第 1 行为COL2NULL,第 2 行为 NULL,则两行都将转换为 NULL 字符串。我相信正确处理 NULL 是正确转换整行的最难部分。INT 列的所有允许值都是可能的。
先抢答一些问题:
将 32 个可INT为空的列转换为 aBINARY(X)或VARBINARY(X)string的最快方法是什么?
承诺的示例数据和代码:
-- create sample data
DROP TABLE IF EXISTS dbo.TABLE_OF_32_INTS;
CREATE TABLE dbo.TABLE_OF_32_INTS (
COL1 INT NULL,
COL2 INT NULL, …Run Code Online (Sandbox Code Playgroud) 我的 SQL Server 版本是 SQL Server 2019 (RTM-CU18)。以下重现代码要求创建内存中文件组。对于后续操作的任何人,请记住,内存中的文件组一旦创建就无法从数据库中删除。
我有一个简单的内存表,在其中插入 1 - 1200 之间的整数:
DROP TABLE IF EXISTS [dbo].[InMem];
CREATE TABLE [dbo].[InMem] (
i [int] NOT NULL,
CONSTRAINT [PK_InMem] PRIMARY KEY NONCLUSTERED (i ASC)
) WITH ( MEMORY_OPTIMIZED = ON , DURABILITY = SCHEMA_ONLY );
INSERT INTO [dbo].[InMem]
SELECT TOP (1200) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
Run Code Online (Sandbox Code Playgroud)
我还有以下本机编译的存储过程:
GO
CREATE OR ALTER PROCEDURE p1
WITH NATIVE_COMPILATION, SCHEMABINDING
AS
BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL …Run Code Online (Sandbox Code Playgroud) sql-server stored-procedures memory-optimized-tables table-valued-parameters
我有一个性能问题,非常大的内存授予处理这个包含几NVARCHAR(4000)列的表。事情是这些列永远不会大于NVARCHAR(260).
使用
ALTER TABLE [table] ALTER COLUMN [col] NVARCHAR(260) NULL
Run Code Online (Sandbox Code Playgroud)
导致 SQL Server 重写整个表(并在日志空间中使用 2 倍的表大小),这是数十亿行,只是什么都不改变,不是一个选项。增加列宽没有这个问题,但减少它。
我曾尝试创建约束CHECK (DATALENGTH([col]) <= 520)或CHECK (LEN([col]) <= 260)SQL Server 仍然决定重写整个表。
有没有办法将列数据类型更改为仅限元数据的操作?无需重写整个表?我使用的是 SQL Server 2017(14.0.2027.2 和 14.0.3192.2)。
这是用于重现的示例 DDL 表:
CREATE TABLE [table](
id INT IDENTITY(1,1) NOT NULL,
[col] NVARCHAR(4000) NULL,
CONSTRAINT [PK_test] PRIMARY KEY CLUSTERED (id ASC)
);
Run Code Online (Sandbox Code Playgroud)
然后运行ALTER.