RussianLDP Рейтинг@Mail.ru
WebMoney: 
WMZ Z294115950220 
WMR R409981405661 
WME E134003968233 
YandexMoney: 
41001198119846 
E-gold:
5128052

5. MySQLdb: Python-интерфейс для MySQL

MySQLdb представляет собой поточно-совместимый интерфейс с популярной СУБД MySQL, который обеспечивает Python API для баз данных. Здесь рассматривается его версия 0.9.1 (автор Andy Dustman, andy@dustman.net). Пакет распространяется по лицензии GPL и доступен для закачки с http://sourceforge.net/projects/mysql-python. Цели проекта:

  • Совместимость с Python API для баз данных (версия 2.0).
  • Поточно-безопасная работа.
  • Потоки теперь не будет блокировать друг друга.
  • Совместимость с MySQL-3.23 и выше.

Требования:

Python 1.5.2 или выше:

  • http://www.python.org
  • Версии ниже, чем 1.5.2, НЕ РАБОТАЮТ.
  • Все версии, начиная с 1.5.2, работать должны. 1.6.x не тестировались. 2.0.1, 2.1.1 и 2.2a3 были протестированы успешно.
  • Если Вы имеете Red Hat Linux или подобную систему установки пакетов, надлежит установить бибилотеки и заголовки разработчика для Python (python-devel). Для версий Python-2.x это python2-devel.

Distutils 1.0.2 или выше:

  • Поставляется с Python 1.6 и выше.
  • 1.0.2 включена в Python version 2.1 и выше.
  • http://www.python.org/sigs/distutils-sig/download.html

MySQL 3.22.19 или выше.

  • http://www.mysql.com/downloads
  • Версии ниже, чем 3.22 точно НЕ РАБОТАЮТ.
  • Версии ниже, чем 3.22.19 могут не работать.
  • MySQL-4.0 поддерживается.
  • MySQL-3.23 поддерживается.
  • Если Вы имеете Red Hat Linux или подобную систему установки пакетов, надлежит установить бибилотеки и заголовки разработчика для MySQL. Если Вы используете пакеты с mysql.com, Вам нужен пакет MySQL-devel. Если Вы используете пакеты Red Hat, Вам нужен mysql-devel. Я предпочитаю пакеты с сайта www.mysql.com.

5.1. Введение

Первое, что Вы будете делать, редактирование скрипта setup.py. Имеются некоторые переменные, которые сообщают где искать MySQL include-файлы и библиотеки. Значения корректны для стандартной установки MySQL в Red Hat Linux (6.2) RPM. Если Вы имеете другую платформу, Вы должны будете вычислить нужные значения самостоятельно. Вам почти никогда не придется изменять это. Если Вы имеете старую версию distutils (до 1.0.2), обновитесь или удалите параметры, относительно которых система возражает.

Обратите внимание, что недавние двоичные дистрибутивы с www.mysql.com включают два набора библиотек пользователей: mysqlclient и mysqlclient_r. Последний хранит поточно-безопасные библиотеки, так что используйте именно его, если потоки Вам нужны.

Если Вы имеете динамические библиотеки пользователей (в Linux .so-файлы), они будут использоваться по умолчанию. Если они не в Вашем стандартном пути загрузчика, Вы должны будете установить или откорректировать системную переменную LD_LIBRARY_PATH (в Linux) или ту, которую Ваша платформа требует. Иначе Вы можете скорректировать setup.py, чтобы компоновать со статической библиотекой. Если Вы используете стандартный пакет RPM, с этим не должно быть особых проблем.

ПРЕДУПРЕЖДЕНИЕ: Если Вы используете двоичный пакет Zope, Вы нуждаетесь в выполнении скрипта setup.py программой python из Zope. Иначе Zope (ZMySQLDA) не может найти _mysql.

Если Вы предпочитаете RPM, Вы можете использовать команду bdist_rpm с setup.py. Это только формирует RPM, но не устанавливает его.

Этот модуль должен быть совместим с более старым интерфейсом, написанным Joe Skinner. Однако, старая версия:

  • Не поточно-совместимая (операции базы данных могли бы блокировать все другие потоки)
  • Написан для MySQL 3.21 (не компилируется для более новых версий без заплаток)
  • Не очень активно поддерживается

MySQLdb полностью новый модуль, распространяемый бесплатно согласно GNU Public License. Никакой код из той версии не используется в MySQLdb.

5.1.1 Платформы

Linux/UNIX

