Главное меню

EN | RU | UK

На главную

10. Оптимизация работы БД

Наверх страницы
Лекция
Цель

Цель раздела - определение критериев и рассмотрение основных направлений оптимизации работы БД, которые отвечают требованиям пользователей и обеспечивают комфортность их работы.

10.1. Направления оптимизации работы БД

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

1) оптимизация структуры БД;

2) оптимизация запросов;

3) оптимизация приложения клиента.

В свою очередь оптимизации структуры БД выполняют по следующим направлениям:

1) нормализация отношений;

2) методы доступа к данным;

3) налаживание внутренних механизмов СУБД.

Оптимизация запросов состоит из:

1) построения структуры БД, адекватной запросам;

2) оптимизации структуры индексов таблиц БД;

3) оптимизации текстов запросов.

Оптимизацию приложений клиентов будем рассматривать только со стороны организации эффективного доступа к БД.

10.2. Оптимизация структуры БД

10.2.1. Нормализация отношений

Нормализация отношений влияет на быстродействие операций выборки данных [4 - 7, 10, 12]. Часто нормализация ухудшает быстродействие. С точки зрения теории, она приводит к наиболее ясному отображению сущностей предметной области. Устраняя избыточность данных, она тем самым существенно экономит дисковое пространство. С точки зрения практики, и именно на больших объемах данных, оптимизация может существенно уменьшить производительность. При обращении к одной, пусть крупной, таблице БД, тратится меньше времени, чем при обращении к нескольким, более мелким таблицам с применением операций соединения.

Кроме того, высокая степень нормализации приводит к увеличению количества отношений в БД. Вследствие этого структура БД не воспринимается разработчиками и пользователями целостно. Срабатывает так называемый «человеческий фактор», то есть уже на стадии логического проектирования возникают серьезные ошибки в структуре БД, что в дальнейшем может привести к негативным последствиям.

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

10.2.2. Методы доступа к данным

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

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

10.2.3. Настройки внутренних механизмов СУБД

Для быстрого доступа к таблице БД необходимо, чтобы она физически занимала непрерывный блок страниц. Известно, что при выделении новых страниц в СУБД InterВase не предпринимает никаких попыток выделить смежные страницы для хранения одной и той таблицы [14, 15]. Поэтому данные, относящиеся к одной страницы, могут быть информациею из нескольких таблиц, то есть фрагментированы. Хранение множественных поколений записей также приводит к сильному «загрязнения» БД и замедляет работу с ней. При изменении записи фактически создается новая ее версия, измененная или добавленная транзакциями, которые впоследствии отменялись и не удалялись из БД. Кроме того, при изъятии записей не происходит перемещения тех их версий, которые остались для того, чтобы удалить «дыры», которые образовывались на страницах БД.

Решением указанных проблем является периодическое резервное копирование и обновление с ее помощью БД. В этом случае собирается «мусор», то есть версии записей, которые в дальнейшем не будут использоваться, и устраняются «дыры» на страницах БД, образовавшиеся после удаления записей. Каждая таблица размещается в непрерывном блоке страниц.

Размер самой страницы БД имеет большое значение, поскольку чтение-запись в СУБД InterВase осуществляются страницами. Поэтому запись таблицы по возможности должно находиться в пределах одной страницы. Если размер страницы мал для хранения одной записи и для него требуется несколько страниц, то для чтения такой записи необходимо выполнить несколько физических операций. С другой стороны, размер страницы не должен быть слишком большим, так как в этом случае будут считываться ненужные записи.

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

10.3. Индексация таблиц

Наличие индекса может существенно повысить скорость выполнения некоторых запросов. Однако, поскольку индексы должны обновляться системой при каждом внесении изменений в базовую таблицу, то они создают дополнительную нагрузку на систему. Индексы обычно создаются с целью удовлетворения определенных критериев поиска, после того как таблица уже находилась некоторое время в работе и увеличилась в размерах. Они могут создаваться только для таблиц БД, но не для представлений.

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

Он подобен предметному указателю, который приведен в конце книги. Это структура, которая связана с файлом и предназначена для поиска информации по тому же принципу, что и предметный указатель в книге. Индекс позволяет избежать последовательного или пошагового сканирования файла в поисках нужных данных. При его использовании объектом поиска может быть один или несколько записей файла. Как и предметный указатель книги, индекс упорядоченный и каждый его элемент содержит название объекта поиска, а также один или несколько указателей (идентификаторов записей), ссылающиеся на место его размещения.

