Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
mssql-python-drivrutinen inkluderar en bulkkopieringsfunktion som effektivt infogar stora mängder data i SQL Server, Azure SQL Database, Azure SQL Managed Instance och SQL-databas i Microsoft Fabric.
Metoden cursor.bulkcopy() erbjuder en högpresterande väg för att ladda stora datamängder:
- Minimerar nätverkets tur-och-retur-resor.
- Undviker valfritt begränsningskontroll under belastning.
- Använder det optimerade TDS bulk insert-protokollet.
- Uppnår genomströmning jämförbar med
bcp.exeochSqlBulkCopy.
Den Rust-baserade mssql_py_core inbyggda tillägget driver bulkkopieringsfunktionen. Den körs utanför det normala markör- execute() flödet.
Grundläggande användning
Anropa bulkcopy() på en markör och ange namnet på måltabellen samt en itererbar samling med radtupler eller Row-objekt:
Important
Om du skapar eller ändrar måltabellen i samma session, anropa conn.commit() före bulkcopy(). Bulkkopieringsprotokollet använder en separat intern kanal för att läsa tabellmetadata, så en obunden DDL-ändring kan orsaka deadlock eller timeout.
import mssql_python
conn = mssql_python.connect(connection_string)
cursor = conn.cursor()
# Create a temp table for the demo
cursor.execute("""
CREATE TABLE ##BulkDemo (
ID INT,
Name NVARCHAR(50),
Amount MONEY
)
""")
conn.commit()
data = [
(1, "Alice", 50000.00),
(2, "Bob", 60000.00),
(3, "Carol", 55000.00),
]
result = cursor.bulkcopy("##BulkDemo", data)
print(f"Copied {result['rows_copied']} rows in {result['batch_count']} batch(es)")
print(f"Elapsed: {result['elapsed_time']}")
Returvärde
bulkcopy() återlämnar en ordbok:
| Key | Type | Beskrivning |
|---|---|---|
rows_copied |
int | Antal rader som har kopierats. |
batch_count |
int | Antal batcher som bearbetats. |
elapsed_time |
float | Tiden som tas för operationen i sekunder. |
Metodsignatur
cursor.bulkcopy(
table_name, # str – target table (can include schema, e.g. "dbo.MyTable")
data, # Iterable[Tuple | Row] – rows to insert
batch_size=0, # int – rows per batch; 0 = server optimal
timeout=30, # int – operation timeout in seconds
column_mappings=None, # List[str] | List[Tuple[int,str]] | None
keep_identity=False, # bool – preserve identity values from source
check_constraints=False, # bool – check constraints during load
table_lock=False, # bool – use table-level lock
keep_nulls=False, # bool – preserve NULLs instead of defaults
fire_triggers=False, # bool – fire INSERT triggers on target
use_internal_transaction=False, # bool – use internal transaction per batch
)
Kolumnmappningar
Som standard mappar bulkcopy() kolumner efter ordinalposition. Varje datakolumn mappas till tabellkolumnen vid samma index. Använd parametern column_mappings för att åsidosätta detta beteende.
Kolumnnamnslista
Varje position i listan motsvarar källdataindexet:
result = cursor.bulkcopy(
"##BulkDemo",
data,
column_mappings=["ID", "Name", "Amount"],
)
Avancerat format: explicit indexkartläggning
Varje tuple har formen (source_index, target_column_name). Använd detta format för att hoppa över eller omordna kolumner:
result = cursor.bulkcopy(
"##BulkDemo",
data,
column_mappings=[(0, "ID"), (1, "Name"), (2, "Amount")],
)
Ladda från filer
Du kan ladda data från CSV-filer och andra filformat genom att skicka en generator till bulkcopy().
CSV-fil
import csv
import io
import mssql_python
# In production, replace io.StringIO with open("data.csv", "r", ...)
csv_data = """ID,Name,Value
1,Widget,9.99
2,Gadget,24.50
3,Gizmo,4.75
"""
def csv_row_generator(file_obj):
"""Generator that yields tuples from a CSV file object."""
reader = csv.reader(file_obj)
next(reader) # Skip header
for row in reader:
if row: # skip blank lines
yield (
int(row[0]), # ID
row[1], # Name
float(row[2]), # Value
)
conn = mssql_python.connect(connection_string)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE ##CSVImport (ID INT, Name NVARCHAR(100), Value FLOAT)
""")
conn.commit()
result = cursor.bulkcopy("##CSVImport", csv_row_generator(io.StringIO(csv_data)))
print(f"Imported {result['rows_copied']} rows from CSV")
Stora filer med batchning
Ställ in parametern batch_size för att styra hur många rader drivrutinen skickar per batch. Denna metod fungerar bra för stora filer:
import csv
import io
import mssql_python
# In production, replace io.StringIO with open("large_file.csv", "r", ...)
csv_data = "\n".join(
["ID,Name,Value"] + [f"{i},Item {i},{i * 1.5}" for i in range(1, 201)]
)
def csv_rows(file_obj):
reader = csv.reader(file_obj)
next(reader) # Skip header
for row in reader:
if row:
yield (int(row[0]), row[1], float(row[2]))
conn = mssql_python.connect(connection_string)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE ##LargeCSV (ID INT, Name NVARCHAR(100), Value FLOAT)
""")
conn.commit()
result = cursor.bulkcopy(
"##LargeCSV",
csv_rows(io.StringIO(csv_data)),
batch_size=50,
)
print(f"Imported {result['rows_copied']} rows in {result['batch_count']} batches")
Ladda pandas DataFrames
Konvertera en pandas DataFrame till en lista med tupler innan du skickar den till bulkcopy():
import pandas as pd
import mssql_python
df = pd.DataFrame({
'ID': [1, 2, 3],
'Name': ['Alice', 'Bob', 'Carol'],
'Amount': [50000.0, 60000.0, 55000.0],
})
conn = mssql_python.connect(connection_string)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE ##PandasDemo (ID INT, Name NVARCHAR(50), Amount MONEY)
""")
conn.commit()
data = [tuple(row) for row in df.itertuples(index=False, name=None)]
result = cursor.bulkcopy("##PandasDemo", data)
Hantera NULL-värden
Skicka None in valfri kolumnposition för att infoga ett SQL-värde NULL :
cursor.execute("""
CREATE TABLE ##NullDemo (ID INT, Name NVARCHAR(50), Amount MONEY)
""")
conn.commit()
data = [
(1, "Alice", 50000.00),
(2, "Bob", None), # NULL Amount
(3, None, 55000.00), # NULL Name
]
cursor.bulkcopy("##NullDemo", data)
Identitetskolumner
För att infoga explicita identitetsvärden, sätt keep_identity=True:
cursor.execute("""
CREATE TABLE ##IdentDemo (ID INT, Name NVARCHAR(50), Amount MONEY)
""")
conn.commit()
data = [
(100, "Alice", 50000.00),
(200, "Bob", 60000.00),
]
cursor.bulkcopy("##IdentDemo", data, keep_identity=True)
När keep_identity=False (standardvärdet) utelämnar du identitetskolumnen i dina data och använder column_mappings för att ange de kolumner som inte är identitetskolumner.
Masskopieringsalternativ
| Parameter | Standardinställning | Beskrivning |
|---|---|---|
batch_size |
0 |
Rader per sats.
0 låter servern välja den optimala storleken. |
timeout |
30 |
Tidsgräns för operationen i sekunder. |
keep_identity |
False |
Bevara identitetsvärden från källdata. |
check_constraints |
False |
Kontrollera tabellbegränsningar under belastningen. |
table_lock |
False |
Skaffa ett bordsnivålås istället för radnivålås. |
keep_nulls |
False |
Bevara NULL-värden istället för att infoga kolumnstandarder. |
fire_triggers |
False |
Kör INSERT utlösare på måltabellen. |
use_internal_transaction |
False |
Omslut varje batch med en intern transaktion. |
Hantering av fel
bulkcopy() utlöser ett undantag om inläsningen misslyckas, så omge anropet med ett try/except-block för att fånga upp fel. Tänk på att bulkcopy() körs på sin egen interna anslutning och sparar de kopierade raderna oberoende, så en conn.rollback() på huvudanslutningen inte kan ångra dem. För att göra en batch atomisk anger du use_internal_transaction=True, som omger varje batch med en egen transaktion som automatiskt rullas tillbaka om batchen misslyckas:
import mssql_python
conn = mssql_python.connect(connection_string)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE ##ImportDemo (ID INT, Name NVARCHAR(50), Value FLOAT)
""")
conn.commit()
data = [
(1, "Alice", 50000.00),
(2, "Bob", 60000.00),
(3, "Carol", 55000.00),
]
try:
result = cursor.bulkcopy("##ImportDemo", data, use_internal_transaction=True)
print(f"Successfully copied {result['rows_copied']} rows")
except (mssql_python.DatabaseError, ValueError) as e:
# bulkcopy() commits on its own connection, so there's nothing to roll back
# here. With use_internal_transaction=True, a failed batch is already rolled
# back on the bulk copy connection.
print(f"Bulk copy failed: {e}")
För att grinda en last bakom din egen valideringslogik, bulkkopiera in i en staging-tabell och promovera sedan raderna till måltabellen med en INSERT ... SELECT inuti en transaktion på din huvudanslutning. Det INSERT körs via din anslutning, så conn.rollback() återställer det om valideringen misslyckas.
Authentication
Bulkkopiering använder en separat intern kanal som kräver sin egen token. Drivrutinen hanterar hämtning av token automatiskt för de autentiseringsmetoder som stöds.
Hanterad identitet (ActiveDirectoryMSI)
Användning Authentication=ActiveDirectoryMSI för systemtilldelad eller användartilldelad hanterad identitet. Denna autentiseringsmetod rekommenderas för Azure-hostade tjänster såsom Azure-VM:ar, App Service, Functions och AKS.
import mssql_python
# System-assigned managed identity
conn = mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryMSI;"
"Encrypt=yes"
)
cursor = conn.cursor()
cursor.execute("CREATE TABLE ##MsiDemo (ID INT, Name NVARCHAR(50))")
conn.commit()
result = cursor.bulkcopy("##MsiDemo", [(1, "Alice"), (2, "Bob")])
print(f"Copied {result['rows_copied']} rows")
För en användartilldelad hanterad identitet anger du klient-ID:t i anslutningssträngen:
conn = mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryMSI;"
"UID=<client-id>;"
"Encrypt=yes"
)
Tjänstens huvudnamn (ActiveDirectoryServicePrincipal)
Använd Authentication=ActiveDirectoryServicePrincipal för autentisering med tjänstens huvudnamn (klientautentiseringsuppgifter).
conn = mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryServicePrincipal;"
"UID=<application-client-id>;"
"PWD=<client-secret>;"
"Encrypt=yes"
)
cursor = conn.cursor()
cursor.execute("CREATE TABLE ##SpDemo (ID INT, Value FLOAT)")
conn.commit()
result = cursor.bulkcopy("##SpDemo", [(1, 1.5), (2, 2.5)])
print(f"Copied {result['rows_copied']} rows")
Standardkedja för inloggningsuppgifter (ActiveDirectoryDefault)
ActiveDirectoryDefault försöker flera autentiseringsuppgiftsleverantörer i följd, till exempel miljövariabler, arbetsbelastningsidentitet, hanterad identitet med mera. Det fungerar både för lokal utveckling och Azure-hostade tjänster utan kodändringar.
För mer information om autentisering, se Microsoft Entra-autentisering.
Prestandatips
Följande tekniker hjälper dig att maximera masskopikapaciteten.
Använd generatorer för stora datamängder
Generatorer minimerar minnesanvändningen eftersom bulkcopy() de accepterar alla iterabla alternativ:
def data_generator(count):
"""Generate rows without loading all into memory."""
for i in range(count):
yield (i, f"Item {i}", i * 1.5)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE ##LargeDemo (ID INT, Name NVARCHAR(50), Value FLOAT)
""")
conn.commit()
result = cursor.bulkcopy("##LargeDemo", data_generator(1000))
Använd bordslås för snabbare laster
När du inte har några samtidiga läsare, ställ table_lock=True in för att minska låsningsbelastning under stora initiala laster.
result = cursor.bulkcopy(
"##LargeDemo",
data,
table_lock=True,
batch_size=100000,
)
Inaktivera index under laddning
Inaktivera tillfälligt icke-grupperade index innan massinläsningen och bygg sedan om dem för förbättrad prestanda:
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE ##IndexDemo (ID INT, Name NVARCHAR(50), Value FLOAT)
""")
cursor.execute("CREATE NONCLUSTERED INDEX IX_Name ON ##IndexDemo(Name)")
conn.commit()
cursor.execute("ALTER INDEX IX_Name ON ##IndexDemo DISABLE")
conn.commit()
result = cursor.bulkcopy("##IndexDemo", data)
conn.commit()
cursor.execute("ALTER INDEX IX_Name ON ##IndexDemo REBUILD")
conn.commit()
Ladda tabeller parallellt
Öppna en separat anslutning för varje tabell och kör lasterna samtidigt.
import concurrent.futures
def load_table(table_name, rows):
conn = mssql_python.connect(connection_string)
cursor = conn.cursor()
cursor.execute(f"CREATE TABLE {table_name} (ID INT, Name NVARCHAR(50), Value FLOAT)")
conn.commit()
result = cursor.bulkcopy(table_name, rows)
conn.commit()
conn.close()
return result["rows_copied"]
data = [(i, f"Item {i}", i * 1.5) for i in range(100)]
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
futures = [
executor.submit(load_table, "##Load1", data),
executor.submit(load_table, "##Load2", data),
executor.submit(load_table, "##Load3", data),
]
for future in concurrent.futures.as_completed(futures):
print(f"Loaded {future.result()} rows")
Jämförelse med alternativ
Följande tabell jämför bulkkopiering med andra metoder för datainsättning.
| Metod | Användningsfall | Performance |
|---|---|---|
cursor.bulkcopy() |
Stora datamängder (mer än 1 000 rader). | Snabbaste |
cursor.executemany() |
Medelstora datamängder med parametrar. | Medel |
cursor.execute() i en slinga |
Små datamängder med enkel logik. | Långsammaste |