Oracle limit не работает

Обновлено: 30.06.2024

Неправильно отрабатывает автоматически сгенерированный NHibernate запрос.

возвращает строки:
0 1
0 2
0 3

а должен возвращать то же что и этот запрос:

при этом если убрать rownum ответ правильный
если заменить left outer join на join - ответ правильный (но суть в том что запрос генерируется автоматически)
если убрать order - работает правильно


и на последок, запросы:

возвращают одно и тоже.
1 1
1 2
1 3

0_о
Как такое возможно?

Oracle Database 11g Enterprise Edition Release 11.1.0.7.0 - 64bit Production
PL/SQL Release 11.1.0.7.0 - Production
CORE 11.1.0.7.0 Production
TNS for 64-bit Windows: Version 11.1.0.7.0 - Production
NLSRTL Version 11.1.0.7.0 - Production

Delphi неправильно выполняет условия
Делаю простейшее условие: if nkv_0=0 then begin if n_0>0 then updater; end else updater;.

Программа неправильно выполняет последнюю операцию деления
Почему программа не правильно выполняет последнюю операцию деления a4 / (-a3)? Нужна помощь!

Даже не знаю что сказать.

Во-первых, зачем эти все охватывающие запросы (кроме случая с rownum)?
Во-вторых, если достаточно inner join, зачем outer join? Поскольку в общем случае outer join работает медленее.
В-третьих, если можно без order by, то лучше обойтись без него (тоже добавляет работы).
Сказать, как должно быть правильно, не видя данных нереально.
Ссылки на автоматическую генерацию запросов не убедительны - никто не сказал, что генерируются оптимальные запросы.
Наконец никто не мешает сравнить планы запросов - может действительно оптимизатор Oracle где-то глючит, хотя и вряд ли.

PS
К слову сказать, ANSI-синтаксис join-ов появился только в Ora 9, содержал массу глюков, да и сейчас не свободен от них. Поэтому если есть сомнения в правильном выполнении запроса, до сих пор рекомендуют переписать запрос на "родной" синтаксис Oracle.

Добавлено через 5 минут
А люди, давно работающие с Oracle, практически этот самый ANSI-синтаксис не используют (большинство во всяком случае)

И если вам нужно сделать это навалом для каждого идентификатора (SQLite 3.25.0):

Введение

Ниже я представляю два решения, чтобы решить проблему и выполнить правильный UPDATE . В конце каждого решения приведен живой пример с образцами данных.

Первый не требует от вас ввода id и работает для всей таблицы, выбирая последний orderid для каждого id и изменяя его flag в 1

Второй требует от вас ввода id и работает только для обновления одного id на ходу

Я бы лично выбрал первое решение, но я не уверен в вашем требовании, поэтому опубликовал две возможности.

Первое решение - обновить всю таблицу

Объяснение здесь, для прокрутки кода вниз.

Для этого мы будем использовать конструкцию Значение строки (id, orderid) , как и для второго решения. Он найдет самую последнюю строку на основе orderid и обновит только эту строку для данной пары (id, orderid) . Подробнее об этом говорится в объяснении второго решения.

Нам также нужно будет смоделировать функцию row_number для назначения номеров ранжирования каждой строки, чтобы выяснить, какая строка имеет самый последний orderid для каждого id , и пометить его как 1 для в состоянии вытащить только те, для обновления. Это позволит нам обновить несколько строк для разных идентификаторов в одном операторе. SQLite будет иметь эту функцию, встроенную в версию 3.2.5, но сейчас мы будем работать с подзапросом.

Для генерации номеров строк мы будем использовать это:

Тогда нам просто нужно отфильтровать вывод в rn = 1 , и у нас есть то, что нам нужно.

Тем не менее, весь UPDATE оператор будет выглядеть так:

Код

Live DEMO

Вот db fiddle , чтобы увидеть это решение вживую в образец данных.

Второе решение - обновить только один идентификатор

Если вы знаете, что ваш ID должен быть обновлен, и хотите выполнить оператор UPDATE только для одного идентификатора, тогда это будет работать:

Код

Объяснение

(id, orderid) - это конструкция, которая называется Значение строки , для которой SQLite сравнивает скалярные значения слева направо.

Пример взят из документации:

Live DEMO

Вот db fiddle , чтобы увидеть это решение вживую образец данных.

Есть ли способ заставить Oracle запрос вести себя так, как будто он содержит MySQL limit предложение?

В MySQL , я могу сделать это:

чтобы получить 21-й по 30-й ряды (пропустите первые 20, дайте следующие 10). Строки выбираются после order by , так что это действительно начинается с 20-го имени в алфавитном порядке.

В Oracle , единственное , что люди уже является rownum псевдо-столбец, но он оценивается до order by того , что означает следующее:

вернет случайный набор из десяти строк, упорядоченных по имени, что обычно не то, что я хочу. Это также не позволяет указывать смещение.

@YaroslavShabalin В частности, выгружаемый поиск использует этот паттерн все время. Практически любое приложение с любой функцией поиска будет использовать его. Другим вариантом использования будет загрузка только части длинного списка или клиентской части таблицы и предоставление пользователю возможности расширения. @YaroslavShabalin Вы не можете получить другой набор результатов, если базовые данные не изменятся из-за ORDER BY . Вот и весь смысл заказа в первую очередь. Если базовые данные изменяются, и ваш набор результатов изменяется из-за этого, то почему бы не показать пользователю обновленные результаты вместо устаревшей информации? Кроме того, государственное управление - это чума, которую следует избегать, насколько это возможно. Это постоянный источник осложнений и ошибок; вот почему функционал становится таким популярным. И когда бы вы знали, чтобы истечь весь набор результатов в памяти? В Интернете у вас нет возможности узнать, когда пользователь уходит.

Начиная с Oracle 12c R1 (12.1), то есть строка ограничение пункт . Он не использует знакомый LIMIT синтаксис, но он может сделать работу лучше с большим количеством опций. Вы можете найти полный синтаксис здесь . (Также читайте больше о том, как это работает внутри Oracle в этом ответе ).

Чтобы ответить на оригинальный вопрос, вот запрос:

(Для более ранних версий Oracle, пожалуйста, обратитесь к другим ответам в этом вопросе)

Примеры:

Следующие примеры были процитированы со ссылочной страницы в надежде предотвратить гниение ссылок.

Настроить

Что в таблице?

Получить первые N строки

Получить первые N строки, если N й строки имеет связи, получить все связанные строки

Верх x % строк

Использование смещения, очень полезно для нумерации страниц

Вы можете комбинировать смещение с процентами

Просто для расширения: OFFSET FETCH синтаксис является синтаксическим сахаром. Подробности

Вы можете использовать подзапрос для этого как

Посмотрите также тему О ROWNUM и ограничении результатов в Oracle / AskTom для получения дополнительной информации.

Обновление : чтобы ограничить результат нижними и верхними границами, все становится немного более раздутым

(Скопировано из указанной AskTom-статьи)

Обновление 2 : Начиная с Oracle 12c (12.1), доступен синтаксис, ограничивающий строки или начинающийся со смещений.

Смотрите этот ответ для большего количества примеров. Спасибо Крумии за подсказку.

Это, безусловно, способ сделать это, но имейте в виду (как говорится в статье о спросе), производительность запросов снижается по мере увеличения вашего максимального значения. Это хорошее решение для результатов запросов, когда вы хотите видеть только первые несколько страниц, но если вы используете это в качестве механизма для кодирования страниц по всей таблице, вам было бы лучше выполнить рефакторинг кода +1 Ваша нижняя / верхняя версия фактически помогла мне обойти проблему, когда простое ограниченное сверху предложение rownum резко замедлило мой запрос. Ли Рифель "аналитическое решение только с одним вложенным запросом" является тем. В статье AskTom также есть подсказка оптимизатора, в которой используется SELECT / * + FIRST_ROWS (n) / a. , rownum rnum Перед косой чертой должна стоять звездочка. ТАК чистит это. Обратите внимание, что для Oracle 11 внешний SELECT с ROWNUM не позволит вам вызвать deleteRow для UpdatableResultSet (с ORA-01446) - ожидая этого изменения 12c R1!

Я провел тестирование производительности для следующих подходов:

Asktom

аналитический

Короткая альтернатива

В таблице было 10 миллионов записей, сортировка осуществлялась по неиндексированной строке даты и времени:

  • План объяснения показал одинаковое значение для всех трех вариантов (323168)
  • Но победителем является AskTom (с аналитическим следом за ним)

Выбор первых 10 строк занял:

Выбор строк от 100 000 до 100 010:

  • AskTom: 60 секунд
  • Аналитический: 100 секунд

Выбор строк между 9 000 000 и 9 000 010:

  • AskTom: 130 секунд
  • Аналитический: 150 секунд
Хорошо сделано. Вы пробовали короткую альтернативу с промежуточным вместо> = и <=? zeldi - На какой версии это было? Oracle внесла аналитические улучшения производительности в 11.1. и 11.2. @Leigh Riffel Это было 10.2.0.5; Однажды я мог бы потратить время, а также проверить версию 11i. Я провел несколько быстрых тестов и получил аналогичные результаты для 12c. Новый offset синтаксис имеет тот же план и производительность, что и аналитический подход.

Аналитическое решение только с одним вложенным запросом:

