Datatype để lưu trữ địa chỉ ip trong SQL Server


113

Tôi nên chọn loại dữ liệu nào để lưu trữ Địa chỉ IP trong SQL Server?

Bằng cách chọn đúng loại dữ liệu, liệu có đủ dễ dàng để lọc theo địa chỉ IP không?


Hãy nhìn vào câu trả lời này: stackoverflow.com/questions/1038950/...
Đánh dấu Redman

Câu trả lời:


130

Cách chính xác về mặt kỹ thuật để lưu trữ IPv4 là nhị phân (4), vì đó thực sự là nó (không, thậm chí không phải là INT32 / INT (4), dạng văn bản số mà tất cả chúng ta đều biết và yêu thích (255.255.255.255) chỉ là chuyển đổi hiển thị nội dung nhị phân của nó).

Nếu bạn làm theo cách này, bạn sẽ muốn các hàm chuyển đổi sang và từ định dạng hiển thị văn bản:

Đây là cách chuyển đổi dạng hiển thị văn bản sang dạng nhị phân:

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

Và đây là cách chuyển đổi nhị phân trở lại dạng hiển thị văn bản:

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

Dưới đây là bản demo về cách sử dụng chúng:

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

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

Cuối cùng, khi thực hiện tra cứu và so sánh, hãy luôn sử dụng dạng nhị phân nếu bạn muốn có thể tận dụng các chỉ mục của mình.


CẬP NHẬT:

Tôi muốn thêm rằng một cách để giải quyết các vấn đề về hiệu suất cố hữu của UDF vô hướng trong SQL Server, nhưng vẫn giữ lại việc sử dụng lại mã của một hàm là sử dụng iTVF (hàm có giá trị bảng nội tuyến). Đây là cách hàm đầu tiên ở trên (chuỗi thành nhị phân) có thể được viết lại dưới dạng 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

Đây là nó trong ví dụ:

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

Và đây là cách bạn sẽ sử dụng nó trong CHÈN

INSERT INTo myIpTable
SELECT {other_column_values,...},
       (SELECT bin FROM dbo.itvfBinaryIPv4('192.65.68.201'))

33
Tôi nghĩ điều này chỉ đúng theo nghĩa học thuật. Nếu không biết mục đích và vấn đề miền mà người đăng đang cố gắng giải quyết, tôi nghi ngờ điều này sẽ làm phức tạp một cách không cần thiết việc tương tác với dữ liệu và có khả năng làm giảm hiệu suất.
Eric Sabine

21
IPv4 là một chuỗi có thứ tự gồm bốn byte. Đó nó tên miền, và ở định dạng lưu trữ đó là một BIN (4). Định dạng lưu trữ sẽ không ảnh hưởng đến hiệu suất vì đó là định dạng tối ưu. Hàm chuyển đổi có thể (vì udf bị hút trên máy chủ SQL), có thể được giải quyết bằng cách xếp dòng hoặc thực hiện chuyển đổi trên máy khách. Cuối cùng, cách tiếp cận này có lợi thế đáng kể là nó có thể tìm kiếm các địa chỉ trong mạng con Lớp 1,2 hoặc 3 bằng cách sử dụng quét phạm vi được lập chỉ mục (WHERE ip BETWEEN fnBinaryIPv4 ('132.31.55.00') VÀ fnBinaryIPv4 ('132.31.55.255'))
RBarryYoung 06/09/09

1
@RBarryYoung Tôi sẽ lưu trữ nó dưới dạng số nguyên. bạn có thể giải thích lợi ích hiệu suất của việc lưu trữ nó dưới dạng nhị phân là gì không?
Pacerier

3
@Pacerier: 1) xem nhận xét trước để làm ví dụ, và 2) Tôi không khẳng định rằng Binary sẽ nhanh hơn Integer. Tôi đã tuyên bố rằng A) Đó là định dạng chính xác (và đúng như vậy), và B) nó sẽ không chậm hơn.
RBarryYoung 17/10/11

1
Vâng, bạn không chính xác, đó không phải là những gì Dan đang nói. Ngoài ra, đây không phải là một diễn đàn thảo luận, và nó không phù hợp với nó. Stackoverflow là một câu hỏi & giải đáp, nếu bạn có câu hỏi, vui lòng đăng nó.
RBarryYoung

23

Bạn có thể sử dụng varchar. Độ dài của IPv4 là tĩnh, nhưng độ dài của IPv6 có thể rất thay đổi.

Trừ khi bạn có lý do chính đáng để lưu trữ nó ở dạng nhị phân, hãy gắn bó với loại chuỗi (văn bản).


