- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我们发现,如果 where
子句包含参数化值而不是字符串文字,则 SQL Server 将使用索引扫描而不是索引查找。
下面是一个例子:
SQL Server 在以下情况下执行索引扫描(where 子句中的参数)
declare @val1 nvarchar(40), @val2 nvarchar(40);
set @val1 = 'val1';
set @val2 = 'val2';
select
min(id)
from
scor_inv_binaries
where
col1 in (@val1, @val2)
group by
col1
另一方面,以下查询执行索引查找:
select
min(id)
from
scor_inv_binaries
where
col1 in ('val1', 'val2')
group by
col1
是否有人观察到类似的行为,以及他们如何修复此问题以确保查询执行索引查找而不是索引扫描?
我们无法使用forceseek表提示,因为SQL Sserver 2005支持forceseek。
我也更新了统计数据。非常感谢您的帮助。
最佳答案
好吧,回答你的问题为什么SQL Server要这样做,答案是查询不是按逻辑顺序编译的,每个语句都是根据自己的优点编译的,因此,当生成 select 语句的查询计划时,优化器不知道 @val1 和 @Val2 将分别变为“val1”和“val2”。
当 SQL Server 不知道该值时,它必须对该变量将在表中出现的次数做出最佳猜测,这有时会导致计划不理想。我的主要观点是,具有不同值的相同查询可以生成不同的计划。想象一下这个简单的例子:
IF OBJECT_ID(N'tempdb..#T', 'U') IS NOT NULL
DROP TABLE #T;
CREATE TABLE #T (ID INT IDENTITY PRIMARY KEY, Val INT NOT NULL, Filler CHAR(1000) NULL);
INSERT #T (Val)
SELECT TOP 991 1
FROM sys.all_objects a
UNION ALL
SELECT TOP 9 ROW_NUMBER() OVER(ORDER BY a.object_id) + 1
FROM sys.all_objects a;
CREATE NONCLUSTERED INDEX IX_T__Val ON #T (Val);
我在这里所做的只是创建一个简单的表格,并为 val
列添加 1000 行值 1-10,但是 1 出现 991 次,其他 9 只出现一次。前提是这个查询:
SELECT COUNT(Filler)
FROM #T
WHERE Val = 1;
扫描整个表会比使用索引进行查找,然后执行 991 次书签查找来获取 Filler
的值更有效,但是以下查询只有 1 行:
SELECT COUNT(Filler)
FROM #T
WHERE Val = 2;
将更有效地进行索引查找,并且通过单个书签查找来获取 Filler
的值(并且运行这两个查询将批准这一点)
我非常确定搜索和书签查找的截止实际上会根据情况而变化,但它相当低。使用示例表,经过一些试验和错误,我发现在优化器通过索引查找进行全表扫描之前,我需要 Val
列有 38 行且值为 2书签查找:
IF OBJECT_ID(N'tempdb..#T', 'U') IS NOT NULL
DROP TABLE #T;
DECLARE @I INT = 38;
CREATE TABLE #T (ID INT IDENTITY PRIMARY KEY, Val INT NOT NULL, Filler CHAR(1000) NULL);
INSERT #T (Val)
SELECT TOP (991 - @i) 1
FROM sys.all_objects a
UNION ALL
SELECT TOP (@i) 2
FROM sys.all_objects a
UNION ALL
SELECT TOP 8 ROW_NUMBER() OVER(ORDER BY a.object_id) + 2
FROM sys.all_objects a;
CREATE NONCLUSTERED INDEX IX_T__Val ON #T (Val);
SELECT COUNT(Filler), COUNT(*)
FROM #T
WHERE Val = 2;
因此,对于此示例,限制为匹配行的 3.7%。
由于查询不知道当您使用变量时将匹配多少行,因此它必须猜测,最简单的方法是找出总行数,并将其除以列,因此在本例中,WHERE val = @Val
的估计行数为 1000/10 = 100,实际算法比这更复杂,但为了举例,这样做就可以了。因此,当我们查看执行计划时:
DECLARE @i INT = 2;
SELECT COUNT(Filler)
FROM #T
WHERE Val = @i;
我们可以在这里看到(使用原始数据)估计行数为 100,但实际行数为 1。从前面的步骤我们知道,如果行数超过 38,优化器将选择聚集索引扫描通过索引查找,因此由于对行数的最佳猜测高于此值,因此对于未知变量的计划是聚集索引扫描。
为了进一步证明这一理论,如果我们创建一个包含 1000 行数字 1-27 均匀分布的表(因此估计行数约为 1000/27 = 37.037)
IF OBJECT_ID(N'tempdb..#T', 'U') IS NOT NULL
DROP TABLE #T;
CREATE TABLE #T (ID INT IDENTITY PRIMARY KEY, Val INT NOT NULL, Filler CHAR(1000) NULL);
INSERT #T (Val)
SELECT TOP 27 ROW_NUMBER() OVER(ORDER BY a.object_id)
FROM sys.all_objects a;
INSERT #T (val)
SELECT TOP 973 t1.Val
FROM #T AS t1
CROSS JOIN #T AS t2
CROSS JOIN #T AS t3
ORDER BY t2.Val, t3.Val;
CREATE NONCLUSTERED INDEX IX_T__Val ON #T (Val);
然后再次运行查询,我们得到一个带有索引查找的计划:
DECLARE @i INT = 2;
SELECT COUNT(Filler)
FROM #T
WHERE Val = @i;
所以希望这非常全面地涵盖了您制定该计划的原因。现在我想下一个问题是如何强制执行不同的计划,答案是,使用查询提示OPTION (RECOMPILE)
,强制查询在执行时编译,当值参数已知。恢复到原始数据,其中 Val = 2
的最佳计划是查找,但使用变量会产生带有索引扫描的计划,我们可以运行:
DECLARE @i INT = 2;
SELECT COUNT(Filler)
FROM #T
WHERE Val = @i;
GO
DECLARE @i INT = 2;
SELECT COUNT(Filler)
FROM #T
WHERE Val = @i
OPTION (RECOMPILE);
我们可以看到后者使用索引查找和键查找,因为它在执行时检查了变量的值,并为该特定值选择了最合适的计划。 OPTION (RECOMPILE)
的问题在于,这意味着您无法利用缓存的查询计划,因此每次编译查询都会产生额外的成本。
关于sql-server - 当 WHERE 子句包含参数化值时,为什么 SQL Server 使用索引扫描而不是索引查找,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27564852/
SQLite、Content provider 和 Shared Preference 之间的所有已知区别。 但我想知道什么时候需要根据情况使用 SQLite 或 Content Provider 或
警告:我正在使用一个我无法完全控制的后端,所以我正在努力解决 Backbone 中的一些注意事项,这些注意事项可能在其他地方更好地解决......不幸的是,我别无选择,只能在这里处理它们! 所以,我的
我一整天都在挣扎。我的预输入搜索表达式与远程 json 数据完美配合。但是当我尝试使用相同的 json 数据作为预取数据时,建议为空。点击第一个标志后,我收到预定义消息“无法找到任何内容...”,结果
我正在制作一个模拟 NHL 选秀彩票的程序,其中屏幕右侧应该有一个 JTextField,并且在左侧绘制弹跳的选秀球。我创建了一个名为 Ball 的类,它实现了 Runnable,并在我的主 Draf
这个问题已经有答案了: How can I calculate a time span in Java and format the output? (18 个回答) 已关闭 9 年前。 这是我的代码
我有一个 ASP.NET Web API 应用程序在我的本地 IIS 实例上运行。 Web 应用程序配置有 CORS。我调用的 Web API 方法类似于: [POST("/API/{foo}/{ba
我将用户输入的时间和日期作为: DatePicker dp = (DatePicker) findViewById(R.id.datePicker); TimePicker tp = (TimePic
放宽“邻居”的标准是否足够,或者是否有其他标准行动可以采取? 最佳答案 如果所有相邻解决方案都是 Tabu,则听起来您的 Tabu 列表的大小太长或您的释放策略太严格。一个好的 Tabu 列表长度是
我正在阅读来自 cppreference 的代码示例: #include #include #include #include template void print_queue(T& q)
我快疯了,我试图理解工具提示的行为,但没有成功。 1. 第一个问题是当我尝试通过插件(按钮 1)在点击事件中使用它时 -> 如果您转到 Fiddle,您会在“内容”内看到该函数' 每次点击都会调用该属
我在功能组件中有以下代码: const [ folder, setFolder ] = useState([]); const folderData = useContext(FolderContex
我在使用预签名网址和 AFNetworking 3.0 从 S3 获取图像时遇到问题。我可以使用 NSMutableURLRequest 和 NSURLSession 获取图像,但是当我使用 AFHT
我正在使用 Oracle ojdbc 12 和 Java 8 处理 Oracle UCP 管理器的问题。当 UCP 池启动失败时,我希望关闭它创建的连接。 当池初始化期间遇到 ORA-02391:超过
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 9 年前。 Improve
引用这个plunker: https://plnkr.co/edit/GWsbdDWVvBYNMqyxzlLY?p=preview 我在 styles.css 文件和 src/app.ts 文件中指定
为什么我的条形这么细?我尝试将宽度设置为 1,它们变得非常厚。我不知道还能尝试什么。默认厚度为 0.8,这是应该的样子吗? import matplotlib.pyplot as plt import
当我编写时,查询按预期执行: SELECT id, day2.count - day1.count AS diff FROM day1 NATURAL JOIN day2; 但我真正想要的是右连接。当
我有以下时间数据: 0 08/01/16 13:07:46,335437 1 18/02/16 08:40:40,565575 2 14/01/16 22:2
一些背景知识 -我的 NodeJS 服务器在端口 3001 上运行,我的 React 应用程序在端口 3000 上运行。我在 React 应用程序 package.json 中设置了一个代理来代理对端
我面临着一个愚蠢的问题。我试图在我的 Angular 应用程序中延迟加载我的图像,我已经尝试过这个2: 但是他们都设置了 src attr 而不是 data-src,我在这里遗漏了什么吗?保留 d
我是一名优秀的程序员,十分优秀!