对带有空白 NVARCHAR 或 NULL 检查的 VARCHAR 索引进行 Count(*) 会导致返回的行数加倍

2024-05-14

我有一张桌子,上面有VARCHAR列及其上的索引。每当一个SELECT COUNT(*)是在这张表上完成的,该表检查了COLUMN = N'' OR COLUMN IS NULL它返回双倍的行数。SELECT *与相同的where子句将返回正确的记录数。

读完这篇文章后:https://sqlquantumleap.com/2017/07/10/impact-on-indexes-when-mixing-varchar-and-nvarchar-types/ https://sqlquantumleap.com/2017/07/10/impact-on-indexes-when-mixing-varchar-and-nvarchar-types/并进行一些测试,我相信列的排序规则和隐式转换不是错误(至少不是直接错误)。该列的排序规则是Latin1_General_CI_AS.

数据库在SQL Server 2012上,我也在2016上测试过。

我创建了一个测试脚本(如下)来演示这个问题。这样做,我相信它可能与数据分页有关,因为它需要表中的一些数据才能发生。

CREATE TABLE [dbo].TEMP 
(
    ID [varchar](50) COLLATE Latin1_General_CI_AS NOT NULL,
    [DATA] [varchar](200) COLLATE Latin1_General_CI_AS NULL,
    [TESTCOLUMN] [varchar](50) COLLATE Latin1_General_CI_AS NULL,
    CONSTRAINT [PK_TEMP] PRIMARY KEY CLUSTERED ([ID] ASC)
)
GO

CREATE NONCLUSTERED INDEX [I_TEMP_TESTCOLUMN] ON dbo.TEMP (TESTCOLUMN ASC)
GO

DECLARE @ROWS AS INT = 40; 

WITH NUMBERS (NUM) AS 
(
    SELECT 1 AS NUM
    UNION ALL
    SELECT NUM + 1 FROM NUMBERS WHERE NUM < @ROWS
)
INSERT INTO TEMP (ID, DATA)
SELECT NUM, '1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901324561234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890' 
FROM NUMBERS

SELECT @ROWS AS EXPECTED, COUNT(*) AS ACTUALROWS
FROM TEMP
GO

SELECT COUNT(*) AS INVALIDINDEXSEARCHCOUNT
FROM TEMP
WHERE (TESTCOLUMN = N'' OR TESTCOLUMN IS NULL)
GO

DROP TABLE TEMP

我能够在某种程度上修改数据库(我无法更改数据,或更改允许的列NULL),不幸的是我无法修改进行搜索的代码,任何人都可以找到一种方法来获得正确的结果COUNT(*)结果返回?