Rank() может быть заменено, Row_Number() но может вернуть больше записей, чем вы ожидаете, если для имени есть повторяющиеся значения.

Я люблю аналитику. Возможно, вы захотите уточнить, в чем разница в поведении между Rank () и Row_Number (). Действительно, не уверен, почему я не думал о дубликатах. Таким образом, в этом случае, если есть повторяющиеся значения для имени, тогда RANK может дать больше записей, чем вы ожидаете, поэтому вы должны использовать Row_Number. При упоминании rank() этого также стоит отметить, dense_rank() что может быть более полезным для управления выводом, так как последний не «пропускает» числа, тогда как rank() может. В любом случае для этого вопроса row_number() лучше всего подходит. Еще один не является этот метод применим к любой БД, которая поддерживает упомянутые функции.

В Oracle 12c (см. Предложение по ограничению строк в справочнике по SQL ):

И, конечно же, им пришлось использовать совершенно другой синтаксис, чем все остальные Очевидно, после того, как LIMIT они сошлись со всеми другими поставщиками, чтобы договориться о SQL: 2008, им пришлось взять листок из книги Microsoft и нарушить стандарт. Интересно, что недавно я слышал, что самый последний стандарт включает этот синтаксис, поэтому, возможно, Oracle перед этим внедрил его. Возможно, это более гибкий, чем LIMIT . OFFSET @Derek: Да, несоблюдение стандарта вызывает сожаление. Но недавно представленная функциональность в 12cR1 более мощная, чем просто LIMIT n, m (см. Мой ответ). Опять же, Oracle должен был быть реализован LIMIT n, m как синтаксический сахар, как это эквивалентно OFFSET n ROWS FETCH NEXT m ROWS ONLY .

Запросы на нумерацию страниц с упорядочением действительно сложны в Oracle.

Oracle предоставляет псевдостолбец ROWNUM, который возвращает число, указывающее порядок, в котором база данных выбирает строку из таблицы или набора объединенных представлений.

ROWNUM - это псевдоколонка, которая доставляет многим людям неприятности. Значение ROWNUM не всегда назначается строке (это распространенное недоразумение). Это может сбивать с толку, когда значение ROWNUM фактически назначается. Значение ROWNUM присваивается строке после прохождения предикатов фильтра запроса, но до агрегации или сортировки запроса .

Более того, значение ROWNUM увеличивается только после его назначения.

Вот почему следующий запрос не возвращает строк:

Первая строка результата запроса не передает предикат ROWNUM> 1, поэтому ROWNUM не увеличивается до 2. По этой причине никакое значение ROWNUM не будет больше 1, следовательно, запрос не возвращает строк.

Правильно определенный запрос должен выглядеть так:

Узнайте больше о запросах на нумерацию страниц в моих статьях в блоге Vertabelo :

Я хочу использовать синтаксис Oracle, чтобы выбрать только 1 строку из таблицы DUAL . Например, я хочу выполнить такой запрос:

. и было бы около 40 записей. Но мне нужна только одна запись. . И я хочу, чтобы это произошло без WHERE оговорок.

Мне нужно что-то в поле table_name, например:

Какая версия Oracle? Использование ROWNUM или ROW_NUMBER (9i +) означало бы необходимость предложения WHERE Вы действительно пробовали бежать select user from dual ? Если нет, попробуйте это и посмотрите, что у вас получится. В стандартной системе Oracle вы вернетесь пользователя, с которым выполняете команду.

Вы используете ROWNUM.

@ypercube, насколько я могу судить, это так. (По крайней мере, это работает для моей установки oracle10g.) @bdares: будет работать, да. Но не ваш ответ с расширением order by . Да. ROWNUM - это специальный столбец, который добавляется к набору результатов и перечисляет результаты. Вы также можете использовать его, чтобы выбрать несколько, например, если вы хотите найти 10 самых высокооплачиваемых сотрудников, вы можете сказать: «ВЫБРАТЬ пользователя ИЗ СОТРУДНИКОВ, ГДЕ ROWNUM <= 10 ЗАКАЗАТЬ ПО УБЫТКУ ЗАПЛАТЫ» Вам понадобится: SELECT * FROM (SELECT user FROM Employees ORDER BY SALARY DESC) WHERE ROWNUM <= 10

Я нашел это "решение" спрятанным в одном из комментариев. Поскольку я некоторое время искал это, я хотел бы немного выделить его (пока не могу комментировать или делать такие вещи . ), поэтому я использовал следующее:

Это напечатает мне желаемую запись [Столбец] из самой новой записи в таблице, предполагая, что [Дата] всегда вставляется через SYSDATE.