Этот модуль разработан на RedHat Linux (в настоящее время 7.1) для Intel. Это должно формироваться без больших трудностей на большинстве платформ, используя скрипт setup.py. Возможно этот модуль работает и под MacOS X. Вы нуждаетесь в пакете Distutils, который поставляется с Python 2.0. Если Вы не имеете его (то есть Вы имеете Python 1.5.2), Вы можете скачать пакет с сайта www.python.org.

Windows (3.11, 95, 98, NT, 2000, CE)

Windows не поддерживаемая платформа. Однако, скрипт setup.py по сообщениям работает нормально.

5.1.2 Python

MySQLdb требует Python 1.5.2 или новее. Более ранние версии не будут работать потому, что MySQL нужна поддержка для C long long. Если Вы имеете более раннюю версию Python, обновитесь хотя бы до 1.5.2. Текущая разработка выполнена в Python 2.1, но старый Python 1.5.2 все еще будет поддержан в обозримом будущем.

5.1.3 MySQL

MySQL-3.22

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

MySQL-3.22 имеет проблему при попытке вставить значения TIME с дробными секундами. Значения, подобные 12:56:13.00, возвращены как 344:13:00, очевидно интерпретируя первоначальный ввод как 12 дней, 56 часов, 13 минут и 0 секунд (12 дней и 56 часов=344 часа). Чтобы избежать этой проблемы, используйте тип DateTimeDelta.

MySQL-3.23

MySQLdb поддерживает транзакции, если их поддерживает сервер. Но не гарантирует, что транзакции будут работать. Для этого Вы должны использовать транзакционно-безопасную таблицу (TST). Текущие TST: BDB и InnoDB. Таблицы GEMINI намечены для MySQL-4.0. Обратите внимание, что MySQL функционирует в режиме AUTOCOMMIT по умолчанию, и MySQLdb считает, что AUTOCOMMIT включен. Чтобы изменить это, используйте инструкции SQL SET AUTOCOMMIT=0.

5.1.4 DateTime

Если Вы имеете установленный пакет mx.DateTime , MySQLdb использует его для связанных с датой объектов. Иначе они будут возвращены как строки. Вы можете также изменять словарь преобразования типов, чтобы возвратить их как другие объектные классы.

5.1.5 MySQLmodule

MySQLmodule, старый интерфейс MySQL, разработанный Joe Skinner, является также интерфейсом C/Python. MySQL, C-часть, имеет интерфейс, подобный perl DBI. Кроме того, имеется часть Python, Mysqldb, которая обеспечивает интерфейс DB API v1.0, написанный James Henstridge. MySQLdb-0.2.2 и выше включает CompatMysqldb, который является адаптацией Mysqldb к _mysql. Это должно рассмотреться экспериментальным решением.

Напротив, C-часть MySQLdb, _mysql, разработана в соответствии с MySQL C API объектно-ориентированным способом. MySQLdb обеспечивает интерфейс DB API v2.0, который имеет некоторые отличия от v1.0, в частности такие изменения:
ДействиеMysqldbMySQLdb
Соединениеdb=Mysqldb.Mysqldb("db@host user pass") db=MySQLdb.connect(db='db', host='host', user='user', passwd='pass')
Неявный курсорdb.execute(SQL) Неявные курсоры удалены из DB API v2.0. Всегда используйте c=db.cursor()
Строка выборок как словарьc.fetchDict(), ключи: "table.column" Не стандарт: альтернативный класс DictCursor предоставляет интерфейс словаря, ключи являются "column" или "table.column , если имеются два столбца с одним именем. Используйте SQL-оператор AS для переименования полей.
Транзакцииdb.commit() и db.rollback() мирно сосуществуют вместе и тихо не делают ничего (опасно!) db.commit() и db.rollback() работают, если сервер MySQL может выполнять транзакции, иначе db.rollback() всегда терпит неудачу.

5.1.6 Zope и ZMySQLDA

Был написан пакет ZMySQLDA для использования с MySQLdb. Это адаптируется из ZOracleDA Digital Creations разработчиков системы Zope.

5.1.7 FAQ

FAQ доступны на http://dustman.net/andy/python/MySQLdb/faq/MySQLdb-FAQ.html.

5.2. Модуль _mysql

Если Вы хотите писать прикладные программы, которые переносимы между базами данных, избегайте использовать этот модуль непосредственно. Модуль _mysql обеспечивает интерфейс, который обычно осуществляет MySQL C API. Для получения большего количества информации обратитесь к документации на пакет MySQL. Документация для этого модуля преднамеренно слаба потому, что Вы, вероятно, должны использовать более высокий уровень (модуль MySQLdb).