В InterВase понятие ключей и индексов разделены на логическом уровне [14, 15]. Первичный (PRIMARY KEY) и внешний (FOREIGN KEY) ключи строятся для обеспечения ссылочной целостности реляционно-связанных таблиц. Первичный ключ, кроме этого, выполняет функции поддержания уникальности своих значений, что обусловлено его основным назначением - однозначно характеризовать запись в таблице БД. Для тех же целей может использоваться и уникальный ключ (UNIQUE). Однако он используется оператором SELECT для ускорения доступа к данным. «Обычные» индексы в отличие от ключей, служат лишь для оптимизации доступа к данным. С физической точки зрения, то, что логически при создании таблиц делилось на ключи и индексы, физически преобразуется в индексы. Однако, если «обычным» индексам можно присвоить имя, то физические индексы, реализованные на основе определений ключей, строятся и именуются системой автоматически.

Большинство диалектов, в том числе InterВase, поддерживают следующий оператор, позволяет создавать индексы для таблиц БД:

CREATE [UNIQUE] [ASС [ENDING] | DESC [ENDING]] INDEX Имя

    ON Имя таблицы (атрибут[, атрибут ...]).

Здесь обязательно необходимо присвоить имя индекса, имя таблицы и перечислить имена атрибутов, по которым необходимо построить индекс. Напомним, что формат оператора CREATE INDEX позволяет исключить дублирование значений в ключевых атрибутах, указав предложение UNIQUE. В этом случае уникальность значений потенциального ключа автоматически поддерживаться системой. По умолчанию сортировка значений индексных полей осуществляется по возрастанию. Это можно указать явно, используя предложение ASС[ENDING]. Если необходимо обеспечить обратную последовательность сортировки - по убыванию, то используют предложение DESC[ENDING].

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

Для извлечения индекса используется оператор

DROP INDEX Имя;

Однако этим оператором нельзя удалить индекс, созданный с помощью CREATE TABLE (PRIMARY KEY, FOREIGN KEY, UNIQUE). Для этого следует применять оператор ALTER TABLE. Нельзя также удалить индекс, который на этот момент используется другими пользователями БД. Кроме этого, для удаления индекса необходимо иметь соответствующие привилегии доступа к БД.

10.4. Оптимизация структуры индексов

10.4.1. Общие рекомендации

От структуры индексов в значительной степени зависит эффективность выполнения запросов [4, 6]. При этом СУБД сначала просматривает список индексов, которые определены для таблиц. Затем выбирается одна из двух схем выполнения запроса - использовать имеющиеся индексы или последовательно просмотреть таблицы. Оптимизатор СУБД стремится выполнить запрос с максимальным быстродействием и минимальным использованием вычислительных ресурсов. Перед первым вызовом СУБД всегда оптимизирует выполнение запроса, основываясь на текущем состоянии БД. Вторично запросы, в которых меняются только значения параметров, не оптимизируются: происходит лишь предварительное соединение формальных и фактических параметров, после чего запрос выполняется.

Однозначно сказать по какой схеме оптимизатор СУБД будет выполнять запрос невозможно, поскольку состояние БД может изменяться. Но существуют общие положения, которые следует учитывать при проектировании запросов и структуры индексов. Индексы необходимо создавать тогда, когда по столбцу или группе столбцов часто:

1) ведется поиск в БД: атрибут или группа атрибутов часто перечисляются в предложении WHERE оператора SELECT;

2) выполняются соединения таблиц;

3) осуществляется сортировка результатов в наборах данных, возвращаемых запросами: атрибут или группа атрибутов часто используются в предложении ORDER BY оператора SELECT.

Не рекомендуется создавать индексы по столбцам или группах столбцов, которые:

1) мало используются для поиска, объединения и сортировки результатов запросов;

2) нередко меняют значение, что приводит к необходимости частого обновления индексов и соответственно к существенному замедлению скорости работы с БД;

3) содержат небольшое число вариантов значений.

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

10.4.2. Частичное использование составного индекса

Частичное использование составного индекса позволяет существенно уменьшить затраты ресурсов СУБД [2, 4, 12]. Для этого составной индекс следует строить так, чтобы столбцы, по которым чаще всего ведется поиск, находились в начале списка. Тогда для поиска может быть использована часть индекса. Например, пусть часто выполняются запросы

SELECT *

    FROM SOMETABLE

    WHERE A = :ParamA AND В = :ParamВ;

SELECT *

    FROM SOMETABLE

    WHERE A = :ParamA AND В = :ParamВ AND С = :ParamC;

SELECT *

    FROM SOMETABLE

    WHERE A = :ParamA AND В = :ParamВ AND С = :ParamC AND D = :ParamD;

Тогда, построив составной индекс по столбцам А, В, C, D, мы можем утверждать, что данный индекс будет использован при оптимизации всех трех запросов. В первом случае будет использована подмножество индекса, то есть значение А, В, во втором - значение А, В, C, в третьем - А, В, C, D (т.е. все значение индекса).

Если условия отбора данных объединяются посредством AND, то последовательность использования столбцов не имеет большого значения. Например, для выполнения следующих запросов может применяться индекс из предыдущего случая:

SELECT *

    FROM SOMETABLE

    WHERE A = 100 AND В = 200;

SELECT *

    FROM SOMETABLE

    WHERE B = 200 AND A = 100;

Для предложения ORDER BY последовательность столбцов в составном индексе играет не малую роль. Например, для оптимизации выполнения следующих запросов необходимо использовать разные индексы:

SELECT *

    FROM SOMETABLE

    ORDER BY А, В, C;

SELECT *

    FROM SOMETABLE

    ORDER BY А, C, В.

Следует помнить, что в предложении ORDER BY можно использовать только непрерывную последовательность значений. Например, предыдущий индекс не может использоваться для выполнения запросов:

SELECT *

    FROM SOMETABLE

    ORDER BY А, C;

SELECT *

    FROM SOMETABLE

    ORDER BY B, D.

Но он будет полезным для следующего запроса:

SELECT *

    FROM SOMETABLE

    ORDER BY А, B;

10.4.3. Многопоточность поиска по OR и IN

При частом использовании в условиях поиска предложения WHERE нескольких столбцов, которые связаны между собой операцией «или» (OR) вместо индекса по столбцам А, В, C создают несколько индексе, построенных по каждому столбцу отдельно, иначе будет осуществлен последовательный просмотр всей таблицы. Это связано с тем, что индексно-последовательный доступ по атрибутам А, В, С может быть осуществлен только для столбца А, значения столбцов В и С в этом случае разбросаны по индексу. Важно помнить, что при использовании оператора OR каждая часть условий поиска вызывает отдельное сканирование таблиц, участвующих в запросе. Так, например, при выполнении оператора SELECT

SELECT *

    FROM SOMETABLE

    WHERE A = 100 OR В = 200 OR C = 300

будет выполнено три отдельных сканирование таблицы для поиска значений, удовлетворяющих условиям А = 100; В = 200; C = 300.

Отдельный поток поиска порождает и каждый элемент в списке IN. Например, предложение WHERE A IN (100, 200, 300) интерпретируется как WHERE A = 100 OR A = 200 OR A = 300. Однако при указании диапазона BETWEEN WHERE A BETWEEN 100 AND 300 этого не происходит. Поэтому там, где возможно, следует заменять IN на BETWEEN.

10.4.4. Уменьшение общего количества индексов

Уменьшение общего количества индексов осуществляется за счет рационального применения составных индексов, а также обеспечение ссылочной целостности с помощью триггеров. При большом количестве индексов снижается скорость добавления, изменения и удаления записей из таблиц БД. Нам известно, что в БД используется два вида индексов: одни для ускорения доступа к данным, другие - для обеспечения ссылочной целостности. Если последние не участвуют в оптимизации доступа к данным, тогда их целесообразно удолять, а ссылочную целостность обеспечить с помощью триггеров.

10.5. Улучшение производительности работы индекса

10.5.1. Разбалансировка индекса

Разбалансировка индекса приводит к тому, что его глубина (depth) превышает критическое значение (2). Глубина индекса - параметр, показывающий максимальное число операций, необходимых для нахождения нужного значения в таблице БД с помощью данного индекса. Индексы таблицы могут быть разбалансированные после многократного внесения изменений. При разбалансировке ценность индекса при выполнении запросов снижается из-за увеличения времени, расходуемого на выборку данных. Поэтому время от времени необходимо:

1) перестроить индекс;

2) перечислить показатель «быть избранным» индекса;

3) удалить индекс и снова его создать.

10.5.2. Перестройка индекса

Перестройка индекса заключается в его пересоздание и балансировки после деактивизации оператором

ALTER INDEX Имя INACTIVATE

и последующей активизации оператором

ALTER INDEX Имя ACTIVATE.

Отключение индекса полезна также и в том случае, если нужно вставить в таблицу БД большое количество записей. При добавлении записей в таблицу в обычном режиме к её активному индексу синхронно добавляется соответствующая информация, которая может его разбалансировать. Следует помнить, что:

