VBScript — сохранение SHA1 в виде числового или двоичного значения в SQL Server

В настоящее время я сохраняю значение SHA1 в SQL Server как char (40). У меня сложилось впечатление, что я мог бы увеличить скорость поиска, изменив это поле на числовое значение. Однако я не уверен, какое поле/тип данных следует использовать для хранения этого в SQL Server и как преобразовать его в VBScript. Должен ли я использовать число или десятичное число и сколько цифр мне нужно использовать?

Я где-то читал, что рекомендуется использовать Binary(20). Однако работа с двоичными значениями в VBScript не кажется слишком простой, поэтому я предполагаю, что вместо этого мне будет лучше использовать числовое значение.

В настоящее время это моя функция SHA1. Я сохраняю строковое значение, которое оно возвращает, в моем поле char(40) в базе данных и выполняю поиск, используя второй бит кода ниже.

Private Function SHA1(s)
    Dim asc, enc, bytes, outstr, pos
    Set asc = CreateObject("System.Text.UTF8Encoding")
    Set enc = CreateObject("System.Security.Cryptography.SHA1CryptoServiceProvider")
    'Convert the string to a byte array and hash it
    bytes = asc.GetBytes_4(s) 'This is how you use .Net overloaded methods in VBScript
    bytes = enc.ComputeHash_2((bytes))
    outstr = ""
    'Convert the byte array to a hex string
    For pos = 1 To Lenb(bytes)
        outstr = outstr & LCase(Right("0" & Hex(Ascb(Midb(bytes, pos, 1))), 2))
    Next
    SHA1 = outstr
    Set asc = Nothing
    Set enc = Nothing
End Function

Вот моя функция поиска. Он уже работает довольно быстро, но я ищу любой способ оптимизировать свой код. Если я использую двоичный код для хранения данных, мне придется использовать его и при поиске. Я полагаю, что мог бы использовать хранимые процедуры, которые позволили бы мне использовать функции SQL Server для преобразования туда и обратно. Возможно, это был бы лучший маршрут. Пожалуйста, порекомендуйте.

Function GetHTTPRefererIDBySHA1(s)
    Dim r
    Set r = Server.CreateObject("ADODB.Recordset")      
    r.open "SELECT httprefererid FROM httpreferer " & _
            "WHERE sha1 = '" & s & "'", con, adOpenForwardOnly, adLockReadOnly
    If Not (r.eof and r.bof) then
        GetHTTPRefererIDBySHA1 = r("httprefererid")
    End If
    r.close
    set r = nothing
End Function

Изменить:
Благодаря ScottE и Google я смог заметно ускорить свои запросы. Вот небольшая информация о моем решении.
1) Я создал поле с именем SHA1Bin. Это поле типа binary(20).
2) Когда я вставляю новую запись, я использую хранимую процедуру. Поскольку я не слишком беспокоюсь о пространстве, я сохраняю необработанное значение httpreferer и его двоичное значение SHA1 в той же таблице и той же строке. Моя хранимая процедура преобразует необработанное значение в двоичный SHA1 с помощью функции HashBytes (SQL Server 2008).
3) Моя функция SHA1 в VBScript остается такой же, как и выше, но теперь я использую ее при поиске. Вот модифицированная версия функции GetReferer:

Function GetHTTPRefererIDBySHA1(s)
    Dim r
    Set r = Server.CreateObject("ADODB.Recordset")      
    r.open "SELECT httprefererid FROM httpreferer WHERE " & _
            "sha1bin = CONVERT(binary(20), 0x" & SHA1(s) & ")", _
            tcon, adOpenForwardOnly, adLockReadOnly

    If Not (r.eof and r.bof) then
        GetHTTPRefererIDBySHA1 = r("httprefererid")
    Else
        '//Insert new record code intentionally omitted
    End If
    r.close
    set r = nothing
End Function

person HK1    schedule 20.04.2011    source источник
comment
Глупый вопрос. Вы проиндексировали столбец sha1?   -  person Gary Barnett    schedule 20.04.2011
comment
Да, я уже проиндексировал. Дело не в том, что он работает заметно медленно. Просто я предполагаю, что он может работать еще лучше, используя более эффективное хранилище/поиск.   -  person HK1    schedule 20.04.2011


Ответы (1)


Я думаю, что вы относительно на правильном пути; тем не менее, есть пара вещей, которые вы можете сделать, чтобы сделать это немного быстрее.

История SHA1

Везде, где вы читали, что SHA1 использует двоичный код (20), это в значительной степени верно. SHA1 — это 160-битное сообщение (20 байт), с которым мы обычно играем в необработанном формате — как вы уже знаете, поскольку ваша функция преобразует этот необработанный двоичный файл в строку.

Преобразование в ЦИФРОВОЙ

Итак, независимо от того, 20 байтов - это 20 байтов. Вы не можете преобразовать его во что-то другое, чтобы ускорить его работу с базой данных. Попытка преобразовать его в числовое будет безуспешной, так как вы получите арифметическую ошибку переполнения (число имеет место только для 17 байтов).

Как сделать лучше

Вы сделали полдела. Вы можете сохранить данные как символьный тип данных, если с ним проще работать в VBScript. В качестве альтернативы вы можете сохранить его как BINARY(20); это подход, который я использую для своих проектов хранилища данных. Если вы собираетесь сохранить его как строку, сделайте его CHAR(20), а не CHAR(40). Тип данных CHAR хранит указанное количество байтов, даже если половина из них пуста (что почти так и есть). Единственная «загвоздка» в этом заключается в том, что ваша функция будет отображать «0x ...» в начале строки, которая технически не является частью значения, но необходима для указания того, что значение является двоичным при построении вашего Оператор SQL. Таким образом, вы можете использовать CHAR(22) или просто выполнять конкатенацию там, где это необходимо. В любом случае, за счет уменьшения количества символов в определении поля SQL выполняет меньше операций чтения, чтобы получить ваши данные, что ускорит работу. Другим альтернативным типом данных может быть VARCHAR, который обрезает пробелы в конце строки (опять же, меньшее количество операций чтения делает запрос более удачным).

Кроме того, проиндексируйте его так же, как вы это сделали. Если вы еще этого не сделали, создайте индекс в столбце SHA1 и включите в индекс httprefererid, ваш запрос будет использовать только индекс для выполнения вашего выбора и будет самым быстрым, насколько это возможно, поскольку необходимы только элементы данных. будет прочитано. Это называется покрывающим индексом (поскольку он охватывает ваш фильтр и выбранные столбцы). Этот индекс будет выглядеть примерно так:

create index ix_httpreferer_sha1 on dbo.httpreferer (sha1) include (httprefererid);

Надеюсь, это поможет!

person scottE    schedule 21.04.2011
comment
У меня есть один вопрос. Вы упомянули, что я должен использовать CHAR(20) вместо CHAR(40). Если я правильно понимаю, моя функция SHA1 фактически возвращает 40-символьный ascii/шестнадцатеричный. Чтобы уменьшить его до 20 байт, мне придется использовать какой-то другой формат. Могу ли я просто преобразовать свою 20-байтовую переменную, называемую байтами, в представление этих 20 байтов в формате ascii? Если правильно понять, одна из проблем заключается в том, что любое появление chr(0) приведет к завершению моей строковой переменной. - person HK1; 22.04.2011
comment
Одна идея, которая у меня была, но я не пробовал, состоит в том, чтобы сохранить 20-байтовый массив непосредственно в базе данных как двоичный (20), но тогда я не уверен, как сделать запрос к нему. - person HK1; 22.04.2011