Vkontakte
+7 (499) 705-13-20
skype: kuzma.feskov

ADODB - русская документация ч.3 - оптимизация

Автор: Кузьма Феськов, 23 сентября 2014

Оригинал на http://phplens.com/lens/adodb/docs-adodb.htm
Перевод Феськов Кузьма

(c) 2002-2003 John Lim (jlim#natsoft.com)

(c) 2005 Feskov Kuzma (kuzma#russofile.ru)

Это программное обеспечение распространяется под двумя лицензиями: BSD-style и LGPL. Это означает, что вы можете использовать его в компилируемых и коммерческих продуктах.

Рекомендации по созданию переносимого кода

Если вы создаете приложение, которое может быть использовано в множестве операционных системах, то вам необходимо распланировать поддержку различных баз данных. Эта статья опирается на опыт работы со множеством баз данных в том числе и на компьютере Макинтош. В настоящее время я использую такие базы данных как: Oracle, FoxPro, Access, MS SQL Server и MySQL. Хотя большинство советов относится в пользователям SQL на Perl, Python и др. языках программирования, я сосредоточусь на использовании PHP и на том, какую помощь предлагает нам ADODB.

Большинство баз данных являются закрытыми. Самый лучший или быстрый способ сделать что-то – это использовать патентованные (платные) расширения SQL. Все это чрезвычайно затрудняет написание переносимого SQL, который бы хорошо работал при любых условиях. Когда ANSI комитет собрался в 1984 году, чтобы стандартизировать SQL, большинство производителей баз данных имели настолько различные способы обработки данных, что они смогли договориться только об основных функциональных возможностях SQL. Множество важных определений небыло стандартизировано. И даже после принятия ANSI-92 SQL, который стандартизировал гораздо больше, мы все еще должны осуществлять переносимость на уровне кода.

SELECT

Конструкция SELECT была стандартизована лучше других. Почти каждая база данных поддерживает следующее:

SELECT [cols] FROM [tables]
  [WHERE conditions]
  [GROUP BY cols]
  [HAVING conditions]
  [ORDER BY cols]

Но много полезных возможностей можно реализовать только через патентованные расширения. Для примера, используя SQL выведем только первые 10 записей:

База данных SQL синтаксис
DB2 select * from table fetch first 10 rows only
Informix select first 10 * from table
Microsoft SQL Server and Access select top 10 * from table
MySQL and PostgreSQL select * from table limit 10
Oracle 8i select * from (select * from table) where rownum <= 10

Эта возможности при получении данных настолько полезна, что в ADODB у нас есть функция SelectLimit(). Эта функция позволяет вам скрывать детали формирования запроса в пределах функции, которая напишет запрос за вас.


$connection->SelectLimit('select * from table', 10);


Способы выдачи результата

PHP позволяет вам получать данные SQL запросов в виде массива. Вы можете выбрать, каким образом массив будет проиндексирован: названием поля или числом. Однако разные драйверы баз данных противоречивы в способах индексации. ADODB позволяет вам четко определить способ индексации. Нужный вам формат вы можете определить в переменной $ADODB_FETCH_MODE указав ей в виде значения любую из констант: ADODB_FETCH_NUM (для числовой индексации), или ADODB_FETCH_ASSOC (для индексации по названию полей).

Значение поумолчанию определяется ADODB в зависимости от базы данных. Для точного определения – используйте описанные выше значения. ADODB_FETCH_NUM – для более быстрой работы, ADODB_FETCH_ASSOC – для удобства.


Подсчет количества возвращаемых результатов

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

В ADODB, функция RecordCount() возвращает число выбранных данных, или она будет эмулировать это число путем буферизации данных и их «ручному» пересчету, после того, как все запрошенные данные будут получены. Вы можете отключить эмуляцию по соображениям скорости при запросе большого количества результатов. Для этого вам необходимо задать значение глобальной переменной $ADODB_COUNTRECS = false. Эта переменная проверяется всегда, когда вы запускаете запрос, так что вы можете выбирать, когда вам нужно посчитать количество возвращаемых результатов, а когда нет.

Даже если вы установили $ADODB_COUNTRECS = false, у вас есть функция PO_RecordCount(). Эта функция вернет вам количество строк, а в случае, если эта информация будет не доступна, возвратит вам результат запроса SELECET COUNT(*):

$rs = $db->Execute("select * from table where state=$state");
$numrows = $rs->PO_RecordCount('table', "state=$state");


Locking (блокирование, захват)

При использовании SELECT обычно требуется заблокировать на время выборки данные. Ряд баз данных, например, Oracle, Interbase, PostgreSQL и MySQL с InnoDB не требуют блокирования, потому что используют versionning (версионность), чтобы показать данные на данный конкретный момент времени.

На данный момент я рекомендую инкапсулировать блокировку при помощи функции RowLock($table, $where).

$connection->BeginTrans( );
$connection->RowLock($table, $where); 

# какие-то действия

if ($ok) $connection->CommitTrans( );
else $connection->RollbackTrans( );


Outer Joins (Внешнее присоединение)

Не все базы данных поддерживают внешнее присоединение. К тому же, синтаксис у разных баз данных сильно отличается. Один из переносимых способов (возможно, медленных) делать внешние присоединения – это использование UNION.

Например, в ANSI-92 внешнее присоединение слева между двумя таблицами может выглядеть так:

SELECT t1.col1, t1.col2, t2.cola 
  FROM t1 LEFT JOIN t2 ON t1.col = t2.col

Это можно эмулировать следующим образом:

SELECT t1.col1, t1.col2, t2.cola FROM t1, t2 
           WHERE t1.col = t2.col 
   UNION ALL
SELECT col1, col2, null FROM t1 
           WHERE t1.col not in (select distinct col from t2)

С версии 2.13 в ADODB есть некоторые функции для помощи в составлении таких запросов. Эти функции все еще не полны и не совершенны и часто зависят от версии используемой базы данных. Но они могут быть полезны как общее руководство:

$conn->leftOuter: возвращает оператор для внешнего присоединения слева (например, *=), или FALSE, если составление оператора невозможно.

$conn->rightOuter: возвращает оператор для внешнего присоединения справа (например, =*), или FALSE, если составление оператора невозможно.

$conn->ansiOuter: булевая переменная. TRUE – если ваша база данных поддерживает ANSI-92, FALSE – если вы не уверены или не знаете об этом.

INSERT

Когда вы заносите в таблицу запись, то необходимо создать уникальный ID записи. Существует 2 общих способа: 1) auto-increment колонки, 2) последовательности.

