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
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
È possibile utilizzare BCP, non T-SQL, ma funziona bene.
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
Utilizzando l'opzione -N sembra fare questo lavoro correttamente –
Utilizzare le seguenti opzioni quando richiesto: - Inserire il tipo di campo XXX archiviazione di file [varbinary (max)]:
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:
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.
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. :-)
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);
}
Pitch Perfect .. – irfandar
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 !! –
L'hai mai usato? Non sembra così. Immagino che Irfandar e io lo stiamo solo immaginando. – JohnOpincar
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");
}
Per eseguire ciò potrebbe essere necessario abilitare le procedure di automazione OLE https://msdn.microsoft.com/en-us/library/ms191188.aspx. –