带有 OFFSET/FETCH 的 SQL 查询返回意外结果

An SQL query with OFFSET/FETCH is returning unexpected results

我有一个名为 User 的 SQL Server 2019 数据库 table,其中包含 1,000 行,如下所示:

我很难理解使用 OFFSET/FETCHSELECT 查询如何返回意外结果:

SELECT * 
FROM [User]
WHERE (([NameGiven] LIKE '%1%') 
       OR ([NameFamily] LIKE '%2%'))
ORDER BY [Id] ASC 
    OFFSET 200 ROWS FETCH NEXT 100 ROWS ONLY;

查询结果:

结果从264到452共100行。为什么记录 201、211 等不显示?是我的预期有误还是查询条件有误?

如果我从 ORDER BY 子句中删除 OFFSET/FETCH 选项,结果与预期一致。这让我认为 WHERE 子句不是问题。

如有任何建议,我们将不胜感激。

问题是您希望偏移发生在过滤器之前,但实际上它直到过滤器之后才发生。考虑一个更简单的示例,其中您希望所有名为 'sam' 的人和名为 'sam' 的人多于您的偏移量:

CREATE TABLE dbo.foo(id int, name varchar(32));

INSERT dbo.foo(id, name) VALUES
(1, 'sam'),
(2, 'sam'),
(3, 'bob'),
(4, 'sam'),
(5, 'sam'),
(6, 'sam');

如果你只是说:

SELECT id FROM dbo.foo WHERE name = 'sam';

你得到:

1
2
4
5
6

如果再添加偏移量 3,

-- this offsets 3 rows _from the filtered result_,
-- not the full table

SELECT id FROM dbo.foo 
WHERE name = 'sam'
ORDER BY id
OFFSET 3 ROWS FETCH NEXT 2 ROWS ONLY;

你得到:

5
6

它获取与过滤器匹配的所有行,然后跳过那些过滤行的前三个(1,2,4 ) - 不是 1,2,3 就像你的问题暗示你期望的那样。

回到你在问题中的情况,你正在过滤掉像 77 和 89 这样的行,因为它们不包含 1 或 2。所以你要求的偏移量是 200,但是就哪些行而言这意味着,偏移量实际上更像是:

200 PLUS the number of rows that *don't* match your filter
         until you hit the 200th row that *does*

您可以尝试强制过滤发生在之后,例如:

;WITH u AS 
(
  SELECT * 
  FROM [User]
  ORDER BY [Id]
  OFFSET 200 ROWS FETCH NEXT 100 ROWS ONLY
)
SELECT * FROM u
  WHERE (([NameGiven] LIKE '%1%') 
     OR ([NameFamily] LIKE '%2%'))
ORDER BY [Id]; -- yes you still need this one

...但是您几乎肯定不会在每个页面中获得 100 行,因为这 100 行中的一些随后会被过滤器删除。我不认为这就是你想要的。