Введение в СУБД MySQL

         

AES_DECRYPT(зашифрованная_строка, строка__ключа)


Эти функции позволяют выполнять шифрование и дешифрацию данных с использованием официального алгоритма AES (Advanced Encryption Standard), ранее известного как "Rijndael". Применяется кодирование с 128-разрядным ключом, но можно расширить его до 256 разрядов, должным образом изменив исходные тексты. Длина ключа 128 бит выбрана, поскольку он работает намного быстрее и при этом обеспечивает приемлемый уровень безопасности.

Входные аргументы могут иметь любую длину. Если любой из них равен NULL, результатом функции также будет NULL.

Поскольку AES - алгоритм блочного типа, дополнение применяется для строк с нечетным количеством символов, и поэтому длина результирующей строки может быть рассчитана как 16* (trunc (длина_строки/16) +1).

Если функция AES_DECRYPT() обнаруживает неверные данные или неправильное дополнение, она возвращает NULL. Однако существует вероятность, что AES_DECRYPT() вернет значение, не равное NULL (возможно, "мусор"), если входные данные или ключ не верны.

Вы можете использовать AES-функции для сохранения данных в зашифрованной форме, модифицировав существующие запросы:

INSERT INTO t VALUES (1, AES_ENCRYPT('text', 'password'));

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

SELECT @password:='my password1; INSERT INTO t VALUES (1, AES_ENCRYPT('text', 'password'));

Функции AES_ENCRYPT() и AES_DECRYPT() были добавлены в MySQL 4.0.2 и могут рассматриваться как наиболее криптографически безопасные функции, доступные в MySQL на текущий момент.



BENCHMARK(количество, выражение)


Функция BENCHMARK() выполняет выражение выражение в точности количество раз. Она может использоваться для определения того, насколько быстро MySQL выполняет выражение. Возвращаемый результат всегда равен 0. Предполагаемое применение - в среде клиента mysql, который сообщает время выполнения запроса:

mysql> SELECT BENCHMARK(1000000,ENCODE("hello","goodbye")); +----------------------------------------------+ | BENCHMARK(1000000,ENCODE("hello","goodbye")) | +----------------------------------------------+ | 0 | +----------------------------------------------+ 1 row in set (4.74 sec)

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



BIT_COUNT(N)


Возвращает количество битов аргумента N, которые установлены в единицу

mysql> SELECT BIT_COUNT(29); -> 4



Битовые функции


MySQL использует для двоичных операций 64-битовые величины BIGINT, следовательно, для двоичных операторов максимальный диапазон составляет 64 бита.





CHARSET(строка)


Возвращает набор символов аргумента строка.

mysql> SELECT CHARSET('abc'); -> 'latinl'

mysql> SELECT CHARSET(CONVERT('abc' USING utf8)); -> 'utf8'

mysql> SELECT CHARSET(USER()); -> 'utf8'



COERCIBILITY(строка)


Возвращает значение принуждения сопоставления для аргумента строка.

mysql> SELECT COERCIBILITY('abc' COLLATE latinl_swedish_сi); -> 0

mysql> SELECT COERCIBILITY('abc') ; -> 3

mysql> SELECT COERCIBILITY(USER()); -> 2

Возвращаемые значения имеют следующий смысл:

0 - явное сравнение

1 - нет сравнения

2 - неявное сравнение

3 - принуждаемое

Меньшие значения обладают большим приоритетом.



COLLATION(строка)


Возвращает наименование порядка сопоставления символьного набора для заданного аргумента строка.

mysql> SELECT COLLATION('abc'); -> 'latinl_swedish_ci'



CONNECTION_ID()


Возвращает идентификатор соединения (идентификатор потока) текущего сеанса. Каждое клиентское соединение получает свой собственный уникальный идентификатор.

mysql> SELECT CONNECTION_ID(); -> 23786



CURRENT_USER()


Возвращает комбинацию имени пользователя и имени хоста после аутентификации в текущем сеансе. Это значение соответствует пользовательской учетной записи MySQL, которая определяет ваши права доступа. Оно может отличаться от значения, возвращаемого функцией USER().

mysql> SELECT USER(); -> 'davida@localhost'

mysql> SELECT * FROM mysql.user;

ERROR 1044: Access denied for user: '@localhost' to database 'mysql'

mysql> SELECT CURRENT_USER(); -> '@localhost'

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



DATABASE()


Возвращает имя базы данных по умолчанию (текущей базы данных).

mysql> SELECT DATABASE();

-> 'test1'

Если текущей базы данных нет, DATABASE() возвращает NULL.



DECODE(зашифрованная_строка, строка_пароля)


Расшифровывает строку зашифрованная_строка, используя значение строка_пароля в качестве пароля. Аргумент зашифрованная_строка должен быть строкой, ранее возвращенной функцией ENCODE().



DES_DECRYPT(зашифрованная_строка [, строка_ключа])


Расшифровывает строку зашифрованная_строка, зашифрованную с помощью DES_ENCRYPT(). В случае ошибки возвращает NULL. Следует отметить, что эта функция работает, только если MySQL настроен на поддержку SSL. Если не указан аргумент строка_ключа, DES_DECRYPT() проверяет первый байт зашифрованной строки для определения номера DES-ключа, использованного при шифровании исходной строки, а затем читает ключ из файла DES-ключей для расшифровки сообщения. Чтобы это работало, пользователь должен иметь привилегию SUPER. Файл ключей может быть указан с помощью опции сервера - des-key-file.

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

Если аргумент зашифрованная_строка не выглядит как зашифрованная строка, MySQL вернет строку зашифрованная_строка без изменений.



DES_ENCRYPT(строка[, (номер_ключа\строка_ключа)])


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

Следует отметить, что эта функция работает, только если MySQL настроен на поддержку SSL. Ключ шифрования выбирается на базе второго аргумента DES_ENCRYPT(). Если таковой указан, то берётся первый ключ из используемого файла DES-ключей. Если задан номер_ключа, то он берётся из используемого файла DES-ключей. Если задана строка_ключа, то она используется в качестве ключа для шифрования.

Имя файла ключей указывается в опции сервера - des-key-file.

Длина строки результата рассчитывается как новая_длина = оригинальная_длина + (8 – (оригинальная_длина % 8)) + 1. Каждая строка в файле DES-ключей имеет следующий формат: номер_ключа строка_ключа_des.

Каждый номер_ключа должен быть числом в диапазоне от 0 до 9. Строки в файле могут следовать в любом порядке. строка_ключа_des - это строка, которая будет использоваться для шифрования сообщения. Между номером и ключом должен быть, по меньшей мере, один пробел. Первый ключ является ключом по умолчанию, который применяется в случае, если не указан аргумент строка_ключа в функции DES_ENCRYPT().

Можно указать MySQL на необходимость чтения новых значений ключа из файла ключей с помощью оператора FLUSH DES_KEY_FILE. Это требует наличия привилегии RELOAD.

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

