sql >> Databasteknik >  >> RDS >> Sqlserver

to_sql pyodbc count field felaktigt eller syntaxfel

När denna fråga ställdes hade pandas 0.23.0 precis släppts. Den versionen ändrade standardbeteendet för .to_sql() från att anropa DBAPI .executemany() metod för att konstruera en tabellvärdekonstruktor (TVC) som skulle förbättra uppladdningshastigheten genom att infoga flera rader med en enda .execute() anrop av en INSERT-sats. Tyvärr överskred det tillvägagångssättet ofta T-SQL:s gräns på 2100 parametervärden för en lagrad procedur, vilket ledde till felet som citerades i frågan.

Kort därefter lade en efterföljande release av pandor till en method= argument till .to_sql() . Standard – method=None – återställde det tidigare beteendet med .executemany() , medan du anger method="multi" skulle berätta för .to_sql() att använda den nyare TVC-metoden.

Ungefär samtidigt släpptes SQLAlchemy 1.3 och den lade till en fast_executemany=True argument till create_engine() vilket avsevärt förbättrade uppladdningshastigheten med hjälp av Microsofts ODBC-drivrutiner för SQL Server. Med den förbättringen, method=None visade sig vara minst lika snabb som method="multi" samtidigt som man undviker gränsen på 2100 parametrar.

Så med nuvarande versioner av pandor, SQLAlchemy och pyodbc är det bästa sättet att använda .to_sql() med Microsofts ODBC-drivrutiner för SQL Server är att använda fast_executemany=True och standardbeteendet för .to_sql() , dvs.

connection_uri = (
    "mssql+pyodbc://scott:tiger^[email protected]/db_name"
    "?driver=ODBC+Driver+17+for+SQL+Server"
)
engine = create_engine(connection_uri, fast_executemany=True)
df.to_sql("table_name", engine, index=False, if_exists="append")

Detta är det rekommenderade tillvägagångssättet för appar som körs på Windows, macOS och de Linux-varianter som Microsoft stöder för sin ODBC-drivrutin. Om du behöver använda FreeTDS ODBC, sedan .to_sql() kan anropas med method="multi" och chunksize= som beskrivs nedan.

(Originalt svar)

Före pandas version 0.23.0, to_sql skulle generera en separat INSERT för varje rad i datatabellen:

exec sp_prepexec @p1 output,N'@P1 int,@P2 nvarchar(6)',
    N'INSERT INTO df_to_sql_test (id, txt) VALUES (@P1, @P2)',
    0,N'row000'
exec sp_prepexec @p1 output,N'@P1 int,@P2 nvarchar(6)',
    N'INSERT INTO df_to_sql_test (id, txt) VALUES (@P1, @P2)',
    1,N'row001'
exec sp_prepexec @p1 output,N'@P1 int,@P2 nvarchar(6)',
    N'INSERT INTO df_to_sql_test (id, txt) VALUES (@P1, @P2)',
    2,N'row002'

Förmodligen för att förbättra prestandan genererar pandas 0.23.0 nu en tabellvärdekonstruktor för att infoga flera rader per anrop

exec sp_prepexec @p1 output,N'@P1 int,@P2 nvarchar(6),@P3 int,@P4 nvarchar(6),@P5 int,@P6 nvarchar(6)',
    N'INSERT INTO df_to_sql_test (id, txt) VALUES (@P1, @P2), (@P3, @P4), (@P5, @P6)',
    0,N'row000',1,N'row001',2,N'row002'

Problemet är att SQL Server-lagrade procedurer (inklusive systemlagrade procedurer som sp_prepexec ) är begränsade till 2100 parametrar, så om DataFrame har 100 kolumner så to_sql kan bara infoga cirka 20 rader åt gången.

Vi kan beräkna den nödvändiga chunksize använder

# df is an existing DataFrame
#
# limit based on sp_prepexec parameter count
tsql_chunksize = 2097 // len(df.columns)
# cap at 1000 (limit for number of rows inserted by table-value constructor)
tsql_chunksize = 1000 if tsql_chunksize > 1000 else tsql_chunksize
#
df.to_sql('tablename', engine, index=False, if_exists='replace',
          method='multi', chunksize=tsql_chunksize)

Men det snabbaste tillvägagångssättet är troligen fortfarande:

  • dumpa DataFrame till en CSV-fil (eller liknande), och sedan

  • Låt Python anropa SQL Server bcp verktyg för att ladda upp filen till tabellen.



  1. Hur kan jag säkerhetskopiera en fjärrbaserad SQL Server-databas till en lokal enhet?

  2. Lägg till Ordinal Indicator till ett datum i Oracle

  3. Hur exporterar man en mysql-databas med kommandotolken?

  4. Använda guiden Offline Reorg