amaslyaev / noorm

NoORM (Not only ORM) - Python library that makes your database operations convenient and natural
MIT License
16 stars 0 forks source link

support sqlite executemany #5

Closed LecronRu closed 4 days ago

LecronRu commented 2 months ago

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

Если параметры отдельные переменные, можно импортировать текст запроса и выполнить его напрямую, минуя библиотеку. Это всего лишь некрасиво, когда одиночное изменение выполняется функцией ins_data(conn, param1, param2), а множественное conn.executemany(stmt, params_sequence). Но совсем иначе выглядит, когда это последовательность dataclass-ов из выполненного ранее select запроса. При одиночной модификации через функцию, dataclass "распаковывается" в nm.params в нужном объеме, а для составления params_sequence, эту "распаковку" приходится дублировать (вне DB API модуля).

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

amaslyaev commented 1 month ago

Да, согласен, надо приделать. Сам когда-то этой штукой пользовался.

amaslyaev commented 2 weeks ago

Пообщался сегодня с executemany, и вообще расхотелось добавлять это в noorm. Всего-то мне нужно было запихнуть жалкий миллион маленьких записей в Постгрес через psycopg2. Выяснилось, что у себя под капотом executemany тупо крутит цикл и скармливает записи в сервер по одной. Ожидаемое время на миллионе записей было что-то вроде 2 часов. Оказалось быстрее заимплементить честный батчинг и насладиться тем, что процесс занял меньше минуты.

Это я к тому, что на маленьком количестве "many" сделать в цикле через одиночный "execute" получается проще и нагляднее. А на большом "many" лучше этим самым executemany вообще не пользоваться, а выкручиваться как-то по-другому.

Этот executemany обладает мерзким свойством – он эффективно прячет от глаз разработчика ситуацию "запрос в цикле", и поэтому автоматически попадает в графу "уродливые опасные антипаттерны". Добавлять это в noorm, тем самым провоцируя пользователя библиотеки на использование этой дряни, нет никакого желания. Мир будет чуточку чище, если в нём будет меньше хитро спрятанных трудноидентифицируемых косяков.

Кстати, по итогу наших с вами обсужденй в #6 родилась идея, как ничего не ломая таки сделать удобняшку, уменьшающую бойлерплейт: #8

LecronRu commented 2 weeks ago

Не стоит делать вывод о поведении функции, по ее реализации в одном из драйверов. Вставка в in-memory sqlite3 миллиона записей 'INSERT INTO test (text, data) VALUES (?, ?)' 1.22 сек executemany 1.88 сек execute в цикле

Плюс оверхед от noorm на миллион вызовов оберток. Впрочем согласен, при записе в файловую систему, с учетом нечастых случаев пакетной вставки таких объемов, он не столь заметен. Больше заметно неудобство, когда вставляемые данные уже́ в списке/генераторе. Напрашивается передача списка как аргумента.

amaslyaev commented 1 week ago

Там не только оверхед от noorm, но и то, что, насколько я понимаю, в случае с executemany SQL парсится один раз в "prepared statement". В любом случае, 55% добавки погоды не делает.

Фундаментальная проблема executemany, по-видимому, проистекает из того, что невозможно сделать автоматический универсальный батчинг, надёжно работающий во всех мыслимых случаях жизни. Соответственно, даже считающийся эталоном psycopg2 не пытается занматься самодеятельностью и тупо крутит цикл. Что, конечно, работает, но если база где-то там далеко в облаке, миллион последовательно проделываемых I/O операций через сеть это капец как долго.

Если вставляемые данные в списке или генераторе, просто кладём insert под цикл. Всего одна доп. строчка. Зато сразу видно, что вот тут у нас запрос в цикле, что потенциально мощный тормоз.

amaslyaev commented 4 days ago

Decided not to implement. Issue is closed.