BCP e SqlBulkCopy

Su suggerimento di Lorenzo, relativamente al mio precedente post, sono andato a studiarmi l'utility da comand prompt bcp, ma mi sono accorto che è analoga alla classe SqlBulkCopy che espone le stesse (o quasi) funzionalità all'interno del framework .NET 2.0 (o superiore) - per tutte le versioni di SQL Server ad esclusione della Compact edition:

Remarks

Microsoft SQL Server includes a popular command-prompt utility named bcp for moving data from one table to another, whether on a single server or between servers. The SqlBulkCopy class lets you write managed code solutions that provide similar functionality. There are other ways to load data into a SQL Server table (INSERT statements, for example), but SqlBulkCopy offers a significant performance advantage over them.

The SqlBulkCopy class can be used to write data only to SQL Server tables. However, the data source is not limited to SQL Server; any data source can be used, as long as the data can be loaded to a DataTable instance or read with a IDataReader instance.

Peccato che al momento non riesco a farla funzionare :-(

Appena ci riesco posto i risultati.

Comunque un grazie di cuore a Lorenzo, sempre così gentile a seguire anche chi come me annaspa in acque mooolto basse (circa mezzo bicchiere!).

Se non altro, visto che mi sento un privilegiato che può studiare senza l'assillo di deadline, milestones, clienti incazzati che aspettano il rilascio di qualche progetto (al masimo il cliente sono io stesso, quindi mooolto accomodante), spero di poter fare qualcosa di utile per la community.

Buon Natale!

Importare un file di testo in SQL Server (Ce ed Express)

Milk Data Logging 

In azienda ho una bascula (per pesare il latte munto in stalla) che ha una porta seriale. Per controllare la produzione di ciascuna munta e per registrare le consegne alla cooperativa di raccolta ho fatto un paio di anni fa un programmino con Borland Delphi, al volo come al solito, che riceve sulla seriale la lettura in continuo del peso. Le variazioni di peso vengono registrate su un file di testo (essendo una versione 0.0.0.0.0.0...1). Una volta verificato il funzionamento avrei dovuto modificare il programma per farlo girare come servizio in background, registrando i dati in un database.

Indovinate un po', sono passati poco più di due anni e il programmino sta ancora girando, con il file di testo che ha superato il milione di righe.

Ora, tralasciando il fatto che con questo post mi sono definitivamente sputtanato, viene la parte interessante.

Come importare un file di testo in una tabella di database, o meglio, come farlo in modo efficiente ? E poi, c'è differenza tra usare SQL Server Express o SQL Server Compact Edition ?

Dopo aver googlato un po' per documentarmi, ho deciso di fare alcune prove, partendo dal classico comando di INSERT sino a SqlBulkCopy. I diversi metodi usati sono:

  1. Loop di comandi SQL INSERT;
  2. Preparazione di un comando SQL INSERT con parametro, e poi loop con variazione del valore del parametro ed esecuzione del comando;
  3. Uso di una transazione al cui interno viene eseguito l'inserimento dei dati come al punto 2;
  4. Uso di un ResultSet ottenuto da un comando TableDirect con insert di UpdatableRecord al posto di SQL INSERT;

Inoltre, solo per SQL Server Express, perchè SqlBulkCopy non è disponibile per SQL Server Compact Edition:

  1. Uso del comando SqlBulkCopy () a cui viene passata una DataTable contenente le righe lette dal file di testo (Step 1 e 2);
  2. Uso del comando SqlBulkCopy () a cui viene passato (poichè implementa l'interfaccia iDataReader) direttamente l'OdbcDataReader del file di testo;

E questi i primi risultati (mancano i metodi BulkCopy, al momento non ancora implementato), con un file di testo di 1.238.000 righe (47 caratteri ciascuna):

Using SQL Server Compact Edition
====================================================================
Import from text file (modify cmd each iteration)....: 00.11.29.9476

Deleting imported table..............................: 00.00.43.3449

Import from text file (with param and cmd prepare)...: 00.06.05.5234

Deleting imported table..............................: 00.00.43.6101

Transactional import from text file..................: 00.06.23.0232

Transactional deleting imported table................: 00.00.43.2467

Direct table import from text file (ResultSet).......: 00.06.22.4188

Deleting imported table..............................: 00.00.43.3742

Using SQL Server Express Edition
====================================================================

Import from text file (modify cmd each iteration)....: 00.37.19.1644

Deleting imported table..............................: 00.01.49.0421

Import from text file (with param and cmd prepare)...: 00.32.54.1606

Deleting imported table..............................: 00.00.40.2665

Transactional import from text file..................: 00.08.30.1257

Transactional deleting imported table................: 00.00.17.2543

La prima cosa che balza agli occhi è che in SQLCe non sembra esservi differenza tra i vari metodi (escludendo il primo del tutto inefficiente), mentre per SQL Express vi è un netto peggioramento se non utiliziamo una transazione che racchiuda l'attività di import e di cancellazione.

Facendo alcune prove, interrompendo ed abortendo col debugger le attività di import del metodo 2 (with param and cmd prepare) con SQLCe mi sono accorto di una cosa molto curiosa: nessun record viene inserito nella tabella. In pratica è come se internamente SQLCe in automatico crei una transaction che non viene commit-ata se non viene effettata la corretta chiusura della connection, perdendo tutte le modifiche.

Mi piacerebbe sapere se è vero e se qualcuno ne sa qualcosa al riguardo.

And the winner is...

  • SQL Server Compact Edition
    Import from text file (with param and cmd prepare):  6' 05" 523,4 millisecondi
  • SQL Server Express Edition
    Transactional import from text file (with param and cmd prepare):   8' 30" 125,7 millisecondi

Appena posso finisco SqlBulkCopy e aggiorno il post.

«dicembre»
domlunmarmergiovensab
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345