SQL Server нумерация строк и отфильтрованные результаты
У меня есть приложение, в котором мне нужно отобразить "страницы" данных в списке. Основная идея довольно распространена. На дисплее отображается список элементов, а в нижней части дисплея находятся какие-то элементы управления, которые позволяют перейти к следующей "странице" данных.
Все хорошо и хорошо. У меня это работает.
Ниже приведен SQL в представлении, которое я использую для поддержки" следующего " поведения.
CREATE VIEW CampaignParticipants AS
SELECT row_number() OVER (ORDER BY TPT.LastName, TPT.FirstName, TPT.DoB) AS RowNumber
,CGP.*
,TPT.*
FROM tblCampaignGEDPush CGP
JOIN tblParticipants TPT
ON CGP.PartID = TPT.PartID
Вот как я использую вид
SELECT *
FROM CampaignParticipants
WHERE RowNumber >= 0
AND RowNumber <= 100
Это имитирует захват "первого страница " из 100 результатов просмотра. Страницы через каждый набор результатов просто персиковые.
Отлично.. Но:
Как некоторые из вас, кто имел дело с этим, вероятно, знают, это неправильно. Если я хочу искать поTPT.LastName like 'R%'
и получить первый набор результатов, я обречен.
Я собираюсь начать смотреть на RowNumber = 0
, остановиться на RowNumber = 100
, но результаты "R", вероятно, будут далеко за пределами этого диапазона. Итог: список возвращается пустым.
И он становится более липким: пользователь хочет иметь возможность " фильтровать" на фамилию, имя, доб, местоположение, телефон, почтовый индекс, что угодно.
**edit: я действительно не могу поместить фильтр на" внутренний " запрос, так как он находится в представлении, и фильтр может меняться произвольно
У кого-нибудь есть идеи, как получить этот результирующий набор, имеющий row_number()
на отфильтрованный набор результатов?
3 ответа:
Что-то вроде этого должно быть сделано...
SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY LastName, FirstName, DoB) AS __RN FROM CampaignParticipants WHERE LastName LIKE 'R%') innerData WHERE __RN BETWEEN 1 and 100
Однако вы должны использовать имена столбцов, а не '*'. Я не знаю, как выглядят ваши столы, поэтому не могу заполнить это для вас.
Прежде всего попробуйте использовать proc вместо view, и поскольку пользователи просят вас иметь так много фильтрации, то proc-это единственное решение, которое вы получили.Inside proc фильтрует данные, используя все эти фильтры, а затем генерирует row_number и затем показывает, скажем, первые 100 записей или что-то в этом роде.
Я бы хотел, чтобы было что-то более элегантное, чем это. Это мое сохраненное решение proc. Лучшее, что могут придумать мои хромые навыки SQL.
Параметр OUT в списке параметров предназначен для общего числа строк в этом наборе, поэтому интерфейс знает, сколько страниц вместе с этим конкретным набором фильтров.
CREATE PROC [dbo].[procCampaignGEDPushSelect] @LastName VARCHAR(50) = null ,@FirstName VARCHAR(50) = null ,@Location VARCHAR(255) = null ,@DoB DateTime = null ,@Zip VARCHAR(50) = null ,@Phone VARCHAR(50) = null ,@Email VARCHAR(255) = null ,@Gender VARCHAR(20) = null ,@IsGED Bit = 0 ,@IsBTT Bit = 0 ,@IsOACE Bit = 0 ,@Completed Bit = 0 ,@TotalCount INT OUT AS BEGIN SELECT @LastName = @LastName + '%' SELECT @FirstName = @FirstName + '%' SELECT @Location = @Location + '%' SELECT @Zip = @Zip + '%' SELECT @Phone = @Phone + '%' SELECT @Email = @Email + '%' SELECT @Gender = @Gender + '%' SELECT row_number() OVER (ORDER BY LastName, FirstName, DoB) AS RowNumber , TPT.LastName , TPT.FirstName , TPT.WF1Site , TPT.DOB , TPT.Zip , TPT.Telephone , TPT.CellPhone , TPT.Email , TPT.Gender , TPT.IsBTT , TPT.IsGED , TPT.IsOACE , TPT.IsSRS ,CGP.* FROM tblCampaignGEDPush CGP JOIN tblParticipants TPT ON CGP.PartID = TPT.PartID WHERE 1=1 AND 1 = (CASE WHEN @LastName IS NOT NULL THEN (CASE WHEN TPT.LastName LIKE @LastName THEN 1 ELSE 0 END) ELSE 1 END) AND 1 = (CASE WHEN @FirstName IS NOT NULL THEN (CASE WHEN TPT.FirstName LIKE @Firstname THEN 1 ELSE 0 END) ELSE 1 END) AND 1 = (CASE WHEN @Location IS NOT NULL THEN (CASE WHEN TPT.WF1Site LIKE @Location THEN 1 ELSE 0 END) ELSE 1 END) AND 1 = (CASE WHEN @Zip IS NOT NULL THEN (CASE WHEN TPT.Zip LIKE @Zip THEN 1 ELSE 0 END) ELSE 1 END) AND ( 1 = (CASE WHEN @Phone IS NOT NULL THEN (CASE WHEN TPT.Telephone LIKE @Phone THEN 1 ELSE 0 END) ELSE 1 END) OR 1 = (CASE WHEN @Phone IS NOT NULL THEN (CASE WHEN TPT.CellPhone LIKE @Phone THEN 1 ELSE 0 END) ELSE 1 END) ) AND 1 = (CASE WHEN @Email IS NOT NULL THEN (CASE WHEN TPT.Email LIKE @Email THEN 1 ELSE 0 END) ELSE 1 END) AND 1 = (CASE WHEN @Gender IS NOT NULL THEN (CASE WHEN TPT.Gender LIKE @Gender THEN 1 ELSE 0 END) ELSE 1 END) AND 1 = (CASE WHEN @DoB IS NOT NULL THEN (CASE WHEN TPT.DoB = @DoB THEN 1 ELSE 0 END) ELSE 1 END) AND 1 = (CASE WHEN @IsGED != 0 THEN (CASE WHEN TPT.IsGED = 1 THEN 1 ELSE 0 END) ELSE 1 END) AND 1 = (CASE WHEN @IsBTT != 0 THEN (CASE WHEN TPT.IsBTT = 1 THEN 1 ELSE 0 END) ELSE 1 END) AND 1 = (CASE WHEN @IsOACE != 0 THEN (CASE WHEN TPT.IsOACE = 1 THEN 1 ELSE 0 END) ELSE 1 END) AND 1 = (CASE WHEN @Completed != 0 THEN (CASE WHEN CGP.Completed = 1 THEN 1 ELSE 0 END) ELSE 1 END) ORDER BY TPT.LastName , TPT.FirstName , TPT.DoB SELECT @TotalCount = @@ROWCOUNT END
И тогда я начал думать. Вместо того, чтобы использовать этот хитрый, подверженный ошибкам proc (который, кстати, работает довольно хорошо), так как я нахожусь в .NET, я задаюсь вопросом, есть ли хороший жесткий решение есть.
Теперь я знаю, что исходный вопрос не имеет ничего общего с .NET, поэтому я оставляю вышеприведенный proc доступным для тех, кто заинтересован в строгом решении SQL, которое, я думаю, работает довольно хорошо.Поэтому я начал копаться в интерфейсе IQueryable и нашел золото:
IQueryable<queryParticipant> qparticipant = db.queryParticipants.AsQueryable(); ... qparticipant = qparticipant.Where( ... any filter you choose ); ... return qparticipant .OrderBy( p => p.LastName ) .OrderBy( p => p.FirstName ) .OrderBy( p => p.DOB ) .Select( ... whatever you like ... ) .Skip( StartRecordNumber ) // This is the trick! Start the query here.. .Take( PageSize ) // Take only as many as you need ;
И это все. Интернет .Чистый подход-это хорошо, если доступно. Сохраненный Proc отлично подходит, когда такой API недоступен.