Этот баннер — требование Роскомнадзора для исполнения 152 ФЗ.
«На сайте осуществляется обработка файлов cookie, необходимых для работы сайта, а также для анализа использования сайта и улучшения предоставляемых сервисов с использованием метрической программы Яндекс.Метрика. Продолжая использовать сайт, вы даёте согласие с использованием данных технологий».
Политика конфиденциальности

Новые сообщения [новые:0]
Дайджест
Горячие темы
Избранное [новые:0]
Форумы
Пользователи
Статистика
Статистика нагрузки
Мод. лог
Поиск
|
|
13.12.2001, 08:39
|
|||
|---|---|---|---|
FAQ.Постраничная выборка |
|||
|
#18+
Вопрос порционной(постраничной) выборки тесно связан с вопросом нумерации строк в запросе (см. FAQ.Нумерация записей в запросе ). Другими словами, для того, чтобы выбрать N-ую порцию из результатов запроса, нужно сначала пронумеровать результаты этого запроса. Отсюда и похожие методы решения Вариант 1 «Классический». SELECT TOP 100 * FROM MyTable WHERE id NOT IN (SELECT TOP 100 id FROM MyTable ORDER BY id) ORDER BY id Главный недостаток этого метода в наличии подзапроса, который будет выполняться для каждой строки главного запроса. Ну и конечно все условия выборки "WHERE" и сортировки "ORDER" в подзапросе и основном запросе должны совпадать. Достоинство метода в его универсальности, академичности. Он не требует специфики T-SQL, этот метод можно применить практически на любом SQL-сервере. Вариант 2 «Эффективный, специфический для T-SQL». Как и в случае нумерации строк данный метод основан на использовании временной таблицы. Для удобства оформим наш запрос как хранимую процедуру, возвращающую n-ую порцию(страницу), содержащую m записей CREATE PROCEDURE dbo.get_this_page (@rec_per_page int, @page_num int) AS SELECT identity(int, 1,1) AS RowNum, MyId AS OrigId INTO #tmp FROM mytable SELECT b.* FROM #tmp AS a INNER JOIN mytable AS b on a.OrigId = b.MyId WHERE a.RowNum BETWEEN (@rec_per_page * @page_num) AND (@rec_per_page * (@page_num+1) - 1) DROP TABLE #tmp Прмечания. - предложенный вариант процедуры будет блокировать базу tempdb на все время выполнения 1-го запроса. Если время блокировки становиться неприемлимым, то необходимо разбить этот запрос таким образом CREATE TABLE #temp(RowNum int identity, OrigId int) INSERT INTO #temp(OrigId) SELECT MyId FROM mytable - Если, поле MyId было создано признаком «IDENTITY», то это поле в запросе необходимо «завернуть» в функцию «CONVERT», иначе будет сообщение об ошибке. PS 2Глеб Уфимцев Ну как на этот раз у меня получилось - лучше ? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
13.12.2001, 09:25
|
|||
|---|---|---|---|
|
|||
FAQ.Постраничная выборка |
|||
|
#18+
Предложение для Glory и Глеба Уфимцева: дабы расширить круг участвующих в обсуждении народного SQL FAQ людей, предлагаю размещать ваши предложения в рассылке. Если даёте "добро", прямо сегодня это и сделаю. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
13.12.2001, 09:26
|
|||
|---|---|---|---|
|
|||
FAQ.Постраничная выборка |
|||
|
#18+
Больше всего меня удивляет (в век интернета) отсутствие у Микрософта сдандартных средств для этих целей ! ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
13.12.2001, 10:02
|
|||
|---|---|---|---|
|
|||
FAQ.Постраничная выборка |
|||
|
#18+
2Glory > Ну как на этот раз у меня получилось - лучше ? Твои советы всегда отличные, за редчайшим исключением. > предложенный вариант процедуры будет блокировать базу tempdb на все время выполнения 1-го запроса. Такая проблемка была только на sql6.5 (видимо, из-за отсутствия строчных блокировок), и то больше шума и криков, чем это заслуживало бы, так как это превращалось в проблему достаточно редко. Проблема непомерно раздута и надумана. В пользу этого утверждения говорит то, что первая статья об этой проблеме появилась незадолго перед появлением sql7.0, т.е. после многолетней эксплуатации sql4.2/6.0/6.5, и никто этой проблемы не замечал. Поэтому переделывать select into в create&insert нужно, только если есть 100% уверенность, что затык в этом. Во всех остальных случаях можно применять смело select into без всяких оглядок. Чтобы не быть голословным, специально только что перепроверил это на sql7.0sp2. Не блокируется. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
14.12.2001, 12:07
|
|||
|---|---|---|---|
FAQ.Постраничная выборка |
|||
|
#18+
Мое почтение, Glory Сталкивались с вами на activeserverpages.ru. Небольшая поправка. Поскольку seed у identity задан 1, то: WHERE a.RowNum BETWEEN (@rec_per_page * @page_num + 1) AND (@rec_per_page * (@page_num + 1)) ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
14.12.2001, 12:32
|
|||
|---|---|---|---|
FAQ.Постраничная выборка |
|||
|
#18+
Как же - помню, помню . И спасибо за поправочку И сам себя поправлю Фраза "Главный недостаток этого метода в наличии подзапроса, который будет выполняться для каждой строки главного запроса." неправильна. Подзапрос конечно будет выполняться один раз. Недостаток в том, что т.к. TOP n записей выбираются уже из конечного результата запроса , то проверка условия WHERE будет выполняться для каждой строки главного запроса. При этом, время выполнения этой проверки будет расти вместе с номером выбираемой порции(страницы). Если, например, таблица содержит 100 записей и необходимо выбирать данные порциями по 10 записей, то для 2-ой порции нужно будет будет проверять подзапрос из 10 записей для 3-ей порции нужно будет будет проверять подзапрос из 20 записей для 4-ой порции нужно будет будет проверять подзапрос из 30 записей и т.д. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
14.12.2001, 17:08
|
|||
|---|---|---|---|
FAQ.Постраничная выборка |
|||
|
#18+
Я обычно использую следующий вид запроса для выдачи записей из середины таблицы. Например записи 401-500 SELECT [col1], ..., [coln] FROM ( SELECT TOP 100 [col1], ..., [coln] FROM ( SELECT TOP 500 [col1], ..., [coln] FROM [table] ORDER BY [key1] ASC, ..., [keyn] ASC ) x ORDER BY [key1] DESC, ..., [keyn] DESC ) x ORDER BY [key1] ASC, ..., [keyn] ASC То есть вначале берутся все 500 записей, затем выборка как бы ставится с ног на голову, а потом с перевернутой выборки берется 100 записей сверху, третий шаг - пересортировка записей в первоначальном порядке. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
13.04.2002, 10:25
|
|||
|---|---|---|---|
|
|||
FAQ.Постраничная выборка |
|||
|
#18+
Время идёт, поправок и дополнений к этому FAQ нет... Может быть пора опубликовать в рассылке и на сайте? Я готов сделать редакторскую работу и после получения согласия Glory опубликовать в виде статьи... ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
13.04.2002, 20:12
|
|||
|---|---|---|---|
|
|||
FAQ.Постраничная выборка |
|||
|
#18+
Как-то однажды с утра я зашел в книжный магазин. Смотрю на полке стоит двадцаток книг – выстроены по алфавиту. Решил их купить. Но портфель маленький – влезает только 10 штучек. Думаю, куплю десяток, а потом еще зайду. Продавец со склада вынес 10 книг точно таких как на полке, и я ушел. Захожу после обеда еще за 10-ью. Говорю продавцу – мне с 11 по 20-ую. Продавец снова вынес мне книги со склада и я ушел. Прихожу домой смотрю принес три книги таких же как и с утра. А на полке то были все разные… Иду ругаться в магазин. Оказывается напрасно - в обед завезли три книжки на букву «А». Наученный горьким опытом, в следующий раз я решил поступить по-другому. Зашел в магазин и говорю - отложите мне все 20-ть книг – 10 я сейчас заберу, а 10 потом. Так и сделал. Но когда я нес домой второй десяток, сосед меня спросил – ты зачем купил старое издание, там в магазин в обеденный перерыв завезли новое - исправленное и дополненное. И решил я на счет книги больше не покупать… Мораль. Кто-то с мудрецов сказал: «Нельзя войти дважды в одну и ту же реку.». Тоже касается оператора Select. Дважды выполнив один и тот же оператор – можно получить разные результаты. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
13.04.2002, 21:16
|
|||
|---|---|---|---|
FAQ.Постраничная выборка |
|||
|
#18+
Ну хоть кто-нибудь, напишите, зачем это надо? Порядковые номера, выборка с 2000 по 3000, первые 20 по алфавиту? Извините я написал кучу прог и баз, но мне НИ РАЗУ не понадобилось вся эта тряхомудия. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
14.04.2002, 07:46
|
|||
|---|---|---|---|
FAQ.Постраничная выборка |
|||
|
#18+
2 CAT2 А ты не обращал внимание на циферки "1 2 3 4 5 6 7 8 9 10 .." внизу страницы этом форуме? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
14.04.2002, 12:13
|
|||
|---|---|---|---|
FAQ.Постраничная выборка |
|||
|
#18+
В данном случае выборку я бы курсором сделал, а циферок навыводил бы for i=1 to ЧИСЛО_ЗАПИСЕЙ/ЗАПИСЕЙ_НА_СТРАНИЦЕ и т.д. Все равно на задние страницы редко кто заходит. Еще варианты? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
14.04.2002, 17:41
|
|||
|---|---|---|---|
FAQ.Постраничная выборка |
|||
|
#18+
Курсор - дело конечно самое простое, но и самое ресурсоемкое. Поэтому к нему прибегают только в самых крайних случаях, когда нельзя ничего сделать при помощи set-based операций. Хотя наименее опытные програмисты на первых порах все делают при его помощи. Насчет того, что на задние страницы никому не нужны - глубокое заблуждение. Если проектируешь серьезную систему, не стоит ее проектировать исходя из того, что что-то не будет использоваться. Клиент может как раз больше всего хотеть видеть именно заднюю страницу. А насчет иных вариантов - так вот вначале они были и перечислены. Слон ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
15.04.2002, 06:59
|
|||
|---|---|---|---|
FAQ.Постраничная выборка |
|||
|
#18+
2Александр Гладченко Да, конечно, если никто больше ничего не хочет добаваить, то пожалуйста. Кстати была еще одна тема с именем FAQ - Про динамический запрос ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
15.04.2002, 07:49
|
|||
|---|---|---|---|
FAQ.Постраничная выборка |
|||
|
#18+
Я могу добавить. Для очень частного случая, когда существует кластерный индекс и требуется отобрать записи именно в порядке возрастания значений этого кластерного индекса можно использовать следующую команду: SELECT * FROM MyTable WHERE IDENTITYCOL between 101 and 200 Но, повторюсь. В данном случае, под "порядковым" номером строки будет пониматься именно положение строки в соответсвии с кластерным индексом. Никакие ORDER BY на порядок нумерации не повлияют. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
15.04.2002, 08:16
|
|||
|---|---|---|---|
FAQ.Постраничная выборка |
|||
|
#18+
2ВладимирМ На самом деле может оказаться, что из-за "дырок" в значениях IDENTITYCOL запрос вернет записей меньше заданной порции (или вернет вообще пустой набор) ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
15.04.2002, 10:12
|
|||
|---|---|---|---|
FAQ.Постраничная выборка |
|||
|
#18+
Да, поторопился Очень уж частный случай у меня получился. Извиняюсь. Правда у меня есть замечание по 2 способу. Я не вижу смысла делать временную таблицу #tmp по ВСЕЙ таблице MyTable. Ведь нужно всего-лишь отсечь первые N записей. Почему бы не записать во временную таблицу TOP N, а при выполнении запроса из осносновной таблицы не сделать тот же TOP 100 ... NOT IN (SELECT ... FROM #tmp) Т.е. фактически повторить первый вариант, но разбив его на 2 независимых запроса. Ну, или в крайнем случае во временную таблицу записать TOP N+100, а далее уже как у Вас ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
15.04.2002, 11:04
|
|||
|---|---|---|---|
|
|||
FAQ.Постраничная выборка |
|||
|
#18+
=================================================== Это из MSDN Замените тип рекордсета на серверный и юзайте на здоровье =================================================== AbsolutePage, PageCount, and PageSize Properties Example (VB) [This is preliminary documentation and subject to change.] 'BeginAbsolutePageVB 'To integrate this code 'replace the data source and initial catalog values 'in the connection string Public Sub AbsolutePageX() 'recordset and connection variables Dim rstEmployees As ADODB.Recordset Dim Cnxn As ADODB.Connection Dim strCnxn As String Dim strSQL As String 'record variables Dim strMessage As String Dim intPage As Integer Dim intPageCount As Integer Dim intRecord As Integer 'Open connection Set Cnxn = New ADODB.Connection strCnxn = "Provider=sqloledb;Data Source=MyServer;Initial Catalog=Pubs;User Id=sa;Password=;" Cnxn.Open strCnxn ' Open employee recordset ' Use client cursor to enable AbsolutePosition property Set rstEmployees = New ADODB.Recordset strSQL = "employee" rstEmployees.Open strSQL, strCnxn, adUseClient, adLockReadOnly, adCmdTable ' Display names and hire dates, five records at a time rstEmployees.PageSize = 5 intPageCount = rstEmployees.PageCount For intPage = 1 To intPageCount rstEmployees.AbsolutePage = intPage strMessage = "" For intRecord = 1 To rstEmployees.PageSize strMessage = strMessage & _ rstEmployees!fname & " " & _ rstEmployees!lname & " " & _ rstEmployees!hire_date & vbCr rstEmployees.MoveNext If rstEmployees.EOF Then Exit For Next intRecord MsgBox strMessage Next intPage ' clean up rstEmployees.Close Cnxn.Close Set rstEmployees = Nothing Set Cnxn = Nothing End Sub 'EndAbsolutePageVB See Also AbsolutePage Property | PageCount Property | PageSize Property | Recordset Object ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
15.04.2002, 16:21
|
|||
|---|---|---|---|
FAQ.Постраничная выборка |
|||
|
#18+
a) Про серьезную систему. Вот именно, если проектировать серьезную систему, то надо сто раз подумать, а почему пользователь должен жать клавиши "Выбрать 100001 страницу","Выбрать 100002 страницу". Если он вынужден это делать, значит программа слепана кое-как. Возьмем пример форума. Меня соверщенно не устраивают эти цифирки внизу. Я бы сделал такие выборки: 1. По дате последнего ответа за период 2. По дате вопроса за период 3. По поиску в заголовке (с шаблонами) 4. По поиску в тексте (с шаблонами) 5. По отвечающему, с выбором имени из списка 6. По вопрошающему, с выбором имени из списка А потом бы скомбинировал все это в одном окошечке. b) Про курсоры. "Бить, бить и бит!", использующих их, как писал И.В.Сталин. Однако настоящий мастер должен уметь использовать всю палитру инструментов. Только надо их применять там, где они уместны. Я думаю, что выборка курсором первых десяти строк сожрет ресурсов не больше, чем создание временной таблицы с identity. Сколько времени будет создавться временная таблица на базе с миллионом записей? Во временную таблицу тянется вся база, а курсор прекрашает работу, отобрав нужно количество записей. c)Про задние страницы форума. 100% приходит на первую страницу. Некоторые, задавшие вопрос, на 2-3 страницы назад. Все просматривают только наиболее упертые и совестливые, которым неудобно беспокоить людей вопросами, на которые уже отвечали ранее. Нужно всегда учитывать конкретные условия работы. Телефонный справочник отличается от системы диагностики оборудования. d) Про варианты "Классический". На самом деле это "T-SQL для вер. 7 и более". Оборот "TOP n" не входит в стандарт ANSI SQL. Наверное, все верят в свою счастливую звезду, и в то, что им никогда не придется переносить базы с MS SQL на что-нибудь другое. Если использовать только стандартные средства, то эта процедура проидет более менее безболезненно. Надеймся на лучшее, готовся к худшему. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|
19.04.2002, 09:07
|
|||
|---|---|---|---|
FAQ.Постраничная выборка |
|||
|
#18+
Упорядоченная выборка 3000-4500 email-ов. На больших объемах и последних страницах летает так же, как и на первых. Если хочется совсем оптимизировать для последних страниц, можете развернуть выборку с конца. Давно уже проверено. select email from (select top 1500 email from (select top 4500 email from spamtmp order by email)a order by email desc) b order by email ... |
|||
|
:
Нравится:
Не нравится:
|
|||
|
|
|

start [/forum/search_topic.php?author=Tyrax&author_mode=last_posts&do_search=1]: |
0ms |
get settings: |
10ms |
get forum list: |
24ms |
get settings: |
10ms |
get forum list: |
19ms |
get settings: |
10ms |
get forum list: |
20ms |
check forum access: |
3ms |
check topic access: |
3ms |
track hit: |
143ms |
get topic data: |
11ms |
get forum data: |
3ms |
get page messages: |
64ms |
get tp. blocked users: |
1ms |
| others: | 1116ms |
| total: | 1437ms |

| 0 / 0 |