- нельзя перестраивать индекс, который в этот момент используется другими пользователями БД;

- нельзя перестраивать индекс, созданный в операторе CREATE TABLE (PRIMARY KEY, FOREIGN KEY, UNIQUE). Для этого следует применять оператор ALTER TABLE;

- для выполнения оператора ALTER INDEX нужно иметь соответствующие привилегии доступа к БД.

10.5.3. Показатель «полезности» индекса

Показатель «полезности» индекса основан на сведениях о числе повторяющихся его строк. Он используется СУБД при обращении к таблице для создания оптимального плана выполнения запроса. Эффективность использования индекса при поиске информации в таблице БД зависит от того, построен он по уникальным значениям и, если нет, насколько отличаются данные, по которым он построен.

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

SET STATISTICS INDEX Имя.

Для участия в выполнении запроса выбираются индексы с максимальным показателем «полезности». Такие индексы обеспечивают наиболее быстрый поиск. Максимальный показатель «полезности» имеют уникальные индексы.

Среднее количество записей - показатель, который рассчитывается каждый раз при оптимизации запроса как количество страниц БД, занятых этой таблицей, деленное на максимальное число записей на странице. Сокращение числа страниц, занятых БД и удаление на них «дыр» приводит к уменьшению показателя среднего числа записей и, как следствие, к повышению показателя «полезности» индексов. Это еще один аргумент в пользу периодической оптимизации БД путем создания резервной копии и её обновления.

10.6. Просмотр и составление плана выполнения запросов

Утилита WISQL СУБД InterBase позволяет показать план выполнения запроса. Для этого выберите Session | Basic Settings, а затем установит режим Display Query Plan. Тогда вместе с результатом работы запроса будет выводиться план его выполнения. Под планом выполнения запроса понимается перечень индексов, используемых СУБД для выбора данных из таблиц.

Для принудительного выполнения запроса по тому или иному плану необходимо в операторе SELECT указать следующее предложение:

PLAN <план выполнения запроса>, где <план выполнения запроса> =

    [JOIN | [SORTMERGE] ({элемент плана | план выполнения запроса}[, {элемент плана | план выполнения запроса} ...]),

        где <элемент плана> = {таблица | псевдоним}

    NATURAL | INDEX (<индекс>[, <индекс> ...]) | ORDER <индекс>.

Синтаксис предложения <план выполнения запроса> позволяет соединить между собой несколько таблиц (предложение JOIN). В случае отсутствия индексов, при которых записи таблицы могут быть упорядочены, применяется принудительное сортировки (предложение [SORT] MERGE). В предложении <элемент плана> - указывают таблицу, в которой производится поиск данных. Если таблица несколько раз задействована в запросе, то для сокращения текста плана полезно применять ее псевдоним. Предложение NATURAL указывает, что для поиска записей применяется последовательный доступ. Это единственный способ поиска в том случае, когда нет соответствующих индексов. Можно указать один или несколько индексов, которые должны использоваться для поиска записей, удовлетворяющих условию запроса (предложение INDEX). А предложение ORDER позволяет определить индекс, который необходим для сортировки таблицы. Примеры использования предложения PLAN рассмотрены в соответствующей документации на СУБД.

10.7. Оптимизация приложений клиентов

10.7.1. Минимизация количества соединений с БД

Минимизация количества соединений с БД позволяет экономить ресурсы системы [3, 5, 13, 18]. Расход системных ресурсов может сказаться на эффективности доступа к данным. Рекомендуется сводить количество соединений к минимуму, а в идеале использовать только одно соединение для каждого приложения клиента.

Методы минимизации соединений с БД зависят от той среды разработки, в котором написано приложение-клиент. Поэтому Вы должны ознакомиться с соответствующей технической документацией. Например, для подключения к удаленной БД в приложениях - клиентах, написанных в среду Delphi или C-Bilder, используется компонент TDatabase. Он служит для:

1) создание постоянного соединения с БД;

2) создание локального псевдонима БД;

3) изменения параметров соединения, установленного для псевдонима БД;

4) управления трансакциями.

Если не использовать компонент TDatabase, то соединение с БД будет устанавливать каждый компонент типа «набор данных» - Ttable (таблица), TQuery (SQL-запрос), TStoredProc (хранимая процедура). Кроме того, при соединении с удаленной БД непосредственно, представленными компонентами типа «набор данных», изменять предустановленные параметры соединения невозможно.

10.7.2. Снижение сетевого трафика

