Главная страница  Карта сайта  Печать  Написать письмо  RSS
Войти
Персональный сайт
Стеллецкого Владимира
Обновлено: 26.07.2021 г.

Примеры с SQL

«   ‹   1   2   3   4   5   следующая ›   последняя »

И снова про индексы (26.07.2021) #

В очередной раз хочу напомнить про простые и в тоже время важные вещи:

  • необходимость правильного проектирования структуры таблиц и индексов (с учётом планируемого использования и, что немаловажно, ожидаемого роста количества данных);
  • необходимость периодической проверки актуальности и использования уже созданных индексов и, главное, регулярный анализ списка "тяжёлых"/ресурсоёмких запросов (хотя бы через стандартные средства MS SQL).

Некоторое время назад, при "дежурном" осмотре параметров работы MS SQL-сервера, моё внимание привлёк один из таких "тяжёлых" запросов. Анализ ситуации показал, что из таблицы с более чем 25 млн. записей/строк регулярно выбирались данные отфильтрованные по значению одного поля. И хотя в результирующей выборке было всего около 3-5 записей, запрос выполнялся довольно долго, а также попал в список "тяжелых", так как индекса по этому полю не было и каждый раз происходило сканирование всей таблицы.

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

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

PS И ещё один случай - таблица с почти 3,5 млн. строк. Запросом выбирается одна (TOP 1) строка по значениям трёх полей (одно из которых "дата"). Ранее были созданы отдельные индексы практически на все поля этой таблицы (здесь можно и даже нужно вспомнить про понятие селективности индекса и, соответственно, его эффективности для, например, битовых полей). В результате вместо использования существующих индексов каждый раз идёт практически полный перебор. Как решение в данном случае создал один индекс из этих трёх полей, но без включения доп. полей (include), чтобы излишне не раздувать его размер.

Heap-таблицы и forwarded-записи в SQL Server (20.05.2021) #

Интересная статья про кучи. Основные тезисы:

  • Куча — это таблица без кластерного индекса. Данные в куче хранятся без какого-либо логического порядка. Между страницами нет никакой связи.
  • Таблицы-кучи в базе данных можно найти с помощью системного представления sys.indexes (см. мою заметку)
  • Для просмотра дополнительной информации (процент фрагментации индекса, тип индекса, количество страниц и количество forwarded-записей) можно использовать динамическую административную функцию (Dynamic Management Function, DMF) sys.dm_db_index_physical_stats (в DETAILED-режиме)
  • При изменении данных, если SQL Server не может разместить новые данные на существующей странице из-за их большого размера, данные размещаются в другом месте, а на данной странице SQL Server вставляет Forwarded Record (запись о перенаправлении). Также в новом местоположении данных добавляется указатель в обратную сторону на созданную Forwarded Record для отслеживания цепочки forwarding-указателей в случае перемещения данных.
  • Большое количество перемещений влияет на количество логических чтений при выборке данных (их можно посмотреть, если перед выполнением запроса включить сбор и отображение необходимой статистики - SET STATISTICS IO ON).

Варианты решения проблемы большого количество логических чтений из-за forwarded-записей (если она есть):

  • Использование типов данных фиксированной длины (при изменении данные поместятся на уже выделенное место и перенаправления не будет)
  • Отказываться от кучи (добавлением кластерного индекса)
  • Мониторинг и регулярное перестроение кучи (ALTER TABLE ... REBUILD)

Тёмная сторона SQL Server In-Memory OLTP (12.05.2021) #

Выдержка из статьи: принимая решение об использовании In-Memory OLTP в промышленном окружении нужно обязательно учитывать, что:

  1. Файловую группу In-Memory нельзя удалить из БД, она останется с вами навсегда.
  2. Файловая группа In-Memory, при совпадении ряда условий, может быстро и значительно расти.
  3. Таблицы, объявленные как SCHEMA_ONLY, не генерируют IO и не используют Data и Delta файлы на диске, но эти файлы для них создаются при каждом CHECKPOINT'е.
  4. При использовании Availability Group, файловая группа In-Memory будет гораздо больше, чем в точно такой же БД, не входящей в Availability Group.

Сводные таблицы в SQL (03.07.2020) #

