Питон и БД - пните в верном направлении
Задача: читаем таблицу, меняем в ней некоторые значения, пишем изменения назад. На PL/SQL это бы делалось через CURSOR ... FOR UPDATE или BULK COLLECT, если по-быстрому, но через память. На C# можно считать DataSet, поменять все что надо, и сказать ему записать обновления в базу. А как что-то такое сделать на питоне?
Если это важно, то используется Snowpark Session, из которой есть доступ к Snowflake Connector, если приспичит. Этот Snowpark оперирует DataFrame, насколько я понимаю, это какой-то стандартный подход к работе с данными в питоне.
Пока сумасшедшая идея состоит в том, чтобы все считать в память, сформировать новый список строк (поскольку объект Row нельзя модифицировать, надо делать новый), и перезаписать его в исходную таблицу. Хотелось бы этого все же по возможности избежать. Размер памяти тут не является преградой, там облачный сервер, съест что угодно.
Поменяй аву скуф
закрой рот,ты вообще без авы
Иди в спортзал качаться
Failed to initialize model.
📚 https://pythonlib.ru/post138
Snowpark-DataFrame ленивый: все, что вы над ним пишете, превращается в единый SQL-план и исполняется прямо в кластере Snowflake. Поэтому самый прямой путь – описать, как меняется столбец, и вызвать update/merge/overwrite, не вытаскивая данные из базы.
Если изменение можно выразить обычным SQL, то банально:
session.sql("""
update my_schema.my_table
set price = price * 1.2
where category = 'books'
""").collect() Никакой памяти на клиенте, выполняется распределённо.
Когда логика на чистом SQL не помещается и хочется Python, используем Snowpark-UDF. Пишем функцию, регистрируем как UDF, цепляем её в DataFrame и делаем update:
from snowflake.snowpark.functions import col, udf
@udf(name="normalize_phone", replace=True)
def normalize_phone(phone: str) -> str:
import re
return re.sub(r'\D', '', phone) # пример «сложной» питоновской логики
df = session.table("contacts")
df_to_update = df.with_column("phone", normalize_phone(col("phone")))
df_to_update.update({"phone": col("phone")}) # будет сгенерирован UPDATE ... SET phone = normalize_phone(phone) Метод update у DataFrame появился начиная с версии 1.1 Snowpark и генерирует обычный DML, так что всё опять же выполняется на стороне Snowflake.
Если трансформация затрагивает сразу много столбцов, проще создать временную таблицу и потом сделать MERGE. В коде это выглядит так:
orig = session.table("sales")
transformed = (orig
.with_column("net_amount", col("gross_amount") - col("discount"))
.with_column("updated_at", current_timestamp()))
transformed.write.mode("overwrite").save_as_table("sales_tmp")
session.sql("""
merge into sales t
using sales_tmp s
on t.id = s.id
when matched then update
set net_amount = s.net_amount,
updated_at = s.updated_at
""").collect()
session.sql("drop table sales_tmp").collect() При желании можно сделать SWAP вместо MERGE, если нужно полностью заменить таблицу без блокировки читателей: alter table sales_tmp swap with sales.
Читать всё в память, строить список Row и обратно грузить смысла нет, разве что вы тренируете модель в Pandas или нужен scikit-learn. Но для типового «обнови значения в таблице» правильный вектор такой: описываем изменение либо чистым UPDATE, либо через Snowpark-UDF, либо через цепочку DataFrame→save_as_table→MERGE, и даём Snowflake выполнить работу внутри себя. Вся магия DataSet из C# на самом деле как раз к этому и сводится, только Snowflake делает это эффективнее и без лишних раунд-трипов.
Ну то есть идея все равно сводится к тому, чтобы сделать новый набор данных и замерджить его в исходный. Только мне оно туда не надо, потому что это временная таблица и мне ее надо обрабатывать полностью, поэтому я могу ее полностью перезаписать.
Нужна полная непрерывность для тех, кто читает таблицу? Тогда сначала пишите во временную таблицу, потом меняйте их местами командой SWAP:
df2.write.mode("overwrite").save_as_table("tmp_raw_new", table_type="temporary")
session.sql("alter table tmp_raw_new swap with tmp_raw").collect()
session.sql("drop table tmp_raw_new").collect() Какой ты умный, это что-то.
Если я правильно понимаю вопрос, то судя по доке Snowflake, это делается через SELECT ... FOR UPDATE:
https://docs.snowflake.com/en/sql-reference/constructs/for-update
Но это напрямую если запросами, а как это реализовано в используемых обёртках - не в курсе.
Не-не-не.
Во-первых, там очень сложные расчеты, засунуть их в простое SQL-выражение не получится. Если бы это было возможно, так и вопроса бы не было.
А во-вторых процедуреный Snowflake SQL работает ОЧЕНЬ медленно. Мне там надо ежедневно обновлять около двух миллионов записей, это буквально за день не закончится никогда. Поэтому нужен питоний.
Нейросети пробовал?
Пробовал, пишут полную чушь. Например, упорно предлагают использовать метод DataFrame.map , которого не существует в природе. Еще идеи?
пнуть ии 2й раз.