SQL 'ORDER BY' медлительность - PullRequest
       28

SQL 'ORDER BY' медлительность

11 голосов
/ 02 апреля 2009

Правда ли, что ORDER BY обычно довольно медленный? Я пытаюсь запустить некоторые операторы sql, где предложение WHERE довольно просто, но затем я пытаюсь ORDER BY в VARCHAR(50) индексированном столбце.

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

На данный момент, я ищу либо

  • оптимизировать SQL-запрос
  • отсортировать набор результатов в коде

Вот фактический запрос, который я пытаюсь выполнить:

// B.SYNTAX is a TEXT/CLOB field
// Indexes on NAME, MODULENAME. PREVIOUS is a CHAR(1) with no index
"SELECT A.NAME, B.SYNTAX, B.DESCRIPTION, A.RATE1, A.RATE2, 
A.RATE3, A.STARTDATE, A.ENDDATE, A.HIDE, A.CATEGORYNAME 
FROM A, B WHERE A.MODULENAME='"+loadedModuleName+"' 
AND A.NAME = B.NAME AND (A.PREVIOUS<>'N' OR A.PREVIOUS IS NULL) 
ORDER BY A.NAME"

Размер таблицы A составляет ~ 2000 строк, а B - ~ 500.

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

Я ожидаю, что будут возвращены сотни записей (менее 1000).

Что бы вы сделали? Любые советы приветствуются. Спасибо.

Ответы [ 14 ]

8 голосов
/ 02 апреля 2009

Порядок по индексируемому полю должен , а не быть медленным, поскольку он может извлекать данные в индексном порядке. Вы можете разместить информацию о структуре базы данных (DDL) и фактическом запросе, чтобы люди могли взглянуть.

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

UPDATE: Хорошо, несколько вещей. Во-первых, вы не должны использовать конструкцию «+loadedModuleName +», поскольку она делает каждый запрос уникальным и портит оптимизатор. Используйте параметр. Во-вторых, ваше предложение Order by неоднозначно относительно того, является ли это таблицей A или B - сделайте это явным и выберите таблицу с индексом (даже если оба имеют индексы, сделайте это явным). Наконец, ваше «Предыдущее» поле все еще может быть проиндексировано даже как символ (1). Я бы сделал все, кроме последнего предложенного индекса, скорости тестирования и, если все еще медленно, пошел бы на индекс и проверил снова.

ОБНОВЛЕНИЕ Итак, вы будете возвращать <1000 записей, но каков размер таблицы в целом? </p>

ОБНОВЛЕНИЕ О, чувак, извини, я не уловил этого раньше. Если вы хотите правильно развернуть его на SQL Server, ваш запрос должен быть:

SELECT A.NAME, B.SYNTAX, B.DESCRIPTION, A.RATE1, A.RATE2, A.RATE3, A.STARTDATE, A.ENDDATE, A.HIDE, A.CATEGORYNAME 
FROM Table1 A join Table2 B on (A.Name=B.Name)
WHERE (A.MODULENAME=@ModuleName) AND (A.PREVIOUS<>'N' OR A.PREVIOUS IS NULL) 
ORDER BY A.NAME

Попробуйте, и я почти гарантирую, что вы увидите огромное ускорение.

6 голосов
/ 02 апреля 2009

ORDER BY обычно не медленный, при условии, что база данных может найти индекс, который соответствует выражению ORDER BY.

Однако ваш оператор SQL может включать в себя другие вещи, которые вынуждают базу данных сканировать всю таблицу перед возвратом результатов, например SELECT TOP n

3 голосов
/ 02 апреля 2009

Если ваш фильтр выглядит так:

WHERE col1 = @value1
      AND col2 = @value2
      AND col3 = @value3
ORDER BY
      col4

, тогда вам нужно будет создать индекс для (col1, col2, col3, col4).

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

Если у вас нет такого индекса, произойдет одно из следующих действий:

  1. Оптимизатор будет использовать индекс для фильтрации условия WHERE, но ему все равно придется ORDER оставшиеся строки.
  2. Оптимизатор будет использовать индекс для ORDER значений, но необходимо отфильтровать ВСЕ значения, чтобы отфильтровать их.
  3. Оптимизатор не будет использовать индекс вообще, поэтому оба минуса от 2 «нужно будет отфильтровать ВСЕ значения, чтобы отфильтровать их» и 1 «все оставшиеся строки должны быть упорядочены» верны.
2 голосов
/ 02 апреля 2009

ОБНОВЛЕНИЕ : Поскольку вы отправили запрос, я думаю, что лучшим вариантом будет считать запрос хорошим, потому что:

  • Для нескольких рядов не важно, кто выполняет работу. Тогда вам будет проще использовать ORDER BY.
  • Для большого количества строк не оставляйте клиента выполнять работу: RDMBS более специализирован и уверен, что сервер имеет больше памяти и ЦП.

Советы по заказам, которые вы должны учитывать:

  • ORDER BY - это ТОЛЬКО способ гарантийной сортировки по запросу SQL.
  • best работник при сортировке - это база данных в любом случае: БУДЬТЕ УВЕРЕНЫ В ЭТОМ!
  • Попробуйте свести к минимуму количество возвращаемых строк.
  • Создание индексов по запросу. Это означает, что упорядоченные столбцы должны быть последними в индексе.
  • Избегайте индексации, если запрос быстрый.
  • Вы можете считать, что индексы отсортированы, тогда, если вы сортируете только по таблице и имеете хорошие индексы, сортировка может стоить около нуля.

Дополнительные практические советы по индексам ищите этот другой вопрос SO .

1 голос
/ 02 апреля 2009

