Kodowanie Base64 w SQL Server 2005 T-SQL

124

Chciałbym napisać zapytanie T-SQL, w którym koduję ciąg jako łańcuch Base64. O dziwo nie mogę znaleźć żadnych natywnych funkcji T-SQL do kodowania Base64. Czy istnieje funkcja natywna? Jeśli nie, jaki jest najlepszy sposób kodowania Base64 w T-SQL?

Jakub
źródło
1
Chciałbym zapytać, dlaczego dane powinny być przechowywane jako ciąg base64. Istnieje dobry powód, aby używać base64 zamiast http, mianowicie, że zapewnia on interoperacyjność między systemami, które nie obsługują nic więcej niż zestaw znaków ASCII (i traktują wszystkie dane jako tekst). Możesz łatwo przekonwertować tablicę bajtów na base-64 i odwrotnie, więc dlaczego nie przechowywać danych wydajnie? Widziałem nawet, jak ludzie przechowują łańcuchy base64 w kolumnach nvarchar, co zajmuje 275% miejsca varbinary, co prowadzi do marnowania dysku, pamięci RAM, sieci itp.
The Dag
9
Chodzi o wygenerowanie ciągu base64, a nie jego przechowywanie.
Jacob

Odpowiedzi:

187

Wiem, że na to już udzielono odpowiedzi, ale spędziłem więcej czasu niż chciałbym przyznać, wymyślając jednowierszowe instrukcje SQL, aby to osiągnąć, więc udostępnię je tutaj na wypadek, gdyby ktoś inny musiał zrobić to samo:

-- Encode the string "TestData" in Base64 to get "VGVzdERhdGE="
SELECT
    CAST(N'' AS XML).value(
          'xs:base64Binary(xs:hexBinary(sql:column("bin")))'
        , 'VARCHAR(MAX)'
    )   Base64Encoding
FROM (
    SELECT CAST('TestData' AS VARBINARY(MAX)) AS bin
) AS bin_sql_server_temp;

-- Decode the Base64-encoded string "VGVzdERhdGE=" to get back "TestData"
SELECT 
    CAST(
        CAST(N'' AS XML).value(
            'xs:base64Binary("VGVzdERhdGE=")'
          , 'VARBINARY(MAX)'
        ) 
        AS VARCHAR(MAX)
    )   ASCIIEncoding
;

Musiałem użyć tabeli wygenerowanej przez podzapytanie w pierwszym zapytaniu (kodowanie), ponieważ nie mogłem znaleźć żadnego sposobu na przekonwertowanie oryginalnej wartości („TestData”) na jej reprezentację w postaci ciągu szesnastkowego („5465737444617461”), aby dołączyć ją jako argument do xs: hexBinary () w instrukcji XQuery.

Mam nadzieję, że to komuś pomoże!

bystry
źródło
7
Podczas kodowania działa również xs:base64Binary(sql:column("bin"))(bez xs:hexBinarypołączenia). Wielka pomoc!
amfetamachine
3
Aby obsługiwać kodowanie tekstu Unicode, należy dodać „N” przed TestData : „SELECT CAST ( N „ TestData ”AS VARBINARY (MAX)) AS bin”
Kjetil Klaussen
Nie działa dla tekstu Unicode ... SELECT CAST (N '' AS XML) .value ('xs: base64Binary (xs: hexBinary (sql: column ("bin")))', 'VARCHAR (MAX)') Base64Encoding FROM (SELECT CAST (N 'मन्त्रीले उल्ट्याए सात छन्।' AS VARBINARY (MAX)) AS bin) AS bin_sql_server_temp;
hsuk
3
@hsuk varchar nie jest kompatybilny z Unicode. Działa dobrze, jeśli zamiast tego używasz nvarchar (max), np .:SELECT CAST( CAST(N'' AS XML).value( 'xs:base64Binary("LgkoCU0JJAlNCTAJQAkyCUcJIAAJCTIJTQkfCU0JLwk+CQ8JIAA4CT4JJAkgABsJKAlNCWQJ")' , 'VARBINARY(MAX)' ) AS NVARCHAR(MAX) ) UnicodeEncoding ;
AlwaysLearning
7
Ponieważ czasami ludzie muszą osiągnąć pewne rzeczy w oprogramowaniu z powodów, których nie zawsze można przewidzieć ...?
Mercurial
87

Najprostszy i najkrótszy sposób, jaki udało mi się znaleźć dla programu SQL Server 2012 i nowszych, to BINARY BASE64:

SELECT CAST('string' as varbinary(max)) FOR XML PATH(''), BINARY BASE64

Dla Base64 do string

SELECT CAST( CAST( 'c3RyaW5n' as XML ).value('.','varbinary(max)') AS varchar(max) )

(lub nvarchar(max)dla ciągów Unicode)

Slai
źródło
1
Jest to znacznie prostsze niż inne odpowiedzi i działa równie dobrze
sXe
2
jaki jest cel BINARY BASE64 w pierwszej linii? Czy to potrzebne? Próbowałem bez i wydaje się, że daje ten sam wynik.
Mattpm
1
Pierwszy fragment dał mi inny wynik niż się spodziewałem; Zmieniłem „varbinary” na „varbinary (max)” i brakujące znaki znalazły się na swoim miejscu
Hraefn
3
To powinna być odpowiedź, ponieważ rzeczywista odpowiedź wymaga literałów łańcuchowych i nie może akceptować zmiennych, takich jak ta odpowiedź.
Matthew
2
W przypadku base64 do string, zauważam znaczny wzrost perf z .value ('data [1]', 'varbinary (max)') vice .value ('.', 'Varbinary (max)').
Geary M. McIver
25