39
Độ dài của IPv6 rất cố định - 128 bit.
Broam

4
Trừ khi bạn đang nói về dữ liệu mà một người sẽ không bao giờ đọc hoặc một lượng lớn dữ liệu, thì đây là câu trả lời tốt nhất.
Aren Cambre

10
Một lý do đơn giản để sử dụng nhị phân chứ không phải chuỗi: Phiên bản nhị phân cho phép kiểm tra phạm vi số của địa chỉ IP! Phiên bản văn bản không. Tất nhiên, điều này phụ thuộc vào nhu cầu sử dụng, nhưng các số nhị phân hữu ích hơn vì chúng có ý nghĩa thực tế.
Gone Coding

4
varchar chiếm nhiều dung lượng hơn đáng kể trong DB. Địa chỉ IPv4 32 bit cần 4 byte để lưu trữ số và địa chỉ IPv6 128 bit cần 16 byte để lưu trữ số. Trong khi đó, địa chỉ IPv4 đó cần 15 byte để lưu trữ dưới dạng chuỗi và địa chỉ IPv6 có thể mất tới 39 byte dưới dạng chuỗi.
Aaron Schultz

1
varbinary (16) là con đường để đi
jjxtra

17

Đây là một số mã để chuyển đổi IPV4 hoặc IPv6 ở định dạng varchar sang nhị phân (16) và ngược lại. Đây là hình thức nhỏ nhất mà tôi có thể nghĩ ra. Nó sẽ lập chỉ mục tốt và cung cấp một cách tương đối dễ dàng để lọc trên các mạng con. Yêu cầu SQL Server 2005 trở lên. Không chắc nó hoàn toàn chống đạn. Hi vọng điêu nay co ich.

-- 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 

Câu trả lời này hoạt động hoàn hảo cho cơ sở dữ liệu db-ip IP đến quốc gia. Một chuyển đổi khứ hồi chỉ cho thấy những khác biệt nhỏ trong đó số 0 được cắt bớt từ ipv6 (đầu và sau).
crokusek

1
Trong ToBinary (), gặp một số vấn đề với kế hoạch truy vấn và sử dụng fn_varbintohexstr () không được đánh dấu là xác định. Còn đối với cái khác thì sao '.' phần: select @ vbzone = convert (varbinary (2), convert (tinyint, @ token))? Có vẻ tương đương. Không cần động cơ @ vùng hoặc xml? Có vẻ như tăng tốc tốt nếu công cụ xml bị xóa bằng cách nào đó khỏi ':'.
crokusek

concat_ws ('.', (IPAddr & 0xFF000000) >> 24, (IPAddr & 0xFF0000) >> 16, (IPAddr & 0xFF00) >> 8, (IPAddr & 0xFF))) sẽ chuyển đổi một đoạn dài chưa được đánh dấu có chứa địa chỉ IP thành dạng người có thể đọc được.
theking 2

@ theking2 - điều này không áp dụng cho SQL Server vì >> không được hỗ trợ
Alex

Lưu ý rằng có một lỗi trong fn_ConvertIpAddressToBinary. Xem câu trả lời của C.Plockcủa tôi .
Alex

10

Khi tôi muốn xử lý cả IPv4IPv6, tôi đang sử dụng VARBINARY(16)và các SQL CLRchức năng sau để chuyển đổi textbản trình bày địa chỉ IP thành byte và ngược lại:

[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);
}

8

Đối với những người sử dụng .NET có thể sử dụng lớp IPAddress để phân tích cú pháp chuỗi IPv4 / IPv6 và lưu trữ dưới dạng VARBINARY(16). Có thể sử dụng cùng một lớp để chuyển đổi byte[]thành chuỗi. Nếu muốn chuyển đổi VARBINARYtrong 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

7

sys.dm_exec_connectionssử dụng varchar (48) sau SQL Server 2005 SP1. Nghe có vẻ đủ tốt đối với tôi, đặc biệt nếu bạn muốn sử dụng nó so với giá trị của bạn.

Thực tế, bạn sẽ không thấy IPv6 là chủ đạo trong một thời gian, vì vậy tôi thích con đường 4 tinyint hơn. Nói như vậy, tôi đang sử dụng varchar (48) vì tôi phải sử dụng sys.dm_exec_connections...

Nếu không thì. Câu trả lời của Mark Redman đề cập đến một câu hỏi tranh luận SO trước đây .


4
thực tế chúng ta sẽ được nhìn thấy IPv6
Pacerier

