Разница между TOP X и Row_Number () - PullRequest
1 голос
/ 27 августа 2011

Недавно я столкнулся со странной проблемой.У меня есть два простых запроса, где один из них использует TOP X, а другой делает то же самое, используя ROW_NUMBER, а затем выбирает элементы с rowNumber от 1 до X, и оба они упорядочены по одному столбцу, но результат совершенно другой.

Например, скажем, у нас есть простая БД, как показано ниже, с некоторыми фиктивными данными:

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 ]

4 голосов
/ 27 августа 2011

Вам необходимо реализовать вторичную сортировку.

Пример:

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 голос
/ 27 августа 2011

Я согласен с @ 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...