Typ danych do przechowywania adresu IP w SQL Server

Odpowiedzi:

130

Technicznie poprawny sposób przechowywania IPv4 jest binarny (4), ponieważ tak właśnie jest (nie, nawet INT32 / INT (4), numeryczna forma tekstowa, którą wszyscy znamy i kochamy (255.255.255.255) jest po prostu konwersja wyświetlania jego zawartości binarnej).

Jeśli zrobisz to w ten sposób, będziesz chciał, aby funkcje konwertowały do ​​iz formatu wyświetlania tekstowego:

Oto jak przekonwertować tekstowy formularz wyświetlania na binarny:

CREATE FUNCTION dbo.fnBinaryIPv4(@ip AS VARCHAR(15)) RETURNS BINARY(4)
AS
BEGIN
    DECLARE @bin AS BINARY(4)

    SELECT @bin = CAST( CAST( PARSENAME( @ip, 4 ) AS INTEGER) AS BINARY(1))
                + CAST( CAST( PARSENAME( @ip, 3 ) AS INTEGER) AS BINARY(1))
                + CAST( CAST( PARSENAME( @ip, 2 ) AS INTEGER) AS BINARY(1))
                + CAST( CAST( PARSENAME( @ip, 1 ) AS INTEGER) AS BINARY(1))

    RETURN @bin
END
go

A oto jak przekonwertować plik binarny z powrotem na tekstową formę wyświetlania:

CREATE FUNCTION dbo.fnDisplayIPv4(@ip AS BINARY(4)) RETURNS VARCHAR(15)
AS
BEGIN
    DECLARE @str AS VARCHAR(15) 

    SELECT @str = CAST( CAST( SUBSTRING( @ip, 1, 1) AS INTEGER) AS VARCHAR(3) ) + '.'
                + CAST( CAST( SUBSTRING( @ip, 2, 1) AS INTEGER) AS VARCHAR(3) ) + '.'
                + CAST( CAST( SUBSTRING( @ip, 3, 1) AS INTEGER) AS VARCHAR(3) ) + '.'
                + CAST( CAST( SUBSTRING( @ip, 4, 1) AS INTEGER) AS VARCHAR(3) );

    RETURN @str
END;
go

Oto demonstracja, jak ich używać:

SELECT dbo.fnBinaryIPv4('192.65.68.201')
--should return 0xC04144C9
go

SELECT dbo.fnDisplayIPv4( 0xC04144C9 )
-- should return '192.65.68.201'
go

Wreszcie, podczas wyszukiwania i porównywania, zawsze używaj formy binarnej, jeśli chcesz mieć możliwość wykorzystania swoich indeksów.


AKTUALIZACJA:

Chciałem dodać, że jednym ze sposobów rozwiązania nieodłącznych problemów z wydajnością skalarnych funkcji UDF w SQL Server, ale nadal zachowując możliwość ponownego wykorzystania kodu funkcji, jest użycie iTVF (wbudowanej funkcji wycenianej w tabeli). Oto, jak pierwszą powyższą funkcję (ciąg do binarnej) można ponownie zapisać jako iTVF:

CREATE FUNCTION dbo.itvfBinaryIPv4(@ip AS VARCHAR(15)) RETURNS TABLE
AS RETURN (
    SELECT CAST(
               CAST( CAST( PARSENAME( @ip, 4 ) AS INTEGER) AS BINARY(1))
            +  CAST( CAST( PARSENAME( @ip, 3 ) AS INTEGER) AS BINARY(1))
            +  CAST( CAST( PARSENAME( @ip, 2 ) AS INTEGER) AS BINARY(1))
            +  CAST( CAST( PARSENAME( @ip, 1 ) AS INTEGER) AS BINARY(1))
                AS BINARY(4)) As bin
        )
go

Oto przykład:

SELECT bin FROM dbo.fnBinaryIPv4('192.65.68.201')
--should return 0xC04144C9
go

A oto jak użyłbyś tego w INSERT