5.2.1 Трансляция MySQL C API

MySQL C API был обернут объектно-ориентированным способом. Единственные MySQL структуры данных, которые выполнены в данном интерфейсе, это MYSQL (дескриптор подключения базы данных) и MYSQL_RES (дескриптор результата). Вообще, любая функция, которая берет как параметр MYSQL *mysql, теперь представляет собой метод объекта подключения, и любая функция, которая берет MYSQL_RES *result, теперь метод объекта результата. Функции, не требующие ни одной структуры MySQL, выполнены как функции в модуле. Функции, требующие какую-то из других структур данных MySQL, вообще не выполнены. Во всех случаях префикс mysql_ удален из имени. Большинство перечисленных методов conn также доступно как методы объекта MySQLdb Connection. Их использование не переносимо между базами данных.
C API_mysql
mysql_affected_rows() conn.affected_rows()
mysql_close()conn.close()
mysql_connect()_mysql.connect()
mysql_data_seek()result.data_seek()
mysql_debug()_mysql.debug()
mysql_dump_debug_info conn.dump_debug_info()
mysql_escape_string() _mysql.escape_string()
mysql_fetch_row()result.fetch_row()
mysql_get_client_info() _mysql.get_client_info()
mysql_get_host_info() conn.get_host_info()
mysql_get_proto_info() conn.get_proto_info()
mysql_get_server_info() conn.get_server_info()
mysql_info()conn.info()
mysql_insert_id()conn.insert_id()
mysql_list_dbs()conn.list_dbs()
mysql_list_fields()conn.list_fields()
mysql_list_processes() conn.list_processes()
mysql_list_tables()conn.list_tables()
mysql_num_fields()result.num_fields()
mysql_num_rows()result.num_rows()
mysql_options()_mysql.connect()
mysql_ping()conn.ping()
mysql_query()conn.query()
mysql_real_connect()_mysql.connect()
mysql_real_query()conn.query()
mysql_real_escape_string() conn.escape_string()
mysql_row_seek()result.row_seek()
mysql_row_tell()result.row_tell()
mysql_select_db()conn.select_db()
mysql_stat()conn.stat()
mysql_store_result()conn.store_result()
mysql_thread_id()conn.thread_id()
mysql_use_result()conn.use_result()
CLIENT_*MySQLdb.constants.CLIENT.*
CR_*MySQLdb.constants.CR.*
ER_*MySQLdb.constants.ER.*
FIELD_TYPE_* MySQLdb.constants.FIELD_TYPE.*
FLAG_*MySQLdb.constants.FLAG.*

5.2.2 Примеры использования _mysql

Допустим, что Вы хотите использовать _mysql. Имеются некоторые примеры.

Самое простое подключение к базе данных:

import _mysql
db=_mysql.connect()

Это создает подключение к серверу MySQL на локальной машине, используя стандартый сокет UNIX, Ваше имя входа в систему (из системной переменной USER), пустой пароль и не применяет команду USE. Возможно, это будет работать у Вас, если Вы установили файл конфигурации (~/.my.cnf ). Но скорее всего Вы должны обеспечить большее количество информации:

db=_mysql.connect("localhost","joebob","moonpie","thangs")

Это создает подключение к серверу MySQL на локальной машине, используя TCP на стандартном порте (3306), имя пользовател joebob, пароль moonpie и выбирает начальную базу данных thangs.

Конечно, Вы должны использовать TCP, если работаете с удаленной системой. Здесь я не рассмотрел часть параметров connect(), и Вы обратите внимание, что, если Вы используете позиционные параметры, настроить связь по TCP не так-то просто. Кроме того, сокеты UNIX быстрее. Я предпочитаю использовать параметры ключевого слова:

db=_mysql.connect(host="localhost",user="joebob",
                  passwd="moonpie",db="thangs")
Это делает точно то же, что делал последний пример, но проще для чтения. Теперь, если Вы действительно хотели использовать сокет UNIX, и Ваше имя входа в систему joebob, Вы могли бы сократить этот пример до:
db=_mysql.connect(passwd="moonpie",db="thangs")
Имеются некоторые другие параметры, которые Вы можете использовать, и большинство их не так уж и необходимо, кроме одного, о котором чуть ниже. Во всем остальном обратитесь к встроенной документации. Модуль Python 2.1 pydoc представляет собой хороший справочник.

Теперь Вы имеете открытое подключение db и хотите сделать запрос. Не имеется никаких курсоров в MySQL и никакой подстановки параметров, так что Вы должны передать полную строку запроса db.query():

db.query("""SELECT spam, eggs, sausage FROM breakfast
         WHERE price < 5""")
Не имеется никакого значения возврата, но исключительные ситуации могут быть вызваны. Исключительные ситуации определены в отдельном модуле, _mysql_exceptions, но _mysql экспортирует их. Читайте спецификацию DB API (http://www.python.org/topics/database/DatabaseAPI-2.0.html), чтобы выяснить то, чем они являются, или Вы можете использовать MySQLError.

В этой точке Ваш запрос был выполнен, и Вы должны получить результаты. Вы имеете два параметра:

r=db.store_result()
# ...или...
r=db.use_result()
Оба метода возвращают объект результата. В чем же разница? А в том, что store_result() возвращает весь набор результатов пользователю немедленно. Если Ваш набор результатов действительно большой, это станет проблемой. Один путь обхода этого состоит в том, чтобы добавить предложение LIMIT к Вашему запросу, чтобы ограничить число возвращенных строк. Но можно использовать use_result(), который хранит набор результатов на сервере и посылает его построчно, когда Вы выбираете. Это связывает ресурсы сервера и подключение: Вы не можете делать больше запросов, пока Вы не выбрали все строки. Вообще я рекомендую использовать store_result(), если Ваш набор результатов не огромен, и Вы не можете использовать LIMIT.

Теперь для фактического получения реальных результатов надо:

>>> r.fetch_row()
(('3','2','0'),)
Первая вещь, которую Вы должны знать: fetch_row() берет некоторые дополнительные параметры. Первый задает, сколько строк (maxrows) должны быть возвращены. По умолчанию, это возвращает одну строку. Это может возвращать меньшее количество строк, чем Вы просите, но никогда не больше. Если Вы устанавливаете maxrows=0, это возвращает все строки набора результатов. Если Вы когда-либо получаете пустой набор, значит Вы исчерпали строки.

Второй параметр (how) сообщает как строка должна представиться. По умолчанию, это ноль, что означает вернуть как набор. how=1 значит вернуть данные как словарь, где ключи представляют собой имена столбца или table.column, если имеются два столбца с тем же самым именем. how=2 аналогично how=1, кроме того, что ключи всегда table.column, это для совместимости со старым модулем Mysqldb.

Другая причуда: известно, что обрабатываются числовые столбцы, почему они возвращены как строки? Потому, что MySQL возвращает все данные как строки просто по определению и ожидает, что Вы преобразуете их непосредственно. Как я понимаю, все данные в базе хранятся именно как строки, как бы они не выглядели снаружи. Это было бы реальной проблемой, но фактически _mysql может делать это для Вас. MySQLdb делает это для Вас сам. Чтобы иметь автоматическое выполненное преобразование типов, Вы должны создать словарь конвертера типов и передать его в connect() как параметр ключевого слова conv.

Ключи в conv должны быть типами столбцов MySQL, которые в C API являются FIELD_TYPE_*. Вы можете получать эти значения так:

from MySQLdb.constants import FIELD_TYPE

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

my_conv = {FIELD_TYPE.LONG: int}
Это означает, если это FIELD_TYPE_LONG, обработать это встроенной int(). Обратите внимание, что FIELD_TYPE_LONG представляет собой столбец INTEGER, который соответствует C long, который является также типом, используемым для нормального целого числа в Python. Но остерегайтесь: если это столбец UNSIGNED INTEGER, это может вызвать переполнение. По этой причине MySQLdb фактически использует long(), чтобы сделать преобразование.

Затем, если Вы используете db=_mysql.connect(conv=my_conv...), результаты возвратятся в виде ((3, 2, 0),), который является тем, что и ожидается.

5.3. MySQLdb: интерфейс DB API

MySQLdb представляет собой тонкую обертку на Python вокруг _mysql. MySQLdb делает его совместимым с интерфейсом Python DB API (version 2). В действительности код, который осуществляет API, находится в _mysql ради эффективности.

5.3.1 Функции и атрибуты

Только несколько высокопоставленных функций и атрибутов определены внутри MySQLdb.

connect(parameters...)
Конструктор для создания подключения. Возвращает объект подключения (Connection Object). Параметры те же, как и для MySQL C API. Кроме того, имеются несколько дополнительных ключевых слов, которые соответствуют тому, что Вы передали бы как mysql_options() перед соединением. Обратите внимание, что некоторые параметры должны быть определены как параметры ключевого слова! Значение по умолчанию для каждого параметра: NULL или 0, зависит от типа. Важные параметры:
host
Имя компьютера, с которым надлежит соединиться Значение по умолчанию: используйте локальный компьютер.
user
Пользователь, чтобы авторизоваться на сервере. Значение по умолчанию: текущий эффективный пользователь.
passwd
Пароль, чтобы авторизоваться на сервере. Значение по умолчанию: никакого пароля (пустой пароль).
db
База данных, которую надо использовать. Значение по умолчанию: никакой заданной по умолчанию базы данных.
port
TCP-порт сервера MySQL. Значение по умолчанию: стандартный порт (3306).
unix_socket
Расположение сокета UNIX. Значение по умолчанию: использование TCP.
conv
Словать преобразования типов. Значение по умолчанию: копия MySQLdb.converters.conversions.
compress
Включить сжатие протокола. Значение по умолчанию: никакого сжатия.
connect_timeout
Аварийное прекращение работы, если соединение не завершено в рамках данного числа секунд. Значение по умолчанию: нет тайм-аута.
named_pipe
Использовать именованный канал (только под Windows). Значение по умолчанию: не делать этого.
init_command
Начальная команда, которую надо выдать на сервер при подключении. Значение по умолчанию: нет.
read_default_file
Файл настроек MySQL. Отсюда берутся mysql_options().
read_default_group
Заданная по умолчанию группа в файле настроек. Отсюда берутся mysql_options().
cursorclass
Класс курсора, который использует cursor(), если не перекрыт. Значение по умолчанию: MySQLdb.cursors.Cursor. Это должно быть параметром ключевого слова.
apilevel
Строковая константа, задающая поддерживаемый уровень DB API: '2.0'.
threadsafety
Константа типа Integer, устанавливающая уровень поточной безопасности, который поддерживает интерфейс. С MySQLdb version 0.9.0 это установлено в 1, что означает: потоки могут совместно использовать модуль.

Протокол MySQL не может обрабатывать много потоков, использующих то же самое подключение, сразу. Более ранние версии MySQLdb использовали блокировку, чтобы достигнуть threadsafety=2. Несмотря на то, что это не должно активно использовать стандартный класс Cursor (который используется в mysql_store_result()), это усложнено SSCursor (который используется в mysql_use_result(). Здесь Вы должны гарантировать, что все строки прочитались прежде, чем другой запрос сможет быть выполнен. Это далее усложнено добавлением транзакций, начинающихся когда курсор выполняет запрос, и завершающихся выполнением COMMIT или ROLLBACK объектом Connection. Два потока не могут совместно использовать подключение, в то время как транзакция происходит, в дополнение к неспособности совместно использовать его в течение выполнения запроса. К сожалению, это чрезмерно усложнило код.

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

paramstyle
Строковая константа, устанавливающая тип маркера параметра, форматирующего ожидаемое интерфейсом. Установите в 'format'=ANSI C printf-коды формата, например '...WHERE name=%s'. Если объект отображения используется для conn.execute(), то интерфейс фактически использует 'pyformat'= расширенные форматные коды Python, например, '...WHERE name=%(name)s'. Однако, API теперь не позволяет спецификацию больше, чем одного стиля в paramstyle.

Примечание о совместимости: старые версии MySQLmodule используют подобную схему задания параметра, но требуют, чтобы кавычки были помещены вокруг строк формата, которые будут содержать строки, даты и подобные символьные данные. Это не требуется для MySQLdb. Рекомендуется, чтобы %s (но не '%s') использовался для всех параметров, независимо от типа. Интерфейс выполняет все необходимое цитирование сам.

conv
Словарь, отображающий типы MySQL (из FIELD_TYPE.*) к вызываемым объектам Python (обычно функциям), которые преобразовываются из строк в нужные типы. Это инициализировано с приемлемыми значениями по умолчанию для большинства типов. При создании объекта Connection Вы можете передать Ваш собственный словарь конвертера типа как параметр ключевого слова. Иначе это использует копию MySQLdb.converters.conversions. Словарь включает некоторые из функций модуля DateTime, если это доступно. Несколько ненормативных типов возвращены как строки,

Начиная с MySQL-3.23, MySQL поддерживает различные наборы символов на клиенте и сервере, а также новую функцию цитирования mysql_real_escape_string(). Это требует, чтобы функция цитирования строки была методом, связанным с объектом connection. MySQLdb обрабатывает это для Вас автоматически. Однако, если Вы чувствуете потребность сделать что-то особое со строками, Вы должны изменить словарь после открытия подключения.

5.3.2 Объекты Connection

Объекты Connection возвращены функцией connect().

commit()
Если база данных и таблица поддерживают транзакции, это передает текущую транзакцию, Иначе этот метод успешно не делает ничего.
rollback()
Если база данных и таблица поддерживают транзакции, это отменяет текущую транзакцию, Иначе этот метод вызывает исключение NotSupportedError.

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

cursor([cursorclass])
MySQL не поддерживает курсоры, однако, курсоры легко эмулируются. Вы можете обеспечивать альтернативный класс курсора как факультативный параметр. Если это не представлено, берется значение по умолчанию данное при создании объекта подключения или стандартного класса Cursor.
begin()
Явно запускает транзакции. Обычно Вы не должны использовать это: выполнение запроса начинает новую транзакцию, если ни одной работающей нет. Если включен AUTOCOMMIT, Вы можете использовать begin() для его временного отключения. AUTOCOMMIT продолжит работу после следующего вызова commit() или rollback.

5.3.3 Объекты Cursor

callproc()
Не реализован.
close()
Закрывает курсор. Будущие операции вызывают исключение ProgrammingError. Если Вы используете курсоры стороны сервера, очень важно закрыть курсор, когда Вы с ним закончили, но перед созданием нового курсора.
insert_id()
Возвращает последнее значение поля AUTO_INCREMENT, вставленное в базу данных.
info()
Возвращает некоторую информацию относительно последнего запроса. Обычно Вы не должны проверять это. С заданным по умолчанию курсором любое предупреждение MySQL вызовет исключение Warning. Если Вы используете класс курсора без предупреждений, рекомендую проверять info(). Подробности в документации MySQL на mysql_info().
setinputsizes()
Не делает ничего.
setoutputsizes()
Не делает ничего.

5.3.4 Некоторые примеры использования

Метод connect() работает почти также, как и с _mysql:

import MySQLdb
db=MySQLdb.connect(passwd="moonpie",db="thangs")
Чтобы выполнить запрос, Вы сначала нуждаетесь в курсоре, а затем Вы можете выполнять запросы на нем.
c=db.cursor()
max_price=5
c.execute("""SELECT spam, eggs, sausage FROM breakfast
          WHERE price < %s""", (max_price,))

В этом примере max_price=5. Почему затем в строке использована опция %s? Потому, что MySQLdb преобразует это в литеральное значение SQL, которое является строкой '5'. Когда это закончено, запрос будет фактически таким: "...WHERE price < 5".

Так, а теперь результаты:

>>> c.fetchone()
(3L, 2L, 0L)
В отличие от примера с _mysql, это возвращает одиночный блок результатов, который является строкой, и значения правильно преобразованы.

Как упомянуто ранее, в то время как столбец MySQL INTEGER транслируется в Python integer, UNSIGNED INTEGER может вызвать переполнение, так что эти значения преобразованы в Python long integer. До Python 1.6 long integer сохраняли L, когда были преобразованы в строки с помощью str(). В 1.6 и позже str() не включает L. Конечно, L всегда выводится при использовании repr().

Когда Вы закончили работу с транзакцией, Вы должны выполнить db.commit() или db.rollback(). Если сервер и таблицы не поддерживает транзакции, commit() будет работать, но rollback() вызовет исключительную ситуацию. Обратите внимание, что это методы connection, а не cursor, даже при том, что транзакция запускается через c.execute(...).

Если Вы хотели получить большее количество строк, Вы могли бы использовать c.fetchmany(n) или c.fetchall(). На c.fetchmany(n) параметр n факультативный и имеет значение по умолчанию c.arraysize (обычно 100). Оба этих метода возвращают последовательность строк, или пустую последовательность, если строки уже кончились.

Обратите внимание, что в отличие от вышеупомянутого, c.fetchone() вернет None, когда не имеется больше строк для выборки.

Единственный другой метод, который Вы, очень вероятно, используете, это многострочная вставка:

c.execute("""INSERT INTO breakfast (name, spam, eggs, sausage, price)
          VALUES (%s, %s, %s, %s, %s)""",
          [ ("Spam and Sausage Lover's Plate", 5, 1, 8, 7.95 ),
            ("Not So Much Spam Plate", 3, 2, 0, 3.95 ),
            ("Don't Wany ANY SPAM! Plate", 0, 4, 3, 5.95 )])

Здесь мы вставляем три строки по пять значений в каждой. Обратите внимание, что имеется смесь типов (строки, int, float), хотя все еще используется только %s. А также обратите внимание, что включили только строки формата для одной строки. MySQLdb выбирает и дублирует их для каждой строки.

5.4. Использование и расширение

Базы данных, даже SQL-базы данных, изменяются по возможностям очень сильно и могут иметь ненормативные свойства. DB API делает хорошую работу по обеспечению приемлемо переносимого интерфейса, но некоторых методов там нет. Специфические параметры для connect() полностью зависимы от реализации.

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

В качестве примера рекомендую изучить модуль SQLDict (http://dustman.net/andy/python), который позволяет стандартным запросам работать, используя объект, который напоминает словарь, а также читать и писать определяемые пользователем дополнительные объекты.

Поскольку объекты MySQLdb Connection и Cursor написаны на Python, Вы можете легко получать Ваши собственные подклассы. Имеются несколько классов Cursor в MySQLdb.cursors:

BaseCursor
Основной класс для объектов Cursor. Не вызывает Warning.
CursorWarningMixIn
Создает исключительную ситуацию Warning на запросах, которые производят предупреждения.
CursorStoreResultMixIn
Заставляет Cursor использовать функцию mysql_store_result(), чтобы получить результат запроса. Весь набор результатов сохранен на стороне пользователя.
CursorUseResultMixIn
Заставляет курсор использовать функцию mysql_use_result(), чтобы получить результат запроса. Набор результатов сохранен на стороне сервера и передается построчно.
CursorTupleRowsMixIn
Заставляет cursor возвращать строки как блоки значений столбца.
CursorDictRowsMixIn
Заставляет cursor возвращать строки как как словарь, где ключи имена столбца, а значения представляют значения столбца. Обратите внимание, что, если имена столбца не уникальны, то есть, Вы выбираете из двух таблиц, которые совместно используют имена столбца, некоторые из них будут переделаны в table.column. Этого можно избежать, используя ключевое слово SQL AS
Cursor
Заданный по умолчанию класс курсора. Этот класс составлен из CursorWarningMixIn, CursorStoreResultMixIn, CursorTupleRowsMixIn и BaseCursor, то есть он создает исключительную ситуацию Warning, использует mysql_store_result() и возвращает строки как блоки.
DictCursor
Аналогичен Cursor за исключением того, что возвращает строки как словари.
SSCursor
Серверный курсор. Похож на Cursor, но использует CursorUseResultMixIn. Используйте только, если Вы имеете дело с потенциально большими наборами результатов.
SSDictCursor
Аналогичен SSCursor за исключением того, что возвращает строки как словари.
XXXCursorNW
Курсоры с суффиксом NW не создают исключительную ситуацию Warning.

5.5. MySQLdb FAQ

5.5.1. Компиляция _mysql.so

Имеются некоторые общие ошибки, которые случаются в ходе построения пакета. Этот раздел покрывает только проблемы UNIX/Linux, поскольку я не делаю пакетов под Windows. Файлы типа .so представляют собой динамические библиотеки в Linux и большинстве других вариантов UNIX. Windows использует расширение .dll.

5.5.1.1 ImportError: libmysqlclient.so.6: cannot open shared object file: No such file or directory

Вы имеете динамические библиотеки MySQL, и по умолчанию Ваш компилятор линкует _mysql.so с ними, но они не в пути загрузчика, когда Вы запускаете Python. Вы имеете два базисных параметра:

  1. Модифицируйте setup.py так, чтобы это компоновалось со статической библиотекой: уж ее-то искать не понадобится.
  2. Если Ваш компоновщик поддерживает переключатель пути загрузчика во время выполнения, Вы можете также устанавливать это в setup.py.
  3. Измените Вашу среду системы так, чтобы MySQL библиотеки нашлись в Вашем пути загрузчика. В Linux Вы можете изменять /etc/ld.so.conf или Вы можете добавить каталог к системной переменной LD_LIBRARY_PATH перед запуском Python.
    LD_LIBRARY_PATH=/path/to/mysql/libs python ... # Bourne-ish shell
    

5.5.1.2 ImportError: ./_mysql.so: undefined symbol: PyLong_FromUnsignedLongLong

PyLong_FromUnsignedLongLong() сначала появляется в Python 1.5.2, так что Вы компонуете с более старой версией. Вы можете также иметь больше, чем одну установленную версию. Получите Python 1.5.2 (а лучше посвежее) с python.org.

5.5.1.3 ImportError: ./_mysql.so: undefined symbol: uncompress

Библиотеки пользователей MySQL-3.23 требуют libz для сжатия gzip. Скрипт setup.py должен бы добавить это автоматически.

5.5.1.4 ./_mysql.c:33: mysql.h: No such file or directory

Путь для include-файлов (-I) к Вашим MySQL include-файлам ошибочен. Поправьте скрипт setup.py. Другой вариант: Вы не имеете набора разработчика для MySQL. Если Вы используете Red Hat RPM, Вы нуждаетесь в RPM-пакете MySQL-devel, чтобы откомпилировать _mysql.so. Однако, если Вы компонуетесь со статическими библиотеками MySQL, Вы можете устанавливать _mysql.so на системе, которая не имеет библиотек пользователей MySQL (libmysqlclient).

5.5.1.5 Я использую только Windows...

А я не использую Windows. Скрипт setup.py, как предполагается, работает. Может также быть связь с каким-либо сторонним пакетом.

5.5.2. Проблемы с ZMySQLDA

5.5.2.1 Я установил MySQLdb, но ZMySQLDA его не видит

Вероятно, Вы установили двоичную версию Zope, которая приходит со своим собственным интерпретатором Python. Вы должны компилировать MySQLdb с той специфической установкой Python. Выясните, где находятся двоичные модули python и используйте это, чтобы выполнить setup.py.

5.5.2.2 Я получаю буквы L в столбцах INTEGER

Вообще-то они должны быть преобразованы в длинные целых числа, чтобы избежать переполнения на столбцах UNSIGNED INT. Решения: используйте атрибут fmt=%d на элементах dtml-var или поставьте Zope 2.4, который приходит с Python 2.1, который не добавляет L.

5.5.2.3 Я получаю синтаксическую ошибку SQL на предложениях LIMIT, но я не помещал в запрос слово LIMIT!

Z метода SQL имеют параметр max_rows. Если это установлено к значению, отличному от нуля, ZMySQLDA автоматически добавляет предложение LIMIT к инструкции SELECT. Это сильно повышает эффективность, особенно, если набор результатов мог бы быть большим. Если это сталкивается с чем-то, установите max_rows в ноль, и это не будет добавлять предложение LIMIT. В частности, Вы, вероятно, должны будете сделать это при вставке строк со столбцами AUTO_INCREMENT, поскольку обычно Вы используете SELECT, чтобы получить LAST_INSERT_ID(), а LIMIT может блокировать это.

5.5.3. Использование MySQLdb

MySQLdb представляет собой модуль, соответствующий спецификации Python Database API 2.0 (http://www.python.org/topics/database/DatabaseAPI-2.0.html), так что Вы должны быть знакомы с этой спецификацией. Здесь указаны отличия: http://dustman.net/andy/python/MySQLdb/doc/MySQLdb.html.

5.5.3.1 cursor.rollback() всегда падает!

MySQLdb теперь поддерживает транзакции, если сервер поддерживает транзакционно-безопасные таблицы (TST), и Вы используете их. Если Ваш сервер не поддерживает их, rollback всегда будет терпеть неудачу потому, что система не в состоянии выполнить Ваши инструкции. Даже если Ваш сервер поддерживает транзакционно-безопасные таблицы, rollback будет терпеть неудачу, если Вы изменили любую не-TST таблицу.

cursor.commit(), который пытается завершить транзакцию всегда работает нормально потому, что MySQL по существу всегда в режиме auto-commit mode (если Вы не его не выключили).

5.5.3.2 Как я могу использовать некоторые из специальных свойств СУБД MySQL?

С одной стороны, никак. Не делайте этого, если Вы можете избежать этого. Ваша программа не будет переносима к другим базам данных.

Но если Вас эта переносимость не интересует, то почти все специальные обращения API выполнены в объекте _mysql, и объект связи в MySQLdb может также вызывать их.

5.5.3.3 Я все еще хочу использовать _mysql

Хорошо, это может быть необходимо в каком-то случае. ZMySQLDA делает это потому, что Zope-модуль ZRDB сам по себе API, а слишком много уровней API имеют тенденцию к разброду и шатанию. С этим поделать что-либо трудно. Кроме того, было фактически довольно просто сделать это именно таким способом, да и эффективность малость повысилась.

  1. Читайте документацию на MySQL, особенно C API для краткого обзора.
  2. Читайте документацию на MySQLdb. Это показывает, как C API транслируется в Python. К тому же, это неплохой пример реализации.
  3. Читайте исходники для MySQLdb, особенно рекомендуется для изучения файл MySQLdb/cursors.py. Этот файл содержит большинство деталей, особенно в реализации методов _query.

Поиск

 

Найди своих коллег!