Comments 38
Чем ваш connection отличается от http://php.net/manual/ru/class.pdo.php (да знаю сейчас отличается, и текст запроса в курсоре, но тот вариант что по ссылкам кажется более продуман тем то что описано у вас)
ну и соотвественно cursor от http://php.net/manual/ru/class.pdostatement.php
мне кажется немного обсуждений и придете к тому же интерфейсу что я указал по ссылкам
Так если посмотреть, то и принцип работы с файлами, и принципы парсинга JSON или XML, те же регулярные выражения очень похоже реализованы в разных языках.
Проблема то здесь в чем?
1) cursor.execute(«SELECT * FROM 100MBtable»)Интересует, что происходит «под капотом»
2) results = cursor.fetchall()
Правильно ли я понимаю, что первая команда создаст на сервере БД структуру размером 100МБ, а вторая — перекачает её по сети клиенту? Или это как-то иначе работает?
* Объем данных будет значительно больше чем размер самой базы
* Даже если используется fetchone() все равно будет загружена вся информация целиком сразу
Для решения таких проблем существуют специальные классы курсоров (например SSCursor), которые позволяют хранить результат запроса на сервере.
Вот подобные вопросы на StackOverflow:
http://stackoverflow.com/questions/4559402/the-memory-problem-about-mysql-select
http://stackoverflow.com/questions/337479/how-to-get-a-row-by-row-mysql-resultset-in-python
cur.execute("SELECT * FROM Test WHERE testID > :tid", {'tid': 10})
получаю:
You have an error in your SQL syntax;...
пакеты MySQLdb и mysql.connector, оба ругаются на "?":
cur.execute("SELECT * FROM Test WHERE testID > ?", (10,))
Not all parameters were used in the SQL statement
Если не указать запятую при одном параметре (10,) параметр будет передан как есть, а не в кортеже. MySQLdb выругается вот так:
not all arguments converted during string formatting
а mysql.connector ругается на ошибку sql-синтаксиса (near ?)
python v2.7
mariadb 10.1
Судя по документации, именованные параметры в mysql должны задаваться через "@". Но в таком случае, хотя и нет исключений при вызове execute, результат пустой.
У себя в скриптах использую .format(), но их запускаю только я, поэтому sql-инъекции маловероятны.
http://stackoverflow.com/questions/775296/python-mysql-parameterized-queries
это должно быть можно настраивать через paramstyle
В MySQLdb paramstyle == 'format', по умолчанию. Выставил в 'pyformat', теперь работает так:
cur.execute("SELECT * FROM Test WHERE testID > %(tid)s", {'tid': 10})
Единственное, в данном модуле не получится использовать %d, например. П.ч. все аргументы прогоняются через db.literal(), который возвращает строку.
На форумах в нескольких местах находил информацию, что он информационный и «вшит» в сам модуль базы данных.
Я пробовал менять его для SQLite — значение параметра меняется, но работать с новым типом подстановок он не начинает:
import sqlite3
print(sqlite3.paramstyle) # qmark
sqlite3.paramstyle = 'format'
print(sqlite3.paramstyle) # format
conn = sqlite3.connect('Chinook_Sqlite.sqlite')
cursor = conn.cursor()
cursor.execute("SELECT Name FROM Artist ORDER BY Name LIMIT %s", ('2'))
# sqlite3.OperationalError: near "%": syntax error
Вот кстати полезная ссылка с изящным обходным решением проблемы: http://stackoverflow.com/questions/12184118/python-sqlite3-placeholder
в том-то и запутанность ситуации, что PEP утверждает, что в этой переменной уровня модуля должен содержаться используемый модулем paramstyle, но ни слова не говорит, можно или нельзя его изменить, и если можно, то как это делать, а также не накладывает ограничений на те варианты paramstyle, который реализовать обязательно.
и да, я не прав, про то, что это можно нормально настраивать. Там на самом деле обычный pyformat.
Автор вкатал тупой if в этом месте и подстановку в запрос без всякой защиты от инъекций https://github.com/farcepest/MySQLdb1/blob/master/MySQLdb/cursors.py#L183
Я ловил ошибки, пытаясь применить именованные параметры по докам mysql (вида param). Бегло прочитал про paramstyle и установил его в pyformat. Попробовал %(param)s — сработало, обрадовался, хотя paramstyle в действительности не причем. Если я правильно понимаю, то этот атрибут модуля носит информативный характер, т.е. автор модуля этим атрибутом указывает способ форматирования параметров. Копание в исходниках показывает, что он нигде не используется и никуда не передается.
try:
cursor.execute(sql_statement)
result = cursor.fetchall()
except sqlite3.DatabaseError as err:
print("Error: ", err)
else:
conn.commit()
За что любил PHP — при любой правке порядка полей в SQL запросе, если поле хотя бы есть, конструкция вида $row['field'] вполне успешно отдаёт его значение. В случае с простой реализацией запросов в питоне уже не всё так радужно, но можно сделать чуть посложнее.
Такую приятную вещь, как row_factory Вы забыли. Она позволяет брать метаданные из запроса и обращаться в итоге к результату, например по имени столбца.
По сути — callback для обработки данных при возврате строки. Да еще и полезнейший cursor.description, где есть всё необходимое.
import sqlite3
def dict_factory(cursor, row):
d = {}
for idx, col in enumerate(cursor.description):
d[col[0]] = row[idx]
return d
con = sqlite3.connect(":memory:")
con.row_factory = dict_factory
cur = con.cursor()
cur.execute(«select 1 as a»)
print(cur.fetchone()[«a»])
а это уже разное от драйвера к драйверу, чем меня спецификация db-api2 изрядно расстраивает.
Очень не хватает четкой спеки на параметры, специфицированного autocommit в стандарте, serverside-курсоров,
dict или объекты во всех драйверах тоже реализованы, но по-разному и это печально.
Нет connection pooling и асинхронщины, что встаёт костью в горле, когда скрещиваешь это с каким-нибудь asyncio (я в своё время пытался скрещивать с twisted, примерно такого же порядка проблемы).
Пришло время описывать db api3 =)
cursor.execute('''INSERT INTO t1(f) VALUES('v1'); INSERT INTO t2(f) VALUES('v2'); INSERT INTO t3(f) VALUES('v3');''', [])
python 3.6, psycopg 2.6
Лучше всё-таки не лепить три запроса в один скрипт, а: выполнить коннект, три раза отдельно выполнить execute с правильной интерполяцией значений, а потом выполнить commit.
А если будут траблы со вставкой множества значений в одну таблицу (там действительно ужасный оверхед, вставлял как-то котировки), то здесь поможет рецепт.
а почему все операции через курсор делаются, а коммит у коннекта вызывается?
with closing(connection.cursor()) as cursor:
Потому что существуют СУБД, умеющие несколько курсоров в одной транзакции. Например может вернуться несколько курсоров как результат выполнения хранимой процедуры у какого-нибудь Oracle.
Но при этом авторы спецификации не стали заморачиваться и объединили сущность транзакции и соединения, чем обломали кайф любителям firebird, у них общепринятая практика — открыть одну длинную читающую транзакцию в read committed и записи делать короткими пишущими транзакциями внутри одного и того же соединения.
На самом деле мне тоже не нравится подобная ситуация — можно закрыть транзакцию, сломав вдребезги напополам ещё не закрытые и недочитанные курсоры.
PEP-249 ничего по этому поводу не говорит, но я понимаю, почему авторы вытащили курсоры в отдельную сущность — их точно даже может существовать больше одного на соединение в некоторых БД.
MySQLdb и psycopg2 емнип делают close всем открытым курсорам этого соединения при commit.
я вот и говорю, что в одной транзакции может быть теоретически открыто больше одного объекта курсора в зависимости от СУБД и это нормально. Транзакция при этом будет одна, общая.
Допустим, мы хотим почитать из нескольких табличек параллельно так, чтобы данные из обеих были консистентны. Допустим мы делаем джойн или какую-то сверку данных из двух больших таблиц на клиенте.
Нужно открыть транзакцию режиме в SNAPSHOT ISOLATION и читать с помощью serverside cursor кусочками, потом оба курсора закрыть и выбросить.
Я очень не уверен, что все СУБД поддерживают несколько транзакций в одном соединении, так что возможность открывать по транзакции на курсор фича попросту нереализуемая в большинстве реализаций и мешающая распространению стандарта.
Стандарт — это всегда какое-то общее подмножество того, что есть на рынке.
На самом деле мы сейчас гадаем на кофейной гуще, пытаясь понять, что двигало авторами спеки. Вероятнее всего уже до PEP уже существовала какая-то реализация, где всё ограничивалось фичами конкретной СУБД, и которую почти без изменений втащили в стандарт.
Судя по автору PEP, уши растут из драйвера mxODBC.
>> # Обратите внимание, даже передавая одно значение — его нужно передавать кортежем!
Не совсем понимаю, почему обязательно делать список из кортежей, вместо обычных строк?
http://stackoverflow.com/questions/19154324/psycopg2-executemany-with-simple-list
http://stackoverflow.com/questions/5331894/i-cant-get-pythons-executemany-for-sqlite3-to-work-properly
Ёжик оказался не прав.
https://habrahabr.ru/post/321510/#comment_10074610
Listing a lot of resources is the good way for all your audience may be they are not able to understand through those resource which you have listed.
Python: Работа с базой данных, часть 1/2: Используем DB-API