INSERT INTo myIpTable
SELECT {other_column_values,...},
       (SELECT bin FROM dbo.itvfBinaryIPv4('192.65.68.201'))
RBarryYoung
źródło
33
Myślę, że jest to poprawne tylko w sensie akademickim. Podejrzewam, że bez znajomości celu i problemu domeny, dla którego plakat próbuje rozwiązać, niepotrzebnie skomplikuje to interakcję z danymi i potencjalnie obniży wydajność.
Eric Sabine
21
IPv4 to uporządkowana sekwencja czterech bajtów. To JEST to domena, aw formacie przechowywania to BIN (4). Format przechowywania nie wpłynie na wydajność, ponieważ jest to format optymalny. Funkcja konwersji może (ponieważ udf jest do bani na serwerze SQL), co można rozwiązać albo przez wstawianie liniowe, albo wykonując konwersję na kliencie. Wreszcie, to podejście ma tę istotną zaletę, że może wyszukiwać adresy w podsieciach klasy 1, 2 lub 3 przy użyciu skanowania zakresów indeksowanych (WHERE ip BETWEEN fnBinaryIPv4 ('132.31.55.00') AND fnBinaryIPv4 ('132.31.55.255'))
RBarryYoung
1
@RBarryYoung Przechowałbym to jako liczbę całkowitą. czy możesz wyjaśnić, jakie są zalety związane z wydajnością przechowywania go jako pliku binarnego?
Pacerier,
3
@Pacerier: 1) zobacz przykład w poprzednim komentarzu i 2) Nie twierdziłem, że Binary będzie szybsze niż Integer. Twierdziłem, że A) to jest poprawny format (i tak jest) i B) nie byłby wolniejszy.
RBarryYoung
1
Tak, mylisz się, nie o tym mówi Dan. Nie jest to również forum dyskusyjne i nie jest do tego przystosowane. Stackoverflow to strona z pytaniami i odpowiedziami, jeśli masz pytanie, opublikuj je.
RBarryYoung
23

Możesz użyć varchar. Długość IPv4 jest statyczna, ale IPv6 może być bardzo zmienna.

Jeśli nie masz dobrego powodu, aby przechowywać go jako plik binarny, trzymaj się typu string (tekstowego).

NDC
źródło
39
Długość IPv6 jest bardzo stała - 128 bitów.
Broam,
4
O ile nie mówisz o danych, których człowiek nigdy nie przeczyta, ani o ogromnej ilości danych, to najlepsza odpowiedź.
Aren Cambre
10
Jeden prosty powód, aby używać binarnych, a nie ciągów: wersja binarna umożliwia numeryczne sprawdzanie zakresu adresów IP! Wersja tekstowa nie. To oczywiście zależy od wymaganego zastosowania, ale liczby binarne są bardziej przydatne, ponieważ mają rzeczywiste znaczenie.
Gone Coding
4
varchar zajmuje znacznie więcej miejsca w DB. 32-bitowy adres IPv4 zajmuje 4 bajty do przechowywania numerycznego, a 128-bitowy adres IPv6 zajmuje 16 bajtów do przechowywania numerycznego. Tymczasem ten adres IPv4 zajmuje 15 bajtów, aby zapisać go jako ciąg, a adres IPv6 może zająć do 39 bajtów jako ciąg.
Aaron Schultz
1
varbinary (16) jest do zrobienia
jjxtra
17

Oto kod do konwersji IPV4 lub IPv6 w formacie varchar na binarny (16) iz powrotem. To najmniejsza forma, jaką mogłem wymyślić. Powinien dobrze indeksować i zapewniać stosunkowo łatwy sposób filtrowania w podsieciach. Wymaga SQL Server 2005 lub nowszego. Nie jestem pewien, czy jest całkowicie kuloodporny. Mam nadzieję że to pomoże.

-- SELECT dbo.fn_ConvertIpAddressToBinary('2002:1ff:6c2::1ff:6c2')
-- SELECT dbo.fn_ConvertIpAddressToBinary('10.4.46.2')
-- SELECT dbo.fn_ConvertIpAddressToBinary('bogus')

ALTER FUNCTION dbo.fn_ConvertIpAddressToBinary
(
     @ipAddress VARCHAR(39)
)
RETURNS BINARY(16) AS
BEGIN
DECLARE
     @bytes BINARY(16), @vbytes VARBINARY(16), @vbzone VARBINARY(2)
     , @colIndex TINYINT, @prevColIndex TINYINT, @parts TINYINT, @limit TINYINT
     , @delim CHAR(1), @token VARCHAR(4), @zone VARCHAR(4)

SELECT
     @delim = '.'
     , @prevColIndex = 0
     , @limit = 4
     , @vbytes = 0x
     , @parts = 0
     , @colIndex = CHARINDEX(@delim, @ipAddress)

IF @colIndex = 0
     BEGIN
           SELECT
                @delim = ':'
                , @limit = 8
                , @colIndex = CHARINDEX(@delim, @ipAddress)
           WHILE @colIndex > 0
                SELECT
                      @parts = @parts + 1
                      , @colIndex = CHARINDEX(@delim, @ipAddress, @colIndex + 1)
           SET @colIndex = CHARINDEX(@delim, @ipAddress)

           IF @colIndex = 0
                RETURN NULL     
     END

SET @ipAddress = @ipAddress + @delim

WHILE @colIndex > 0
     BEGIN
           SET @token = SUBSTRING(@ipAddress, @prevColIndex + 1, @Colindex - @prevColIndex - 1)

           IF @delim = ':'
                BEGIN
                      SET  @zone = RIGHT('0000' + @token, 4)

                      SELECT
                           @vbzone = CAST('' AS XML).value('xs:hexBinary(sql:variable("@zone"))', 'varbinary(2)')
                           , @vbytes = @vbytes + @vbzone

                      IF @token = ''
                           WHILE @parts + 1 < @limit
                                 SELECT
                                      @vbytes = @vbytes + @vbzone
                                      , @parts = @parts + 1
                END
           ELSE
                BEGIN
                      SET @zone = SUBSTRING('' + master.sys.fn_varbintohexstr(CAST(@token AS TINYINT)), 3, 2)

                      SELECT
                           @vbzone = CAST('' AS XML).value('xs:hexBinary(sql:variable("@zone"))', 'varbinary(1)')
                           , @vbytes = @vbytes + @vbzone
                END

           SELECT
                @prevColIndex = @colIndex
                , @colIndex = CHARINDEX(@delim, @ipAddress, @colIndex + 1) 
     END            

SET @bytes =
     CASE @delim
           WHEN ':' THEN @vbytes
           ELSE 0x000000000000000000000000 + @vbytes
     END 

RETURN @bytes

END
-- SELECT dbo.fn_ConvertBinaryToIpAddress(0x200201FF06C200000000000001FF06C2)
-- SELECT dbo.fn_ConvertBinaryToIpAddress(0x0000000000000000000000000A0118FF)

ALTER FUNCTION [dbo].[fn_ConvertBinaryToIpAddress]
(
     @bytes BINARY(16)
)
RETURNS VARCHAR(39) AS
BEGIN
DECLARE
     @part VARBINARY(2)
     , @colIndex TINYINT
     , @ipAddress VARCHAR(39)

SET @ipAddress = ''

IF SUBSTRING(@bytes, 1, 12) = 0x000000000000000000000000
     BEGIN
           SET @colIndex = 13
           WHILE @colIndex <= 16
                SELECT
                      @part = SUBSTRING(@bytes, @colIndex, 1)
                      , @ipAddress = @ipAddress
                           + CAST(CAST(@part AS TINYINT) AS VARCHAR(3))
                           + CASE @colIndex WHEN 16 THEN '' ELSE '.' END
                      , @colIndex = @colIndex + 1

           IF @ipAddress = '0.0.0.1'
                SET @ipAddress = '::1'
     END
