带有 OFFSET/FETCH 的 SQL 查询返回意外结果
An SQL query with OFFSET/FETCH is returning unexpected results
我有一个名为 User
的 SQL Server 2019 数据库 table,其中包含 1,000 行,如下所示:
我很难理解使用 OFFSET
/FETCH
的 SELECT
查询如何返回意外结果:
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 行中的一些随后会被过滤器删除。我不认为这就是你想要的。
我有一个名为 User
的 SQL Server 2019 数据库 table,其中包含 1,000 行,如下所示:
我很难理解使用 OFFSET
/FETCH
的 SELECT
查询如何返回意外结果:
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 行中的一些随后会被过滤器删除。我不认为这就是你想要的。