Следует ли индексировать битовое поле в SQL Server?

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

Так что, если у меня есть таблица со 100 миллионами строк, и я выбираю записи, в которых битовое поле равно 1? И предположим, что в любой момент времени существует только несколько записей, в которых битовое поле равно 1 (в отличие от 0). Стоит ли индексировать это битовое поле или нет? Почему?

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

Если вы не читали, Джейсон Мэсси недавно написал статью, в которой обсуждалась именно эта тема. statisticsio.com/Home/tabid/36/articleType/ArticleView/… Редактировать: новое местоположение статьи - sqlserverpedia.com/blog/sql-server-bloggers/ Never-index-a-bi‌ t

Jeff 09.01.2009 20:11

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

Craig Nicholson 24.10.2008 00:31

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

jason saldo 23.10.2008 23:38

очень поздний ответ ... Да, это может быть полезно по мнению команды SQL CAT

gbn 21.12.2011 19:20

«Я помню, как однажды прочитал, что индексирование поля с низкой мощностью (малое количество различных значений) на самом деле не стоит делать» Это потому, что SQL Server почти всегда найдет более эффективным просто выполнить сканирование таблицы, чем читать показатель. Таким образом, ваш индекс никогда не будет использоваться, и поддерживать его - пустая трата времени. Как говорили другие, это может быть нормально в составном индексе.

DJ. 24.10.2008 00:07

Хотя я не думаю, что я бы индексировал ТОЛЬКО битовый столбец сам по себе, очень часто включать битовые столбцы как часть составного индекса. Простым примером может быть указатель на ACTIVE, LASTNAME, а не только на фамилию, когда ваше приложение почти всегда ищет активных клиентов.

BradC 23.10.2008 23:54

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

Mitch Wheat 25.10.2008 12:02

Кардинальность - это один фактор, другой - насколько хорошо индекс разделяет ваши данные. Если у вас есть примерно половина единиц и половина нулей, это поможет. (Предполагая, что этот индекс - лучший путь для выбора, чем какой-либо другой индекс). Однако как часто вы вставляете и обновляете? Добавление индексов для производительности SELECT также ухудшает производительность INSERT, UPDATE и DELETE, так что имейте это в виду. Я бы сказал, что если от 1 до 0 (или наоборот) не лучше, чем от 75% до 25%, не беспокойтесь.

Anthony Potts 24.10.2008 00:13

Я бы не согласился. Если у вас распределение 50/50, вы никогда не будете использовать индекс, так как это будет быстрее сканировать таблицу. Однако, если у вас есть только 5, 1 значение и 1 миллион 0 значений, весьма вероятно, что при поиске 1 будет использоваться индекс.

Kibbee 24.10.2008 00:20

Я столкнулся с этим вопросом, задавшись тем же вопросом, потому что у меня была аналогичная дилемма. Решил провести сравнение, когда работал примерно с 75 миллионами строк. Запрос занимал 10 секунд, и как только я добавил индекс, он упал до нескольких миллисекунд. Также запустил его с «set statistics io on» и увидел, что логическое чтение значительно уменьшилось. Все это я тестировал с выключенным кешем. Я бы посоветовал проверить это на себе и узнать.

m0g 07.03.2018 00:47
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
106
11
37 952
19
Перейти к ответу Данный вопрос помечен как решенный

Ответы 19

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

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

Если вы хотите узнать, оказывает ли индекс желаемый эффект: тест и снова тест.

В общем, вам не нужен индекс, который недостаточно сужает вашу таблицу из-за затрат на поддержание индекса. (стоимость> прибыль). Но если индекс в вашем случае сократит таблицу пополам, вы можете кое-что получить, кроме как положить это на стол. Все зависит от точного размера / структуры вашей таблицы и от того, как вы ее используете (количество операций чтения / записи).

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

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

Как говорили другие, вы захотите это измерить. Я не помню, где я это читал, но столбец должен иметь очень высокую мощность (около 95%), чтобы индекс был эффективным. Лучшим тестом для этого будет построение индекса и изучение планов выполнения для значений 0 и 1 поля BIT. Если вы видите операцию поиска по индексу в плане выполнения, значит, вы знаете, что ваш индекс будет использоваться.

Лучше всего протестировать с помощью простой таблицы SELECT * FROM WHERE BitField = 1; query и постепенно наращивайте функциональность оттуда, шаг за шагом, пока не получите реалистичный запрос для вашего приложения, исследуя план выполнения на каждом шаге, чтобы убедиться, что поиск по индексу все еще используется. По общему признанию, нет никакой гарантии, что этот план выполнения будет использован в производственной среде, но есть большая вероятность, что так оно и будет.

Некоторую информацию можно найти на sql-server-performance.com форумы и в указанном статья

Важна не столько мощность столбца в целом. Это избирательность предложения WHERE. Так что, если есть несколько столбцов со значением 1, его все равно можно индексировать. Если это 50/50 (например, мужчина / женщина), то это того не стоит.