Интересная статья с описанием нескольких способов формирования Сводных таблиц в SQL (есть примеры для MySQL, MSSQL Server, Oracle и PostgreSQL). Пополнил знания по нескольким операторам SQL:

  • Выражение COALESCE - замена часто используюмому мною IsNull. У IsNull, как оказалось, есть одна неприятная особенность - тип результата определяется типом первого параметра, что может привести к усечению данных.
    -- Пример "некорректной" работы IsNull
    DECLARE @st VARCHAR(10), @st1 VARCHAR(20)
    SET @st1 = 'Привет, Владимир!'
    SELECT ISNULL(@st, @st1)
    
  • Аргумент ROLLUP у GROUP BY - для создания строки Итого при группировке.

Также взял на вооружение несколько подсмотренных в статье интересных приёмов.

Временные таблицы и табличные переменные (28.12.2018) #

Наткнулся на старенькую статью посвященную сравнению временных таблиц (create table #T (…)) и табличных переменных (declare @T table (…)).

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

  1. Табличные переменные не учитывают транзакции (откат транзакции не вернёт исходного значения).
  2. Процедуры с временными таблицами не могут быть предварительно скомпилированы (компиляция будет осуществляться при каждом вызове, что может сказаться на времени, особенно для больших процедур).
  3. Табличные переменные существуют только в пределах видимости переменных.

Касательно быстродействия в каждом конкретном случае правильнее проводить эксперименты.

Классные индексы (25.09.2018) #

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

Внимание вопрос: насколько и в каких случаях будет эффективным индекс по полю со следующим распределением значений:

Распределение данных в таблице

-- пример запроса для получения такого распределения
select
       r.[Status],
       count(*) as cnt
from dbo.[TableOrder] r
group by r.[Status]  

Думаю, что не особо и только в очень частном случае.

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

Тут, как говорится, вообще без комментариев.

Запросы в текущее время потребляющие память (14.01.2018) #

Предлагаю запрос, который отображает запросы с MS SQL-сервера, которые потребляют память в текущий момент (при выводе информации применяется фильтр, отсекающий запросы, потребляющие менее 500 МБ). Иногда с его помощью удаётся найти неэффективные запросы, которые следует рассмотреть повнимательнее и, возможно, оптимизировать или переписать.

Кратко приведу информацию о колонках:

  • Данные о пользователе (имя и хост)
  • Время запроса (request_time) и выделения (grant_time) памяти
  • Объем запрошенной (requested_memory_kb) и выделенной (granted_memory_kb) памяти в килобайтах
  • Информация по использованию памяти: сколько потребовалось (required_memory_kb), было использовано (used_memory_kb) и максимальное значение (max_used_memory_kb)
  • Коэффициент "стоимости" запроса (query_cost)
  • Время ожидания (wait_time_ms)
  • Сам выполняющийся запрос (query_text)
  • Информация о выполняемом запросе (event_info)
  • Ссылка на план выполнения запроса (query_plan)
-- запросы в текущее время потребляющие память
SELECT mg.session_id,
       CAST(es.nt_user_name as varchar) as nt_user_name,
       CAST(es.host_name as varchar) as host_name,
       mg.request_time,
       mg.grant_time,
       mg.requested_memory_kb,
       mg.granted_memory_kb,
       mg.required_memory_kb,
       mg.used_memory_kb,
       mg.max_used_memory_kb,
       mg.query_cost,
       mg.resource_semaphore_id,
       mg.wait_time_ms,
       st.[text] as query_text,
       ib.event_info,
       qp.query_plan
FROM sys.dm_exec_query_memory_grants AS mg
     INNER JOIN sys.dm_exec_sessions AS es
                ON es.session_id = mg.session_id
     CROSS APPLY sys.dm_exec_input_buffer(mg.session_id, NULL) AS ib
     CROSS APPLY sys.dm_exec_sql_text(mg.sql_handle) AS st
     CROSS APPLY sys.dm_exec_query_plan(mg.plan_handle) AS qp
WHERE mg.requested_memory_kb > 500000
ORDER BY mg.requested_memory_kb DESC

С помощью этого запроса обнаружили у себя запросы с аномальным потреблением памяти (10Гб, 20Гб и даже 40Гб).

«   ‹   1   2   3   4   5   следующая ›   последняя »

  Вы 52 928 посетитель этой странички
с 07 октября 2006 года
© http://svv-home.ru
О сайте