对带有空白 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(*) 会导致返回的行数加倍 的相关文章

  • 模式更新后 jOOQ 生成的类的运行时验证?

    我用org jooq util DefaultGenerator在构建过程中生成 jOOQ 类来表示我的数据库模式 当应用程序运行时 架构预计会在应用程序不知情的情况下发生更改 此类更改可能与已生成的代码兼容 也可能不兼容 如何在运行时检测
  • postgres 有 CLOSEST 运算符吗?

    我正在寻找这样的东西 给定一个表格 id number 1 7 2 1 25 3 1 01 4 3 0 查询SELECT FROM my table WHEREnumberCLOSEST 1 将返回第 3 行 我只关心数字 现在我有一个程序
  • 将大量实体插入 SQL Server 2012 [重复]

    这个问题在这里已经有答案了 我正在进行一个使用 Entity Framework 5 和 SQL Server 2012 的项目 我们需要一次插入大量行 100k 个实体的顺序 基本上 我们有一个物理程序 它输出大量二进制数据 然后我们需要
  • TOAD 将 &String 视为绑定变量

    我正在使用 Oracle Data Integrator 开发一些 ETL 有时会使用 TOAD 测试部分代码 今天我遇到了 TOAD 的问题 我有一行像 AND column value like DEV PROD 当我尝试运行包含上面过
  • sql查询将两列与一列连接起来

    我在 MS Access 2010 中有 2 个表 如下所示 USERS u id u name LOAN l id l from ref users u id l to ref users u id l amount Users u id
  • 对于数据库来说,选择正确的数据类型会影响性能吗?

    如果是这样 为什么 我的意思是 tinyint 的搜索速度比 int 快吗 如果是这样 性能上的实际差异是什么 是的 根据数据类型 它确实有所不同 int vs tinyint不会在速度上产生明显的差异 但会在数据大小上产生差异 假设tin
  • 如何在 SQL Server 中的特定字符后分割字符串并将该值更新到特定列

    我有包含数据的表格1 1 to 1 20在一列中 我想要值 1 到 20 即 前斜杠 之后的值更新到 SQL Server 中同一表中的其他列 Example 专栏有价值1 1 1 2 1 3 1 20新列值1 2 3 20 也就是说 我要
  • DotNET 应用程序中的 GDI 句柄

    我的纯 DotNET 库作为非托管桌面应用程序中的插件运行 我收到了稳定的 虽然低 崩溃报告流 这些报告似乎表明 GDI 句柄存在问题 错误消息中的字体等 恢复为系统字体 各种控件的显示崩溃 不久后发生大规模崩溃 我的窗体几乎没有控件 但我
  • 如何在SSRS 2012中显示基于总金额的前10名

    我只需要显示前 10 名Class基于Total SUM Premium 柱子 我转到类代码属性组 gt 过滤器并按 SUM Net Written Premium 设置前 10 名 但它不起作用 我只需要显示前 10 名 而且总金额也应该
  • 如何让 LinqToSql 将“索引提示”传递给 sql server?

    由于我们不能相信我们的客户会更新 sql server 中的索引统计信息等 因此我们过去不得不使用索引提示 http www sql server performance com tips hints general p1 aspx 由于我
  • 快速转储 SQL Server 表

    我在 SQL Server 2008 R2 中有一个大表 它包含数十亿行 我需要在我们的应用程序中加载整个数据集 查询全表非常慢 我想使用 bcp 将其转储到文件中并加载它 但问题是字符串列包含各种特殊字符 如 t 0 逗号和 n 我找不到
  • Mysql 检索所有有限制的行

    我想检索特定用户的所有行 限制为 0 x 所以我只是想问是否有任何方法可以检索 mysql 中的所有行 而不调用返回 x 的 count id 的方法 而不重载现有函数 该函数在查询中根本没有限制 与我们的 string Relace 功能
  • 如何在 Visual Studio 中更改 Azure 数据库表的列顺序

    我整个下午都在寻找在 MS Visual Studio 2022 中重新排序 Azure 数据库表列的方法 没有运气 在其他应用程序中 可以通过拖动或剪切和粘贴轻松重新排列列 这里无能为力 此时 我什至不确定可以在 VS 中移动列 我只对
  • Oracle:使用SQL或PL/SQL查找动态SQL中的错误位置

    如何在 PL SQL 或 SQL 中找到动态 SQL 语句中的错误位置 从 SQL Plus 中 我看到了错误的位置 例如 无效的 SQL DML 语句 SYS orcl gt SELECT 2 X 3 FROM 4 TABLEX 5 TA
  • 有没有办法以编程方式轻松更改多个 SSIS 包上的服务器名称?

    作为发布周期的一部分 我们正在创建多个 SSIS 包来迁移大型数据库 我们最终可能会得到大约 5 10 个 SSIS 包 由于我们有 4 个环境 开发 QA 登台 生产等 是否有一种有效的方法可以在每个 SSIS 包经历不同的服务器环境时更
  • 在 MySQL 中使用 COUNT 时如何返回 0 而不是 null

    我使用此查询返回存储在 sTable 中的歌曲列表以及存储在 sTable2 中的总项目数 SQL queries Get data to display sQuery SELECT SQL CALC FOUND ROWS str repl
  • 如何从 PostgreSQL 中的时间戳列值提取一天中的时间(或小时)?

    我正在尝试从 PostgreSQL 中的 时间戳 列中提取一天中的时间 这是我的做法 但是 太糟糕了 知道如何做得更好吗 SELECT date part hour date demande text hours date part min
  • MySQL 按重复项从上到下排序

    我有一个lammer问题 因为我不是mysql专业人士 我有类似的字段 id color 1 red 2 green 3 yellow 4 green 5 green 6 red 我想按重复项进行分组 最常见的重复项先进行分组 所以应该这样
  • Amazon RDS for SQL Server 是否支持 SSIS?

    从谷歌搜索中读到一些相互矛盾的答案 不确定答案是是 否还是可能 我觉得读的时候已经很清楚了this http docs aws amazon com AmazonRDS latest UserGuide CHAP SQLServer htm
  • 探查器模板可以迁移到较新版本的 SQL Profiler 吗?

    是否可以将 Profiler 模板迁移到较新版本的 SQL Server 就我而言 我想将 SQL 2008 模板带到 2012 年 我尝试过 1 直接文件复制和 2 导出 导入 在这两种情况下 旧模板都会运行 但无法修改 修改后会出现以下

随机推荐

  • 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子句将返回正确的记录数