Как написать оптимальные SQL-запросы

Я искал вокруг stackoverflow, но каждый просит оптимизировать запросы, которые они уже сделали.

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

Например, Известный факт, что Writing SELECT * FROM следует избегать, учитывая, что движок sql должен сделать "невидимый" запрос, чтобы узнать, какие столбцы должны быть показаны.

Также знайте, что between @min_number AND @max_number работает лучше, чем Id >= @min_number AND Id <= @max_number, но я не помню, почему. Это может быть из-за того, что между предложением, контролируемым на более низком уровне движком, и создается итерация, чтобы показать, как regs каким-то образом "обрабатывается". Но я точно не знаю точно.

Может ли кто-нибудь подтвердить эти данные и составить список наиболее распространенных действий, чего следует избегать?

Ответы

Ответ 1

Мой список специфичен для SQL Server (я уверен, что это намного больше):

Используйте sargable где предложения - это означает, что нет никаких функций, особенно скалярных UDF в предложениях where, среди прочего

WHERE NOT EXISTS имеет тенденцию быть более быстрым выбором, чем левое объединение, где id где имеет нулевую структуру, когда вы ищете те строки, которые не соответствуют второй таблице.

Коррелированные подзапросы, как правило, работают ряд за рядом и ужасно медленны.

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

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

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

При обновлении обязательно добавьте предложение where, чтобы не обновлять строки, в которых новое значение и старое значение совпадают. Это может быть различие между обновлением 10 000 000 строк и обновлением 15. Пример (структура обновления Tsql, если вы используете другую базу данных, вам, возможно, придется искать правильный синтаксис, но он должен дать вам идею.):

Update t
set field1 = t2.field2
from table1 t
join table2 t2 on t.tid = t2.tid
Where t.field1 <> t2.field2

Или же

Update t
set field1 = @variable
from table1 t
Where t.field1 <> @variable

Проверьте свою индексацию. SQL Seerver не индексирует внешние ключи автоматически. Если они используются в объединении, их, как правило, необходимо проиндексировать.

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

Лучше всего получить хорошую книгу по настройке производительности для выбранной вами базы данных (что лучше всего для wokrs, зависит от конкретной базы данных) и прочитать главы, касающиеся написания запросов.

Ответ 2

Изменить, февраль 2012:

Избегайте этих "Десять распространенных ошибок программирования SQL"

Ответ 3

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

BAD

WHERE DATEADD(DAY, 30, [RecordDate]) > GETDATE()

Это может привести к полному сканированию таблицы (в зависимости от оптимизатора запросов для вашей платформы), даже если [RecordDate] индексируется, потому что DATEADD(DAY, 30, [RecordDate]) необходимо оценить для сравнения с GETDATE(). Если вы измените его на:

ЛУЧШЕ

WHERE [RecordDate] > DATEADD(DAY, -30, GETDATE())

Теперь это всегда будет возможность использовать индекс на [RecordDate] независимо от того, насколько оптимизирован оптимизатор плана запросов на вашей платформе, потому что DATEADD(DAY, -30, GETDATE()) получает оценку один раз и затем может использоваться как поиск в индексе. Тот же принцип применяется к использованию оператора CASE, UDF и т.д.

Ответ 4

Несколько общих вопросов об оптимизации запросов:

  • Знайте свои данные. Знайте свои данные. Знать свои данные. Я бы рискнул предположить, что половина всех проблем с производительностью базы данных связана с неполным пониманием данных и требований запроса. Знайте, будет ли ваш запрос обычно возвращать 50 строк или 5 миллионов строк. Знайте, нужно ли возвращать 3 столбца или 50 столбцов. Знайте, какие столбцы являются ключевыми столбцами в таблицах, и фильтруйте их.

  • Поймите свою структуру базы данных. Если вы работаете с базой данных в третьей нормальной форме, признайте, что эта структура обычно лучше всего подходит для запросов для множества небольших транзакционных операторов, работающих на отдельных строках. Если вы работаете в дизайне звезды или снежинки, узнайте, что он оптимизирован для больших запросов и агрегаций.

Ответ 6

Я не могу утверждать ваше утверждение, но могу сказать, что не используя * звучит тихо логично, что я могу сделать, это добавить к ним точку или два, если вы можете вместе с предоставлением select columnname из tablename добавить предложение where, которое оно помогает так как вы сократили бы множество ненужных строк и строк данных, которые можно было бы вытащить, также избегая перекрестных соединений и приветствуя внутренние соединения, внешние соединения или более полные соединения должны быть способом идти в соответствии с моим личным опытом: )

Ответ 7

добавив несколько советов в список:

Использование EXISTS/NOT EXISTS вместо IN/NOT IN для индексированных столбцов

 --instead of 
 SELECT * FROM table1
  WHERE id1 NOT IN (SELECT id2 FROM table2)

 --you better write
 SELECT * FROM table1 WHERE NOT EXISTS (SELECT 1 FROM table2 WHERE id1=id2)  

Избегайте использования UNION, когда его можно использовать UNION ALL
когда вам не нужно исключать дублированные строки или вы уверены, что не вернете дублированные строки

Избегайте использования HAVING, когда возможно использовать WHERE

 --instead of 
 SELECT col1, sum(col2) 
   FROM table1
  GROUP BY col1
 HAVING col1 > 0

 --you better write :
 SELECT col1, sum(col2)
   FROM table1
  WHERE col1 > 0
 GROUP BY col1

Используйте EXISTS вместо DISTINCT, когда вы объединяете таблицы "один ко многим"

--instead of
SELECT distinct a.col1, a.col2
  FROM table1 a, table2 b
 WHERE a.id = b.id

--you better write
SELECT a.col1, a.col2
  FROM table1 a
 WHERE EXISTS (SELECT 1 FROM table2 b where a.id = b.id)  

Надеюсь, это поможет несколько советов, ожидающих больше советов;)

Ответ 8

Мои простые правила для написания запроса:

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

  2. Сначала вы должны написать INNER JOIN, затем LEFT OUTER JOIN. Это помогает уменьшить количество строк, в которых SQL Engine будет искать ваши данные.

    Например:

    SELECT 
        pe.Name,
        de.Name,
        bu.Name
    FROM dbo.Persons pe
    INNER JOIN dbo.Departments de ON pe.ID = de.id_Person -- at first INNER JOIN
    LEFT JOIN dbo.Bureau bu ON bu.ID = de.id_Bureau -- then LEFT OUTER JOIN
    
  3. Используйте псевдонимы и имя схемы, чтобы избежать сканирования схемы SQL Server. Использование имени схемы помогает обналичить ваш план запросов для специальных запросов, которые могут быть использованы другими пользователями не только для ваших запросов.

  4. Избегайте использования SELECT *...

Ответ 9

Из того, что я прочитал, использование BETWEEN вместо двух проверок индекса с использованием AND повышает производительность, поскольку ваша база данных может не полностью использовать преимущества индексов, когда обнаружит, что она используется с обеих сторон от AND или OR.

Оптимизатор запросов может не понять, что это проверка диапазона, и что сортировка индекса может пригодиться. Вместо этого он может выполнить сканирование каждого условия, а затем объединить результаты. С другой стороны, это очень ясно с предложением BETWEEN, которое сравнивает столбец индекса с двумя значениями.