TLDR:这是产品中的一个错误(已报告here https://feedback.azure.com/d365community/idea/d12ba420-6d74-ed11-a81b-000d3ae49307).

暴露此错误的不良做法是数据类型不匹配(varchar列被比较nvarchar) - 在 SQL 排序规则上,这只会导致列隐式转换为nvarchar和全面扫描。

在 Windows 排序规则上,这仍然会导致查找。这通常是一个有用的性能优化,但在这里你遇到了一个边缘情况......


更多详情:使用以下设置...

CREATE TABLE dbo.TEMP 
(
    ID INT IDENTITY PRIMARY KEY,
    [TESTCOLUMN] [varchar](50) COLLATE Latin1_General_CI_AS NULL INDEX [I_TEMP_TESTCOLUMN],
    Filler AS CAST('X' AS CHAR(8000)) PERSISTED
)

--Add 7 rows where TESTCOLUMN is NOT NULL
INSERT dbo.TEMP([TESTCOLUMN]) VALUES ('aardvark'), ('badger'), 
                                     ('badges'), ('cat'), 
                                     ('dog'), ('elephant'), 
                                     ('zebra');

--Add 49 rows where TESTCOLUMN is NULL 
INSERT dbo.TEMP([TESTCOLUMN]) 
SELECT NULL 
FROM dbo.TEMP T1 CROSS JOIN dbo.TEMP T2

那么首先看一下实际的执行计划

SELECT COUNT(*) 
FROM dbo.TEMP
WHERE TESTCOLUMN = N'badger'
OPTION (RECOMPILE)

在 SQL 排序规则中,隐式转换为nvarchar将使谓词完全不可控制。通过 Windows 排序规则,SQL Server 能够将设备添加到计算标量调用内部函数的计划中GetRangeThroughConvert(N'badger',N'badger',(62))结果值最终被输入到嵌套循环连接中,以给出索引查找的起点和终点。 (文章《动态查找和隐藏的隐式转换 https://www.sql.kiwi/2012/01/dynamic-seeks-and-hidden-implicit-conversions.html“有一些关于这个计划形状的更多细节)

执行计划中没有公开此内部函数返回的范围起始值和结束值,但如果您碰巧有一个可用的 SQL Server 版本(其中短暂存在),则可以看到它们query_trace_column_values https://www.sqlshack.com/query-trace-column-values/扩展事件尚未被禁用。在上面的例子中,函数返回(badger, badgeS, 62)这些值用于索引查找。在本例中,当我添加了一行值为“badges”的行时,查找最终会比严格需要的行多读取一行,并且剩余谓词仅保留“badger”的行。

Now try

SELECT COUNT(*) 
FROM dbo.TEMP
WHERE TESTCOLUMN = N''
OPTION (RECOMPILE)

The GetRangeThroughConvert当要求提供空字符串和输出的范围时,函数似乎放弃(null, null, 0).

The null这里表示范围的末尾是无界的,因此有效地索引查找最终会读取从第一行到最后一行的整个索引。

上面显示索引查找读取了所有 56 行,但剩余谓词完成了删除所有不匹配行的工作TESTCOLUMN = N''(因此运算符返回零行)。

一般来说,这里使用的搜索谓词看起来就像一个前缀搜索(例如,seek[TESTCOLUMN] = N'A'将至少读取以以下内容开头的所有行A剩余谓词进行相等性检查),所以我对这里的空字符串的期望首先不会很高,但是保罗·怀特表示 https://www.sql.kiwi/2012/01/dynamic-seeks-and-hidden-implicit-conversions.html?showComment=1670328317977#c5212039192927765430无论如何,这里正在寻找的范围可能是一个错误。

当您添加OR执行计划更改的查询的谓词。

现在,它最终将两个外部行连接到嵌套循环连接,因此最终执行两次搜索(在嵌套循环内部执行两次搜索运算符)。

一个为TESTCOLUMN = N''案例和一个TESTCOLUMN IS NULL案件。用于的值TESTCOLUMN = N''分支仍然通过计算GetRangeThroughConvert调用(因为这是 SQL Server 可以查找这种不匹配数据类型情况的唯一方法),因此仍然具有扩展范围,包括NULL.

问题是索引查找上的剩余谓词现在也发生了变化。

就是现在

CONVERT_IMPLICIT(nvarchar(50),[tempdb].[dbo].[TEMP].[TESTCOLUMN],0)=N'' 
OR [tempdb].[dbo].[TEMP].[TESTCOLUMN] IS NULL 

之前的残差谓词

CONVERT_IMPLICIT(nvarchar(50),[tempdb].[dbo].[TEMP].[TESTCOLUMN],0)=N''

不合适,因为这会错误地删除带有NULL需要保留的OR TESTCOLUMN IS NULL branch.

这意味着当寻求N''分支完成后它仍然会读取所有行NULL和以前一样,但剩余谓词不再适合删除这些谓词。

问题计划中的合并间隔没有合并索引查找的重叠范围,这似乎也有点失误。

我认为由于两个分支的标志值不同,这种情况不会发生。Expr1014 has a 的价值60 https://dba.stackexchange.com/a/14812/3690为了IS NULL分支和0为了= N'' branch.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

对带有空白 NVARCHAR 或 NULL 检查的 VARCHAR 索引进行 Count(*) 会导致返回的行数加倍 的相关文章

  • 如何创建包含多列MD5的GENERATED列?

    我尝试在 PostgreSQL 14 3 中添加下表 CREATE TABLE client cache id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY request VARCHAR
  • 单个查询删除并显示重复记录

    采访中提出的问题之一是 一张表有100条记录 其中 50 个 是重复的 是否可以用单个 查询删除重复记录 从表中以及选择和 显示剩余 50 条记录 这可以在单个 SQL 查询中实现吗 Thanks SNA 对于 SQL Server 你会使
  • MySQL通过UPDATE/DELETE合并重复数据记录

    我有一个看起来像这样的表 mysql gt SELECT FROM Colors ID USERNAME RED GREEN YELLOW BLUE ORANGE PURPLE 1 joe 1 null 1 null null null 2
  • 如何将表移动到 T-SQL 中的架构中

    我想使用 T SQL 将表移动到特定架构中 我正在使用 SQL Server 2008 ALTER SCHEMA TargetSchema TRANSFER SourceSchema TableName 如果你想搬家all表到一个新的模式
  • SQL Server 2008 GUID 列全为 0

    我希望这是我做的一个简单的傻事 我的数据库中有一个表 设置如下 column name widget guid data type uniqueidentifier allow nulls false default value newid
  • 如何在postgresql中编写有关最大行数的约束?

    我认为这是一个很常见的问题 我有一张桌子user id INT 和一张桌子photo id BIGINT owner INT 所有者是一个参考user id 我想向表照片添加一个约束 以防止每个用户将超过 10 张照片输入数据库 写这个的最
  • 如何插入包含“&”的字符串

    如何编写包含 字符的插入语句 例如 如果我想将 J J Construction 插入数据库的列中 我不确定这是否有什么不同 但我正在使用 Oracle 9i 我总是忘记这一点 然后又回到它 我认为最好的答案是迄今为止提供的答复的组合 首先
  • 如何获得组中“中间”值的平均值?

    我有一个包含值和组 ID 的表 简化示例 我需要获取中间 3 个值的每组的平均值 因此 如果有 1 2 或 3 个值 则它只是平均值 但如果有 4 个值 它将排除最高值 5 个值将排除最高值和最低值 等等 我正在考虑某种窗口函数 但我不确定
  • 在 SQL 中如何获得整数的最大值?

    我试图从 MySQL 数据库中找出整数 有符号或无符号 的最大值 有没有办法从数据库本身提取这些信息 是否有我可以使用的内置常量或函数 标准 SQL 或 MySQL 特定的 At http dev mysql com doc refman
  • IN 运算符对 SQL 查询性能的影响有多大?

    我的 SQL 查询需要 9 个小时才能执行 见下文 Select Field1 Field2 From A Where Field3 IN 45 unique values here 当我将此查询拆分为 3 个完全相同的查询 仅每个 IN
  • 从头开始构建 OLAP 解决方案时应该注意什么?

    我在一家运行基于 MS SQL 数据库服务器的软件产品的公司工作 多年来我已经用 PHP 开发了 20 30 个相当高级的报告 直接从数据库获取数据 这非常成功 人们对此感到高兴 但它有一些缺点 对于新的变化 它可能是相当开发密集型的 用户
  • where 子句中的双 %% ?

    我有一个 where 子句 如下例所示 WHERE subject LIKE chef AND dep LIKE psy 使用 1 或 2 符号有什么区别 我知道其中一个的含义 通配符 但不知道第二个添加的功能是什么 该查询可能是一个拼写错
  • 我们可以使用sql列出MS Access数据库中的所有表吗?

    我们可以使用 sql 找到 ms access 中的所有表吗 就像我们在 sql server 中所做的那样 select from sys tables 在sqlite中 SELECT FROM sqlite master where t
  • 更高效的 LINQ 查询

    有人可以帮我将此查询循环变成高效的 Linq 查询吗 我将其加载到 TreeView 中 因此必须附加每个项目 包含也非常低效 延迟加载项目也不起作用 事实上 这个查询访问数据库的次数比应有的要多 public IQueryable
  • 将数据表传递到存储过程。有没有更好的办法?

    数据表可以以某种方式传递到 SQL Server 2005 或 2008 中吗 我知道标准方法似乎是将 XML 传递给 SP 并且可以通过某种方式轻松地将数据表转换为 XML 来实现这一点 将 NET 对象传递到 SP 怎么样 那可能吗 我
  • 什么是更好的?子查询或内连接十个表?

    一个旧系统已抵达我们的办公室进行一些更改和修复 但它也存在性能问题 我们并不确切知道这种缓慢的根源是什么 当我们重构旧代码时 我们发现了几个具有以下模式的 sql 查询 出于示例目的 简化了查询 SELECT SELECT X FROM A
  • 如何安全地使用保留的 SQL 名称?

    我正在使用 Cakephp 3 使用 sqlserver 作为数据源服务器 我确信我的数据库连接没有问题 因为 home ctp 提示我已连接到我的数据库 并且我还使用迁移插件来创建我的表 似乎使用它没有问题这些工具 但是在我烘焙 MVC
  • sql直接获取表行数的方法

    stackoverflow 的朋友们大家好 我的例行程序中有一个我认为不必要的步骤 假设您想从图库中获取所有图像 并限制每页一定数量的图像 db PDO object start pagenum x images per page limi
  • 如何在sql server 2008R2中将单个单元格拆分为多个列?

    我想将每个名称拆分为各个列 create table split test value integer Allnames varchar 40 insert into split test values 1 Vinoth Kumar Raj
  • 在 Oracle 中如何将多行组合成逗号分隔的列表? [复制]

    这个问题在这里已经有答案了 我有一个简单的查询 select from countries 结果如下 country name Albania Andorra Antigua 我想在一行中返回结果 如下所示 Albania Andorra

随机推荐

  • 0-1背包算法

    以下 0 1 背包问题是否可解 浮动 正值和 浮动 权重 可以是正数或负数 背包的 浮动 容量 gt 0 我平均有 这是一个相对简单的二进制程序 我建议用蛮力进行修剪 如果任何时候你超过了允许的重量 你不需要尝试其他物品的组合 你可以丢弃整
  • 如何在 Databricks 中使用 OPTIMIZE ZORDER BY

    我有两个数据框 来自三角洲湖表 它们通过 id 列进行左连接 sd1 sd2 sql select a columnA b columnB from sd1 a left outer join sd2 b on a id b id 问题是我
  • 如何解决此错误“不要使用对象作为类型”?

    我不明白这个错误消息造成的 我的组件有两个和一个包含对象的数据数组 我收到一条错误消息 不要使用object作为一种类型 这object类型目前很难使用 我该如何解决它 我附加了数组包含对象的数据 first tsx import data
  • 在iOS中设置框架的原点

    我正在尝试以编程方式设置框架的原点 Method1 button frame origin y 100 方法二 CGRect frame button frame frame origin y 100 我尝试了方法 1 但它不起作用 显示错
  • 将字符串分解为标记,保持引用的子字符串完整

    我不知道我在哪里看到它 但是谁能告诉我如何使用 php 和 regex 来完成这个任务 this is a string that has quoted text inside 我希望能够像这样爆炸它 0 this 1 is 2 a 3 s
  • 有没有办法从画布上清除一个元素而不消除其他元素?

    我正在使用画布构建页面加载器 并使用 es6 类 虽然目前我无法使其正常工作 原因之一是我找不到清除画布的方法进展 到目前为止 这是我的代码 class Loader constructor width height this width
  • 为什么我的 PyGame 应用程序根本不运行?

    我有一个简单的 Pygame 程序 usr bin env python import pygame from pygame locals import pygame init win pygame display set mode 400
  • 字符串文字上的 SQL Server T-SQL N 前缀[重复]

    这个问题在这里已经有答案了 这可能是一个菜鸟问题 但我发现了一些 T SQL 查询示例来验证数据库大小SELECT and WHERE clause here http technet microsoft com en us library
  • 检测到 JVM 正在关闭

    我有一个使用 addShutdownHook 处理 Ctrl C 的 Swing 应用程序 它工作正常 直到我的关闭任务之一调用一个在正常情况下更改 JLabel 文本的函数 此时它挂起 我认为问题是 Swing EDT 已终止或正在等待某
  • YouTube API v3 检测是否订阅频道

    我希望能够检测当前经过身份验证的用户是否订阅了 YouTube API v3 中的特定 YouTube 频道 一种可能的解决方案是检索当前经过身份验证的用户的所有订阅的列表 并检查该列表中是否包含该频道的频道 ID 这将是一个非常低效的解决
  • 使用 javascript/jquery 从数据库格式化日期的正确方法

    我正在调用包含日期时间数据类型的数据库 日期看起来像这样 2005 05 23 16 06 00 000 当用户从列表中选择某个项目时 我想在表格中显示它 我调用我的控制器操作并返回所有时间的 Json 并将它们放入表中 问题是日期完全错误
  • JavaScript 正则表达式两个标签之间的多行文本

    我编写了一个正则表达式来从 HTML 中获取字符串 但似乎多行标志不起作用 这是我的模式 我想将文本输入h1 tag var pattern div class box content 5 h1 lt lt h1 gt mi m html
  • MultiFieldQueryParser 正在从首字母缩略词中删除点

    我再次发布这个问题 因为我的查询没有得到答复 我正在使用 Lucene 开发图书搜索 api 用户可以搜索标题或描述字段包含 C F A 的书籍 我正在使用 StandardAnalyzer 以及停用词列表 我使用 MultiFieldQu
  • 初始化 LPCTSTR /LPCWSTR [重复]

    这个问题在这里已经有答案了 我很难理解并使其正常工作 基本上归结为我无法成功初始化这种类型的变量 它需要有说的内容7 2E25DC9D 0 USB003 有人可以解释 展示这种类型的正确初始化和类似的值吗 我已查看此站点上的所有帮助 将项目
  • 对 postgresql 中使用 array_agg 创建的文本聚合进行排序

    我在 postgresql 中有一个表 下表 动物 可以解释我的问题 name tiger cat dog 现在我正在使用以下查询 SELECT array to string array agg name FROM animals 结果是
  • YouTube iFrame Player API 无法在 DOMWindow 上执行 postMessage

    我正在尝试使用以下命令将 youtube 加载到我的网页中YouTube iFrame Player API 并在加载时出现以下错误 Failed to execute postMessage on DOMWindow The target
  • 使用 cmake add_definitions 的文件路径

    我正在尝试替换硬编码预处理器 define MY FILE PATH usr local myfile 在 cmake 中使用 add definitions add definitions DMY FILE PATH MY FILE PA
  • Cassandra data stax 查询生成器更新

    我正在尝试编写一个简单的更新查询 更新表 set col1 val1 col2 val2 where col3 val3 您能否提供一个使用查询生成器 API 在 Cassandra 中编写简单 UPDATE 的示例 试试这个 v1 x Q
  • AllowMultiple 不适用于属性属性?

    我正在收集放置在属性上的所有自定义属性 有多个相同类型的属性分配给该属性 但是在收集它们时 结果集合仅包含特定类型的第一个属性 属性类 AttributeUsage System AttributeTargets Property Allo
  • 对带有空白 NVARCHAR 或 NULL 检查的 VARCHAR 索引进行 Count(*) 会导致返回的行数加倍

    我有一张桌子 上面有VARCHAR列及其上的索引 每当一个SELECT COUNT 是在这张表上完成的 该表检查了COLUMN N OR COLUMN IS NULL它返回双倍的行数 SELECT 与相同的where子句将返回正确的记录数