10
Trên thực tế, chúng ta sẽ không thấy Năm 2000 trong một thời gian nữa, cũng có thể sử dụng ngày 2 chữ số để tiết kiệm một vài byte. Ồ, chờ đã.
Eric J.

1

Cảm ơn RBarry. Tôi đang tập hợp một hệ thống phân bổ khối IP và lưu trữ dưới dạng nhị phân là cách duy nhất để thực hiện.

Tôi đang lưu trữ biểu diễn CIDR (ví dụ: 192.168.1.0/24) của khối IP trong trường varchar và sử dụng 2 trường được tính toán để giữ dạng nhị phân của phần đầu và phần cuối của khối. Từ đó, tôi có thể chạy các truy vấn nhanh để xem liệu một khối nhất định như đã được cấp phát hay được gán miễn phí.

Tôi đã sửa đổi hàm của bạn để tính toán Địa chỉ IP kết thúc như vậy:

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

1

Tôi thường sử dụng bộ lọc VARCHAR cũ cho một Địa chỉ IP hoạt động tốt.

Nếu bạn muốn lọc các dải địa chỉ IP, tôi sẽ chia nó thành bốn số nguyên.


1
Phạm vi là gì? Không phải tất cả các mạng con đều là 8 byte. Dải địa chỉ IP cho mạng mà máy chủ lưu trữ này là: 50.50.50.50/20 là bao nhiêu?
Bradley Kreider

2
Số nguyên quá lớn để lưu trữ giá trị 0-255. Thay vào đó, hãy sử dụng một típ nhỏ.
SandRock

0

Tôi thích các chức năng của SandRock. Nhưng tôi đã tìm thấy lỗi trong mã của dbo.fn_ConvertIpAddressToBinary . Tham số đến của @ipAddress VARCHAR (39) quá nhỏ khi bạn nối @delim với nó.

SET @ipAddress = @ipAddress + @delim

Bạn có thể tăng nó lên 40. Hoặc tốt hơn là sử dụng một biến mới lớn hơn và sử dụng nó trong nội bộ. Bằng cách đó, bạn không bị mất cặp cuối cùng với số lượng lớn.

SELECT dbo.fn_ConvertIpAddressToBinary('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')

Thật vậy, có một lỗi
Alex

0

Câu trả lời sau đây dựa trên câu trả lời của M. TurnhoutJerry Birchler cho câu hỏi này nhưng với những cải tiến sau:

  • Đã thay thế việc sử dụng các hàm không có tài liệu ( sys.fn_varbintohexsubstring, fn_varbintohexstr) bằng CONVERT()cho kiểu nhị phân
  • Đã thay thế "hacks" ( CAST('' as xml).value('xs:hexBinary())) XML bằng CONVERT()cho kiểu nhị phân
  • Đã sửa lỗi trong quá trình triển khai của Jerry Birchlerfn_ConvertIpAddressToBinary (như được chỉ ra bởi C.Plock )
  • Thêm đường cú pháp nhỏ

Mã đã được kiểm tra trong SQL Server 2014SQL Server 2016 (xem các trường hợp kiểm tra ở cuối)

IPAddressVarbinaryToString

Chuyển đổi giá trị 4 byte thành giá trị IPV4 và 16 byte thành biểu diễn chuỗi IPV6 . Lưu ý rằng chức năng này không rút ngắn địa chỉ.

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

Các trường hợp thử nghiệm:

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

Chuyển đổi biểu diễn chuỗi IPV4IPV6 thành các giá trị nhị phân 4 byte và 16 byte tương ứng. Lưu ý rằng hàm này có thể phân tích cú pháp hầu hết (tất cả các hàm thường được sử dụng) các biểu diễn địa chỉ viết tắt (ví dụ: 127 ... 1 và 2001: db8 :: 1319: 370: 7348). Để buộc hàm thins luôn trả về các giá trị nhị phân 16 byte, bỏ ghi chú nối các số 0 ở cuối hàm.

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

Các trường hợp kiểm tra

Các trường hợp hợp lệ

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

Các trường hợp không hợp lệ

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

-2

Tôi đang sử dụng varchar(15)cho đến nay mọi thứ đang hoạt động cho tôi. Chèn, Cập nhật, Chọn. Tôi vừa mới bắt đầu một ứng dụng có Địa chỉ IP, mặc dù tôi chưa thực hiện nhiều công việc dành cho nhà phát triển.

Đây là câu lệnh select:

select * From dbo.Server 
where  [IP] = ('132.46.151.181')
Go
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.