WW. 25.10.2008 12:06

Хотя я не думаю, что я бы индексировал ТОЛЬКО битовый столбец сам по себе, очень часто включать битовые столбцы как часть составного индекса.

Простым примером может быть указатель на ACTIVE, LASTNAME, а не только на фамилию, когда ваше приложение почти всегда ищет активных клиентов.

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

Mitch Wheat 25.10.2008 12:02

«Я помню, как однажды прочитал, что индексирование поля с низкой мощностью (малое количество различных значений) на самом деле не стоит делать»

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

Кардинальность - это один фактор, другой - насколько хорошо индекс разделяет ваши данные. Если у вас есть примерно половина единиц и половина нулей, это поможет. (Предполагая, что этот индекс - лучший путь для выбора, чем какой-либо другой индекс). Однако как часто вы вставляете и обновляете? Добавление индексов для производительности SELECT также ухудшает производительность INSERT, UPDATE и DELETE, так что имейте это в виду.

Я бы сказал, что если от 1 до 0 (или наоборот) не лучше, чем от 75% до 25%, не беспокойтесь.

Я бы не согласился. Если у вас распределение 50/50, вы никогда не будете использовать индекс, так как это будет быстрее сканировать таблицу. Однако, если у вас есть только 5, 1 значение и 1 миллион 0 значений, весьма вероятно, что при поиске 1 будет использоваться индекс.

Kibbee 24.10.2008 00:20

Вы не могу индексируете битовое поле в SQL Server 2000, как было указано в электронной документации в то время:

bit

Integer data type 1, 0, or NULL.

Remarks

Columns of type bit cannot have indexes on them.

Да, если у вас всего несколько строк из миллионов, вам поможет индекс. Но если вы хотите сделать это в этом случае, вам нужно сделать столбец tinyint.

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

CREATE INDEX IX_Users_IsActiveUsername ON Users
(
   IsActive,
   Username
)

Но SQL Server 2000 на самом деле не будет использовать такой индекс - выполнение запроса, в котором индекс будет идеальным кандидатом, например:

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

SQL Server 2000 вместо этого выполнит сканирование таблицы, действуя так, как будто индекса даже не существует. Если вы измените столбец на tinyint, SQL Server 2000 воля выполнит поиск по индексу. Также следующий не охваченный запрос:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

Он выполнит поиск по индексу, а затем по закладке.


SQL Server 2005 имеет ограниченную поддержку индексов по битовым столбцам. Например:

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

вызовет поиск индекса через индекс покрытия. Но незащищенный случай:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

не вызовет поиск по индексу с последующим поиском по закладкам, он будет выполнять сканирование таблицы (или сканирование кластерного индекса), а не выполнять поиск по индексу с последующим поиском по закладке.

Проверено экспериментально и прямым наблюдением.

К вашему сведению - SQL Server 2005 Management Studio действительно позволяет вам это делать.

jeremcc 24.10.2008 00:36

Моя копия SQL Server 2000 позволила мне установить индекс для битового столбца.

Kibbee 24.10.2008 01:02

Моя копия SQL Server 2000 не позволяет мне устанавливать индекс для битового столбца.

Ian Boyd 29.10.2008 00:07

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

Ответ принят как подходящий

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

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

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

Когда вы индексируете целочисленный столбец, индекс SQL содержит набор строк для каждого значения индекса. Если у вас есть диапазон от 1 до 10, у вас будет 10 указателей индекса. В зависимости от количества строк это может быть разбито на страницы по-разному. Если ваш запрос ищет индекс, соответствующий «1», а затем, где Name содержит «Fred» (при условии, что столбец Name не проиндексирован), SQL очень быстро получает набор строк, соответствующих «1», затем таблица просматривает, чтобы найти остальные.

Итак, на самом деле SQL пытается уменьшить рабочий набор (количество строк), который он должен перебирать.

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

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

Итак, похоже, что если бы у меня было только несколько строк, в которых битовое поле равно 1 (например, отслеживание «IsProcessed»), тогда индекс был бы хорош, потому что он упорядочит их по значению, а затем сможет выбрать небольшой рабочий набор очень быстро. Если вы согласны, добавьте это, и я приму это.

jeremcc 24.10.2008 01:01

В своем предыдущем комментарии я имел в виду, что это утверждение: «Когда вы индексируете битовое поле (или некоторый узкий диапазон), вы уменьшаете рабочий набор только вдвое», неверно, если распределение сильно взвешено в сторону одного значения. Но мне нравится остальная часть вашего ответа, поэтому, если вы это исправите, я приму его.

jeremcc 24.10.2008 01:06

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

Geoff Cox 26.10.2008 02:34

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

onupdatecascade 10.02.2010 20:04

А как насчет индексации битового поля + другого поля? Например. в журнале веб-активности можно было бы проиндексировать временную метку, но другой полезный индекс мог бы быть в битовом поле «IsHTTPS» + временная метка, чтобы быстро просмотреть все действия https. Было бы это тоже неэффективно?

