这是另一个查询优化器难题。
也许我只是高估了查询优化器,或者我遗漏了一些东西 - 所以我把它放在那里。
我有一张简单的桌子
CREATE TABLE [dbo].[MyEntities](
[Id] [uniqueidentifier] NOT NULL,
[Number] [int] NOT NULL,
CONSTRAINT [PK_dbo.MyEntities] PRIMARY KEY CLUSTERED ([Id])
)
CREATE NONCLUSTERED INDEX [IX_Number] ON [dbo].[MyEntities] ([Number])
Run Code Online (Sandbox Code Playgroud)
有一个索引和几千行,Number
均匀分布在值 0、1 和 2 中。
现在这个查询:
SELECT * FROM
(SELECT
[Extent1].[Number] AS [Number],
CASE
WHEN (0 = [Extent1].[Number]) THEN 'one'
WHEN (1 = [Extent1].[Number]) THEN 'two'
WHEN (2 = [Extent1].[Number]) THEN 'three'
ELSE '?'
END AS [Name]
FROM [dbo].[MyEntities] AS [Extent1]
) P
WHERE P.Number = 0;
Run Code Online (Sandbox Code Playgroud)
是否 …
SQL Server 有一种叫做“多列统计”的东西,但这并不是人们认为的意思。
我们来看看下面的示例表:
CREATE TABLE BadStatistics
(
IsArchived BIT NOT NULL,
Id INT NOT NULL IDENTITY PRIMARY KEY,
Mystery VARCHAR(200) NOT NULL
);
CREATE NONCLUSTERED INDEX BadIndex
ON BadStatistics (IsArchived, Mystery);
Run Code Online (Sandbox Code Playgroud)
这样,我们就在我们拥有的两个索引上创建了两个统计信息:
BadIndex 的统计数据:
+--------------+----------------+-------------------------+
| All density | Average Length | Columns |
+--------------+----------------+-------------------------+
| 0.5 | 1 | IsArchived |
+--------------+----------------+-------------------------+
| 4.149378E-06 | 37 | IsArchived, Mystery |
+--------------+----------------+-------------------------+
| 4.149378E-06 | 41 | IsArchived, Mystery, Id |
+--------------+----------------+-------------------------+
+--------------+------------+---------+---------------------+----------------+
| RANGE_HI_KEY | RANGE_ROWS | EQ_ROWS …
Run Code Online (Sandbox Code Playgroud) 考虑以下具有多列索引的示例表:
create table BigNumbers (
col1 tinyint not null,
col2 tinyint not null,
col3 tinyint not null,
index IX_BigNumbers clustered (col1, col2, col3)
)
DECLARE @n INT = 100;
DECLARE @x1 INT = 0;
DECLARE @x2 INT = 0;
DECLARE @x3 INT = 0;
SET NOCOUNT ON;
WHILE @x3 <= @n BEGIN
SET @x2 = 0;
WHILE @x2 <= @n BEGIN
SET @x1 = 0;
WHILE @x1 <= @n BEGIN
insert into BigNumbers values (@x1, @x2, @x3);
SET @x1 = …
Run Code Online (Sandbox Code Playgroud) like \'foo%\'
我天真地认为 a和 a之间的关系>= \'foo\'
是后者与前者以及索引顺序中后面的一些附加行相匹配。更一般地说,我认为对于给定的文本,t
字符列上的索引分为四个区域,分别是
<
( )之前的条目t
,=
) t
,>
大于 ( )t
且仍以t
( ) 作为前缀的条目like concat(t, \'%\')
以及并且这些区域是连续的并且按顺序排列。
\n此外,具有t
前缀 ( like concat(t, \'%\')
) 的所有行也在索引中形成一个连续区域,并且该区域在其开头包含区域 2(这意味着它恰好是区域 2 和 3 在一起)。
但事情可能没那么简单。例如,在 SQL Server 中,单词在排序规则中H\xc3\xa4user
比较等于,并且两者都是。Haeuser
German_PhoneBook_CI_AI
>= \'Ha\'
但是,仅Haeuser
匹配like \'Ha%\'
,因此有关前缀的附加假设并不完全成立。
你可以在这个小提琴中看到这一点。 …
子句LIKE
可以测试某个字符串是否出现在另一个字符串中,并且该CHARINDEX
函数可以给出第一个匹配的开始位置。
就我而言,我对结束位置感兴趣,由于排序规则的复杂性,它无法从开始位置推导出来。例如,在德语排序规则 ( German_PhoneBook_100_CI_AS_SC_UTF8
) 中,
h\xc3\xa4
出现在 'H\xc3\xa4ger' 的位置 1 处,结束于位置 2 处h\xc3\xa4
出现在“Haeger”中的位置 1 并结束于位置 3。这样做的问题是为了用户的利益标记搜索结果文本的匹配部分。
\n我一直在考虑反转字符串,但我仍然只能得到第一个匹配项CHARINDEX
,在反转的情况下我需要最后一个匹配项。
有人有什么想法吗?
\n我发现了一个类似的问题,我知道为了查询表格
SELECT COUNT(1)
FROM foo f
LEFT OUTER JOIN bar b ON f.Value = b.Value AND f.Value = b.Value2
Run Code Online (Sandbox Code Playgroud)
要在不接触的情况下执行bar
,需要在有问题的两列上有一个唯一索引。
事实上,到目前为止,这有效,表被定义为:
CREATE TABLE [dbo].[Foo](
[Value] [varchar](255) NOT NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[Bar](
[Value] [varchar](1024) NULL,
[Value2] [varchar](1024) NULL
)
CREATE UNIQUE CLUSTERED INDEX [IX] ON [dbo].[Bar]
(
[Value] ASC,
[Value2] ASC
)
Run Code Online (Sandbox Code Playgroud)
查询计划没有触及bar
,太好了。
在我探索为什么这在我编写的某些应用程序的实际查询中不起作用时,我将那里的查询简化为以下简单的测试,并且查询计划器无法将其从桌子上移开bar
:
WITH numbers AS (
SELECT 1 AS i
UNION ALL SELECT i + 1
FROM numbers …
Run Code Online (Sandbox Code Playgroud) SQL Server 中的读已提交快照和快照隔离级别消除了大多数锁定,除了以下一种情况:一个写入者仍会锁定其他写入者。
该文档小心翼翼地说了这么多,随后没有记录任何其他值得了解的内容:
它真的只是被独占锁定的修改行吗?或者它也可以是不相关的行(例如索引中相邻的行)或页面?
我确实查看了其中的锁sys.dm_tran_locks
,并且在未提交的事务期间只看到了修改行上的独占锁 - 页面仅被锁定为IX
.
我还测试了两个事务是否可以在一个非常小的表(可能适合一页)中的两个未提交事务期间同时修改两个不同的行,并且效果也很好。
如果确实只有修改的行被独占锁定,那么如果确保没有两个连接同时写入同一行,那么这将为具有数据库独占访问权限的应用程序提供无锁写入的保证。
在我想到的场景中这是可能的 - 但如果页面锁发挥作用,几乎没有办法做类似的事情,因为无法预测哪些行到底会受到影响。
部分出于好奇,我想知道是否可以使用索引(物化)视图来加速对某个基表的计数查询。
查询类似于
SELECT COUNT(*)
FROM BaseTable
WHERE Slot = ?;
Run Code Online (Sandbox Code Playgroud)
所以我创建了一个视图
CREATE VIEW IndexedView
WITH SCHEMABINDING AS
SELECT bt.Slot, COUNT_BIG(*) AS COUNT
FROM dbo.BaseTable bt
GROUP BY bt.Slot;
Run Code Online (Sandbox Code Playgroud)
有聚集索引
CREATE UNIQUE CLUSTERED INDEX IX_Main
ON IndexedView (Slot);
Run Code Online (Sandbox Code Playgroud)
这有效,我现在可以将原始查询编写为
SELECT COUNT
FROM IndexedView
WHERE Slot = ?
Run Code Online (Sandbox Code Playgroud)
并更快地获得所需的结果。
唉,这对我来说没什么用,因为我的查询通常不是手工制作的。我真的需要通过使用索引视图作为某种索引来使原始查询变得更快BaseTable
- 我想我在某处读到这可能在某些情况下发生,但根据我的测试,而不是在这个测试中。
所以我的问题是:
编辑:关于重复的问题 - 我对 GROUP BY 和索引视图的聚合方面更感兴趣。答案帮助我找到了我犯的一个愚蠢的错误,现在它也对我有用。
JOYOUS ADDENDUM:现在我让它工作了,我成功地测试了它甚至可以在查询包含左联接的情况下工作,而在这些情况下,左联接实际上可以被优化掉(即 on-clause 涵盖了一个唯一索引在连接表中)。
这真的很棒,因为这意味着即使在带有左连接的查询的情况下,也可以以这样一种方式设计模式,即快速获取所有内容或特定分组的总行数。
sql-server ×8
collation ×2
btree ×1
index ×1
join ×1
locking ×1
order-by ×1
statistics ×1
substring ×1
transaction ×1