Auto-increment колонки поддерживаются MySQL, Sybase и Microsoft Access и SQL Server. Однако большинство других баз не поддерживают такую возможность. Отсюда видно, что для создания переносимого кода у вас не такой большой выбор, а именно – использовать последовательности. Последовательности – это специальные функции, которые возвращают уникальное число каждый раз, когда вы к ним обращаетесь. Эти числа вполне подходят для использования в качестве уникальных ключей. В ADODB мы используем функцию GenId(). В качестве параметра она принимает название последовательности. Различные таблицы могут иметь разные последовательности.

$id = $connection->GenID('sequence_name');
$connection->Execute("insert into table (id, firstname, lastname) 
                      values ($id, $firstname, $lastname)");

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

Binding (обвязка)

Обвязка переменный в SQL запросе – это еще одна хитра особенность. Обвязка полезна, поскольку позволяет перекомпилировать SQL запрос. При многократной вставке однотипных данных в цикле, обвязка может сохранить до 50% (или больше) ресурсов и времени. Однако, многие базы данных, например Access и MySql не поддерживают нативно обвязку, а потому приходится делать некоторую надстройку для эмуляции обвязки. Кроме того, отдельные базы данных (особенно Oracle!), делают обвязку подругому. Я рекомендую делать обвязку только в том случае, если ваши запросы к базе данных очень медленные. Но удостоверьтесь, что ваша база данных поддерживает обвязку, например, Oracle.

ADODB поддерживает переносимые Portable/Execute:

$stmt = $db->Prepare('select * from customers where custid=? and state=?');
$rs = $db->Execute($stmt, array($id,'New York'));

Oracle использует именованные placeholders (не «?»). Для создания переносимого кода мы имеем функцию Param(), которая производит правильный placeholder (с версии ADODB 3.92):

$sql = 'insert into table (col1,col2) values ('.$DB->Param('a').','.$DB->Param('b').')';
# генерирует 'insert into table (col1,col2) values (?,?)'
# или        'insert into table (col1,col2) values (:a,:b)'
$stmt = $DB->Prepare($sql);
$stmt = $DB->Execute($stmt,array('one','two'));

Переносимый нативный SQL

ADODB обеспечивает следующие функции для генерации частей SQL запроса для встраивания затем их в основной запрос (некоторые доступны только с версии 3.92):

Функция Описание
DBDate($date) Берет UNIX timestamp или дату в формате ISO, и конвертирует ее в строку, пригодную для запросов INSERT/UPDATE
DBTimeStamp($date) Берет UNIX timestamp или дату в формате ISO, и конвертирует ее в строку, пригодную для запросов INSERT/UPDATE
SQLDate($date, $fmt) Переносимо форматирует дату по маске из $fmt для использвания ее в SELECT запросе
OffsetDate($date, $ndays) Переносимо генерирует дату ($date) с добавлением дней ($ndays).
Concat($s1, $s2, ...) Переносимо объединяет строки. Как альтернатива, для mssql, используйте mssqlpo драйвер, который принимеет || оператор.
IfNull($fld, $replaceNull) Возвращает строку иквивалентную MySQL IFNULL или Oracle NVL.
Param($name) Генерирует placeholders, используя ? или именованные соглашения.
$db->sysDate Формирует SQL запрос, который возвращает системную дату.
$db->sysTimeStamp Формирует запрос, который возвращает тайм штамп (date+time).
$db->concat_operator Формирует оператор объединения.
$db->length Возвращает название strlen SQL функции.
$db->upperCase Возвращает название strtoupper SQL функции.
$db->random Возвращает SQL, который генерирует случайное число между 0.00 и 1.00.
$db->substr Возвращает название substring SQL функции.