当PRIMARY KEY为UNIQUEIDENTIFIER时,OFFSET FETCH意外结果

时间:2017-06-05 05:20:42

标签: sql sql-server pagination fetch offset

梗概:
查询使用UNIQUEIDENTIFIER(使用newsequentialid()填充)作为其主键的表时,如果插入了具有乱序键的记录,OFFSET / FETCH似乎会返回不准确的结果。

我最近使用SQL Server表的实体属性值模式实现了一个解决方案。我创建了一个利用Entity Framework进行分页数据访问的数据层。提到实体框架的唯一相关性是它负责创建我将要讨论的OFFSET / FETCH SQL语句。我知道可以实现的替代解决方案在不使用OFFSET / FETCH的情况下工作,但是,这不是我的帖子的意图。这篇文章的目的是准确理解OFFSET / FETCH查询发生了什么。

我包含了复制此问题所需的必要SQL。

以下SQL语句将创建一个实现EAV模式的表:

CREATE TABLE [dbo].[OptionalValues]
(
     [Id] [uniqueidentifier] NOT NULL 
         CONSTRAINT [DF_OptionalValues_Id] DEFAULT (newsequentialid()),
     [Type] [nvarchar](450) NOT NULL,
     [Name] [nvarchar](450) NOT NULL,
     [Value] [nvarchar](450) NOT NULL,

     CONSTRAINT [PK_OptionalValues] 
         PRIMARY KEY CLUSTERED ([Id] ASC)
) ON [PRIMARY]

以下语句将测试数据插入表中:

INSERT [dbo].[OptionalValues] ([Id], [Type], [Name], [Value]) 
VALUES (N'04c9f78a-5114-e711-90c2-005056a9316b', N'TargetOrgans', N'Eyes', N'Eyes');
INSERT [dbo].[OptionalValues] ([Id], [Type], [Name], [Value]) 
VALUES (N'05c9f78a-5114-e711-90c2-005056a9316b', N'TargetOrgans', N'Skin', N'Skin');
INSERT [dbo].[OptionalValues] ([Id], [Type], [Name], [Value]) 
VALUES (N'06c9f78a-5114-e711-90c2-005056a9316b', N'TargetOrgans', N'Autonomic Nervous System', N'Autonomic Nervous System');
INSERT [dbo].[OptionalValues] ([Id], [Type], [Name], [Value]) 
VALUES (N'07c9f78a-5114-e711-90c2-005056a9316b', N'TargetOrgans', N'Kidneys', N'Kidneys');
INSERT [dbo].[OptionalValues] ([Id], [Type], [Name], [Value]) 
VALUES (N'08c9f78a-5114-e711-90c2-005056a9316b', N'TargetOrgans', N'Liver', N'Liver');
INSERT [dbo].[OptionalValues] ([Id], [Type], [Name], [Value]) 
VALUES (N'09c9f78a-5114-e711-90c2-005056a9316b', N'TargetOrgans', N'Respiratory System', N'Respiratory System');
INSERT [dbo].[OptionalValues] ([Id], [Type], [Name], [Value]) 
VALUES (N'0ac9f78a-5114-e711-90c2-005056a9316b', N'TargetOrgans', N'Lungs', N'Lungs');
INSERT [dbo].[OptionalValues] ([Id], [Type], [Name], [Value]) 
VALUES (N'0bc9f78a-5114-e711-90c2-005056a9316b', N'TargetOrgans', N'Central Nervous System', N'Central Nervous System');
INSERT [dbo].[OptionalValues] ([Id], [Type], [Name], [Value]) 
VALUES (N'0cc9f78a-5114-e711-90c2-005056a9316b', N'TargetOrgans', N'Blood', N'Blood');
INSERT [dbo].[OptionalValues] ([Id], [Type], [Name], [Value]) 
VALUES (N'0dc9f78a-5114-e711-90c2-005056a9316b', N'TargetOrgans', N'Heart', N'Heart');
INSERT [dbo].[OptionalValues] ([Id], [Type], [Name], [Value]) 
VALUES (N'0ec9f78a-5114-e711-90c2-005056a9316b', N'TargetOrgans', N'Reproductive System', N'Reproductive System');
INSERT [dbo].[OptionalValues] ([Id], [Type], [Name], [Value])  
VALUES (N'0fc9f78a-5114-e711-90c2-005056a9316b', N'TargetOrgans', N'Cardiovascular System', N'Cardiovascular System');
INSERT [dbo].[OptionalValues] ([Id], [Type], [Name], [Value]) 
VALUES (N'd6fdf6b9-6014-e711-815c-080027f9bf3f', N'TargetOrgans', N'TEST_ONE', N'TEST_ONE');
INSERT [dbo].[OptionalValues] ([Id], [Type], [Name], [Value]) 
VALUES (N'5a8c5533-6114-e711-815c-080027f9bf3f', N'TargetOrgans', N'TEST_TWO', N'TEST_TWO')
INSERT [dbo].[OptionalValues] ([Id], [Type], [Name], [Value]) 
VALUES (N'801a12da-6214-e711-815c-080027f9bf3f', N'TargetOrgans', N'TEST_THREE', N'TEST_THREE');

插入的最后三个记录的[Id]值由表newsequentialid()函数生成 NOT ,并且与其他记录不同步。

此查询将返回所有15条记录,包括具有“无序”ID

的记录
SELECT * 
FROM [dbo].[OptionalValues] 
ORDER BY [Type]

接下来的3个查询是对我没有意义的查询。它们包括ORDER BY [Type] NOT 唯一,并且 NOT 包含任何显式[Id]排序

此查询执行 NOT 显示具有“无序”ID的记录

SELECT * 
FROM [dbo].[OptionalValues] 
ORDER BY [Type] 
    OFFSET 0 ROWS 
    FETCH NEXT 10 ROWS ONLY

此查询 NOT 显示具有“无序”ID的记录,即使结果计数仍为15 ???

SELECT * 
FROM [dbo].[OptionalValues] 
ORDER BY [Type] 
    OFFSET 10 ROWS 
    FETCH NEXT 10 ROWS ONLY

此查询显示所有15条记录,包括具有“无序”ID

的行
SELECT * 
FROM [dbo].[OptionalValues] 
ORDER BY [Type] 
    OFFSET 0 ROWS 
    FETCH NEXT 15 ROWS ONLY

“修复”是使用[Id]列添加真正独特的排序。

SELECT * 
FROM [dbo].[OptionalValues] 
ORDER BY [Type], [Id] 
    OFFSET 0 ROWS 
    FETCH NEXT 10 ROWS ONLY

SELECT * 
FROM [dbo].[OptionalValues] 
ORDER BY [Type], [Id] 
    OFFSET 10 ROWS 
    FETCH NEXT 10 ROWS ONLY

我的问题实际上围绕着没有真正独特排序的查询的不准确性。为什么查询似乎在分页行计数中识别出具有“无序ID”的记录,但这些记录未显示在查询结果中?与主键一起创建的索引是否会以任何方式影响先前查询的排序行为?

感谢您提供的任何帮助和说明。

1 个答案:

答案 0 :(得分:1)

当缺少order by子句时,或者order by子句中指定的列包含重复值时,SQL Server不保证结果的顺序。这个意味着行的顺序是随机的,这只意味着它不是你可以信任的东西是一致的。对于任何关系数据库来说都是如此。桌子本质上没有订购。
这也是您在使用order by子句时必须指定offset...fetch next子句的原因,以及为什么在视图,派生表或不使用{cte时不能使用order by的原因{1}}或top