Pyton jest dobrym środowiskiem do analizy danych, ale te dane skąś trzeba brać i gdzieś trzeba je zapisywać. Tu z pomocą przyjdzie moduł pyodbc. Oto przykład gdzie:
- pobieramy dane z bazy danych AdventureWorks
- zapisujemy te dane do obiektu data frame
- tworzymy tabelę w bazie danych tempdb
- zapisujemy wybrane dane z dataframe do tej tabeli
import pandas as pd
import pyodbc
sql_conn = pyodbc.connect('DRIVER={ODBC Driver 13 for SQL Server};\
SERVER=Dragon01;\
DATABASE=Adventureworks;\
Trusted_Connection=yes')
query = "select CurrencyCode, Name, ModifiedDate from Sales.Currency"
df = pd.read_sql(query, sql_conn)
print(df.head(3))
sql_conn = pyodbc.connect('DRIVER={ODBC Driver 13 for SQL Server};\
SERVER=Dragon01;\
DATABASE=tempdb;\
Trusted_Connection=yes')
cursor = sql_conn.cursor()
cursor.execute("IF OBJECT_ID('dbo.currencies') IS NOT NULL DROP TABLE dbo.currencies")
sql_conn.commit()
cursor.execute("CREATE TABLE dbo.currencies(\
code VARCHAR(3),\
name VARCHAR(30))")
sql_conn.commit()
for index,row in df.iterrows():
cursor.execute("INSERT INTO dbo.currencies(code,name) values (?,?)",
row['CurrencyCode'], row['Name'])
sql_conn.commit()
cursor.close()
sql_conn.close()
Po kolei:
-ładujemy moduły
import pandas as pd import pyodbc
-tworzymy obiekt połączenia – należy w odpowiedni sposób wykonać to połączenie. Jeśli Trusted_Connection=yes, to nie trzeba podawać user, bo zostanie wykorzystane uwierzytelnienie windows
sql_conn = pyodbc.connect('DRIVER={ODBC Driver 13 for SQL Server};\
SERVER=Dragon01;\
DATABASE=Adventureworks;\
Trusted_Connection=yes')
zapytanie zapisujemy w postaci napisu:
query = "select CurrencyCode, Name, ModifiedDate from Sales.Currency"
dane pobiera się do data frame wykonując polecenie w ramach tego połączenia do bazy danych
df = pd.read_sql(query, sql_conn) print(df.head(3))
A teraz w drugą stronę. Mamy już obiekt df i chcemy go zapisać w bazie tempdb. Interesują nas tylko 2 kolumny
-Znowu inicjuję połączenie, bo łączyć się będę do bazy tempdb
sql_conn = pyodbc.connect('DRIVER={ODBC Driver 13 for SQL Server};\
SERVER=Dragon01;\
DATABASE=tempdb;\
Trusted_Connection=yes')
Polecenia jakie będą wykonywane to zapisy, które nie zwracają rekordów, więc trzeba mieć obiekt, który coś wykonuje, a takim obiektem jest cursor:
cursor = sql_conn.cursor()
Ja chcę móc wielokrotnie puszczać ten skrypt, więc sprawdzam czy jest w tempdb tabela dbo.currencies i jak jest to ją usuwam. Polecenie execute mówi co będzie wykonywane, a commit rzeczywiście wykonuje to polecenie:
cursor.execute("IF OBJECT_ID('dbo.currencies') IS NOT NULL DROP TABLE dbo.currencies")
sql_conn.commit()
a teraz tworzę tą tabelę na nowo:
cursor.execute("CREATE TABLE dbo.currencies(\
code VARCHAR(3),\
name VARCHAR(30))")
sql_conn.commit()
Teraz przechodzę przez wszystkie wiersze data frame i każdy z nich zapisuję poleceniem insert w tabeli. Każdy insert jest commitowany:
for index,row in df.iterrows():
cursor.execute("INSERT INTO dbo.currencies(code,name) values (?,?)",
row['CurrencyCode'], row['Name'])
sql_conn.commit()
Na końcu zamykamy cursor i połączenie (istotne ze względu na ograniczoną ilość zasobów – tu połączenia SQL)
U mnie zadziałało, zobacz jak będzie u ciebie. Zapisywanie rekord po rekordzie jest nieoptymalne. Zapisanie całego data frame w jednym commit może zapełnić log transakcyjny bazy danych. Idealnie byłoby to robić w paczkach np po 1000 lub po 10000 insertów w jednym commit.






























Komentarze:
Propozycja ładowania całego data frame od razu:
import urllib
from sqlalchemy import create_engine
params = urllib.parse.quote_plus(„DRIVER={SQL Server};SERVER=server_sql;DATABASE=baza;UID=user;trusted_connection=yes”)
engine = create_engine(„mssql+pyodbc:///?odbc_connect=%s” % params)
df.to_sql(’tabela_testowa’, engine, if_exists=’replace’)