Oto modyfikacja odpowiedzi mercuriala, która używa podzapytania również przy dekodowaniu, umożliwiając użycie zmiennych w obu przypadkach.

DECLARE
    @EncodeIn VARCHAR(100) = 'Test String In',
    @EncodeOut VARCHAR(500),
    @DecodeOut VARCHAR(200)    

SELECT @EncodeOut = 
    CAST(N'' AS XML).value(
          'xs:base64Binary(xs:hexBinary(sql:column("bin")))'
        , 'VARCHAR(MAX)'
    )
FROM (
    SELECT CAST(@EncodeIn AS VARBINARY(MAX)) AS bin
) AS bin_sql_server_temp;

PRINT @EncodeOut

SELECT @DecodeOut = 
CAST(
    CAST(N'' AS XML).value(
        'xs:base64Binary(sql:column("bin"))'
      , 'VARBINARY(MAX)'
    ) 
    AS VARCHAR(MAX)
) 
FROM (
    SELECT CAST(@EncodeOut AS VARCHAR(MAX)) AS bin
) AS bin_sql_server_temp;

PRINT @DecodeOut
Joey Gennari
źródło
22

Oto kod funkcji, które będą działać

-- To Base64 string
CREATE FUNCTION [dbo].[fn_str_TO_BASE64]
(
    @STRING NVARCHAR(MAX)
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
    RETURN (
        SELECT
            CAST(N'' AS XML).value(
                  'xs:base64Binary(xs:hexBinary(sql:column("bin")))'
                , 'NVARCHAR(MAX)'
            )   Base64Encoding
        FROM (
            SELECT CAST(@STRING AS VARBINARY(MAX)) AS bin
        ) AS bin_sql_server_temp
    )
END
GO

-- From Base64 string
CREATE FUNCTION [dbo].[fn_str_FROM_BASE64]
(
    @BASE64_STRING NVARCHAR(MAX)
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
    RETURN (
        SELECT 
            CAST(
                CAST(N'' AS XML).value('xs:base64Binary(sql:variable("@BASE64_STRING"))', 'VARBINARY(MAX)') 
            AS NVARCHAR(MAX)
            )   UTF8Encoding
    )
END

Przykład użycia:

DECLARE @CHAR NVARCHAR(256) = N'e.g., سلام جیران or В России'
SELECT [dbo].[fn_str_FROM_BASE64]([dbo].[fn_str_TO_BASE64](@CHAR)) as converted

wprowadź opis obrazu tutaj

Oleg
źródło
Ogólnie przydatne. Nie dotyczyło to żadnych postaci, takich jak perski i rosyjski, czy emoji. np. سلام جیران lub В России Base64 кодирует вас lub ❤️💥🤪🦌🎅⛄🎄🤐🙈🙉🙊💩
Hunter-Orionnoir
Masz rację. Obsługuje po zamianie varchar na nvarchar
Oleg
8

Uwielbiałem odpowiedź @ Slai. Musiałem tylko dokonać bardzo drobnych modyfikacji w jednowarstwowych, których szukałem. Pomyślałem, że podzielę się tym, z czym skończyłem, na wypadek, gdyby to pomogło komukolwiek, kto natknął się na tę stronę, tak jak ja:

DECLARE @Source VARCHAR(50) = '12345'
DECLARE @Encoded VARCHAR(500) = CONVERT(VARCHAR(500), (SELECT CONVERT(VARBINARY, @Source) FOR XML PATH(''), BINARY BASE64))
DECLARE @Decoded VARCHAR(500) = CONVERT(VARCHAR(500), CONVERT(XML, @Encoded).value('.','varbinary(max)'))
SELECT @Source AS [Source], @Encoded AS [Encoded], @Decoded AS [Decoded]
Jason W
źródło
U mnie musiałem zmienić drugą linię VARBINARYna VARBINARY(56)i wtedy zadziałało.
Lee Grissom
Najkrótsze rozwiązanie, kompatybilne z SQL Server 2005+.
YB,
1
DECLARE @source varbinary(max),  
@encoded_base64 varchar(max),  
@decoded varbinary(max) 
SET @source = CONVERT(varbinary(max), 'welcome') 
-- Convert from varbinary to base64 string 
SET @encoded_base64 = CAST(N'' AS xml).value('xs:base64Binary(sql:variable       
("@source"))', 'varchar(max)') 
  -- Convert back from base64 to varbinary 
   SET @decoded = CAST(N'' AS xml).value('xs:base64Binary(sql:variable             
  ("@encoded_base64"))', 'varbinary(max)') 

 SELECT
  CONVERT(varchar(max), @source) AS [Source varchar], 
   @source AS [Source varbinary], 
     @encoded_base64 AS [Encoded base64], 
     @decoded AS [Decoded varbinary], 
     CONVERT(varchar(max), @decoded) AS [Decoded varchar]

Jest to przydatne do kodowania i dekodowania.

Bharat J.

Bharat J
źródło
0

Zrobiłem skrypt, aby przekonwertować istniejący hash zakodowany w base64 na dziesiętny, może się przydać:

SELECT LOWER(SUBSTRING(CONVERT(NVARCHAR(42), CAST( [COLUMN_NAME] as XML ).value('.','varbinary(max)'), 1), 3, 40)) from TABLE
Phate01
źródło
-1

Możesz użyć tylko:

Declare @pass2 binary(32)
Set @pass2 =0x4D006A00450034004E0071006B00350000000000000000000000000000000000
SELECT CONVERT(NVARCHAR(16), @pass2)

następnie po zakodowaniu otrzymasz tekst „MjE4Nqk5”

jasmintmp
źródło