mysql> SELECT customer_address FROM customer_table WHERE -> crypted_credit_card = DES_ENCRYPT('credit_card_number');



ENCODE(строка, строка_пароля)


Шифрует строку строка, используя значение строка_пароля в качестве пароля. Для расшифровки результата применяется функция DECODE(). Результатом является бинарная строка той же длины, что и строка. Если нужно сохранить ее в столбце, применяйте тип BLOB.



ENCRYPT(строка [, нач])


Шифрует строку строка, используя системный вызов Unix crypt().

mysql> SELECT ENCRYPT('hello'); -> 'VxuFAJXVARROc'

ENCRYPT() игнорирует все, кроме первых восьми символов аргумента строка. Это поведение определяется реализацией лежащего в основе системного вызова crypt().

Если функция crypt() не доступна в вашей системе, ENCRYPT() всегда возвращает NULL. По этой причине следует всегда применять вместо этой функции MD5(), поскольку эта функция представлена на всех платформах.



FORMAT(X,D)


Форматирует число X в формате, подобном '#,###,###.##', округленное до D разрядов, и возвращает результат в виде строки. Если D равно 0, результат не имеет десятичной точки или дробной части.

mysql> SELECT FORMAT(12332.123456, 4) ; -> '12,332.1235'

mysql> SELECT FORMAT(12332.1,4) ; -> '12,332.1000'

mysql> SELECT FORMAT(12332.2,0) ; -> '12,332'



FOUND_ROWS()


Оператор SELECT может включать конструкцию LIMIT для ограничения количества строк, которые сервер возвращает клиенту. В некоторых случаях желательно знать, сколько строк сервер вернул бы без конструкции LIMIT, но без повторного выполнения запроса. Чтобы получить значение счетчика строк, включите опцию SQL_CALC_FOUND_ROWS в состав оператора SELECT, после чего вызовите FOUND_ROWS():

mysql> SELECT SQL_CALC_FOUND_ROWS * FROM tbl_name -> WHERE id > 100 LIMIT 10; mysql> SELECT FOUND_ROWS();

Второй оператор SELECT вернет число, показывающее, сколько строк первый оператор SELECT вернул бы, будь он без конструкции LIMIT.

Следует отметить, что когда используется SELECT SQL_CALC_FOUND_ROWS, то MySQL приходится посчитать, сколько строк будет в полном результирующем наборе. Однако это делается быстрее, чем если запустить запрос снова без конструкции LIMIT, поскольку результирующий набор не приходится отсылать клиенту.

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

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

Применение SQL_CALC_FOUND_ROWS и FOUND_ROWS() более сложно для запросов с UNION, чем для простых операторов SELECT, потому что LIMIT может встретиться в UNION во многих местах. Они могут касаться отдельных операторов SELECT в составе UNION либо общего результата UNION в целом.

Цель SQL_CALC_FOUND_ROWS для UNION состоит в том, что он должен вернуть количество строк, которые будут возвращены без глобального LIMIT. Условия применения SQL_CALC_FOUND_ROWS с UNION перечислены ниже:

Ключевое слово SQL_CALC_FOUND_ROWS должно указываться в первом операторе SELECT.Значение FOUND_ROWS() будет точным только при условии применения UNION ALL. Если указано UNION без ALL, происходит исключение дубликатов, и значение FOUND_ROWS() будет лишь приблизительным.Если в UNION не присутствует LIMIT, то SQL_CALC_FOUND_ROWS игнорируется и возвращается количество строк во временной таблице, которая создается для выполнения UNION.



Функции шифрования


Функции, описанные в данном разделе, шифруют и дешифруют значения данных. Если вы хотите сохранять результаты функции шифрования, которые могут иметь произвольные байтовые значения, применяйте столбцы типа BLOB вместо CHAR или VARCHAR, чтобы избежать потенциальных проблем с удалением завершающих пробелов, которые изменяют значения данных.



Введение в СУБД MySQL


Пытается получить блокировку по имени, заданном строкой строка, с таймаутом длительностью таймаут секунд. Возвращает 1, если блокировка получена успешно, 0, если время ожидания превысило таймаут (например, из-за того, что другой клиент уже заблокировал это имя), либо NULL, если произошла ошибка (такая как переполнение памяти или уничтожение потока командой mysqladmin kill). Если у вас есть блокировка, полученная через GET_LOCK(), она снимается после выполнения RELEASE_LOCK(), нового вызова GET_LOCK() либо разрыва соединения (как нормального, так и не нормального).

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

mysql> SELECT GET_LOCK('lock1',10) ; -> 1

mysql> SELECT IS_FREE_LOCK('lock2'); -> 1

mysql> SELECT GET_LOCK('lock2',10); -> 1

mysql> SELECT RELEASE_LOCK('lock2'); -> 1

mysql> SELECT RELEASE_LOCK('lockl'); -> NULL

Следует отметить, что второй вызов RELEASE_LOCK() возвращает NULL, поскольку блокировка 'lock1' была автоматически снята вторым вызовом GETLOCK().



INET_ATON (выражение)


Принимает сетевой адрес, представленный четырьмя числами с разделителем-точкой, и возвращает целое, представляющее числовое значение адреса. Адрес может быть 4- или 8-байтным.

mysql> SELECT INET_ATON('209.207.224.40'); -> 3520061480

Сгенерированное число всегда содержит байты в порядке, заданном в сетевом адресе. Для только что приведенного примера оно вычисляется как 209 * 25 б3 + 207 * 2562 + 224 * 256 + 40.

INET_ATON() также понимает IP-адреса в сокращенной форме:

mysql> SELECT INET ATON('127.0.0.1'), INET_ATON('127.1'); -> 2130706433,""2130706433



INET_NTOA(выражение)


Принимает сетевой адрес в виде числа (4- или 8- байтного), возвращает адрес, представленный строкой, состоящей из четырех чисел, разделенных точкой.

mysql> SELECT INET_NTOA(3520061480); -> '209.207.224.40'



IS_FREE_LOCK(строка)


Проверяет, свободна ли блокировка с именем строка. Возвращает 1, если блокировка свободна (никем не используется), 0, если занята, и NULL в случае ошибки.

IS_USED_LOCK(строка). Проверяет, используется ли блокировка с именем строка (то есть, установлена ли она). Если это так, возвращает идентификатор соединения клиента, который удерживает блокировку. В противном случае возвращает NULL.



LAST_INSERT_ID(выражение)


Возвращает последнее автоматически сгенерированное значение, которое было вставлено в столбец AUTO_INCREMENT.

mysql> SELECT LAST_INSERT_ID(); -> 195

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

Значение, возвращаемое LAST_INSERT_ID() не изменяется, если вы обновляете столбец AUTO_INCREMENT в строке не с помощью "магических" значений (то есть, не NULL и не 0).

