TOP X和Row_Number()之间的区别

时间:2011-08-27 06:28:07

标签: sql linq-to-sql

最近我遇到了一些奇怪的问题。我有两个简单的查询,其中一个使用TOP X,另一个使用ROW_NUMBER,然后选择rowNumber在1和X之间的项目,并且它们都按相同的列排序,但结果完全不同。

例如,假设我们有一个简单的DB,如下所示,带有一些虚拟数据:

CREATE TABLE [dbo].[Test](
[Id] [int] IDENTITY(1,1) NOT NULL,
[NDate] [datetime] NOT NULL,
CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED 
(
[Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
SET IDENTITY_INSERT [dbo].[Test] ON
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (1, '2011-08-24 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (2, '2011-08-24 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (3, '2011-08-24 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (4, '2011-08-24 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (5, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (6, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (7, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (8, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (9, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (10, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (11, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (12, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (13, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (14, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (15, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (16, '2011-08-21 00:00:00.000')
SET IDENTITY_INSERT [dbo].[Test] OFF

现在,如果我们执行以下查询,我们将得到不同的结果。如果我们使用TOP 10,我们将得到以下结果:

SELECT TOP 10 [Id],[NDate] FROM [Test] order by NDate desc
RESULT=>
Id  NDate
4   2011-08-24 00:00:00.000
3   2011-08-24 00:00:00.000
2   2011-08-24 00:00:00.000
1   2011-08-24 00:00:00.000
11  2011-08-21 00:00:00.000
10  2011-08-21 00:00:00.000
9   2011-08-21 00:00:00.000
8   2011-08-21 00:00:00.000
7   2011-08-21 00:00:00.000
6   2011-08-21 00:00:00.000


select id,NDate from (
  select ROW_NUMBER() over (order by NDate DESC) as RNumber, Id,NDate
  from Test) as t
where RNumber between 1 and 10
RESULT=>
id  NDate
1   2011-08-24 00:00:00.000
2   2011-08-24 00:00:00.000
3   2011-08-24 00:00:00.000
4   2011-08-24 00:00:00.000
5   2011-08-21 00:00:00.000
6   2011-08-21 00:00:00.000
7   2011-08-21 00:00:00.000
8   2011-08-21 00:00:00.000
9   2011-08-21 00:00:00.000
10  2011-08-21 00:00:00.000

问题是,当您使用LINQ to SQL,并且想要进行分页时,用于选择第一页的生成查询将是TOP X,而对于其他页面将使用ROW_NUMBER,结果是,某些项目将永远不会出现在列表中。

2 个答案:

答案 0 :(得分:4)

您需要实施二级排序。

示例:

select id,NDate from (
  select ROW_NUMBER() over (order by NDate DESC, id) as RNumber, Id,NDate -- Note: NDate DESC, id
  from Test) as t
where RNumber between 1 and 10

示例:

SELECT TOP 10 [Id],[NDate] FROM [Test] order by NDate desc, ID   -- Note: NDate DESC, id

否则,您可能会期望每个唯一NDate的随机记录。

如果您希望每个查询都有相同的记录,则必须指定该二级排序列。

答案 1 :(得分:1)

我同意@ hamlin11,但更简单地说: - )

在第一个示例中,您按

排序
order by NDate desc
/* to get same results as example 2, change this to
   order by NDate desc, id 
*/

和第二个

order by NDate DESC, id

在第二个示例中,您按ID排序,这就是ID列按顺序排列的原因,您在第一个示例中没有这样做。

使用这样的数据可能会更好,这样您就可以更清楚地看到发生了什么:

/****** Object:  Table [dbo].[Test]    Script Date: 08/27/2011 07:56:29 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Test](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [NDate] [datetime] NOT NULL,
 CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[Test] ON
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (1, '2011-08-10 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (2, '2011-08-11 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (3, '2011-08-12 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (4, '2011-08-13 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (5, '2011-08-14 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (6, '2011-08-15 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (7, '2011-08-16 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (8, '2011-08-31 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (9, '2011-08-30 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (10, '2011-08-29 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (11, '2011-08-28 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (12, '2011-08-27 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (13, '2011-08-26 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (14, '2011-08-25 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (15, '2011-08-24 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (16, '2011-08-23 00:00:00.000')
SET IDENTITY_INSERT [dbo].[Test] OFF