2010-10-29 12 views
18

Ho alcuni dati varbinary memorizzati in una tabella in MS Sql Server 2005. Qualcuno ha codice SQL che accetta una query come input (diciamo che la query garantisce che una singola colonna di varbinary venga restituita) e restituisce i byte su disco (un file per riga?) Sono sicuro che questo è stato chiesto migliaia di volte prima, ma su Google vengono in gran parte le soluzioni .net. Voglio una soluzione SQL.Script per salvare i dati varbinary sul disco

risposta

27

L'approccio BCP non funziona per me. I byte che scrive sul disco non possono essere deserializzati indietro agli oggetti .net che ho archiviato. Ciò significa che i byte sul disco non sono equivalenti a quelli memorizzati. Forse BCP sta scrivendo una specie di header. Non ne sono sicuro.

Ho trovato il seguente codice here nella parte inferiore dell'articolo. Funziona alla grande! Sebbene fosse destinato alle immagini BMP memorizzate, funziona con qualsiasi varbinary.

DECLARE @SQLIMG VARCHAR(MAX), 
    @IMG_PATH VARBINARY(MAX), 
    @TIMESTAMP VARCHAR(MAX), 
    @ObjectToken INT 

DECLARE IMGPATH CURSOR FAST_FORWARD FOR 
     SELECT csl_CompanyLogo from mlm_CSCompanySettingsLocalizations 

OPEN IMGPATH 

FETCH NEXT FROM IMGPATH INTO @IMG_PATH 

WHILE @@FETCH_STATUS = 0 
    BEGIN 
     SET @TIMESTAMP = 'd:\' + replace(replace(replace(replace(convert(varchar,getdate(),121),'-',''),':',''),'.',''),' ','') + '.bmp' 

     PRINT @TIMESTAMP 
     PRINT @SQLIMG 

     EXEC sp_OACreate 'ADODB.Stream', @ObjectToken OUTPUT 
     EXEC sp_OASetProperty @ObjectToken, 'Type', 1 
     EXEC sp_OAMethod @ObjectToken, 'Open' 
     EXEC sp_OAMethod @ObjectToken, 'Write', NULL, @IMG_PATH 
     EXEC sp_OAMethod @ObjectToken, 'SaveToFile', NULL, @TIMESTAMP, 2 
     EXEC sp_OAMethod @ObjectToken, 'Close' 
     EXEC sp_OADestroy @ObjectToken 

     FETCH NEXT FROM IMGPATH INTO @IMG_PATH 
    END 

CLOSE IMGPATH 
DEALLOCATE IMGPATH 
+2

Per eseguire ciò potrebbe essere necessario abilitare le procedure di automazione OLE https://msdn.microsoft.com/en-us/library/ms191188.aspx. –

11

È possibile utilizzare BCP, non T-SQL, ma funziona bene.

+1

Ciao Dustin: sono riuscito a utilizzare il comando per generare un file, ma non penso che funzioni correttamente. I dati sono un oggetto .net serializzato. So che i dati sono archiviati correttamente perché ho processi che operano su tali dati da .net. Tuttavia, quando provo a deserializzare ottengo un errore, il che significa che i byte non sono scritti correttamente. Pensieri? Se il comando genera un singolo valore varbinary (max), i byte effettivi scritti sul disco o il processo includono intestazioni, ecc.? – SFun28

+1

Utilizzando l'opzione -N sembra fare questo lavoro correttamente –

+3

Utilizzare le seguenti opzioni quando richiesto: - Inserire il tipo di campo XXX archiviazione di file [varbinary (max)]: - Inserire il prefisso di lunghezza di campo XXX [8 ]: 0 - Inserire la lunghezza di campo XXX [0]: - Inserire campo terminatore [nessuno]: - volete salvare queste informazioni in formato di un file? [S/n] n breez

0