Если вы вставляете много строк одним оператором, LAST_INSERT_ID() возвращает значение для первой вставленной строки. Цель этого состоит в том, чтобы облегчить воспроизведение того же оператора INSERT на другом сервере.

Если указан аргумент выражение, значение аргумента возвращается функцией и запоминается как следующее значение, которое LAST_INSERT_ID() вернет при следующем вызове. Это можно использовать для эмуляции последовательностей:

Создать таблицу для хранения счетчика последовательности и инициализировать его:

mysql> CREATE TABLE sequence (id INT NOT NULL); mysql> INSERT INTO sequence VALUES (0); Использовать таблицу для генерации последовательности чисел:

mysql> UPDATE sequence SET id=LAST_INSERT_ID(id+l); mysql> SELECT LAST_INSERT_ID();

Оператор UPDATE увеличивает счетчик последовательности и заставляет следующий вызов LAST_INSERT_ID() возвращать измененное значение.

Вы можете генерировать последовательности без вызова LAST_INSERT_ID(), но польза от ее применения заключается в том, что значение идентификатора поддерживается сервером как последнее автоматически сгенерированное значение.

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



MASTER_POS_WAIT(имя_журнала, позиция_в_журнале [, таймаут])


Эта функция удобна для управления синхронизацией главный/подчиненный. Блокирует главный сервер до тех пор, пока подчиненный сервер не прочитает и не проведет все изменения вплоть до указанной позиции в бинарном журнале главного сервера. Возвращаемое значение представляет количество событий в журнале, обработку которых нужно выполнить системе синхронизации, чтобы дойти до указанной позиции. Функция возвращает NULL, если поток SQL подчиненного сервера не запущен, либо информация о главном сервере не инициализирована на подчиненном, либо указаны неправильные аргументы. Возвращает -1, если истекло время таймаута. Если подчиненный сервер уже достиг указанной позиции, функция возвращает управление немедленно.

Если задано значение таймаут, MASTER_POS_WAIT() прекращает ожидание, когда истекают таймаут секунд. Значение таймаут может быть больше 0, а если его значение равно 0 или является отрицательным, то ожидания нет.



MD5(строка)


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

mysql> SELECT MD5('testing'); -> 'ae2blfca515949e5d54fb22b8ed95575'



PASSWORD(строка)


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

mysql> SELECT PASSWORD ('badpwd'); -> '7f84554057dd964b'

Шифрование функцией PASSWORD() является однонаправленным (то есть необратимым).

Примечание: функция PASSWORD() используется системой аутентификации сервера MySQL, которая не должна быть задействованной в ваших собственных приложениях. Для этой цели вместо нее применяйте функции MD5() и SHA1().



Побитовый XOR (побитовое сложение по модулю 2) ( ^ )


mysql> SELECT 1 ^ 1; -> 0 mysql> SELECT 1 ^ 0; -> 1 mysql> SELECT 11 ^ 3; -> 8



RELEASE_LOCK(строка)


Снимает блокировку с именем строка, которая была получена с помощью функции GET_LOCK(). Возвращает 1, если блокировка снята, 0, если блокировка была установлена другим потоком (а значит, не может быть снята), и NULL, если блокировка с таким именем не существует. Блокировка не существует, если не была установлена вызовом GET_LOCK(), либо она уже снята.



SYSTEM_USER()


Возвращает имя текущего пользователя MySQL и имя хоста, с которого он подключился.

mysql> SELECT USER(); -> 'davida@localhost'

Значение представляет имя пользователя, которое было указано во время подключения к серверу, и имя компьютера-хоста, с которого произошло подключение. Возвращаемое значение может отличаться от того, которое выдает CURRENT_USER().

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

mysql> SELECT SUBSTRING_INDEX(USER(),'@',1); -> 'davida'



UUID()


Возвращает Универсальный Уникальный Идентификатор (Universal Unique Identifier - UUID). Идентификатор UUID спроектирован как число, которое является глобально уникальным во времени и пространстве. Ожидается, что два вызова UUID сгенерируют два разных значения, даже если эти два вызова произойдут на двух разных компьютерах, которые не подключены друг к другу.

UUID - это 128-разрядное число, представленное в виде строки, состоящей из пяти шестнадцатеричных чисел в формате aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:

Первые три числа генерируются на основе временной метки.Четвертое число предохраняет темпоральную уникальность в случае, если значение временной метки теряет монотонность (например, из-за перехода на летнее время и обратно).Пятое число - это номер узла IЕЕ 802, который представляет пространственную уникальность. Случайное число подставляется в случае, если последнее недоступно (например, если компьютер-хост не имеет сетевой платы Ethernet, или нет возможности извлечь аппаратный адрес интерфейса вашего компьютера). В этом случае пространственная уникальность не может быть гарантирована. Однако, несмотря на это, коллизии крайне маловероятны. В настоящее время МАС-адрес интерфейса принимается во внимание только в средах FreeBSD и Linux. В других операционных системах MySQL использует случайно сгенерированное 48-разрядное число.

mysql> SELECT UUID(); -> '6ccd780c-baba-1026-9564-0040f4311e29'



VERSION()


Возвращает строку, содержащую информацию о версии сервера MySQL:

mysql > SELECT VERSION(); -> '4.1.2-alpha-log'

Следует отметить, что если строка версии заканчивается на '-log', это означает, что регистрация в журнале включена.



Сохранение данных в базе данных


Мы научились извлекать данные из базы и выводить их на странице. Теперь давай попробуем осуществить обратное действие. С PHP это не составит большого труда.

Создадим простую форму:

<html> <body> <form method="post" action="<?php echo $PHP_SELF?>"> First name:<input type="Text" name="first"><br> Last name:<input type="Text" name="last"><br> Address:<input type="Text" name="address"><br> Position:<input type="Text" name="position"><br> <input type="Submit" name="submit" value="Enter information"> </form> </body> </html>

Обратите внимание, мы опять используем переменную $PHP_SELF. Как мы уже сказали, PHP-код можно как угодно перемешивать с обычным HTML. Также обратите внимание, что название каждого элемента формы совпадает с названием поля в базе данных.

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

Помимо этого мы присвоили имя кнопке Submit. Это сделано для того, чтобы в коде затем проверить, есть ли переменная $submit. Таким образом, когда страница будет вызываться, мы будем узнавать, вызывается ли она в первый или во второй раз.

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

Итак, давайте добавим код, который будет проверять, введены ли в форму данные. Пока это будет лишь простая проверка, при которой все переменные, передаваемые странице, будут выводиться на экран с помощью переменной $HTTP_POST_VARS. Эта переменная удобна в случае отладки. Если вы хотите вывести на экран вообще все переменные, используемые в странице, вызовите переменную $GLOBALS.


