Introdução
O trabalho diário raramente requer o armazenamento de dados binários diretamente nas colunas do banco de dados. No entanto, é muito útil em alguns casos.
Ao contrário da opinião popular, os arrays de bytes podem ajudar muito mais do que apenas armazenar grandes objetos binários (documentos, multimídia, etc.). Além disso, eles podem ser usados para armazenar valores de hash e dados de amostra para uma pesquisa/análise de alto nível mais rápida. Ou podem conter bytes que estão nos estados ON/OFF em algum relé eletrônico. Assim que começamos a pensar nos dados de hardware armazenados em bancos de dados, as aplicações se tornam mais aparentes.
Ao contrário dos tipos de dados VARCHAR, nos quais você precisa cuidar do agrupamento e das páginas de código, os tipos de dados binários são séries de bytes (às vezes chamados de matrizes de bytes em linguagens de programação orientadas a objetos), de tamanho fixo (BINAR ) ou variável (VARBINAR ).
Para entender melhor os detalhes sobre os tipos binários, primeiro faremos uma breve introdução aos números hexadecimais, que são como esses dados são armazenados internamente.
Números hexadecimais
Se você faltou à aula sobre números hexadecimais no ensino médio, uma boa introdução pode ser encontrada em uma página dedicada da Wikipedia. Lá, você pode se familiarizar com esse formato de numeração.
Para entender este artigo, é importante saber que o SQL Server Management Studio exibe dados binários em formato hexadecimal com o prefixo “0x”.
Não há grande diferença entre o formato de numeração hexadecimal e decimal. O formato hexadecimal usa sinais de base 16 (0-9 e A-F) em vez da base 10 da notação decimal (0-9). Os valores A-F são números 10-15 da notação de numeração decimal.
É por isso que usamos a notação hexadecimal. Como um byte contém 8 bits, permitindo 256 números inteiros discretos, é benéfico apresentar bytes em formato hexadecimal. Se estivermos segmentando o intervalo de 0 a 256, ele será representado como 00-FF em notação hexadecimal. O prefixo no Management studio é para a clareza da leitura, para enfatizar que estamos mostrando números hexadecimais e não o padrão valores decimais.
Conversão de valor manual usando CAST()
Como os valores binários são, em sentido estrito, strings, podemos convertê-los do formato de número para caractere usando CAST ou CONVERTER Métodos SQL.
Dê uma olhada no exemplo que usa o CAST método:
SELECT CAST('HexTest' AS VARBINARY);
SELECT CAST(0x48657854657374 AS VARCHAR);
Usando estilos de conversão com CONVERT()
CONVERTER() método, ao contrário de CAST() , tem uma opção adicional para usar estilos de conversão.
Estilos de conversão são modelos para regras usadas no processo de conversão. CONVERTER() é usado principalmente em operações de data/hora. Quando os dados estão em um formato não padrão, eles podem ser usados na conversão de valores binários. Observe que os tipos de dados binários não oferecem suporte à conversão automática de tipos de dados sem os valores de parâmetro adequados. Em seguida, o SQL Server lançará uma exceção.
Se olharmos para o CONVERT() definição do método, vemos que são necessários dois parâmetros obrigatórios e um opcional.
O primeiro parâmetro é o tipo de dados de destino e o segundo é o valor do qual gostaríamos de converter. O terceiro parâmetro, no nosso caso, pode ter o valor 1 ou 2 . Valor 1 significa que CONVERT() deve considerar a string de entrada como uma string hexadecimal em formato de texto e o valor 2 significa que você deseja pular o 0x prefixo.
Dê uma olhada nos exemplos que exibem esse comportamento:
DECLARE @MyString NVARCHAR(500)='0x48657854657374';
SELECT CONVERT(VARBINARY(MAX), @MyString );
-- String value is directly converted to binary value - we wanted is to change the datatype
-- and not convert "0x.." prefix to the hexadecimal value
SELECT CONVERT(VARBINARY(MAX), @MyString, 1);
Diferença entre BINÁRIO e VARBINÁRIO
Com dados binários, podemos usar dois tipos de dados – tamanho fixo e tamanho variável. Ou, eles são BINÁRIO e VARBINÁRIO.
Se usarmos a variável de tamanho fixo, o conteúdo sempre será estendido ao tamanho definido com preenchimento de 0x00 … – não há preenchimento em comprimento variável. Usar a operação de soma nessas variáveis não é nenhuma adição executada. Os valores são anexados um ao outro. O mesmo acontece com os tipos de string.
Para demonstrar o comportamento do prefixo, usaremos dois exemplos simples com a operação de soma binária:
SELECT CAST('T' AS BINARY(1)) + CAST('e' AS BINARY(1)) + CAST('s' AS BINARY(1)) + CAST('t' AS BINARY(1));
SELECT CAST('T' AS BINARY(2)) + CAST('e' AS BINARY(2)) + CAST('s' AS BINARY(2)) + CAST('t' AS BINARY(2));
Cada valor na instrução BINARY(2) é pós-fixado com 0x00 valores.
Usando valores inteiros com tipos de dados binários
O SQL Server vem com métodos integrados para converter entre tipos numéricos e tipos binários. Demonstramos isso quando transformamos o Teste string no formato binário e, em seguida, de volta ao formato BIGINT, sem usar a função ASCII():
SELECT CAST('Test' AS VARBINARY(MAX));
SELECT CAST(CAST('Test' AS VARBINARY(MAX)) AS BIGINT);
Conversão simples entre caracteres e valores hexadecimais
Para converter entre valores charter e hexadecimais, é útil escrever uma função personalizada que execute essa operação de forma consistente. Uma abordagem possível está abaixo:
-- DROP FUNCTION dbo.FN_CH_HEX(@InputValue CHAR(1)
CREATE OR ALTER FUNCTION dbo.FN_CH_HEX(@InputValue CHAR(1))
RETURNS CHAR(2)
AS
BEGIN
RETURN(CONVERT(CHAR(2), CAST(@InputValue AS BINARY(1)), 2));
END;
-- SELECT dbo.FN_CH_HEX('A')
Desta vez, usamos o valor do parâmetro 2 no CONVERTER() função. Mostra que esta operação não deve ser mapeada para código ASCII e ser exibida sem o 0x… prefixo.
Exemplo de estudo de caso:armazenamento de fotos no tipo binário do SQL Server
Geralmente abordamos esse problema implementando um aplicativo Windows/web personalizado ou escrevendo um pacote SSIS personalizado com código C#. Neste exemplo, usarei apenas a linguagem SQL. Pode ser mais útil se você não tiver acesso às ferramentas de front-end do banco de dados.
Para armazenar imagens na tabela do banco de dados, precisamos criar uma tabela que as contenha. A tabela deve incluir colunas contendo o nome da imagem e o conteúdo binário da imagem:
-- DROP TABLE T_BINARY_DATA
CREATE TABLE T_BINARY_DATA
(
PICTURE_ID INT IDENTITY(1,1) PRIMARY KEY,
PICTURE_NAME NVARCHAR(100),
PICTURE_FILE_NAME NVARCHAR(500),
PICTURE_DATA VARBINARY(MAX)
)
GO
Para habilitar o carregamento de dados binários para a instância do SQL Server, precisamos configurar o servidor com duas opções:
- Ative a opção OLE Automation Procedures
- Atribuindo o privilégio BulkAdmin ao usuário que executa o processo de importação de imagem.
O script abaixo fará a tarefa sob o usuário de alto privilégio da instância do SQL Server:
USE MASTER
GO
EXEC sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
EXEC sp_configure 'Ole Automation Procedures', 1;
GO
RECONFIGURE;
GO
-- Add 'bulkadmin' to the correct user
ALTER SERVER ROLE [bulkadmin] ADD MEMBER [NT AUTHORITY\SYSTEM]
GO
Agora podemos começar a escrever o procedimento de importação e exportação:
-- DROP PROCEDURE dbo.proc_ImportBinary
-- DROP PROCEDURE dbo.proc_ExportBinary
CREATE PROCEDURE dbo.proc_ImportBinary
(
@PICTURE_NAME NVARCHAR(100)
, @FOLDER_PATH NVARCHAR(500)
, @PICTURE_FILE_NAME NVARCHAR(500)
)
AS
BEGIN
DECLARE @OutputPath NVARCHAR(4000);
DECLARE @TSQLDYN NVARCHAR(4000);
SET @OutputPath = CONCAT(@OutputPath,'\',@PICTURE_FILE_NAME)
SET @TSQLDYN = 'INSERT INTO T_BINARY_DATA(PICTURE_NAME,PICTURE_FILE_NAME,PICTURE_DATA) '
+ 'SELECT ' + '''' + @PICTURE_NAME + '''' + ',' + '''' + @PICTURE_FILE_NAME + '''' + ', * '
+ ' FROM Openrowset( Bulk ' + '''' + @OutputPath + '''' + ', Single_Blob) as img'
EXEC (@TSQLDYN)
END
GO
CREATE PROCEDURE dbo.proc_ExportBinary (
@PICTURE_NAME NVARCHAR(100)
, @FOLDER_PATH NVARCHAR(500)
, @PICTURE_FILE_NAME NVARCHAR(500)
)
AS
BEGIN
DECLARE @Binary VARBINARY (max);
DECLARE @OutputPath NVARCHAR(4000);
DECLARE @Obj INT
SELECT @Binary = (
SELECT CONVERT(VARBINARY(max), PICTURE_DATA , 1)
FROM T_BINARY_DATA
WHERE PICTURE_NAME = @PICTURE_NAME
);
SET @OutputPath = CONCAT(@FOLDER_PATH, '\', @PICTURE_FILE_NAME);
BEGIN TRY
EXEC sp_OACreate 'ADODB.Stream', @Obj OUTPUT;
EXEC sp_OASetProperty @Obj ,'Type',1;
EXEC sp_OAMethod @Obj,'Open';
EXEC sp_OAMethod @Obj,'Write', NULL, @Binary;
EXEC sp_OAMethod @Obj,'SaveToFile', NULL, @OutputPath, 2;
EXEC sp_OAMethod @Obj,'Close';
EXEC sp_OADestroy @Obj;
END TRY
BEGIN CATCH
EXEC sp_OADestroy @Obj;
END CATCH
SET NOCOUNT OFF
END
GO
Agora podemos usar esses procedimentos de qualquer aplicativo cliente de uma maneira muito simples.
Vamos imaginar que temos fotos no C:\Pictures\Inp pasta. Para carregar essas imagens, precisamos executar o seguinte código:
-- Load picture to table row
exec dbo.proc_ImportBinary ‘MyPic’, ‘C:\Pictures\Inp’, ‘MyPic.jpg’
De maneira semelhante, podemos exportar dados para o C:\Pictures\Out pasta:
exec dbo.proc_ExportBinary ‘MyPic’, ‘C:\Pictures\Out’, ‘MyPic.jpg’
Conclusão
A escolha entre objetos binários ou meios alternativos de armazenamento de dados binários em um banco de dados (por exemplo, armazenar caminhos de arquivos em um banco de dados e recuperá-los do armazenamento em disco/nuvem) depende de vários fatores.
A regra geral é que se o arquivo tiver menos de 256 kilobytes de tamanho, você deve armazená-lo nas colunas VARBINARY. Se os arquivos binários forem maiores que um megabyte, você deve armazená-los no sistema de arquivos. Se você tiver FILESTREAM disponível nas versões 2008 e posteriores do SQL Server, ele manterá os arquivos sob controle transacional como parte lógica do banco de dados.
Se você decidir armazenar arquivos binários na tabela do SQL Server, use uma tabela separada apenas para conteúdo binário. Então você pode otimizar seu local de armazenamento e acessar o mecanismo, provavelmente usando Arquivos e Grupos de Arquivos separados para esta tabela. As informações detalhadas estão disponíveis no artigo oficial da Microsoft.
De qualquer forma, teste as duas abordagens e use a que melhor se adapte às suas necessidades.