Прошлой ночью я провел тестирование производительности в более производственной базе данных (не для разработчика), и вот что я нашел:

Всего строк в таблице A: 13000

Всего строк в таблице B: 5000

Строки, возвращаемые запросом на соединение: 5000

Время, необходимое при использовании с предложением ORDER BY: ~ 5,422 секунды

Время, которое требуется, если не использовать предложение ORDER BY: ~ 5,345 секунды.

Так что, похоже, ORDER BY не имел большого значения. (Я в порядке с добавлением нескольких миллисекунд).

Я также проверил, установив для всех значений B.SYNTAX значение NULL, чтобы убедиться, что это не просто задержка сети при передаче такого большого количества данных.

Теперь я удалил B.SYNTAX из предложения SELECT, и запрос занял всего 0,8 секунды!

Так что кажется, что весь столбец CLOB является узким местом. Это не означает, что я получил решение сделать этот запрос быстрее, но, по крайней мере, я не буду тратить время на написание алгоритма сортировки.

Спасибо всем, кто ответил. Я многому научился, и это заставило меня попробовать несколько разных вещей.

1 голос
/ 02 апреля 2009

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

Добавление ORDER BY заставит его ждать в базе данных всех результатов, что покажет реальную скорость запроса.

В этих случаях исходный запрос и запрос ORDERed имеют одинаковую скорость; Вы просто были одурачены, думая, что первый был быстрым, потому что ваш редактор быстро получил первые 50 строк.

1 голос
/ 02 апреля 2009

ORDER BY не особенно медленный, особенно если в этом столбце есть индекс. В частности, если в этом столбце есть кластерный индекс , данные уже отсортированы.

Вы также можете использовать подкачку (TOP или ROW_NUMBER) и т. Д., Чтобы помочь.

1 голос
/ 02 апреля 2009

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

Нам нужно больше информации. Что дбмс? Как выглядит план запроса? Вы смотрели на планы запросов с ORDER BY и без него? Какие различия вы видите?


EDIT:

SELECT A.NAME, B.SYNTAX, B.DESCRIPTION, A.RATE1, A.RATE2,  
A.RATE3, A.STARTDATE, A.ENDDATE, A.HIDE, A.CATEGORYNAME  
FROM A, B  
WHERE A.MODULENAME='"+loadedModuleName+"'   
  AND A.NAME = B.NAME  
  AND (A.PREVIOUS<>'N' OR A.PREVIOUS IS NULL)  
ORDER BY NAME

Является ли NAME primary key? Есть ли index на NAME? Само собой или с другими полями? В какой последовательности?
Сколько строк возвращается для одного загруженного имени_модуля?
Я подозреваю, что медлительность происходит от "A.PREVIOUS <> 'N' OR A.PREVIOUS IS NULL" Попробуйте использовать (NOT A.PREVIOUS = 'N'), который я считаю эквивалентным и может немного помочь.
Время запроса с и без ORDER BY и посмотреть, если время вообще отличается. Так не должно быть.


EDIT:

Если NAME не является уникальным в A или B, ваше объединение будет частично баллистическим, когда каждый экземпляр A.NAME станет перекрестным соединением в B.NAME. Если 50 строк соответствуют и 50 строк соответствуют, вы получите 2500 строк результатов, что может не соответствовать вашим ожиданиям.

1 голос
/ 02 апреля 2009

Это не должно быть медленным. Оптимизируйте свой запрос и структуру базы данных (по крайней мере, индексы и статистику, если это SQL Server). Может быть, в вашем запросе есть что-то еще, кроме ORDER BY, которое вызывает такую ​​медлительность?

SELECT A.NAME, B.SYNTAX, B.DESCRIPTION, A.RATE1, A.RATE2, A.RATE3,
       A.STARTDATE, A.ENDDATE, A.HIDE, A.CATEGORYNAME
FROM Table1 A JOIN Table2 B on A.Name = B.Name
WHERE A.MODULENAME = @ModuleName AND A.PREVIOUS<>'N' OR A.PREVIOUS IS NULL
ORDER BY A.NAME

Вариант 1

Если вы запрашиваете всего несколько простых столбцов (2-4), вы также можете включить их в индекс. Таким образом, ваш запрос будет выполняться быстрее. Также убедитесь, что порядок сортировки в этом столбце индекса соответствует порядку сортировки в запросе.

// if your query looks like this:
SELECT [Name], [Title], [Count] ORDER BY [COUNT]

// you can create an index on [Name], [Title], [Count]

Вариант 3

Создайте view и привяжите его к schema. Затем запросите данные из этого view.

Вариант 3

Если вы используете SQL Server 2005 и выше, вы также можете попытаться выполнить запрос в SQL Server Profiler , и он порекомендует вам лучший индекс и статистику, которые вы можете применить к вашей таблице, чтобы оптимизировать производительность этого конкретного запроса.

Вариант 4

Попробуйте перестроить ваши индексы и статистику.

Вариант 5

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

0 голосов
/ 02 апреля 2009

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

Какую базу данных вы используете? Как человек, который тратит кучу времени на MySQL, у меня бросается в глаза выражение OR. MySQL может быть действительно глупым с OR. Я видел, как быстрее сделать два выбора и объединить их вместе.

Если количество строк большое (в таблице не возвращено), это может быть фактором.

Иначе я бы согласился с другими постами. Индексы должны делать это быстро, и часто лучше, чтобы БД делала это, а не обрабатывала это самостоятельно. БД знает, что делает. Если у вас нет ДЕЙСТВИТЕЛЬНО большого набора данных и вы хотите перенести бремя сортировки на клиента (чтобы БД могла принимать больше запросов), я бы позволил БД выполнить сортировку.

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