SQL è progettato per funzionare con oggetti di database, quindi dal suo punto di vista non esiste altro. Certo, ci sono procedure estese come xp_cmdshell che ti permettono di interagire con il sistema operativo, ma sono estensioni proprietarie e non fanno parte di T-SQL.

Forse il massimo avvicinamento sarebbe utilizzando l'attributo FILESTREAM per i tipi di binari di SQL Server 2008, che permettono la memorizzazione di alcune colonne direttamente come file in una cartella invece di utilizzare il database:

FILESTREAM overview

Nota che lo spazio di archiviazione FILESTREAM è progettato per mantenere file di grandi dimensioni fuori dal database al fine di aumentare le prestazioni e non per consentire l'accesso diretto ai file (ad esempio, T-SQL non ha ancora il concetto di un filesystem). A mio avviso, l'accesso diretto al filesystem da SQL vanificherà alcuni degli scopi di un database (principalmente avendo i dati memorizzati in modo strutturato).

Quindi mi consiglia di seguire il consiglio di Dustin e utilizzare uno strumento come BCP o qualsiasi altro Dumper di dati.

3

so che è un vecchio post, ma ho capito perché il seguente non funziona e come risolvere il problema:

BCP "SELECT FileContent FROM table WHERE ID = 1" queryout "C:\file.JPG" -T -N 

Il motivo è BCP messo Lunghezza prefisso proprio all'inizio di il file. È 4 byte o 8 byte, dipende dal tipo di dati della colonna FileContent (testo, ntext, immagine: 4 varchar (max), varbinary (max): 8 Fare riferimento a https://msdn.microsoft.com/en-us/library/ms190779.aspx)

Utilizzare un editor binario, come il uno in Visual Studio, per rimuovere i prefissi, e tutto funziona perfettamente. :-)

1

Se avete LINQPad, questo funziona:

void Main() 
{ 
    var context = this; 
    var query = 
     from ci in context.Images 
     where ci.ImageId == 10 
     select ci.Image 
    ; 
    var result = query.Single(); 
    var bytes = Convert.FromBase64String(result); 
    File.WriteAllBytes(@"c:\image.bmp", bytes); 
} 
+0

Pitch Perfect .. – irfandar

+2

funziona ...? in realtà, così 'contesto = questo' cercherà automaticamente l'istanza di SQL Server, effettuerà il login, troverà il database corretto, che ha uno schema in cui esiste la tabella Immagini ... Mai saputo che LinqPad era così intelligente !! –

+0

L'hai mai usato? Non sembra così. Immagino che Irfandar e io lo stiamo solo immaginando. – JohnOpincar

2

solo un'alternativa. È possibile utilizzare il rospo freeware per SQL Server e salvare direttamente dall'editor.

enter image description here

3

sto aggiungendo questo per costruire su risposta di JohnOpincar, in modo che gli altri che vogliono utilizzare LINQPad può ottenere una soluzione di lavoro più veloce.

/* 
This LinqPad script saves data stored in a VARBINARY field to the specified folder. 
1. Connect to SQL server and select the correct database in the connection dropdown (top right) 
2. Change the Language to C# Program 
3. Change "Attachments" to the name of your table that holds the VARBINARY data 
4. Change "AttachmentBuffer" to the name of the field that holds the data 
5. Change "Id" to the unique identifier field name 
6. Change "1090" to the identity of the record you want to save 
7. Change the path to where you want to save the file. Make sure you choose the right extension. 

Notes: Windows 10 may give you "Access Denied" error when trying to save directly to C:\. Rather save to a subfolder. 
*/ 

void Main() 
{ 
    var context = this; 
    var query = 
     from ci in context.Attachments 
     where ci.Id == 1090 
     select ci.AttachmentBuffer 
    ; 
    byte[] result = query.Single().ToArray(); 
    File.WriteAllBytes(@"c:\DEV\dumpfile.xlsx", result); 
    Console.WriteLine("Done"); 
}