ingredient_15939 30.11.2011 06:30

Я помню, как во времена SQL Server Enterprise Manager по какой-то причине люди, работающие с DWH в моей компании, использовали свой конструктор таблиц, который использовался для создания кластерных PK в столбцах GUID, я считаю, что это было связано с некоторыми из его значений по умолчанию во время создания таблицы и их отсутствие знаний о кластерных индексах.

yv989c 21.08.2020 05:33

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

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

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

http://www.microsoft.com/technet/prodtechnol/sql/2005/impprfiv.mspx

Все это предполагает, что вы используете Microsoft SQL Server 2005 Enterprise. То же самое может относиться к 2008 году, я не знаком с этой версией.

Если вы не читали, Джейсон Мэсси недавно написал статью, в которой обсуждалась именно эта тема.

http://statisticsio.com/Home/tabid/36/articleType/ArticleView/articleId/302/Never-Index-a-BIT.aspx

Обновлено: новое расположение статьи - http://sqlserverpedia.com/blog/sql-server-bloggers/ Never-index-a-bit

Обратный автомат для ранее «Нового» местоположения статьи: http://web.archive.org/web/20120201122503/http://sqlserverpedia.com/blog/sql-server-bloggers/ Never-index-a-bit/

Новое местоположение SQL Server Pedia - Toadworld, в котором есть новая статья Кеннета Фишера, обсуждающая эту тему:

http://www.toadworld.com/platforms/sql-server/b/weblog/archive/2014/02/17/dba-myths-an-index-on-a-bit-column-will- Never-be- used.aspx

обратная машина: http://web.archive.org/web/20150508115802/http://www.toadworld.com/platforms/sql-server/b/weblog/archive/2014/02/17/dba-myths-an-index- on-a-bit-column-will-never-be-used.aspx

эта статья больше не видна

Homer6 20.04.2011 23:26

@ Homer6 Я добавил ссылку на то, что выглядит как новый дом для этой статьи.

Jeff 21.04.2011 20:12

Новая ссылка ведет на домашнюю страницу Toad World.

N West 01.05.2014 20:30

Нашел статью, используя машину Wayback, и нашел новую статью по теме. Надеюсь это поможет.

Jeff 23.05.2014 02:15

Иэн Бойд прав, когда говорит, что вы не можете сделать это с помощью Enterprise Manager for SQL 2000 (см. Его примечание относительно создания его с помощью T-SQL.

очень поздний ответ ...

Да, это может быть полезно по мнению команды SQL CAT (обновлено, консолидировано)

Ссылка теперь кажется мертвой. Однако этот пост, похоже, был объединен вместе с несколькими другими в электронная книга. Упомянутый раздел начинается на странице 86. Электронную книгу можно загрузить с сайта Электронные книги SQLCAT.com по ссылке «Руководство SQLCAT по реляционному механизму».

mwolfe02 05.02.2014 18:45

Я просто столкнулся с этим вопросом в качестве другого. Предполагая, что ваше утверждение о том, что только горстка записей принимает значение 1 (и что это те, которые вас интересуют), тогда отфильтрованный индекс может быть хорошим выбором. Что-то типа:

create index [IX_foobar] on dbo.Foobar (FooID) where yourBitColumn = 1

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

Стоит отметить, что предикат в запросе должен быть жестко привязан к значению в отфильтрованном индексе. Если вы передадите значение в параметре yourBitColumn = @value, оптимизатор не сможет определить, можно ли использовать отфильтрованный индекс.

geofftnz 16.10.2016 23:54

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

Ben Thul 17.10.2016 00:23

Если ваше распределение хорошо известно и несбалансировано, например, 99% строк имеют бит = 1, а 1% - бит = 0, когда вы выполняете предложение WHERE с битом = 1, полное сканирование таблицы будет примерно в то же время, что и индексное сканирование. Если вы хотите получить быстрый запрос, где bit = 0, лучший способ, который я знаю, - это создать отфильтрованный индекс, добавив предложение WHERE bit = 0. Таким образом, этот индекс будет хранить только строку 1%. Затем установка WHERE bit = 0 просто позволит оптимизатору запросов выбрать этот индекс, и все строки из него будут иметь bit = 0. У вас также есть преимущество в том, что требуется очень небольшой объем дискового пространства для сравнения полного индекса на бите. .

Если 99% строк имеют бит = 1, оптимизатор должен игнорировать индекс и выполнить сканирование таблицы. Использование индекса на самом деле будет хуже, чем сканирование таблицы, по крайней мере, на вращающемся диске, больше операций ввода-вывода и непоследовательных чтений с диска. Отфильтрованный индекс (эквивалент Postgres: частичный индекс) - это путь. Думаю, поскольку после вопроса прошло много лет, этот ответ не получил заслуженных голосов.

Andrew Lazarus 18.07.2018 02:20

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

Другие вопросы по теме