<html> <body> <?php if ($submit) { // process form while (list($name, $value) = each($HTTP_POST_VARS)) { echo "$name = $value<br>\n"; } } else { // display form ?> <form method="post" action="<?php echo $PHP_SELF?>"> First name:<input type="Text" name="first"><br> Last name:<input type="Text" name="last"><br> Address:<input type="Text" name="address"><br> Position:<input type="Text" name="position"><br> <input type="Submit" name="submit" value="Enter information"> </form>

<?php } // end if ?> </body> </html>

Теперь давайте возьмем переданную через форму информацию и внесем ее в базу данных.

Листинг 12.2.

(html, txt)

Мы внесли данные в базу. Тем не менее наш код далек от идеального. Что случится, если при заполнении формы кто-то оставит пустые поля или введет текст в поле, в которое надо ввести число? Что произойдет, если в поданных данных будет ошибка?

Ранее мы записывали SQL-выражение в переменную ($sql), прежде чем передать запрос в базу данных через функцию mysql_query(). Это делается на случай отладки. Если что-то пойдет не так, мы всегда сможем вывести интересующее нас SQL-выражение на экран и проверить, нет ли в нем ошибок.

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

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

Листинг 12.3.

(html, txt)



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

Также мы здесь используем слегка измененное SQL-выражение.

Листинг 12.4.

(html, txt)

Вот так. Нам удалось вместить все, что мы знаем и умеем в один код. Здесь вы можете увидеть, как мы используем выражение if() внутри другого выражения if() для последовательно проверки нескольких условий.

Теперь пришло время свести все вместе.

Листинг 12.5.

(html, txt)

На первый взгляд код выглядит сложным, однако это не так. Программа делится на три части. Первое if() выражение проверяет, была ли нажата кнопка Submit, и если была, проводится проверка, есть ли в поданных данных переменная $id. Если ее нет, значит происходит добавление новой записи. В противном случае мы редактируем уже существующую запись.

Далее мы проверяем, определена ли переменная $delete. Если да, мы удаляем запись. Обратите внимание, что в первом выражении if() мы проверяем переменную, которая была подана с помощью метода POST, а в данном if() выражении мы проверяем переменную, которая является частью данных отправленных с помощью метода GET.

Наконец, мы переходим к действию, которое будет выполняться по умолчанию: то есть выводим просто список служащих и форму. Здесь мы опять проверяем существование переменной $id. Если она существует, мы просим базу данных выдать сведения о выбранном служащем. В противном случае выводим пустую форму.

Все, что мы рассмотрели, мы поместили в один большой код. Мы использовали циклы while() и выражения if(), а также целую гамму основных команд языка SQL - SELECT, INSERT, UPDATE, и DELETE.

Наконец, мы рассмотрели, как можно передавать информацию от одной страницы к другой через URL с помощью ссылок и через формы.