Я обнаружил, что это также будет работать, если вы сделаете заказ ROWID , если вы никогда не удаляете какие-либо записи и всегда заботитесь о последней вставленной / измененной. @vapcguy: Не ждите, что ROWID будет заказан, даже если вы никогда не удаляете строку из таблицы! Даже если это сработает для вас сейчас, никогда не будет гарантировано работать в будущих версиях. @ D.Mika На самом деле, если он работает сейчас, и вы никогда не добавляете / не удаляете / не обновляете / не удаляете записи, проблем быть не должно. Записи могут быть изменены только в том случае, если вы действительно их измените. Существует это заблуждение, которое каким-то образом ROWID случайно модифицируется Oracle. Это не так. Он основан на фактическом изменении строк, то есть вы удаляете одну, а затем вставляете ее. Вставленный получит старый ROWID . Есть такие вещи, как статические таблицы, которые никогда не обновляются, как в США, что является хорошим примером. Если бы они изменились, это, вероятно, имело бы другие последствия, в любом случае, когда это нормально. @vapcguy: Ну, почти верно. Но есть и другие операции, которые изменят ROWID. Что делать, если вы по какой-то причине экспортируете / импортируете таблицу? Есть и другие операции, но для некоторых из них требуется РАЗРЕШЕНИЕ СТРОКИ. Я просто хочу сказать, что не стоит полагаться на детали реализации, которые могут измениться в будущем. @ D.Mika Я уверен, что если есть какие-либо операции, в которых ROWID можно изменить, хороший администратор баз данных найдет их и сделает все возможное, чтобы избежать их, если бы существовала вероятность, что они влияют на такую ​​статическую таблицу, как я описал только приложение должно работать. SELECT Вместо этого можно выполнить экспорт таблицы с помощью оператора. Импорт произойдет один раз, а потом никогда больше. Я понимаю, что забота определенно нужна, но проблемы далеко не неизбежны.

Этот синтаксис доступен в Oracle 12c:

^^ Я просто хотел продемонстрировать, что можно использовать строку или строки (во множественном числе) независимо от множества желаемого количества строк.)

введите описание изображения здесь

Согласно Logon_Time, почему все еще живы сеансы с 31/07/2012 или 01/08/2012 или сессия до сегодняшнего дня (21/08/2012)?

Я настроил на sqlnet.ora: SQLNET.EXPIRE_TIME = 20 Таким образом, это означает, что каждые 20 минут Oracle проверяет, все еще активны ли соединения.

Все пользовательские схемы имеют профиль по умолчанию. Это значит, что ни одна сессия никогда не истечет или не умрет?

введите описание изображения здесь

Добавлено в ответ на комментарий Фила:

введите описание изображения здесь

Добавлено в ответ Фила ответ:

введите описание изображения здесь

Что v$session.PREV_EXEC_START для этих сессий? Я подозреваю, что они являются частью пула соединений и поэтому не бездействуют, потому что их часто используют. У меня Oracle 10g, поэтому в v $ session нет столбца PREV_EXEC_START. Но у меня снова запрос, включая столбец Last_Call_ET (я добавил эту картинку в свой вопрос). Нет, INACTIVE просто означает, что в момент проверки вы не выполняете оператор SQL v$session . Если они являются частью пула соединений, они выполняют свою работу должным образом - суть пула соединений состоит в том, чтобы устранить необходимость в большом количестве входов / выходов из системы и поддерживать постоянные сеансы для быстрого запуска (гораздо большие накладные расходы при повторной регистрации снова и снова) просто выполнить один запрос). Я не понимаю, почему вы беспокоитесь об этом.

Я подозреваю, что они являются частью пула соединений и поэтому не бездействуют, потому что их часто используют.

INACTIVE в v$session просто означает , что не является SQL оператор выполняется в тот момент , вы проверить v$session .

Если они являются частью пула соединений, они выполняют свою работу должным образом, выполняя вход в систему в течение длительных периодов времени. Весь смысл пула подключений состоит в том, чтобы устранить необходимость в большом количестве входов / выходов из системы и поддерживать постоянные сеансы для быстрого запуска запроса - существует гораздо большая нагрузка при входе в систему для выполнения одного запроса, а затем при каждом отключении.

Чтобы получить время последней активности для каждого сеанса:

Я бы посоветовал не убивать сеансы, если вы не знаете, что это не вызовет проблем на стороне приложения (например, попытка использовать сеанс, который был убит).

Это может быть случай, когда вы смотрите на неправильно настроенный пул соединений, который создает сотни соединений после запуска приложения - пул соединений может быть на порядок больше, чем нужно. Я предлагаю обратиться к разработчикам / сотрудникам службы поддержки приложений и посмотреть, как настроен пул соединений.

Читайте также: