Контрольная цифра ключа USPS ACS

Asked
Viewd1977

0

Я реализовал алгоритм контрольной цифры "MOD 10" с использованием SQL для ключевой линии службы изменения адресов почтовой службы США в соответствии с методом, описанным в их документе, но, похоже, я получаю неправильные числа! Наши входные строки содержат только числа, что немного упрощает вычисления. Когда я сравниваю свои результаты с результатами их тестового приложения, я получаю разные числа. Я не понимаю, что происходит? Кто-нибудь видит что-нибудь не так с моим алгоритмом? Это должно быть что-то очевидное ...

Документацию по методу можно найти на страницах 12-13 этого документа: http://www.usps.com/cpim/ftp/pubs/pub8a. pdf

Образец приложения можно найти по адресу: http://ribbs.usps.gov/acs/documents/tech_guides/KEYLINE. EXE

ОБРАТИТЕ ВНИМАНИЕ: я исправил приведенный ниже код, основываясь на помощи пользователей форума. Это сделано для того, чтобы будущие читатели могли использовать код целиком.

 ALTER function [dbo].[udf_create_acs] (@MasterCustomerId varchar(26))
returns varchar(30)
as
begin
    --this implements the "mod 10" check digit calculation
    --for the US Postal Service ACS function, from "Publication 8A"
    --found at "http://www.usps.com/cpim/ftp/pubs/pub8a.pdf"
    declare @result varchar(30)
    declare @current_char int
    declare @char_positions_odd varchar(10)
    declare @char_positions_even varchar(10)
    declare @total_value int
    declare @check_digit varchar(1)

    --These strings represent the pre-calculated values of each character
    --Example: '7' in an odd position in the input becomes 14, which is 1+4=5
    -- so the '7' is in position 5 in the string - zero-indexed
    set @char_positions_odd = '0516273849'
    set @char_positions_even = '0123456789'
    set @total_value = 0
    set @current_char = 1

    --stepping through the string one character at a time
    while (@current_char <= len(@MasterCustomerId)) begin
        --this is the calculation for the character's weighted value
        if (@current_char % 2 = 0) begin
            --it is an even position, so just add the digit's value
            set @total_value = @total_value + convert(int, substring(@MasterCustomerId, @current_char, 1))
        end else begin
            --it is an odd position, so add the pre-calculated value for the digit
            set @total_value = @total_value + (charindex(substring(@MasterCustomerId, @current_char, 1), @char_positions_odd) - 1)
        end

        set @current_char = @current_char + 1
    end

    --find the check digit (character) using the formula in the USPS document
    set @check_digit = convert(varchar,(10 - (@total_value % 10)) % 10)

    set @result = '#' + @MasterCustomerId + '   ' + @check_digit + '#'

    return @result
end
 

3 ответов

0
 set @check_digit = convert(varchar, (10 - (@total_value % 10)) % 10)
 
  • DUH! I knew I had something backwards :)

    Thanks!

    Jasmine19 февраля 2009, 20:02
0

Почему у нас есть дополнительный мод:

convert (varchar, 10% <

В документе говорится, что из 10. нужно вычесть только последнюю цифру. Я что-то пропустил?

  • The extra mod 10 is so I end up with only the right-most digit.

    Jasmine19 февраля 2009, 19:45
1

Я не уверен, почему вы возитесь со всеми строковыми представлениями, когда работаете на языке, основанном на наборах.

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

 CREATE FUNCTION dbo.Get_Mod10
(
    @original_string    VARCHAR(26)
)
RETURNS VARCHAR(30)
AS
BEGIN
    DECLARE
        @value_mapping TABLE (original_char CHAR(1) NOT NULL, odd_value TINYINT NOT NULL, even_value TINYINT NOT NULL)

    INSERT INTO @value_mapping
    (
        original_char,
        odd_value,
        even_value
    )
    SELECT '0', 0, 0 UNION
    SELECT '1', 2, 1 UNION
    SELECT '2', 4, 2 UNION
    SELECT '3', 6, 3 UNION
    SELECT '4', 8, 4 UNION
    SELECT '5', 1, 5 UNION
    SELECT '6', 3, 6 UNION
    SELECT '7', 5, 7 UNION
    SELECT '8', 7, 8 UNION
    SELECT '9', 9, 9

    DECLARE
        @i              INT,
        @clean_string   VARCHAR(26),
        @len_string     TINYINT,
        @sum            SMALLINT

    SET @clean_string = REPLACE(@original_string, ' ', '')
    SET @len_string = LEN(@clean_string)
    SET @i = 1
    SET @sum = 0

    WHILE (@i <= @len_string)
    BEGIN
        SELECT
            @sum = @sum + CASE WHEN @i % 2 = 0 THEN even_value ELSE odd_value END
        FROM
            @value_mapping
        WHERE
            original_char = SUBSTRING(@clean_string, @i, 1)

        SET @i = @i + 1
    END

    RETURN (10 - (@sum % 10)) % 10
END
GO
 
  • Well, I did an explicit implementation because I want it to be understandable to programmers in the future, and nobody in this shop speaks SQL. Also, when I did a similar thing as you have, it didn’t work. Will try your idea later today and let you know if it works. Thanks!

    Jasmine19 февраля 2009, 19:46