if ($id) { if ($submit) { $sql = "UPDATE employees SET first='$first',last='$last', address='$address',position='$position' WHERE id=$id";

$result = mysql_query($sql); echo "Thank you! Information updated.\n"; } else { // query the DB $sql = "SELECT * FROM employees WHERE id=$id"; $result = mysql_query($sql); $myrow = mysql_fetch_array($result); ?> <form method="post" action="<?php echo $PHP_SELF?>"> <input type=hidden name="id" value="<?php echo $myrow["id"] ?>"> First name:<input type="Text" name="first" value="<?php echo $myrow["first"] ?>"><br> Last name:<input type="Text" name="last" value="<?php echo $myrow["last"] ?>"><br> Address:<input type="Text" name="address" value="<?php echo $myrow["address"] ?>"><br> Position:<input type="Text" name="position" value="<?php echo $myrow["position"] ?>"><br> <input type="Submit" name="submit" value="Enter information"> </form> <?php } } else { // display list of employees $result = mysql_query("SELECT * FROM employees",$db); while ($myrow = mysql_fetch_array($result)) { printf("<a href=\"%s?id=%s\">%s %s</a><br>\n", $PHP_SELF, $myrow["id"], $myrow["first"], $myrow["last"]); } } ?> </body> </html>

Листинг 12.4.

Вот так. Нам удалось вместить все, что мы знаем и умеем в один код. Здесь вы можете увидеть, как мы используем выражение if() внутри другого выражения if() для последовательно проверки нескольких условий.

Теперь пришло время свести все вместе.

<html> <body> <?php $db = mysql_connect("localhost", "root"); mysql_select_db("mydb",$db);



if ($submit) { // here if no ID then adding else we're editing if ($id) { $sql = "UPDATE employees SET first='$first',last='$last', address='$address',position='$position' WHERE id=$id"; } else { $sql = "INSERT INTO employees (first,last,address,position) VALUES ('$first','$last','$address','$position')"; } // run SQL against the DB $result = mysql_query($sql); echo "Record updated/edited!<p>"; } else if ($delete) { // delete a record $sql = "DELETE FROM employees WHERE id=$id"; $result = mysql_query($sql); echo "$sql Record deleted!<p>"; } else { // this part happens if we don't press submit if (!$id) { // print the list if there is not editing $result = mysql_query("SELECT * FROM employees",$db); while ($myrow = mysql_fetch_array($result)) { printf("<a href=\"%s?id=%s\">%s %s</a> \n", $PHP_SELF, $myrow["id"], $myrow["first"], $myrow["last"]); printf("<a href=\"%s?id=%s&delete=yes\">(DELETE)</a><br>", $PHP_SELF, $myrow["id"]); } } ?> <P> <a href="<?php echo $PHP_SELF?>">ADD A RECORD</a> <P> <form method="post" action="<?php echo $PHP_SELF?>"> <?php if ($id) { // editing so select a record $sql = "SELECT * FROM employees WHERE id=$id"; $result = mysql_query($sql); $myrow = mysql_fetch_array($result); $id = $myrow["id"]; $first = $myrow["first"]; $last = $myrow["last"]; $address = $myrow["address"]; $position = $myrow["position"]; // print the id for editing ?> <input type=hidden name="id" value="<?php echo $id ?>"> <?php } ?> First name:<input type="Text" name="first" value="<?php echo $first ?>"><br> Last name:<input type="Text" name="last" value="<?php echo $last ?>"><br> Address:<input type="Text" name="address" value="<?php echo $address ?>"><br> Position:<input type="Text" name="position" value="<?php echo $position ?>"><br> <input type="Submit" name="submit" value="Enter information"> </form> <?php } ?> </body> </html>



Листинг 12.5.

На первый взгляд код выглядит сложным, однако это не так. Программа делится на три части. Первое if() выражение проверяет, была ли нажата кнопка Submit, и если была, проводится проверка, есть ли в поданных данных переменная $id. Если ее нет, значит происходит добавление новой записи. В противном случае мы редактируем уже существующую запись.

Далее мы проверяем, определена ли переменная $delete. Если да, мы удаляем запись. Обратите внимание, что в первом выражении if() мы проверяем переменную, которая была подана с помощью метода POST, а в данном if() выражении мы проверяем переменную, которая является частью данных отправленных с помощью метода GET.

Наконец, мы переходим к действию, которое будет выполняться по умолчанию: то есть выводим просто список служащих и форму. Здесь мы опять проверяем существование переменной $id. Если она существует, мы просим базу данных выдать сведения о выбранном служащем. В противном случае выводим пустую форму.

Все, что мы рассмотрели, мы поместили в один большой код. Мы использовали циклы while() и выражения if(), а также целую гамму основных команд языка SQL - SELECT, INSERT, UPDATE, и DELETE.

Наконец, мы рассмотрели, как можно передавать информацию от одной страницы к другой через URL с помощью ссылок и через формы.

© 2003-2007 INTUIT.ru. Все права защищены.

Создаем ссылки на лету


Сейчас поработаем с параметрами запроса. Как вы уже наверняка знаете, существует три способа передачи параметров запроса. Первый, использовать метод GET в форме. Второй – набрать параметры прямо в адресной строке броузера. И третий, это вставить параметры в обычную ссылку на странице. То есть сделать ссылку примерно такого вида

<a href="http://my_machine/mypage.php3?id=1">

Научимся создавать такие ссылки на лету.

Для начала, давайте обратимся к базе данных и выведем список персонала. Взгляните на следующий код. Многое в нем вам будет знакомо.

<html> <body> <?php $db = mysql_connect("localhost", "root"); mysql_select_db("mydb",$db); $result = mysql_query("SELECT * FROM employees",$db); if ($myrow = mysql_fetch_array($result)) { do{ printf("<a href=\"%s?id=%s\">%s %s</a><br>\n", $PHP_SELF, $myrow["id"],$myrow["first"], $myrow["last"]); }while ($myrow = mysql_fetch_array($result)); }else { echo "Sorry, no records were found!"; } ?> </body> </html>

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

Далее, в коде используется интересная переменная $PHP_SELF. В этой переменной всегда хранится имя и URL текущей страницы. В данном случае эта переменная важна для нас потому, что мы хотим через ссылку вызвать страницу из нее самой. То есть вместо того, чтобы делать две страницы, содержащие разные коды для разных действий, мы все действия запихнули в одну страницу. С помощью условий if-else мы будем переводить стрелки с одного кода на другой, гоняя одну и ту же страницу по кругу. Это конечно увеличит размер страницы и время, необходимое на ее обработку, но в некоторых случая, такой трюк очень удобен.

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

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

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

Листинг 12.1.

(html, txt)

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

Итак, вы наконец создали действительно полезную PHP-страницу, работающую с MySQL.


<html> <body> <?php $db = mysql_connect("localhost", "root"); mysql_select_db("mydb",$db); // display individual record

if ($id) { $result = mysql_query("SELECT * FROM employees WHERE id=$id",$db);

$myrow = mysql_fetch_array($result); printf("First name: %s\n<br>", $myrow["first"]); printf("Last name: %s\n<br>", $myrow["last"]); printf("Address: %s\n<br>", $myrow["address"]); printf("Position: %s\n<br>", $myrow["position"]); }else { // show employee list $result = mysql_query("SELECT * FROM employees",$db);

if ($myrow = mysql_fetch_array($result)) { // display list if there are records to display do{ printf("<a href=\"%s?id=%s\">%s %s</a><br>\n", $PHP_SELF, $myrow["id"], $myrow["first"], $myrow["last"]); }while ($myrow = mysql_fetch_array($result)); }else { // no records to display echo "Sorry, no records were found!"; } } ?> </body> </html>

Листинг 12.1.

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

Итак, вы наконец создали действительно полезную PHP-страницу, работающую с MySQL.


Вывод данных из базы данных


Давайте теперь попробуем вывести все записи, хранящиеся в нашей базе данных. Обратимся к базе данных со следующим кодом:

<html> <body> <?php $db = mysql_connect("localhost", "root"); mysql_select_db("mydb",$db);

$result = mysql_query("SELECT * FROM employees",$db);

echo "<table border=1>\n"; echo "<tr><td>Name</td><td>Position</td></tr>\n";

while ($myrow = mysql_fetch_row($result)) { printf("<tr><td>%s %s</td><td>%s</td></tr>\n", $myrow[1], $myrow[2], $myrow[3]); }

echo "</table>\n"; ?> </body> </html>

Вы вероятно заметили, что в данном коде вы ввели несколько новых функций и конструкций. Наиболее очевидной из них является цикл while(). Цикл говорит, что до тех пор, пока в переменной $result остается запись для выборки, ее необходимо извлечь с помощью функции mysql_fetch_row и присвоить переменной $myrow. А после этого выполнить код, что расположен внутри фигурных скобок "{}". Приглядитесь к коду внимательнее и разберитесь в этой конструкции.

Для понимания этого кода разберем понятие "массив". Выполнение функции mysql_query дает в результате массив, который хранится в переменной $result. Если представить этот массив схематически, то он будет выглядеть как показано в табл 12.1:

Таблица 12.1.

0 id1 first2 last3 address4 positionПорядковый номер элемента массива

Переменная $result является массивом. Причем не простым массивом, а двумерным. В нем содержатся три строки с номерами от 0 до 2. каждая из которых содержит 5 столбцов от 0 до 4. Для того, чтобы вывести на странице все записи, нам надо пройти от 0-й строчки массива до 2-й. Лучше всего это делать в цикле с помощью функции mysql_fetch_row (которая в переводе буквально означает – "выбрать ряд").

Функции mysql_fetch_row в качестве параметра подается массив $result. Функция выбирает из него строку, которую мы можем записать в переменную $myrow и автоматически переходит на следующую строку. Вызвав снова mysql_fetch_row, выберем следующую строку из массива, и так далее до тех пор, пока не достигнем конца массива. В этом случае mysql_fetch_row вернет значение false, которое послужит сигналом, что все записи выбраны и можно завершить цикл.

Теперь стоит задача как-то вывести в теле цикла полученную запись. Выбранный ряд хранится в переменной $myrow. Она также, как и $result, является массивом, только одномерным. Схематически это выглядит как в табл. 12.2:

1BobSmith128 Here St, CitynameMarketing Manager0
2JohnRoberts45 There St ,TownvilleTelephonist1
3BradJohnson1/34 Nowhere Blvd, SnowstonDoorman2

Таблица 12.2.

0 id1 first2 last3 address4 positionПорядковый номер элемента массива<


/p>
А вот как будет выглядеть содержимое переменной $myrow при втором прохождении цикла (табл. 12.3):
1BobSmith128 Here St, CitynameMarketing Manager0

Таблица 12.3. 0 id1 first2 last3 address4 positionПорядковый номер элемента массива При третьем прохождении – как в табл. 12.4
2JohnRoberts45 There St ,TownvilleTelephonist1

Таблица 12.4. 0 id1 first2 last3 address4 positionПорядковый номер элемента массива К каждому столбцу в массиве $myrow мы можем обратиться по его порядковому номеру, который заключается в квадратные скобки. Например, в первом цикле, $myrow[1] равно "Bob", во втором $myrow[4] равно "Telephonist".
На первый взгляд процедура извлечения данных весьма сложна, но она вполне логически понятна и объяснима. Главное не забывайте, что элементы массивов нумеруются от 0, а не от 1.
Далее, вывод переменных в HTML с помощью функции printf() – дело техники, уже знакомой нам по предыдущему примеру.
Наш код содержит недостаток: если в базе данных не будут найдены записи, удовлетворяющие нашему запросу, мы не получим никакого сообщения об этом. Неплохо было бы, чтобы программа выдавала какое-нибудь сообщение. Сделаем ее более дружественной.
Взгляните на следующий код:
<html> <body> <?php $db = mysql_connect("localhost", "root"); mysql_select_db("mydb",$db);
$result = mysql_query("SELECT * FROM employees",$db); if ($myrow = mysql_fetch_array($result)) { echo "<table border=1>\n"; echo "<tr><td>Name</td><td>Position</td></tr>\n";
do{ printf("<tr><td>%s %s</td><td>%s</tr>\n", $myrow["first"], $myrow["last"], $myrow["address"]); } while ($myrow = mysql_fetch_array($result));
echo "</table>\n"; } else { echo "Sorry, no records were found!"; } ?> </body> </html>
В данном коде мы опять ввели некоторые новые понятия, но они достаточно просты. Во-первых, вместо функции mysql_fetch_row() мы использовали функцию mysql_fetch_array(). Она работает точно так же, как и mysql_fetch_row() за одним замечательным исключением: с помощью этой функции мы можем обращаться к каждому полю массива не по номеру, а по имени.
Например, если раньше для получения имени нам приходилось писать $myrow[1] (1 – второй столбец массива), то теперь мы можем писать $myrow["first"] ("first" – название столбца в базе данных и в массиве). Второй вариант естественно гораздо информативнее и удобнее.
Кроме этого, в коде использован цикл do/while и условная конструкция if-else. Выражение if-else говорит, что если мы можем присвоить значение $myrow, то надо начать выборку, в противном случае мы понимаем, что записей нет, переходим к блоку else и выводим соответствующее сообщение.
Чтобы проверить, как работает эта часть кода, замените SQL-выражение на "SELECT * FROM employees WHERE id=6" или на какое-нибудь другое, которое не даст результата.
Цикл do/while – это всего лишь вариант цикла while(), который мы использовали в предыдущем примере. Мы обратились за помощью к циклу do/while по одной простой причине. В конструкции if мы уже сделали выборку первого ряда и присвоили его переменной $myrow.
Если бы мы сейчас воспользовались прежней конструкцией (т.е. while ($myrow = mysql_fetch_row($result)), мы бы затерли значения первой выбранной записи, заменив ее значениями второй записи. В случае же с циклом do/while мы проверяем условие после того, как код цикла выполнится по крайней мере один раз. Таким образом, ни одна запись не ускользнет из наших рук.
А сейчас давайте сделаем код в цикле и if-else конструкцию еще более красивым.

DBI


Рекомендуемым методом доступа к базам данных MySQL и mSQL из Perl является интерфейс DBD/DBI. DBD/DBI означает DataBase Dependent/DataBase Independent (Зависимый от базы данных/Независимый от базы данных). Название связано с двухъярусной реализацией интерфейса. В нижнем ярусе находится зависимый от базы данных уровень. На нем существуют свои модули для каждого типа базы данных, доступного из Perl. Поверх этого уровня находится независимый от базы данных уровень. Это тот интерфейс, которым вы пользуетесь при доступе к базе данных. Выгода такой схемы в том, что программисту нужно знать только один API уровня независимости от базы данных. Когда появляется новая база данных, кому-нибудь нужно лишь написать для нее модуль DBD (зависимый), и она станет доступна всем программистам, использующим DBD/DBI.

Как и в любом модуле Perl, для получения доступа нужно указать DBI в директиве use:

#!/usr/bin/perl -w use strict; use CGI qw(:standard); use DBI;

При запуске программ Perl для MySQL/mSQL следует всегда задавать аргумент командной строки -w. Благодаря этому DBI будет перенаправлять все специфические для MySQL и mSQL сообщения об ошибках на STDERR, и вы сможете увидеть ошибки, вызванные работой с базой данных, не прибегая к явной проверке их в программе.

Всякое взаимодействие между Perl, с одной стороны, и MySQL и mSQL - с другой, производится с помощью объекта, известного как описатель базы данных (handle). Описатель базы данных (database handle) - это объект, представленный в Perl как скалярная ссылка и реализующий все методы, используемые для связи с базой данных. Одновременно можно открыть любое число описателей базы данных, ограничение накладывают только ресурсы системы. Метод connect() использует для создания описателя формат соединения DBI:servertype:database:hostname:port (имя узла и порта необязательны), дополнительными аргументами служат имя пользователя и пароль:

my $dbh = DBI->connect( 'DBI:mysql:mydata ', undef, Lindef); my $dbh = DBI->connect( 'DBI:mSQL:mydata:myserver', undef, undef); my $dbh = DBI->connect( 'DBI:mysql:mydata', 'me', 'mypass')',


Атрибут servertype является именем специфического для базы данных DBD-модуля, в нашем случае "mysql" или "mSQL" (обратите внимание на точное использование регистра). В первом варианте создается соединение с сервером MySQL на локальной машине через сокет Unix. Это наиболее эффективный способ связи с базой данных, который должен использоваться при соединении на локальном сервере. Если указано имя узла, оно используется для соединения с сервером на этом узле через стандартный порт, если только не задан и номер порта. Если при соединении с сервером MySQL вы не указываете имя пользователя и пароль, то пользователь, выполняющий программу, должен обладать достаточными привилегиями в базе данных MySQL. Для баз данных mSQL имя пользователя и пароль не должны указываться.

В Perl 5 используются два соглашения по вызову модулей. В объектно-ориентированном синтаксисе для ссылки на метод определенного класса используется символ стрелки "->" (как в DBI->connect). Другой метод - использование непрямого синтаксиса, в котором за именем метода следует имя класса, а затем - аргументы. В последнем примере метод connect следовало бы записать как connect DBI 'DBI:mysql:mydata', 'me', ' mypass. В ранних версиях Msql.pm использовался исключительно непрямой синтаксис, и требовалось придерживаться метода использования заглавных букв, обусловленного mSQL С API. Поэтому значительная часть старого кода MsqlPerl содержит строки типа SelectDB $dbh 'test' там, где можно было бы написать проще: $dbh->selectdb('test') . Если вы еще не догадались, то сообщаем, что мы неравнодушны к объектно-ориентированному синтаксису, хотя бы потому, что использование стрелки делает ясной связь между классом и методом.

Как только вы соединились с сервером MySQL или mSQL, описатель базы данных - во всех примерах этого раздела $dbh - становится шлюзом к базе данных. Например, так готовится запрос SQL:

$dbh->prepare($query);

При работе с mSQL для одного описателя базы данных можно одновременно выбрать только одну базу данных, это ограничение накладывается сервером mSQL. Однако в любой момент можно сменить текущую базу данных, повторно вызвав connect . При работе с MySQL можно включать в запрос другие базы данных, явно указывая их имена. Кроме того, и в MySQL, и в mSQL при необходимости одновременного доступа к нескольким базам данных можно создать несколько описателей базы данных и использовать их совместно.

Для иллюстрации использования DBI рассмотрим следующие простые программы. В примере 1 datashow.cgi принимает в качестве параметра имя узла; при отсутствии параметра принимается имя "local-host". Затем программа выводит список всех баз данных, имеющихся на этом узле.

Пример 1. Программа CGI datashow.cgi показывает все базы данных, имеющиеся на сервере MySQL или mSQL

Листинг 13.1.

(html, txt)

В примере 2 tableshow.cgi принимает в качестве параметров имя сервера базы данных (по умолчанию "localhost") и имя базы данных на этом сервере. Затем программа показывает все таблицы, имеющиеся в этой базе данных.

Пример 2. Программа CGI tableshow.cgi выводит список всех таблиц в базе данных

Листинг 13.2.

(html, txt)

И наконец, пример 3 показывает, как вывести все сведения о некоторой таблице.

Пример 3. Программа CGI tabledump.cgi выводит сведения об указанной таблице

Листинг 13.3.

(html, txt)



#!/usr/bin/perl - w use strict; use CGI qw( standard); use CGI::Carp; # Использовать модуль DBI use DBI; CGI::use_named_parameters(1); my ($server, $sock, $host); my $output = new CGI; $server = param('server') or Sserver = ''; # Подготовить DBD-драйвер для MySQL my $driver = DBI->install_driver('mysql'); my @databases = $driver->func($server, '_ListDBs'); # Если параметр @databases неопределен, предполагаем, # что на этом узле не запущен # сервер MySQL. Однако это может быть вызвано # другими причинами. Полный текст сообщения об ошибке # можно получить, проверив $DBI::errmsg. if (not @databases) { print header, start_html('title'=>"Данные no Sserver", 'BGCOLOR'=>'white'); print<<END_OF_HTML; <H1>$server</h1> Ha Sserver , по-видимому, не запущен сервер mSQL. </body></html> END_OF_HTML exit(0); } print header, start_html('title'=>" Данные по $host", 'BGCOLOR'=>'white'); print <<END_OF_HTML; <H1>$host</h1> <P> Соединение с $host на сокете $sock. <p> Базы данных:<br> <UL> END_OF_HTML foreach(@databases) { print "<LI>$_\n"; } print <<END_OF_HTML; </ul> </body></html> HTML exit(0)

Листинг 13.1.

В примере 2 tableshow.cgi принимает в качестве параметров имя сервера базы данных (по умолчанию "localhost") и имя базы данных на этом сервере. Затем программа показывает все таблицы, имеющиеся в этой базе данных.

Пример 2. Программа CGI tableshow.cgi выводит список всех таблиц в базе данных

#!/usr/bin/perl -w use strict; use CGI qw(:standard); use CGI::Carp; # Использовать модуль Msql.pm use DBI; CGI::use_named_parameters(1); my ($db); my $output = new CGI; $db = param('db')'or die("He указана база данных!"); # Connect to the requested server. my $dbh = DBI->connect("DBI:mysql:$db;$server", undef, undef); # Если не существует $dbh, значит, попытка соединения с сервером # базы данных не удалась. Возможно, сервер не запущен, # или не существует указанной базы данных, if (not $dbh) { print header, start_html('title'=>"Данные по $host => $db", 'BGCOLOR'=>'white'); print <<END_OF_HTML; <H1>$host</h1> <H2>$db</h2> Попытка соединения не удалась по следующей причине:<BR> $DBI::errstr </body></html> END_OF_HTML exit(0); } print header, start_html('title'=>"Данные по $host => $db", 'BGCOLOR'=>'white'); print <<END_OF_HTML; <H1>$host</h1> <H2>$db</h2> <р> Таблицы:<br> <UL> END_OF_HTML # $dbh->listtable возвращает массив таблиц, # имеющихся в текущей базе данных. my ©tables = $dbh->func( '_ListTables' ); foreach (@tables) { print "<LI>$_\n"; } print <<END_OF_HTML; </ul> </body></html> END_OF_HTML exit(0);



Листинг 13.2.

И наконец, пример 3 показывает, как вывести все сведения о некоторой таблице.

Пример 3. Программа CGI tabledump.cgi выводит сведения об указанной таблице

#!/usr/bin/perl -w use strict; use CGI qw(:standard); use CGI::Carp; # Использовать модуль DBI use DBI; CGI::use_named_parameters(1); my ($db,Stable); my Soutput = new CGI; $server = param('server') or $server = "; $db = param('db') or die("He указана база данных !"); # Соединиться с указанным сервером. my $dbh = DBI->connect("DBI:mysql:$db:$server", undef, undef); # Готовим запрос к серверу, требующий все данные # таблицы. my $table_data = $dbh->prepare("select * from Stable"); # Посылаем запрос серверу. $table_data->execute; # Если возвращаемое значение не определено, таблица не существует # или пуста; мы не проверяем, что из двух верно. if (not $table_data) { print header, startjtml( 'title'=> "Данные по $host => $db => Stable", 'BGCOLOR'=>'white'); prin<<END_OF_HTML; <H1>$host</h1> <H2>$db</h2> Таблицы'Stable' нет в $db на $host. </body></html> END_OF_HTML exit(0); } # Теперь мы знаем, что есть данные для выдачи. Сначала выведем # структуру таблицы. print header, start_html( title'=>"Данные по $host => $db => $table", 'BGCOLOR'=>'white'); print <<END_OF_HTML; <H1>$host</h1> <H2>$db</h2> <H3>$table</h3> <P> <TABLE BOROER> <CAPTION>Пoля</caption> <TR> <ТН>Поле<ТН>Тип<ТН>Размер<ТН>МОТ NULL </tr> <UL> END_OF_HTML If $table_data->name возвращает ссылку # на массив полей таблицы. my ©fields = @{$table_data->NAME}; # $table_data->type возвращает ссылку на массив типов полей. # Возвращаемые типы имеют стандартные обозначения SQL, # а не специфические для MySQL. my @types = @{$table_data->TYPE}; # $table_data->is_not_null возвращает ссылку на массив типа Boolean, # указывающий, в каких полях установлен флат 'NOT NULL'. my @>not_null = @{$table_data->is_not_null}; # $table_data->length возвращает ссылку на массив длин полей. Они фиксированные # для типов INT и REAL, но переменые (заданные при создании # таблицы) для CHAR. my @length = @{$table_data->length}; # Все перечисленные выше массивы возвращаются в одном и том же порядке, # поэтому $fields[0], $types[0], $ndt_null[0] and $length[0] относятся к одному полю. foreach $field (0..$#fields) { print "<TR>\n"; print "<TD>$fields[$field]<TD>$types[$field]<TD>"; print $length[$field] if $types[$field] eq 'SQL_CHAR'; print "<TD>"; print 'Y' if ($not_null[$field]); print "</tr>\n"; } print <<END_OF_HTML; </table> <P> <B>Data</b><br> <OL> END_OF_HTML # Теперь мы будем построчно перемещаться по данным с помощью DBI::fetchrow_array(). # Мы сохраним данные в массиве в таком же порядке, как и в информационных # массивах (§fields, @types, etc,), которые мы создали раньше. while(my(@data)=$table_data->fetchrow_array) { print "<LI>\n<UL>"; for (0..$#data) { print "<LI>$fields[$_] => $data[$_]</li>\n"; } print "</ulx/li>"; } print "END_OF_HTML; </ol> </body></html> END_OF_HTML

Листинг 13.3.


Пример приложения, использующего DBI


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

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

CREATE TABLE test ( id INT NOT NULL AUTO_INCREMENT, name CHAR(100), subject INT, num INT)

Для каждого отдельного экзамена структура таблицы такая:

CREATE TABLE t7 ( id INT NOT NULL, q1 INT, q2 INT, q3 INT, q4 INT, total INT )

К имени таблицы t присоединен идентификатор экзамена из таблицы test. При создании таблицы пользователь определяет количество вопросов. Поле total содержит сумму баллов по всем вопросам.

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

Листинг 13.4.

(html, txt)

Эта функция выводит форму, позволяющую пользователю выбрать предмет для экзамена, а также количество вопросов и название. Для вывода списка имеющихся предметов выполняется запрос к таблице предметов. При выполнении в DBI запроса SELECT он должен быть сначала подготовлен, а затем выполнен. Функция DBI::prepare полезна при работе с некоторыми серверами баз данных, позволяющими осуществить операции над подготовленными запросами, прежде чем выполнить их. Для MySQL и mSQL это означает лишь запоминание запроса до вызова функции DBI:: execute.

Результаты работы этой функции посылаются функции add2, как показано ниже:


sub add2 { my Ssubject = param('subjects'); [ my $num = param('num'); $name = param('name') if param('name'); my $out = $dbl"prepare(" select name from subject where id=$subject"); $out->execute; my (Ssubname) = $out->fetchrow_a.rray; print header, start_html('title'=>"Создание экзамена по предмету $subname", ' BGCOLOR'=>'white'); print <<END_OF_HTML; <H1> Создание экзамена по предмету $subname</h1> <h2>$name</h2> <P> <FORM ACTION="test.cgi" METHOD=POST> <INPUT TYPE=HIDDEN NAME="action" VALUE="add3"> <INPUT TYPE=HIDDEN NAME="subjects" VALUE="$subject"> <INPUT TYPE=HIDOEN NAME="num" VALUE="$num"> <INPUT TYPE=HIDDEN NAME="name" VALUE="$name"> Введите количество баллов за каждый правильный ответ. Сумма баллов не обязательно должна равняться 100. <Р> END_OF_HTML for (1..$num) { print qq%$_: <INPUT NAME="q$_" SIZE=3> %; if (not.$_ % 5) { print "<br>\n"; } } print <<END_OF_HTML; <P> Введите текст экзамена:<br> <TEXTAREA NAME="test" ROWS=20 COLS=60> </textarea> <p> <INPUT TYPE=SUBMIT VALUE="Ввести экзамен "> <INPUT TYPE=RESET> </form></body></html> END_OF_HTML }
Эта функция динамически генерирует форму для экзамена, основываясь на параметрах, введенных в предыдущей форме. Пользователь может ввести количество баллов для каждого вопроса экзамена и полный текст самого экзамена. Выходные данные этой функции посылаются завершающей функции add3, как показано ниже:
Листинг 13.5.
(html, txt)
Теперь осталось ввести информацию об экзамене в базу данных. Позднее, после сдачи экзамена учащимися, для каждого учащегося будет создана запись в таблице экзамена. Но эти действия ложатся на плечи читателя, поскольку целью данного материала было лишь введение в мир Perl и MySql.


Эта функция динамически генерирует форму для экзамена, основываясь на параметрах, введенных в предыдущей форме. Пользователь может ввести количество баллов для каждого вопроса экзамена и полный текст самого экзамена. Выходные данные этой функции посылаются завершающей функции add3, как показано ниже:
sub add3 { my $subject = para'm( 'subjects'); my $num = param('num'); $name = param('name') if param('name'); my $qname; ($qname = $name) =" s/'/\\'/g; my $q1 = "insert into test (id, name, subject, num) values ( '.'-, '$qname', $subject, $num)"; my Sin = $dbh->prepare($q1); $in->execute; # Извлечем значение ID , которое MySQL создал для нас my $id = $in->insertid; my $query = "create table t$id ( id INT NOT NULL, my $def = "insert into t$id values ( 0, "; my $total = 0; my @qs = grep(/^q\d+$/,param); foreach (@qs) { $query .= $_ . " INT,\n"; my $value = 0; $value = param($_) if param($_); $def .= "lvalue, "; $total += $value; } $query .= "total INT\n)"; $def .=-"$total)"; my $in2 = $dbh->prepare($query); $in2->execute; my $in3 = $dbh->prepare($def); $in3->execute; # Обратите внимание, что мы запоминаем экзамены в отдельных файлах. # Это полезно при работе с mSQL, поскольку он не поддерживает BLOB. # (Тип TEXT, поддерживаемый в mSQL 2, можно использовать, # но это неэффективно.) # Поскольку мы используем MySQL, можно с таким же успехом # поместить весь экзамен в BLOB. open(TEST,">teach/tests/$id") or die("A: $id $!"); print TEST param('test'), "\n"; close TEST; print header, start_html('title'=>'Экзамен создан', 'BGCOLOR'=>'white'); print <<END_OF_HTML; <Н1>Экзамен создан</h1> <P> Экзамен создан. <р> <А HREF=".">Перейти</а> на домашнюю страницу 'В помощь учителю'.<br> <А HREF="test.cgi">nepeimi</a> на главную страницу экзаменов.<br> <А HREF="test.cgi?actio,n=add">Добавить</a> следующий экзамен. </body></html> END_OF_HTML }
Листинг 13.5.
Теперь осталось ввести информацию об экзамене в базу данных. Позднее, после сдачи экзамена учащимися, для каждого учащегося будет создана запись в таблице экзамена. Но эти действия ложатся на плечи читателя, поскольку целью данного материала было лишь введение в мир Perl и MySql.
3BradJohnson1/34 Nowhere Blvd, SnowstonDoorman2







© 2003-2007 INTUIT.ru. Все права защищены.