Портирование с MySql на T-Sql. Любой эквивалент INET_ATON ()? - PullRequest
5 голосов
/ 30 марта 2009

Необходимо переместить некоторый код из MySql в TSql. У меня есть пара вызовов INET_ATON, который преобразует строку, которая напоминает IP-адрес в число. Есть ли эквивалент T-SQL?

Ответы [ 5 ]

12 голосов
/ 30 марта 2009

Злоупотребление функцией parsname:

create function INET_ATON (@addr varchar(15))
returns bigint
with schemabinding
as
begin
  return  
    cast(parsename(@addr, 4) as bigint) * 16777216 +
    cast(parsename(@addr, 3) as bigint) * 65536 +
    cast(parsename(@addr, 2) as bigint) * 256 +
    cast(parsename(@addr, 1) as bigint)
end

Эта вещь "короткий адрес формы" здесь не поддерживается.

3 голосов
/ 30 марта 2009

Вот функция для преобразования IP-адреса в строку:

CREATE FUNCTION dbo.IpToString 
    (@ip_str VarChar(15))
returns BigInt
as
    begin
    declare @i int
    declare @dot_pos int
    declare @current_part VarChar(15)
    declare @result BigInt

    set @result = 0
    set @i = 0

    while Len(@ip_str) > 0
        begin
        set @i = @i + 1
        set @dot_pos = CharIndex('.', @ip_str)
        if @dot_pos > 0
            begin
            set @current_part = Left(@ip_str, @dot_pos - 1)
            set @ip_str = SubString(@ip_str, @dot_pos + 1, 15)
            end
        else 
            begin
            set @current_part = @ip_str
            set @ip_str = ''
            end

        if Len(@current_part) > 3 Return(Null)
        if IsNumeric(@current_part) = 0 Return (Null)
        if not cast(@current_part as int) between 0 and 255 Return (Null)
        set @result = 256 * @result + Cast(@current_part as BigInt)
        end

    if @i = 4 Return(@result)

    Return(Null)
    end

После создания функции ее можно вызвать так:

select dbo.IpToString('1.2.3.4')
1 голос
/ 07 мая 2014

Больше другой вариант, чем прямой ответ на ваш вопрос (я вижу, что идут отрицательные голоса ^^), но вы также можете рассмотреть возможность использования логики преобразования в вашем программном обеспечении вместо запроса. В зависимости от языка и варианта использования это может быть даже лучше.

Примеры

PHP : ip2long("192.168.1.1");

C / C ++ : inet_addr("192.168.1.1");

C #

System.Net.IPAddress ip;
long ipn = (System.Net.IPAddress.TryParse("192.168.1.1", out ip))

    ? (((long) ip.GetAddressBytes()[0] << 24) | (ip.GetAddressBytes()[1] << 16) |
              (ip.GetAddressBytes()[2] <<  8) |  ip.GetAddressBytes()[3])

    : 0;

Вы также можете указать -1 или nulllong? в качестве типа данных) или написать метод, который выдает исключение, в случае сбоя преобразования.

Python

reduce(lambda sum, chunk: sum <<8 | chunk, map(int, '192.168.1.1'.split(".")))

Прежде чем вы начнете понижать голосование: это всего лишь короткий пример, я не знаю, как здесь обрабатывать ошибки.

Заключение

Конечно, большую часть времени лучше позволить dbs выполнять свою работу, но это действительно зависит, и в случае, если у вас нет проекта с миллионами запросов в секунду, это может помочь.

1 голос
/ 27 января 2012

Два небольших улучшения.

  1. Записывается как встроенная табличная функция
  2. Обходит тот факт, что PARSENAME является недетерминированным

Функция:

CREATE FUNCTION dbo.IPv4ToInt 
(
    @ip varchar(15)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
    SELECT
        IPv4Int =
        CASE
            WHEN LEN(@ip) - LEN(REPLACE(@ip COLLATE Latin1_General_BIN2, '.', '')) = 3
            AND @ip COLLATE Latin1_General_BIN2 NOT LIKE '%[^.0-9]%'
            AND @ip COLLATE Latin1_General_BIN2 LIKE '[0-9]%.[0-9]%.[0-9]%.[0-9]%'
                THEN
                CONVERT
                (
                    integer,
                    (
                        CONVERT(binary(1), CONVERT(tinyint, SUBSTRING(@ip COLLATE Latin1_General_BIN2, 1, (CHARINDEX('.', @ip COLLATE Latin1_General_BIN2, 1)) - 1)))
                        +
                        CONVERT(binary(1), CONVERT(tinyint, SUBSTRING(@ip COLLATE Latin1_General_BIN2, (CHARINDEX('.', @ip COLLATE Latin1_General_BIN2, 1)) + 1, (CHARINDEX('.', @ip COLLATE Latin1_General_BIN2, (CHARINDEX('.', @ip COLLATE Latin1_General_BIN2, 1)) + 1)) - (CHARINDEX('.', @ip COLLATE Latin1_General_BIN2, 1)) - 1)))
                        +
                        CONVERT(binary(1), CONVERT(tinyint, SUBSTRING(@ip COLLATE Latin1_General_BIN2, (CHARINDEX('.', @ip COLLATE Latin1_General_BIN2, (CHARINDEX('.', @ip COLLATE Latin1_General_BIN2, 1)) + 1)) + 1, (CHARINDEX('.', @ip COLLATE Latin1_General_BIN2, (CHARINDEX('.', @ip COLLATE Latin1_General_BIN2, (CHARINDEX('.', @ip COLLATE Latin1_General_BIN2, 1)) + 1)) + 1)) - (CHARINDEX('.', @ip COLLATE Latin1_General_BIN2, (CHARINDEX('.', @ip COLLATE Latin1_General_BIN2, 1)) + 1)) - 1)))
                        +
                        CONVERT(binary(1), CONVERT(tinyint, SUBSTRING(@ip COLLATE Latin1_General_BIN2, (CHARINDEX('.', @ip COLLATE Latin1_General_BIN2, (CHARINDEX('.', @ip COLLATE Latin1_General_BIN2, (CHARINDEX('.', @ip COLLATE Latin1_General_BIN2, 1)) + 1)) + 1)) + 1, LEN(@ip) - (CHARINDEX('.', @ip COLLATE Latin1_General_BIN2, (CHARINDEX('.', @ip COLLATE Latin1_General_BIN2, (CHARINDEX('.', @ip COLLATE Latin1_General_BIN2, 1)) + 1)) + 1)))))
                    )
                )
            ELSE NULL
        END;

Показать свойства функции:

SELECT
    IsDeterministic = OBJECTPROPERTYEX(OBJECT_ID(N'dbo.IPv4ToInt', N'IF'), 'IsDeterministic'),
    IsSystemVerified = OBJECTPROPERTYEX(OBJECT_ID(N'dbo.IPv4ToInt', N'IF'), 'IsSystemVerified'),
    IsPrecise = OBJECTPROPERTYEX(OBJECT_ID(N'dbo.IPv4ToInt', N'IF'), 'IsPrecise');

Пример использования:

DECLARE @Data TABLE
(
    IPv4    varchar(15) NULL
);

INSERT @Data
    (IPv4)
VALUES
    ('192.168.0.3'),
    ('0.0.0.0'),
    ('10.0.16.129'),
    ('255.255.255.255');

SELECT * 
FROM @Data AS d      
CROSS APPLY dbo.IPv4ToInt(d.IPv4) AS ipti;
1 голос
/ 28 июля 2009

Чуть лучше. Он использует int (4b) вместо bigint (8b). Ваш результат должен быть только четыре байта ... один на октет:

create function INET_ATON (@ip varchar(15))
returns int
begin
    declare @rslt int 
    -- This first part is a little error checking
    -- Looks for three dots and all numbers when not dots 
    if len(@ip) - len(replace(@ip,'.','')) = 3 
        AND
            isnumeric(replace(@ip,'.','')) = 1
    begin 
    set @rslt = convert(int,
            convert(binary(1),convert(tinyint,parsename(@ip, 4)))
        +   convert(binary(1),convert(tinyint,parsename(@ip, 3)))
        +   convert(binary(1),convert(tinyint,parsename(@ip, 2)))
        +   convert(binary(1),convert(tinyint,parsename(@ip, 1)))
        )
    end
    else set @rslt = 0
    return @rslt
end;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...