|
Программирование >> Полное сканирование таблицы
применить, то специализированные методы обычно проще и понятней. Но существуют проблемы, решаемые только универсальными техниками, которые предлагают решения, подходящие для SQL-запросов в базах данных самых различных производителей. Использование правильного индекса Чтобы добиться эффективного использования индекса, вам необходимо разумное селективное условие по ведущему (или единственному) столбцу индекса. Условие также должно быть выражено таким способом, который позволяет базе данных вьщелить достаточно узкий диапазон индексных значений. В идеальном случае условие принимает форму: НекоторыйПсевдоним.Ведущий индексированный столбец - <Вырамение> В менее удачном случае сравнение выполняется с некоторым диапазоном значений при помощи операторов BETWEEN, LIKE, <, >, <= или >-. Сравнения по диапазону значений потенциально позволяют использовать индекс, но диапазон индекса, вероятнее всего, будет больше, и результирующий запрос будет работать медленнее. Если диапазон индексированных значений слишком большой, оптимизатор может сделать вывод, что индекс использовать не стоит, и выберет другой путь доступа к данным. Если вы комбинируете условия равенства и поиска в диапазоне для многостолбцовых индексов, следует предпочесть индексы, ведущими в которых являются столбцы, для которых указаны условия равенства. Столбцы, на которые наложены условия диапазона значений, должны находиться в конце индекса. Обратите внимание, что в левой части сравнения просто указано имя столбца, без применения к нему функции и безо всяких выражений (например, сложения) с использованием этого столбца. Использование функции, преобразования типов или арифметического выражения для индексированного столбца в общем случае приведет к невозможности использования индекса. Преобразования типов - это особенно тонкий момент. Из-за них SQL-сервер иногда отказывается от использования индекса. DB2 возвращает ошибку при попытке сравнить два выражения несовместимых типов. SQL Server предпочитает вьшолнить неявное преобразование той стороны сравнения, которая не приведет к невозможности использования индекса. Oracle неявно преобразует выражения символьного типа в тот тип данных, который используется запросом, даже если это повлечет за собой отключение индекса. Например, рассмотрим такое выражение: P.Phone Number=5551212 Если Phone NurTiber - столбец символьного типа, то на внутреннем уровне в Oracle и SQL Server это выражение будет преобразовано по-разному. В Oracle мы получим выражение TO NUMBER(Р. Phone Number)=5551212 В SQL Server: Р. Phone Number=CAST(5551212 AS VARCHAR) SQL Server сохраняет индексированный доступ к столбцу в результате произведенного преобразования. В Oracle неявное применение операции TO NUMBER() сделает невозможным использование индекса точно так же, как если бы вы указали это выражение явно. На самом деле разница лишь в том, что неявную форму приведения типов обнаружить сложнее. Та же проблема может помешать использовать индексы для соединений и для однотабличных условий. Например, возьмем соединение: Р.Phone Number-C.Contact Number Если Contact Number принадлежит числовому типу, а Phone Number - символьному, то неявное преобразование в Oracle запрещает использовать вложенные циклы по индексу для соединения С с Р. Соединение в обратном порядке пройдет беспрепятственно. Выражение напротив упоминания индексированного столбца может быть сколь угодно сложным. Но в нем не должны упоминаться столбцы с тем же псевдонимом, который указан для индексированного столбца. Например: P.PhoneJumber=P.Area Code 5551212 База данных не может использовать с этим условием индекс по Р. Phone Number, так как она должна обратиться к псевдониму Р до того, как сможет оценить выражение в правой части. Эта проблема яйца и курицы не позволяет обнаружить (с использованием индекса) то подмножество таблицы, которое отвечает условию, пока база данных не проверит таблицу полностью. Есть еще один случай, когда SQL зачастую запрещает использовать индекс - когда условия соединены оператором OR. Рассмотрим, например такой запрос: SELECT ... FROM Order Details D. WHERE ... AND (D.Order ID=:l or :1 IS NULL) AND ... В этом примере база данных может обратиться к Orcler Detai 1 s при помощи индекса по Orcler ID, если параметр : 1 не равен nul 1. Но если параметр ; 1 все же принимает значение null, то никаких ограничений для Orcler ID не остается и индекс не используется. Так как во время разбора SQL и подготовки плана база данных не может предсказать, какое именно значение будет у параметра : 1, она не увидит никакой подходящей возможности использовать индекс. В этом случае хорошим решением будет создание плана из двух частей, где каждая часть будет оптимизирована для одного из возможных случаев: SELECT ... FROM Order Details D. ... WHERE ... AND D.Drder ID=:l AND :1 IS NOT NULL AND ... UNION ALL SELECT ... FROM Order Details D. , WHERE ., AND :1 IS NULL AND ... Когда вы просматриваете план вьшолнения для этого запроса, вы видите, что используется индексированный доступ при помощи индекса по Orcler Detai 1 s(Orcler ID) и доступ с полным сканированием таблицы к Orcler Detai 1 s. Это может показаться наихудшим решением, но вы защищены дополнительными условиями. AND :1 IS NOT NULL AND :1 IS NULL В этих условиях нет никакого обращения к данным базы, поэтому она оценивает условия перед тем, как начинает считывать данные для этой половины комби- нированного оператора. Таким образом, на самом деле она никогда не выполняет полное сканирование таблицы, если значение параметра : 1 не равно null, и никогда не выполняет индексированное считывание (и любую другую часть плана выполнения для первой половины запроса), если значение параметра : 1 равно nul 1. Так описывается метод разветвления плана выполнения в зависимости от условий по параметрам, которые определяют, какие данные будут использоваться для выполнения запроса. Единственная хитрость: вы должны убедиться, что условия по связанным переменным взаимно исключают друга, чтобы данные возвращались только из одной ветви плана выполнения. Например, если у вас есть другой параметр для определения Custonier Name, вы можете собрать запрос так: SELECT ... FROM Order Details D. Customers С. ... WHERE ... AND D.OrderJD=:l AND :1 IS NOT NULL AND (C.Customer Name=:2 DR :2 IS NULL) AND .. UNION ALL SELECT ... FROM OrderDetails D. Customers C. . WHERE ... AND :1 IS NULL AND :2 IS NOT NULL AND C.Customer Name=:2 AND ... UNION ALL SELECT ... FROM OrderDetails D. Customers C. WHERE ... AND :1 IS NULL AND :2 IS NULL AND ... В итоге получается план из трех частей, в котором сервер баз данных выполняет несколько операций. 1. Обращается к таблице Orders через индекс по Order ID (первый вариант), когда это возможно. 2. В ином случае обращается к таблице Customers при помощи индекса по полю Customer Name (второй вариант), если Order ID не указан, но указано имя покупателя. 3. Если оба предыдущих варианта не подходят, сервер просто получает все строки - вероятно, начиная с полного сканирования таблицы, если нет никаких селективных условий. В любом случае условия для связанных переменных во всех трех частях являются взаимоисключающими: AND :1 IS NOT NULL AND :1 IS NULL AND :2 IS NOT NULL AND ;1 IS NULL AND :2 IS NULL
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |