如何从数字生成最短的数据库标识符名称?

3
由于我正在编写一个生成SQL的软件,该软件可能具有大量参数和记录,并将该SQL记录到磁盘上,因此我有一个不寻常的要求(也许更多是好奇心):生成尽可能短的唯一参数名称。
参数名称遵循标识符命名规则,通常为:
1. 第一个字符是字母。 2. 后续字符可以是字母数字或某些其他字符,例如下划线。 3. 如果加上引号,则几乎可以使用任何内容(忽略 - 引用的标识符至少为三个字符总长度,例如[_])。
SQL生成代码知道总共有多少标识符,因此可以根据整数生成名称。
3个回答

1

这比我预期的更加困难,而且解决方案也不够优雅。因为无效值(以 0 开始)很少,每次试图推导它们都会变得复杂和缓慢,所以我硬编码了它们。我希望能得到如何使其更加优雅的想法。我也会在 CodeReview 上发布。

大多数数据库支持的参数数量少于 2^16(实际使用的数量非常荒谬),但当处理大于 35027(同样荒谬)的数字时,数字的计算结果使得 100 万成为一个好的强制停止点。

public static String intToDatabaseIdentifier(int number)
{
    if(number < 0 || number > 1000000)
        throw new ArgumentOutOfRangeException("number");
    if(number > 25 && number <= 25 + 10) // Skip 0-9 (modified base 36)
        number += 10;
    if(number > 971 && number <= 971 + 360) // Skip 0a-09 (modified base 36)
        number += 360;
    if(number > 35027 && number <= 35027 + 12960) // Skip 0aa-099 (modified base 36)
        number += 12960;
    var stack = new Stack<char>();
    // Base 36 starting with letters rather than numbers
    const string characters = "abcdefghijklmnopqrstuvwxyz0123456789";
    while(number >= 0) {
        stack.Push(characters[number % 36]);
        number = number / 36 - 1;
    }
    return new String(stack.ToArray());
}

以0开头的结果:

a b c d e f g h i j k l m n o p q r s t u v w x y z
aa ab ac ad ae af ag ah ai aj aa ab ac ad ae af ag ah ai aj ak al am an ao
ap aq ar as at au av aw ax ay az a0 a1...

1
也许您可以添加数据库允许使用的字符。我注意到您遵循了标识符不能以数字开头的规则。在此提到的字符是@、下划线、井号和美元符号。 - Valamas
是的,那很容易做到,尽管可能会冒险变得不太数据库无关。 - Charles Burns
似乎你的代码产生了冲突。请尝试使用26和36。 - Timur Mannapov
@TimurMannapov 是的,到处都有冲突,每次添加一个 if(range)...workaround 行之后我都得洗个澡。 - Charles Burns

0

Timur Mannapov的答案与我的其他尝试产生了类似的结果(除了他的结果没有评论中提到的问题),因为进展不是人们所期望的,例如aa,ba,ca而不是aa,ab,ac: (使用String.Concat(ToParamName(i))调用)

// Starts with aa, ba, ba... instead of a, b, c. Probably wouldn't be hard
// to fix but I abandoned this method because it's annoying to call using
// string.Concat(...)
public static IEnumerable<char> ToParamName(int number) {
    const string characters = "abcdefghijklmnopqrstuvwxyz0123456789";
    yield return characters[number % 26];
    number = number / 26;
    do {
        yield return characters[number % 36];
        number = number / 36 - 1;
    } while(number >= 0);
}


// Starts with a, b, c...aa, ba, ba but has collisions starting around 960
public static IEnumerable<char> ToParamName(int number) {
    const string characters = "abcdefghijklmnopqrstuvwxyz0123456789";
    yield return characters[number % 26];
    number = number / 26;
    while(number > 0) {
        yield return characters[number % 36];
        number = number / 36 - 1;
    }
}

我更喜欢以更自然的顺序返回结果,例如a..z, aa, ab, ac...a9(嘿,我并没有声称我是纯粹实用主义者),但我忘记在原始帖子中提到了这一点。Timur的答案涵盖了所有原始要求,所以我会将其标记为正确。

如果有答案按照描述的方式生成结果,我会给它+1。


0
上面的代码会产生冲突。修复后的代码没有冲突和魔法数字。
    public static String intToDatabaseIdentifier(int number)
    {
        const string abcFirst = "abcdefghijklmnopqrstuvwxyz";
        const string abcFull = "abcdefghijklmnopqrstuvwxyz0123456789";
        if (number < 0 || number > 1000000)
            throw new ArgumentOutOfRangeException("number");
        var stack = new Stack<char>();
        //Get first symbol. We will later reverse string. So last - will be first. 
        stack.Push(abcFirst[number % abcFirst.Length]);
        number = number / abcFirst.Length;
        //Collect remaining part
        while (number > 0)
        {
            int index = (number - 1) % abcFull.Length;
            stack.Push(abcFull[index]);
            number = (number - index) / abcFull.Length;
        }
        //Reversing to guarantee first non numeric.
        return new String(stack.Reverse().ToArray());
    }

干得好!如果您能像a..z, aa, ab, ac...a9一样产生结果,再加上+1。 - Charles Burns

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接