Снижение сетевого трафика достигается за счет передачи в приложения клиентов минимально возможного объема информации из БД, которой достаточно для проведения манипуляций с данными. Например, из двух наборов данных, реализованные компонентами TTable и TQuery, для манипулирования данными в приложении-клиенте рекомендуется использовать последний компонент. Во-первых, при доступе к табличных данных компонент TTable считывает все записи таблицы, в то время как компонент TQuery - ровно столько, сколько необходимо для текущих целей визуализации, например, для заполнения таблицы TDВGrid. Во-вторых, при доступе к таблицам большого объема использования компонента TTable может привести к существенным временным задержкам.

Компоненты TTable и TQuery имеют разную природу: TTable ориентирован на навигационный метод доступа к данным, что более характерно для работы с локальными СУБД, TQuery - на работу с множеством записей, что характерно при доступе к удаленным БД архитектуры «клиент-сервер». Компонент TTable позволяет обратиться к одной таблице БД, TQuery может возвращать данные одновременно из нескольких таблиц БД. Соответствующее подтверждение изменений данных в TTable делается для каждой записи, что существенно увеличивает сетевой трафик. Изменение данных при использовании компонента TQuery может производиться сразу над множеством записей операторами INSERT, UPDATE, DELETE.

Компонент TStoredProc используется только для работы с вызываемыми процедурами и не применяется для работы с процедурами выбора, которые также могут возвращать наборы данных. Для работы с процедурами выбора используется компонент TQuery.

10.7.3. Перенос вычислительной нагрузки на сервер

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

  1. Старайтесь в приложении-клиенте реализовывать только интерфейс, отправление запросов к серверу и интерпретацию полученных данных.
  2. Не обращайтесь к серверу с запросом необоснованно большого объема данных, на которые приходится накладывать фильтры в приложении-клиенте.
  3. Максимально используйте возможности сервера, помня, что он может выполнять больше операций, чем приложения, быстрее и оптимальнее, и, кроме того, серверу нужно пересылать данные самому себе по сети.
  4. Реализуйте ограничения на значения, вводимые пользователем данных с помощью аппарата ограничений БД, а ссылочную целостность - с помощью триггеров.
  5. Запросы, требующие для своего выполнения разветвляющихся алгоритмов или циклических действий, а также вычисления значений, основанных на текущих данных из БД, реализуйте с помощью хранимых процедур.
  6. Бизнес-правила, связанные с транзакционными изменениями таблиц, реализуйте с помощью триггеров.
  7. Получайте уникальные значения числовых полей с помощью генераторов.
  8. Действия, которые повторяются и могут разделяться между различными приложениями и использоваться в SQL-операторах, реализуйте с помощью функций, определяемых пользователем (UDF).

10.8. КОНТРОЛЬНЫЕ ВОПРОСЫ

  1. Какие направления оптимизации работы с БД Вам известны?
  2. Как нормализация отношений влияет на работу БД?
  3. В чем разница между теорией и практикой взаимовлияния методов доступа к данным и их структуры?
  4. Каким образом процесс наладки внутренних механизмов СУБД влияет на оптимизацию ее работы?
  5. Дайте определение и характеристику индекса как структуры данных.
  6. Какая существует разница и что общего между ключами и индексами?
  7. Какими возможностями обладает большинство диалектов SQL при создании и удалении индексов из БД?
  8. Какие общие действия выполняет СУБД для оптимизации работы запросов?
  9. В каких случаях рекомендуется создавать индексы, а в каких нет?
  10. Какие особенности частичного использования составного индекса Вам известны?
  11. Когда можно избежать многопоточного поиска, заменив в условиях поиска предикат принадлежности к множеству на попадание в диапазон?
  12. Каким образом уменьшают общее количество индексов в БД?
  13. Что про разбалансировку индекса Вам известно?
  14. В чем заключается перестройка индекса?
  15. Что о показателе «полезности» индекса Вам известно?
  16. Какие возможности дает предложение PLAN оператору SELECT?
  17. В чем заключаются известные Вам направления оптимизации работы приложений-клиентов?
Вывод

Оптимальность работы СУБД всегда при доступе и манипулировании данными оценивается конечным пользователем по ее быстродействию. Оптимизация начинается на этапе логического моделирования предметной области (нормализация отношений), продолжается на этапе организации доступа к данным (оптимизация запросов) и завершается организацией взаимодействия клиентской части с СУБД.

© Куваев Я.Г., 2005—2020.

Все права защищены.

Вся информация, размещенная на данном веб-сайте, предназначена только для персонального использования и не подлежит дальнейшему воспроизведению и/или распространению в какой-либо форме, иначе как с письменного разрешения Автора.