ELSE
     BEGIN
           SET @colIndex = 1
           WHILE @colIndex <= 16
                BEGIN
                      SET @part = SUBSTRING(@bytes, @colIndex, 2)
                      SELECT
                           @ipAddress = @ipAddress
                                 + CAST('' as xml).value('xs:hexBinary(sql:variable("@part") )', 'varchar(4)')
                                 + CASE @colIndex WHEN 15 THEN '' ELSE ':' END
                           , @colIndex = @colIndex + 2
                END
     END

RETURN @ipAddress   

END 
Jerry Birchler
źródło
Ta odpowiedź działała bezbłędnie w przypadku bazy danych IP db-ip do kraju. Konwersja w obie strony wykazała tylko niewielkie różnice, w których zerowano z ipv6 (wiodący i następujący).
crokusek
1
W ToBinary () napotkałem problemy z planem zapytań i używaniem funkcji fn_varbintohexstr (), która nie jest oznaczona jako deterministyczna. A co z innym ”. sekcja: select @ vbzone = convert (varbinary (2), convert (tinyint, @ token))? Wydaje się równoważne. Nie potrzebujesz @ zone ani silnika XML? Wygląda na niezłe przyspieszenie, jeśli silnik xml zostanie w jakiś sposób usunięty z ':'.
crokusek
concat_ws ('.', (IPAddr & 0xFF000000) >> 24, (IPAddr & 0xFF0000) >> 16, (IPAddr & 0xFF00) >> 8, (IPAddr & 0xFF)) skonwertuje długi bez znaku zawierający adres IP na czytelna dla człowieka forma.
theking2
@ theking2 - nie dotyczy to SQL Server, ponieważ >> nie jest obsługiwane
Alex
Zauważ, że jest błąd w fn_ConvertIpAddressToBinary. Zobacz odpowiedź C.Plock i moją .
Alex
10

Ponieważ chcę obsługiwać oba IPv4i IPv6, używam VARBINARY(16)następujących SQL CLRfunkcji do konwersji textprezentacji adresu IP na bajty i na odwrót:

[SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true)]
public static SqlBytes GetIPAddressBytesFromString (SqlString value)
{
    IPAddress IP;

    if (IPAddress.TryParse(value.Value, out IP))
    {
        return new SqlBytes(IP.GetAddressBytes());
    }
    else
    {
        return new SqlBytes();
    }
}


[SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true)]
public static SqlString GetIPAddressStringFromBytes(SqlBytes value)
{
    string output;

    if (value.IsNull)
    {
        output = "";
    }
    else
    {
        IPAddress IP = new IPAddress(value.Value);
        output = IP.ToString();
    }

    return new SqlString(output);
}
gotqn
źródło
8

Użytkownicy korzystający z platformy .NET mogą użyć klasy IPAddress do przeanalizowania ciągu IPv4 / IPv6 i zapisać go jako plik VARBINARY(16). Może użyć tej samej klasy do konwersji byte[]na ciąg. Jeśli chcesz przekonwertować VARBINARYw SQL:

--SELECT 
--  dbo.varbinaryToIpString(CAST(0x7F000001 AS VARBINARY(4))) IPv4,
--  dbo.varbinaryToIpString(CAST(0x20010DB885A3000000008A2E03707334 AS VARBINARY(16))) IPv6

--ALTER 
CREATE
FUNCTION dbo.varbinaryToIpString
(
    @varbinaryValue VARBINARY(16)
)
RETURNS VARCHAR(39)
AS
BEGIN
    IF @varbinaryValue IS NULL
        RETURN NULL
    IF DATALENGTH(@varbinaryValue) = 4
    BEGIN
        RETURN 
            CONVERT(VARCHAR(3), CONVERT(INT, SUBSTRING(@varbinaryValue, 1, 1))) + '.' +
            CONVERT(VARCHAR(3), CONVERT(INT, SUBSTRING(@varbinaryValue, 2, 1))) + '.' +
            CONVERT(VARCHAR(3), CONVERT(INT, SUBSTRING(@varbinaryValue, 3, 1))) + '.' +
            CONVERT(VARCHAR(3), CONVERT(INT, SUBSTRING(@varbinaryValue, 4, 1)))
    END
    IF DATALENGTH(@varbinaryValue) = 16
    BEGIN
        RETURN 
            sys.fn_varbintohexsubstring(0, @varbinaryValue,  1, 2) + ':' +
            sys.fn_varbintohexsubstring(0, @varbinaryValue,  3, 2) + ':' +
            sys.fn_varbintohexsubstring(0, @varbinaryValue,  5, 2) + ':' +
            sys.fn_varbintohexsubstring(0, @varbinaryValue,  7, 2) + ':' +
            sys.fn_varbintohexsubstring(0, @varbinaryValue,  9, 2) + ':' +
            sys.fn_varbintohexsubstring(0, @varbinaryValue, 11, 2) + ':' +
            sys.fn_varbintohexsubstring(0, @varbinaryValue, 13, 2) + ':' +
            sys.fn_varbintohexsubstring(0, @varbinaryValue, 15, 2)
    END

    RETURN 'Invalid'
END
M. Turnhout
źródło
7

sys.dm_exec_connectionsużywa varchar (48) po SQL Server 2005 SP1. Brzmi dla mnie wystarczająco dobrze, zwłaszcza jeśli chcesz go użyć w porównaniu do swojej wartości.

Realistycznie rzecz biorąc, jeszcze przez jakiś czas nie zobaczysz IPv6 jako głównego nurtu, więc wolałbym trasę 4 tinyint. Mówiąc to, używam varchar (48), ponieważ muszę go używaćsys.dm_exec_connections ...

Inaczej. Odpowiedź Marka Redmana wspomina o poprzednim pytaniu z debaty SO .

gbn
źródło
4
realistycznie my będziemy widzieć IPv6
Pacerier
10
Realistycznie nie zobaczymy jeszcze przez jakiś czas Roku 2000, równie dobrze możemy użyć dwucyfrowych dat, aby zaoszczędzić kilka bajtów. Zaczekaj.
Eric J.
1

Dzięki RBarry. Składam system alokacji bloków IP i przechowywanie jako binarne to jedyna droga.

Przechowuję reprezentację CIDR (np .: 192.168.1.0/24) bloku IP w polu varchar i używam 2 pól obliczeniowych do przechowywania postaci binarnej początku i końca bloku. Stamtąd mogę uruchamiać szybkie zapytania, aby sprawdzić, czy dany blok został już przydzielony lub czy można go przypisać.

Zmodyfikowałem twoją funkcję, aby obliczyć końcowy adres IP w następujący sposób:

CREATE FUNCTION dbo.fnDisplayIPv4End(@block AS VARCHAR(18)) RETURNS BINARY(4)
AS
BEGIN
    DECLARE @bin AS BINARY(4)
    DECLARE @ip AS VARCHAR(15)
    DECLARE @size AS INT

    SELECT @ip = Left(@block, Len(@block)-3)
    SELECT @size = Right(@block, 2)

    SELECT @bin = CAST( CAST( PARSENAME( @ip, 4 ) AS INTEGER) AS BINARY(1))
                + CAST( CAST( PARSENAME( @ip, 3 ) AS INTEGER) AS BINARY(1))
                + CAST( CAST( PARSENAME( @ip, 2 ) AS INTEGER) AS BINARY(1))
                + CAST( CAST( PARSENAME( @ip, 1 ) AS INTEGER) AS BINARY(1))

    SELECT @bin = CAST(@bin + POWER(2, 32-@size) AS BINARY(4))
    RETURN @bin
END;
go
Rawk
źródło
1

Zwykle używam zwykłego starego filtrowania VARCHAR, aby adres IP działał dobrze.

Jeśli chcesz filtrować według zakresów adresów IP, podzielę go na cztery liczby całkowite.

Daniel Elliott
źródło
1
Co to jest zakres? Nie wszystkie podsieci mają 8 bajtów. Jaki jest zakres adresów IP sieci, w której znajduje się ten host: 50.50.50.50/20?
Bradley Kreider,
2
Liczby całkowite są zbyt duże, aby zapisać wartość 0-255. Zamiast tego użyj tinyint.
SandRock,
0

Lubię funkcje SandRock. Ale znalazłem błąd w kodzie dbo.fn_ConvertIpAddressToBinary . Przychodzący parametr @ipAddress VARCHAR (39) jest zbyt mały, gdy łączysz z nim @delim.

SET @ipAddress = @ipAddress + @delim

Możesz zwiększyć ją do 40. Lub jeszcze lepiej użyj nowej zmiennej, która jest większa i użyj jej wewnętrznie. W ten sposób nie stracisz ostatniej pary na dużych liczbach.

SELECT dbo.fn_ConvertIpAddressToBinary('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')
C.Plock
źródło
Rzeczywiście jest błąd
Alex
0

Poniższa odpowiedź jest oparta na odpowiedziach M. Turnhouta i Jerry'ego Birchlera na to pytanie, ale z następującymi ulepszeniami:

  • Zamieniono użycie nieudokumentowanych funkcji ( sys.fn_varbintohexsubstring, fn_varbintohexstr) na CONVERT()for stylów binarnych
  • Zastąpiono „hacki” XML ( CAST('' as xml).value('xs:hexBinary())) CONVERT()na stylów binarnych
  • Naprawiono błąd w implementacji Jerry'ego Birchlerafn_ConvertIpAddressToBinary (jak wskazał C.Plock )
  • Dodaj niewielki cukier składniowy

Kod został przetestowany w SQL Server 2014 i SQL Server 2016 (zobacz przykłady testowe na końcu)

IPAddressVarbinaryToString

Konwertuje 4-bajtowe wartości na IPV4 i 16-bajtowe na reprezentacje łańcuchowe IPV6 . Zauważ, że ta funkcja nie skraca adresów.

ALTER FUNCTION dbo.IPAddressVarbinaryToString
(
    @varbinaryValue VARBINARY( 16 )
)
RETURNS VARCHAR(39)
AS
BEGIN
    IF @varbinaryValue IS NULL
        RETURN NULL;
    ELSE IF DATALENGTH( @varbinaryValue ) = 4
        RETURN 
            CONVERT( VARCHAR(3), CONVERT(TINYINT, SUBSTRING( @varbinaryValue, 1, 1 ))) + '.' +
            CONVERT( VARCHAR(3), CONVERT(TINYINT, SUBSTRING( @varbinaryValue, 2, 1 ))) + '.' +
            CONVERT( VARCHAR(3), CONVERT(TINYINT, SUBSTRING( @varbinaryValue, 3, 1 ))) + '.' +
            CONVERT( VARCHAR(3), CONVERT(TINYINT, SUBSTRING( @varbinaryValue, 4, 1 )));
    ELSE IF DATALENGTH( @varbinaryValue ) = 16
        RETURN 
            CONVERT( VARCHAR(4), SUBSTRING( @varbinaryValue,  1, 2 ), 2 ) + ':' +
            CONVERT( VARCHAR(4), SUBSTRING( @varbinaryValue,  3, 2 ), 2 ) + ':' +
            CONVERT( VARCHAR(4), SUBSTRING( @varbinaryValue,  5, 2 ), 2 ) + ':' +
            CONVERT( VARCHAR(4), SUBSTRING( @varbinaryValue,  7, 2 ), 2 ) + ':' +
            CONVERT( VARCHAR(4), SUBSTRING( @varbinaryValue,  9, 2 ), 2 ) + ':' +
            CONVERT( VARCHAR(4), SUBSTRING( @varbinaryValue, 11, 2 ), 2 ) + ':' +
            CONVERT( VARCHAR(4), SUBSTRING( @varbinaryValue, 13, 2 ), 2 ) + ':' +
            CONVERT( VARCHAR(4), SUBSTRING( @varbinaryValue, 15, 2 ), 2 );

    RETURN 'Invalid';
END

Przypadki testowe:

SELECT dbo.IPAddressVarbinaryToString(0x00000000000000000000000000000000) -- 0000:0000:0000:0000:0000:0000:0000:0000 (no address shortening)
SELECT dbo.IPAddressVarbinaryToString(0x00010002000300400500060070000089) -- 0001:0002:0003:0040:0500:0600:7000:0089
SELECT dbo.IPAddressVarbinaryToString(0xC0A80148) -- 255.168.1.72
SELECT dbo.IPAddressVarbinaryToString(0x7F000001) -- 127.0.0.1 (no address shortening)
SELECT dbo.IPAddressVarbinaryToString(NULL) -- NULL

IPAddressStringToVarbinary

Konwertuje reprezentacje łańcuchów IPV4 i IPV6 na odpowiednio 4-bajtowe i 16-bajtowe wartości binarne. Zauważ, że ta funkcja jest w stanie przeanalizować większość (wszystkie powszechnie używane) skrótowych reprezentacji adresu (np. 127 ... 1 i 2001: db8 :: 1319: 370: 7348). Aby wymusić na funkcji thins, aby zawsze zwracała 16-bajtowe wartości binarne, odkomentowanie prowadzące do konkatenacji zera na końcu funkcji.

ALTER FUNCTION [dbo].[IPAddressStringToVarbinary]
(
    @IPAddress VARCHAR( 39 )
)
RETURNS VARBINARY(16) AS
BEGIN

IF @ipAddress IS NULL
    RETURN NULL;

DECLARE @bytes VARBINARY(16), @token VARCHAR(4),
    @vbytes VARBINARY(16) = 0x, @vbzone VARBINARY(2),
    @tIPAddress VARCHAR( 40 ),
    @colIndex TINYINT,
    @delim CHAR(1) = '.',
    @prevColIndex TINYINT = 0,
    @parts TINYINT = 0, @limit TINYINT = 4;

-- Get position if IPV4 delimiter
SET @colIndex = CHARINDEX( @delim, @ipAddress );

-- If not IPV4, then assume IPV6
IF @colIndex = 0
BEGIN
    SELECT @delim = ':', @limit = 8, @colIndex = CHARINDEX( @delim, @ipAddress );

    -- Get number of parts (delimiters)
    WHILE @colIndex > 0
        SELECT @parts += 1, @colIndex = CHARINDEX( @delim, @ipAddress, @colIndex + 1 );

    SET @colIndex = CHARINDEX( @delim, @ipAddress );

    IF @colIndex = 0
        RETURN NULL;
END

-- Add trailing delimiter (need new variable of larger size)
SET @tIPAddress = @IPAddress + @delim;

WHILE @colIndex > 0
BEGIN
    SET @token = SUBSTRING( @tIPAddress, @prevColIndex + 1, @Colindex - @prevColIndex - 1 );

    IF @delim = ':'
    BEGIN
        SELECT @vbzone = CONVERT( VARBINARY(2), RIGHT( '0000' + @token, 4 ), 2 ), @vbytes += @vbzone;

        -- Handles consecutive sections of zeros representation rule (i.e. ::)(https://en.wikipedia.org/wiki/IPv6#Address_representation)
        IF @token = ''
            WHILE @parts + 1 < @limit
                SELECT @vbytes += @vbzone, @parts += 1;
    END
    ELSE
    BEGIN
        SELECT @vbzone = CONVERT( VARBINARY(1), CONVERT( TINYINT, @token )), @vbytes += @vbzone
    END

    SELECT @prevColIndex = @colIndex, @colIndex = CHARINDEX( @delim, @tIPAddress, @colIndex + 1 ) 
END

SET @bytes =
    CASE @delim
        WHEN ':' THEN @vbytes
        ELSE /*0x000000000000000000000000 +*/ @vbytes -- Return IPV4 addresses as 4 byte binary (uncomment leading 0s section to force 16 byte binary)
    END 

RETURN @bytes

END

Przypadki testowe

Prawidłowe przypadki

SELECT dbo.IPAddressStringToVarbinary( '0000:0000:0000:0000:0000:0000:0000:0001' ) -- 0x0000000000000000000000000001 (check bug fix)
SELECT dbo.IPAddressStringToVarbinary( '0001:0002:0003:0040:0500:0600:7000:0089' ) -- 0x00010002000300400500060070000089
SELECT dbo.IPAddressStringToVarbinary( '2001:db8:85a3:8d3:1319::370:7348' )     -- 0x20010DB885A308D31319000003707348 (check short hand)
SELECT dbo.IPAddressStringToVarbinary( '2001:db8:85a3:8d3:1319:0000:370:7348' ) -- 0x20010DB885A308D31319000003707348
SELECT dbo.IPAddressStringToVarbinary( '192.168.1.72' ) -- 0xC0A80148
SELECT dbo.IPAddressStringToVarbinary( '127...1' ) -- 0x7F000001 (check short hand)
SELECT dbo.IPAddressStringToVarbinary( NULL ) -- NULL
SELECT dbo.IPAddressStringToVarbinary( '' ) -- NULL
-- Check that conversions return original address
SELECT dbo.IPAddressVarbinaryToString( dbo.IPAddressStringToVarbinary( '0001:0002:0003:0040:0500:0600:7000:0089' )) -- '0001:0002:0003:0040:0500:0600:7000:0089' 
SELECT dbo.IPAddressVarbinaryToString( dbo.IPAddressStringToVarbinary( '127...1' )) -- 127.0.0.1
SELECT dbo.IPAddressVarbinaryToString( dbo.IPAddressStringToVarbinary( '192.168.1.72' )) -- 192.168.1.72
SELECT dbo.IPAddressVarbinaryToString( dbo.IPAddressStringToVarbinary( '2001:db8:85a3:8d3:1319::370:7348' ))     -- 2001:0db8:85a3:08d3:1319:0000:0370:7348
SELECT dbo.IPAddressVarbinaryToString( dbo.IPAddressStringToVarbinary( '2001:db8:85a3:8d3:1314:0000:370:7348' )) -- 2001:0db8:85a3:08d3:1319:0000:0370:7348
SELECT dbo.IPAddressVarbinaryToString( dbo.IPAddressStringToVarbinary( '2001:db8:85a3:8d3::370:7348' )) -- 2001:0DB8:85A3:08D3:0000:0000:0370:7348
-- This is technically an invalid IPV6 (according to Wikipedia) but it parses correctly
SELECT dbo.IPAddressVarbinaryToString( dbo.IPAddressStringToVarbinary( '2001:db8::1319::370:7348' )) -- 2001:0DB8:0000:0000:1319:0000:0370:7348

Nieprawidłowe przypadki

SELECT dbo.IPAddressVarbinaryToString( dbo.IPAddressStringToVarbinary( '2001:db8::1319::7348' )) -- 2001:0DB8:0000:0000:0000:1319:0000:7348 (ambiguous address)
SELECT dbo.IPAddressStringToVarbinary( '127.1' ) -- 127.0.0.1 (not supported short-hand)
SELECT dbo.IPAddressVarbinaryToString( dbo.IPAddressStringToVarbinary( '127.1' )) -- 127.0.0.1 (not supported short-hand)
SELECT dbo.IPAddressStringToVarbinary( '0300.0000.0002.0353' ) -- octal byte values
SELECT dbo.IPAddressStringToVarbinary( '0xC0.0x00.0x02.0xEB' ) -- hex values
SELECT dbo.IPAddressStringToVarbinary( 'C0.00.02.EB' ) -- hex values
Alex
źródło
-2

Używam do varchar(15)tej pory wszystko działa dla mnie. Wstaw, Aktualizuj, Wybierz. Właśnie uruchomiłem aplikację, która ma adresy IP, chociaż nie wykonałem jeszcze zbyt wielu prac programistycznych.

Oto instrukcja select:

select * From dbo.Server 
where  [IP] = ('132.46.151.181')
Go
Głęboki
źródło