Такой вопрос: если я делаю выборку из некоторой таблицы с сортировкой по некоторому признаку, а затем хочу получить из этой выборки записи с диапазонам номеров с N по M. Как я это могу сделать?
вот так быть должно
исходная таблица:
с1 с2
1 a
2 w
3 q
4 t
теперь сортируем по c2
1 a
3 q
4 t
2 w
а теперь надо получить записи для N = 1, а M = 2, т.е.
3 q
4 t
как это можно сделать, какие варианты есть?
/**
* у человека столько проблем, сколько он их себе создает
*/
Здравствуйте, Par-zzz, Вы писали:
PZ>Всем, доброго времени суток!
PZ>Такой вопрос: если я делаю выборку из некоторой таблицы с сортировкой по некоторому признаку, а затем хочу получить из этой выборки записи с диапазонам номеров с N по M. Как я это могу сделать?
PZ>как это можно сделать, какие варианты есть?
Здравствуйте, Par-zzz, Вы писали:
PZ>Всем, доброго времени суток!
PZ>Такой вопрос: если я делаю выборку из некоторой таблицы с сортировкой по некоторому признаку, а затем хочу получить из этой выборки записи с диапазонам номеров с N по M. Как я это могу сделать?
PZ>вот так быть должно
PZ>исходная таблица:
PZ>с1 с2 PZ>1 a PZ>2 w PZ>3 q PZ>4 t
PZ>теперь сортируем по c2
PZ>1 a PZ>3 q PZ>4 t PZ>2 w
PZ>а теперь надо получить записи для N = 1, а M = 2, т.е.
PZ>3 q PZ>4 t
PZ>как это можно сделать, какие варианты есть?
В Oracle можно использовать трюк с псевдоколонкой rownum:
select c1, c2 from (
select c1, c2, rownum rn from
(select c1, c2 from t order by c2)
where rownum <= :M + 1)
where rn >= :N + 1
C1 C2
3 q
4 t
Так как rownum базируетеся с единицы, а в вашем примере N и M — с нуля, то используются :N+1 и :M+1.
Здравствуйте, Par-zzz, Вы писали:
PZ>Всем, доброго времени суток!
PZ>Такой вопрос: если я делаю выборку из некоторой таблицы с сортировкой по некоторому признаку, а затем хочу получить из этой выборки записи с диапазонам номеров с N по M. Как я это могу сделать?
Здравствуйте, Archistratig!
Давненько я тут не появлялся, но было время когда Вы писали:
A>Сортирован — это слишком строгое требование. Вся эта схема работала бы, если бы в SQL существовала конструкция не TOP <n>, а нечто вроде FROM_ROW <m> TO_ROW <n>. Эмулировать ее пратически нереально кроме специальных случаев (вроде вставить результирующую выборку в темповую таблицу с identity и мделать еще один SELECT из нее и прочая неэффективная экзотика).
Когда-то я тоже сталкивался с такой проблеммой. Добрые люди подсказали решение.
К примеру P — номер страницы
N — количество записей на странице
тогда получается следующая картина для 5 страницы из 20 записей
select * from
(select top 20 * from
(select top 100 * from tbl order by tbl_id) as tmp1
order by tmp1.tbl_id desc) as tmp2
order by tmp2.tbl_id
На выходе получится именно то, что вы и хотели <from 80 to 100>
Оно не всегда лучшее...
Дело в том, что значение в top нельзя указать через переменную, а значит каждый раз запрос будет компилироваться заново.
Избежать этого можно переписав этот запрос примерно в таком же виде используя переменную @table и параметр @@ROWCOUNT, куда можно передавать размер в виде переменной.
Здравствуйте, Adony, Вы писали:
A>Посмотрите здесь
FAQ ясен, но не полон. Там не рассмотрен, например, еще способ выполнения этого действия при помощи курсора. Операция простая: открываем статический курсор с возможностью абсолютного позиционирования, переходим на запись N и достаем M записей. Казалось бы, это медленно, но элементарный тест показывает, что курсором доставать быстрее, чем используя временную таблицу. Впрочем, тестом я занимался недолго, мог сделать какие-то ошибки. Так что, можно подвергать мои слова сомнению и пробовать. Дело недолгое.
Здравствуйте, Андрей Майоров, Вы писали:
АМ> Казалось бы, это медленно, но элементарный тест показывает, что курсором доставать быстрее, чем используя временную таблицу. Впрочем, тестом я занимался недолго, мог сделать какие-то ошибки. Так что, можно подвергать мои слова сомнению и пробовать. Дело недолгое. АМ> Да, речь идет об MSSQL 2k
Ну, по моим данным через переменную table все-таки быстрее...
Здравствуйте, Merle, Вы писали:
M>Ну, по моим данным через переменную table все-таки быстрее...
Айда проверять.
Имеется таблица test1 с 10000 записей. Делаем две процедуры, по-разному реализующие постраничный вывод.
-- PageCursorCREATE PROCEDURE dbo.PageCursor
as begin
set nocount on
declare @cSteps cursor
set @cSteps = cursor static for
select id
from test1
order by f1, f2
open @cSteps
declare @iObject int
declare @i int
set @i = 0
fetch absolute 210
from @cSteps
into @iObject
while @i < 10 begin
select cast (210+@i as varchar(10)) as n, cast( @iObject as varchar(10)) as id
fetch next from @cSteps
into @iObject
if @@fetch_status <> 0
break
set @i = @i+1
end
close @cSteps
deallocate @cSteps
end
GO
-- PageNoCursorCREATE PROCEDURE dbo.PageNoCursor
AS begin
set nocount on
declare @tTemp table( n int identity(1, 1), id int )
insert into @tTemp( id )
select id
from test1
order by f1, f2
select *
from @tTemp
where n >= 210 and n < 220
end
GO
Имеем вот такой (донельзя элементарный) механизм замера производительности:
set nocount on
declare @d datetime
set @d = getdate()
exec pagenocursor
select getdate() - @d
set @d = getdate()
exec pagecursor
select getdate() - @d
На выходе имеем: курсор всегда примерно в полтора раза быстрее. При этом важно, какой метод идет сначала. Если сначала выполняем безкурсорный вариант, то разница скачет от 3-х раз до 1 (курсор быстрее или так же). Если сначала курсорный, то разница как раз примерно полтора раза. Что интересно, время выполнения курсорного варианта скачет в существенно меньших пределах.
Здравствуйте, Андрей Майоров, Вы писали:
АМ> Айда проверять.
Честно? лень..
АМ> На выходе имеем: курсор всегда примерно в полтора раза быстрее. При этом важно, какой метод идет сначала. Если сначала выполняем безкурсорный вариант, то разница скачет от 3-х раз до 1 (курсор быстрее или так же). Если сначала курсорный, то разница как раз примерно полтора раза. Что интересно, время выполнения курсорного варианта скачет в существенно меньших пределах.
1. Мерять лучше через
SET STATISTICS TIME
2. Есть ли индекс хотя бы по ID в основной таблице?
3. Я в свое время мерял с более сложной таблицей, больше данных и помоему таблица была не одна. Фокус в том, что в случае курсора надо сразу тянуть все данные, а потом заниматься изнурительным скроллингом по ним, а во временную таблицу можно слить нужные ID'шники, а потом сджойнить ее с основной/основными.
4. В процессе меряния лучше менять номера страниц и, редко, размер страниц. Тоесть надо написать полноценные процедуры для пэйджинга и погонять их.
Здравствуйте, Merle, Вы писали:
M>Здравствуйте, Андрей Майоров, Вы писали:
АМ>> Айда проверять. M>Честно? лень..
Да и мне тоже лень, в общем-то.
M>1. Мерять лучше через M>
M>SET STATISTICS TIME
M>
Получается не суммарное время выполнения хранпроцы, а по каждому стейтменту. Несколько неудобно, хотя, может быть, и более аккуратно.
M>2. Есть ли индекс хотя бы по ID в основной таблице?
Конечно. Это праймари key, на нем — кластеризованный индекс
M>3. Я в свое время мерял с более сложной таблицей, больше данных и помоему таблица была не одна. Фокус в том, что в случае курсора надо сразу тянуть все данные, а потом заниматься изнурительным скроллингом по ним, а во временную таблицу можно слить нужные ID'шники, а потом сджойнить ее с основной/основными.
То, что с временной таблицей код проще — это однозначно. С другой стороны, тут код однотипный, можно в шаблон загнать. А насчет того, что таблицы более сложные, так это не важно. Мы все равно в курсоре только ИДы достаем. Если фетчем весь объект получать — запаришься.
M>4. В процессе меряния лучше менять номера страниц и, редко, размер страниц. Тоесть надо написать полноценные процедуры для пэйджинга и погонять их.
Здравствуйте, Андрей Майоров, Вы писали:
АМ> А насчет того, что таблицы более сложные, так это не важно. Мы все равно в курсоре только ИДы достаем. Если фетчем весь объект получать — запаришься.
Дык, а нафиг сдались одни ID'шники клиенту? Ему все поля нужны... Тоесть хочешь-не хочешь, а курсором их тащить все придется. Вот тут похоже и случатся основные тормоза.
M>Дык, а нафиг сдались одни ID'шники клиенту? Ему все поля нужны... Тоесть хочешь-не хочешь, а курсором их тащить все придется. Вот тут похоже и случатся основные тормоза.
Предидущий оратор имел ввиду, что ид-шники записей нужной страницы складываются во временную таблицу/переменную далее на выходе ее джойним с остальными данными.
На самом деле получается достаточно оптимально и практически не зависит от номера страницы
Здравствуйте, Merle, Вы писали:
АМ>> А насчет того, что таблицы более сложные, так это не важно. Мы все равно в курсоре только ИДы достаем. Если фетчем весь объект получать — запаришься. M>Дык, а нафиг сдались одни ID'шники клиенту? Ему все поля нужны... Тоесть хочешь-не хочешь, а курсором их тащить все придется. Вот тут похоже и случатся основные тормоза.
Дело в том, что мне всегда как раз нужны были именно ИДы. Каждая такая выборка в моем случае может возвратить объекты разных типов, так что приходится сначала находить список ИДов объектов, соответствующих условиям, потом прорезать в нем "окно", а потом уже доставать данные объекта специфично каждой записи.
Так что, как водится, есть ситуации и есть ситуации. Будем знать, что в моем сценарии быстрее использовать курсор, а в некоем случае простой выборки со сложными таблицами — временную таблицу.
Может ли хотя бы один из предложенных способов использовать индекс?
Например, MySQL в случае запроса без условий WHERE, соединений и агрегатных функций, с сортировкой по индексированному набору столбцов позволяет сделать быструю постраничную выборку.