После подключения переменных и определений функций задается уровень ото+ бражения ошибок (error_reporting(0)), который предотвращает появление PHP+ сообщений об ошибках или предупреждений.
Также можно подавить ошибки, используя префикс @ в вызовах функций mysql_connect() и mysql_select_db().
Если соединение с базой данных создается успешно, то функция db_connect() возвращает идентификатор этого соединения. В противном случае устанавливаются глобальные переменные $MYSQL_ERRNO и $MYSQL_ERROR, и функция возвращает нуль, указывая таким образом на возникновение ошибки. После этого сценарий за+ вершает свою работу:
if(!$link_id) die(sql_error());
Вместо непосредственного определения сообщения об ошибке вызывается функ+ ция sql_error(), создающая соответствующее сообщение:
По существу, эта функция только возвращает символьную строку, содержащую код ошибки и сообщение об ошибке, разделенные двоеточием. Вместе с тем эта функция будет весьма полезной в ситуациях, когда возвращаемые переменные не были опреде+ лены. Например, можно вызывать функцию sql_error() сразу за ошибкой базы дан+ ных; проблема заключается в том, что переменные $MYSQL_ERRNO и $MYSQL_ERROR в таком случае не будут определены. Чтобы эта функция была полезной в такой ситуа+ ции (а также в случае предугадываемого сбоя соединения), необходимо проверять значение глобальной переменной $MYSQL_ERROR. Если переменная пуста, то следу+ ет вызвать функции mysql_errno() и mysql_error() и назначить значения обе+ им переменным.
После успешного подключения функция db_connect() пытается выбрать базу дан+ ных по умолчанию, имя которой задано в глобальной переменной $default_dbname. Можно усовершенствовать функцию db_connect(), заставив ее принимать необязатель+ ный аргумент $dbname, так чтобы она выбирала указанную в аргументе базу данных:
function db_connect($dbname='') {
global $dbhost, $dbusername, $dbuserpassword, $default_dbname; global $MYSQL_ERRNO, $MYSQL_ERROR;
Идентификатор соединения, записываемый в переменную $link_id, автоматиче+ ски будет связываться с базой данных sample_db. Если аргумент не задан, то функция db_connect() по умолчанию будет использовать базу данных, указанную в перемен+ ной $default_dbname.
Создание баз данных и таблиц с помощью MySQL-клиента
Вернемся к созданию баз данных в командной строке из mysql+клиента. Для того чтобы создать базу данных sample_db, необходимо ввести следующую команду:
В количество затронутых строк mysql не включает удаленные строки. Можно уда+ лить тысячи записей, а согласно отчету mysql затронутых строк будет все равно 0. По+ этому эту небольшую и на вид безвредную команду следует использовать с особой ос+ торожностью ++++++ она полностью удаляет заданную базу данных и все таблицы в ней без каких+либо подсказок пользователю, не говоря уже о подтверждении.
Предположим, что база данных sample_db только что была удалена. Создадим ее снова с помощью команды CREATE (эта команда часто используется в двух последую+ щих главах). Представленные далее примеры поэтапно показывают, как создавать структуру и записи в базе данных.
Создание таблицы базы ++++++ более сложный процесс. Предположим, что требуется создать таблицу с именем user. Если начать с очевидного на первый взгляд синтакси+ са, то появится следующее сообщение:
mysql> CREATE TABLE user;
ERROR 1113: A table must have at least 1 column
(ОШИБКА 1113: в таблице должен быть хотя бы один столбец) mysql>
Хотя база данных может нормально существовать вообще без таблиц, сами табли+ цы более требовательны. Необходимо задать некоторую структуру, введя в скобках
406 Глава 9
после имени таблицы несколько описаний полей. Описание поля задается в следую+ щей форме:
fieldname TYPE(length)
Например, создадим очень простую таблицу с двумя полями:
Выше уже рассматривались различные типы переменных, поддерживаемые MySQL. Кроме того, существует возможность задать атрибуты для каждого поля ++++++
чтобы это сделать, необходимо добавить их к дескриптору. Описанные ниже атрибу+ ты можно использовать в любой комбинации.
Атрибуты
Описание
BINARY
Делает значение поля чувствительным к регистру символов.
Работает только с полями типа CHAR и VARCHAR
NULL или NOT NULL
Поля NOT NULL не допускают NULL-значений. Поля, объявленные
как NULL, сохраняют значение, заданное по умолчанию, в случае
если в поле записывается NULL.
Если значение по умолчанию не указано, то в поле просто
сохраняется NULL. Если атрибут не задан, то предполагается NULL
DEFAULT
Определяет значение по умолчанию, если система пытается
default_value
записать в поле значение NULL
AUTO_INCREMENT
Если программа пытается записать в поле AUTO_INCREMENT
значение NULL, то фактически в поле записывается значение,
на единицу превышающее текущее максимальное значение в этом
поле таблицы. AUTO_INCREMENT работает только с уникальными
целочисленными полями.
В таблице может присутствовать только одно поле AUTO_INCREMENT
После того как заданы типы полей, можно определить конкретные поля как ин+ дексы, первичные ключи и уникальные значения. Ниже представлено краткое описа+ ние данных элементов.
KEY/INDEX: ключевые слова+синонимы, указывающие на то, что данное поле используется как индекс. Значения заданных полей копируются в отдельную индексную таблицу, где они перечисляются вместе с указателями на соответст+ вующие записи в исходной таблице. Затем они сортируются, составляя индекс, во многом напоминающий предметный указатель в конце обычной книги. Если указать в качестве индекса несколько полей ("field1", "field2",...), то сначала индекс будет отсортирован по полю "field1", а затем все повторяю+ щиеся значения в поле "field1" будут сортироваться по полю "field2" и т.д.
PRIMARY KEY: в качестве первичного ключа можно указать только одно поле
втаблице. Впоследствии оно будет использоваться как индекс, состоящий из уникальных значений. В результате каждая запись может затем использоваться
вдругих таблицах как уникальная ссылка на запись, которой она принадле+ жит, ++++++ по сути, это связующий материал, на котором основываются реляцион+ ные базы данных. Чтобы использовать поле как первичный ключ, его необходимо
Введение в базы данных и SQL 407
объявить как NOT NULL. Стоит еще раз подчеркнуть, что если при определении первичного ключа указать более одного имени поля, то первичный ключ будет создан на основе объединенных имен полей.
UNIQUE: значение каждой записи в UNIQUE+поле должно быть уникальным в пределах этого поля. Иными словами, если одна запись в поле содержит зна+ чение 10, то система гарантирует, что ни одна другая запись в данном поле не имеет такого же значения. Как уже отмечалось, уникальность ++++++ обязательное требование для AUTO_INCREMENT+полей.
В двух последующих главах приводится множество примеров, основанных на не+ большой реляционной базе данных, состоящей из двух таблиц. Первая таблица назы+ вается user и служит как ведомость всех пользователей, зарегистрированных на во+ ображаемом Web+сервере. Вторая таблица, access_log, содержит относительные пути к Web+страницам, которые посещал каждый пользователь, а также счетчик по+ сещений каждой страницы. Обе таблицы содержатся в базе данных sample_db.
Рассмотрим структуру первой таблицы user:
Поле
Тип
Null
Атрибуты
usernumber
MEDIUMINT(10)
нет
AUTO_INCREMENT
userid
VARCHAR(8)
нет
BINARY
userpassword
VARCHAR(20)
нет
BINARY
username
VARCHAR(30)
нет
не задано
userposition
VARCHAR(40)
нет
не задано
useremail
VARCHAR(50)
нет
не задано
userprofile
VARCHAR (250)
нет
не задано
Поле usernumber имеет атрибут AUTO_INCREMENT, поэтому каждый раз при до+ бавлении новой записи с NULL+значением в первом поле значение этого поля будет увеличиваться на единицу. Например, когда регистрируется новый пользователь и его запись добавляется в таблицу user, номер, назначаемый пользователю, будет на единицу больше, чем максимальное значение, имеющееся в данном поле этой табли+ цы. Кроме того, это поле объявляется как UNIQUE, а это гарантирует, что ни какие две записи не могут иметь одно и то же значение данного поля.
Значениями полей userid и userpassword могут быть строки различной длины (длина указывается как 8 и 20 символов соответственно). Эти поля объявляются как BINARY и, следовательно, они чувствительны к регистру символов. По умолчанию символьные поля нечувствительны к регистру.
Поле userid определяется как первичный ключ и используется для создания свя+ зи с таблицей access_log, в которой также имеется поле userid.
Поле userposition описывает позицию игрока в команде ++++++ в примере имеются такие значения, как Mid и Link, но их можно легко изменить на футбольные позиции, такие как защитник или нападающий, если база данных используется для хранения информации о команде по американскому футболу.
408 Глава 9
Вторая таблица access_log определяется следующим образом:
Поле
Тип
Null
Атрибуты
page
VARCHAR(250)
нет
нет
userid
VARCHAR(8)
нет
BINARY
visitcount
MEDIUMINT(5)
нет
нет
accessdate
TIMESTAMP(14)
да
нет
Поле типа TIMESTAMP всегда может принимать NULL+значение, потому что если дата и время в данном поле не заданы явно, при добавлении новой записи или при обновле+ нии имеющейся сохраняется текущее системное время в формате YYYMMDDhhmmss.
В таблице access_log имеется такое же поле userid, как в таблице user. Оно служит в качестве ключа для определения связи между двумя таблицами.
Теперь, когда структуры таблиц определены, следует создать сами таблицы и базу данных, в которую они входят (sample_db).
Читатели, которые опасаются вводить следующий 12*строчный SQL*оператор (при вводе длинного оператора возможны опечатки), могут воспользоваться приведенными ниже рекомендациями.
Если PHP работает на Unix*машине, то можно использовать в командной строке mysql команду edit, которая вызывает используемый по умолчанию текстовый редактор
(в Windows эта команда не работает). Имя текстового редактора хранится в системной переменной $EDITOR. Как правило, таким редактором в Unix*системах является vi: mysql> edit. Если в процессе ввода SQL*оператора была допущена опечатка, то следует ввести команду edit еще раз. Редактор хранит предыдущий ввод.
Предыдущая команда сохраняется клиентской программой mysql. Нажав клавишу ‘‘стрелка вверх’’, можно вернуться на одну строку.
Можно сохранить SQL*оператор в текстовом файле, а затем передать этот файл программе mysql: mysql uphpuser pphppass hlocalhost < query.sql
Команда CREATE TABLE создает таблицу user в базе данных sample_db:
mysql> CREATE TABLE user (
-> usernumber MEDIUMINT(10) DEFAULT '0' NOT NULL AUTO_INCREMENT, -> userid VARCHAR(8) BINARY NOT NULL,
-> userpassword VARCHAR(20) BINARY NOT NULL, -> username VARCHAR(30) NOT NULL,
-> userposition VARCHAR(50) NOT NULL, -> useremail VARCHAR(50) NOT NULL, -> userprofile TEXT NOT NULL,
-> PRIMARY KEY (userid),
-> UNIQUE usernumber (usernumber) -> );
Query OK, 0 rows affected (0.69 sec) mysql>
Команда CREATE TABLE создает таблицу access_log:
mysql> CREATE TABLE access_log (
-> page VARCHAR(250) NOT NULL,
-> userid VARCHAR(8) BINARY NOT NULL,
Введение в базы данных и SQL 409
-> visitcount MEDIUMINT(5) DEFAULT '0' NOT NULL, -> accessdate TIMESTAMP(14),
В таблице access_log поле userid не объявляется как первичный ключ или как уникальное значение, потому что пользователь может посещать множество Web+ страниц, и каждый раз при посещении страницы в этой таблице остается соответст+ вующая запись. Вместо этого в качестве первичного ключа определена комбинация полей userid и page.
Создание демонстрационной базы данных и таблиц с помощью PHP
Предположим, что все прошло по плану, и теперь в системе имеется база данных и две таблицы. И поскольку таблицы так же просто можно генерировать из PHP+сценариев, попробуем создать базу данных и таблицы, используя PHP вместо mysql+клиента.
Для начала нужен способ, позволяющий вводить из PHP запросы к MySQL+серверу. Для этого предназначена функция mysql_query(). В качестве первого аргумента она принимает строку SQL+запроса и применяет данный запрос к выбранной в текущий момент базе данных, используя при этом идентификатор соединения, заданный вто+ рым аргументом. Как обычно, идентификатор можно опустить, тогда будет использо+ ваться последнее открытое соединение. Если открытых соединений нет, то функция сначала попытается самостоятельно создать соединение.
Использовать точку с запятой для завершения строки SQL*запроса, когда она передается в качестве аргумента в PHP*функцию, не следует, потому что это может привести к появлению ошибок в сценарии.
Если заданный запрос выполняется успешно, то функция mysql_query() возвращает ненулевое значение, которое указывает на возвращенное результирующее множество,
или False в случае ошибки. Следующий вызов эквивалентен выполнению запроса SHOW DATABASES в mysql*клиенте:
Данная функция возвращает список доступных таблиц в базе данных sample_db, даже если эта база данных не была выбрана явно с помощью функции mysql_select_db().
410 Глава 9
Кроме того, функцию mysql_query() можно использовать для создания и удале+ ния баз данных:
В PHP имеется также альтернативный способ создания и удаления баз данных ++++++
пара специально предназначенных для этого функций: mysql_create_db() и mysql_drop_db(). Обе функции в качестве первого аргумента принимают имя базы данных, которую необходимо создать или удалить, а также необязательный иденти+ фикатор соединения и возвращают True, если база данных была успешно созда+ на/удалена, и False в случае ошибки. Например:
echo "База данных dummy_db была успешно удалена.";
Практика Создание базы данных и таблиц
Следующий сценарий представляет собой PHP+аналог описанных выше действий в командной строке. Сначала сценарий создает базу данных sample_db, а затем табли+ цы user и access_log согласно приведенным ранее определениям.
<?php
include "./common_db.inc";
$dbname = "sample_db"; $user_tablename = 'user';
$user_table_def = "usernumber MEDIUMINT(10) NOT NULL AUTO_INCREMENT,"; $user_table_def .= "userid VARCHAR(8) BINARY NOT NULL,"; $user_table_def .= "userpassword VARCHAR(20) BINARY NOT NULL,"; $user_table_def .= "username VARCHAR(30) NOT NULL,";
$user_table_def .= "userposition VARCHAR(50) NOT NULL,";
$user_table_def .= "useremail VARCHAR(50) NOT NULL,"; $user_table_def .= "userprofile TEXT NOT NULL,"; $user_table_def .= "PRIMARY KEY (userid),"; $user_table_def .= "UNIQUE usernumber (usernumber)";
$access_log_tablename = "access_log"; $access_log_table_def = "page VARCHAR(250) NOT NULL,";
1007: Невозможно создать базу данных 'sample_db'. База данных существует
Чтобы избежать этого, можно вернуться к командной строке и, используя клиент mysql, удалить базу данных sample_db, а затем запустить данный сценарий снова. В результате должен появиться следующий вывод:
База данных sample_db была успешно создана. Таблицы user и access_log были успешно созданы.
Как это работает
Данный сценарий весьма прост. Он начинается с подключения файла common_ db.inc при условии, что используются функции db_connect() и sql_error(), ко+ торые были определены ранее:
<?php
include "./common_db.inc";
Затем определяются переменные, описывающие две таблицы. Здесь выполняется основная работа ++++++ необходимо задать те же дескрипторы полей, которые задавались в примере с использованием командной строки:
$dbname = "sample_db"; $user_tablename = 'user';
$user_table_def = "usernumber MEDIUMINT(10) NOT NULL AUTO_INCREMENT,"; $user_table_def .= "userid VARCHAR(8) BINARY NOT NULL,"; $user_table_def .= "userpassword VARCHAR(20) BINARY NOT NULL,"; $user_table_def .= "username VARCHAR(30) NOT NULL,";
$user_table_def .= "userposition VARCHAR(50) NOT NULL,";
$user_table_def .= "useremail VARCHAR(50) NOT NULL,"; $user_table_def .= "userprofile TEXT NOT NULL,"; $user_table_def .= "PRIMARY KEY (userid),"; $user_table_def .= "UNIQUE usernumber (usernumber)";
$access_log_tablename = "access_log"; $access_log_table_def = "page VARCHAR(250) NOT NULL,";
if(!mysql_query("CREATE DATABASE $dbname")) die(sql_error()); echo "База данных $dbname была успешно создана.<BR>";
После чего эта база данных выбирается с помощью функции mysql_select_db():
if(!mysql_select_db($dbname)) die(sql_error());
412 Глава 9
Последующие вызовы функции mysql_query() создают таблицы user и access_log. Если во время выполнения запроса возникает ошибка, то в браузер пользователя вы+ водится номер ошибки и ее текстовое описание (для этого используется функция sql_error()).
Это гарантирует, что функция возвращает номер ошибки и ее описание, даже если подключение к базе данных было успешным, но последующий запрос оказался неудачным.
Изменение структурытаблицы
Итак, база данных создана и в ней имеется две таблицы. Эта база данных уже ис+ пользовалась несколько раз, и очевидно, что она неидеальна, но вполне справляется с поставленными перед ней задачами. Предположим, что в будущем неожиданно вы+ яснится, что база данных работает не так, как нужно, и что таблицы спроектированы неудачно. Кроме того, обнаруживаются новые данные, которые не вписываются в ожидаемый формат. Очень многие факторы могут подтолкнуть разработчика к мо+ дификации таблиц в базе данных.
Для модификации таблиц MySQL предоставляет команду ALTER TABLE. С ее по+ мощью можно выполнять следующие операции:
добавлять или удалять поля или индексы (ключевые слова ADD и DROP);
изменять определения существующих полей (ключевые слова ALTER, CHANGE
и MODIFY);
переименовывать поля (CHANGE) или даже саму таблицу (RENAME AS).
Например, для переименования таблицы test в tested можно ввести следующую команду:
mysql> ALTER TABLE test RENAME AS tested; Query OK, 0 rows affected (0.02 sec)
Ключевое слово AS использовать необязательно, поэтому следующая команда ра+ ботает так же:
mysql> ALTER TABLE test RENAME tested; Query OK, 0 rows affected (0.02 sec)
Введение в базы данных и SQL 413
Добавим в таблицу user новое поле типа ENUM с именем sex. Данное поле можно будет использовать для записи пола пользователя:
mysql> ALTER TABLE user ADD sex ENUM('M', 'F') DEFAULT 'M'; Query OK, 0 rows affected (0.24 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> DESC user;
+----------------
+---------------------
+-------
+-----
+---------
+-----------
+
| Field
| Type
| Null
| Key | Default
| Extra
|
+----------------
+---------------------
+-------
+-----
+---------
+-----------
+
| ...
|
|
|
|
|
|
| sex
| enum('M','F')
| YES
|
| M
|
|
+----------------
+---------------------
+-------
+-----
+---------
+-----------
+
10 rows in set (0.00 sec)
Новое поле добавляется в таблицу как последнее поле. Чтобы вставить новое поле между другими полями, используется ключевое слово AFTER. Теперь удалим только что созданное поле:
mysql> ALTER TABLE user DROP sex; Query OK, 0 rows affected (0.08 sec) Records: 0 Duplicates: 0 Warnings: 0
Чтобы вставить новое поле sex сразу после поля username, можно ввести сле+ дующую команду:
mysql> ALTER TABLE user ADD sex ENUM('M', 'F') DEFAULT 'M' AFTER username;
Query OK, 0 rows affected (0.09 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc user;
+---------------
+----------------------
+------
+-----
+------------
+-----------
+
| Field
| Type
| Null
| Key | Default
| Extra
|
+---------------
+----------------------
+------
+-----
+------------
+-----------
+
| ...
|
|
|
|
|
|
| sex
| enum('M','F')
| YES
|
| M
|
|
+---------------
+----------------------
+------
+-----
+------------
+-----------
+
10 rows in set (0.00 sec)
Чтобы поместить новое поле в начало списка полей, вместо ключевого слова AFTER необходимо использовать ключевое слово FIRST, так как предшествующих по+ лей в этом случае нет.
Если сайт адресован женщинам, вероятно, значение по умолчанию для поля sex следует изменить с M на F:
mysql> ALTER TABLE user ALTER sex SET DEFAULT 'F'; Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
Для полного изменения определения поля используется ключевое слово MODIFY:
mysql> ALTER TABLE user MODIFY userprofile VARCHAR(250) NOT NULL -> DEFAULT 'No Comment';