如何在 JavaScript 中为 BigQuery 实现 T-SQL CHECKSUM()?

How to Implement T-SQL CHECKSUM() in JavaScript for BigQuery?

我要寻找的最终结果是实现 T-SQL CHECKSUM in BigQuery with a JavaScript UDF。我会满足于翻译 C/C++ 源代码,但如果有人已经完成了这项工作,那么我很乐意使用它。

或者,如果有人能想出一种方法来在 Microsoft SQL 服务器中存储的字符串与 BigQuery 中存储的字符串之间创建等效的哈希码,那对我也有帮助。


我花了一整天的时间对此进行逆向工程,首先转储单个 ASCII 字符和成对字符的所有结果。这表明每个字符都有自己不同的 "XOR code" 并且字母无论大小写都相同。之后该算法非常容易理解:向左旋转 4 位并通过查找 table.

中存储的代码进行异或运算。
var xorcodes = [
    0, 1, 2, 3, 4, 5, 6, 7,
    8, 9, 10, 11, 12, 13, 14, 15,
    16, 17, 18, 19, 20, 21, 22, 23,
    24, 25, 26, 27, 28, 29, 30, 31,
    0, 33, 34, 35, 36, 37, 38, 39,  //  !"#$%&'
    40, 41, 42, 43, 44, 45, 46, 47,  // ()*+,-./
    132, 133, 134, 135, 136, 137, 138, 139,  // 01234567
    140, 141, 48, 49, 50, 51, 52, 53, 54,  // 89:;<=>?@
    142, 143, 144, 145, 146, 147, 148, 149,  // ABCDEFGH
    150, 151, 152, 153, 154, 155, 156, 157,  // IJKLMNOP
    158, 159, 160, 161, 162, 163, 164, 165,  // QRSTUVWX
    166, 167, 55, 56, 57, 58, 59, 60,  // YZ[\]^_`
    142, 143, 144, 145, 146, 147, 148, 149,  // abcdefgh
    150, 151, 152, 153, 154, 155, 156, 157,  // ijklmnop
    158, 159, 160, 161, 162, 163, 164, 165,  // qrstuvwx
    166, 167, 61, 62, 63, 64, 65, 66,  // yz{|}~
];

function rol(x, n) {
    // simulate a rotate shift left (>>> preserves the sign bit)
    return (x<<n) | (x>>>(32-n));
}

function checksum(s) {
    var checksum = 0;
    for (var i = 0; i < s.length; i++) {
        checksum = rol(checksum, 4);

        var c = s.charCodeAt(i);
        var xorcode = 0;
        if (c < xorcodes.length) {
            xorcode = xorcodes[c];
        }
        checksum ^= xorcode;
    }
    return checksum;
};

有关详细信息,请参阅 https://github.com/neilodonuts/tsql-checksum-javascript

免责声明:我只研究过 SQL 服务器中 VARCHAR 字符串的兼容性,排序规则设置为 SQL_Latin1_General_CP1_CI_AS。这不适用于多列或整数,但我确信底层算法使用相同的代码,因此不难理解。它似乎也不同于 db<>fiddle 可能是由于整理:https://github.com/neilodonuts/tsql-checksum-javascript/blob/master/data/dbfiddle-differences.png ...里程可能会有所不同!

fyi,对于那些陷入 T-SQL 遗留模式的人来说,这里有一个经过测试的 C# 实现,对于我一直使用的大多数 strings/ints 来说看起来不错:

public static int[] xorcodes = {
    0, 1, 2, 3, 4, 5, 6, 7,
    8, 9, 10, 11, 12, 13, 14, 15,
    16, 17, 18, 19, 20, 21, 22, 23,
    24, 25, 26, 27, 28, 29, 30, 31,
    0, 33, 34, 35, 36, 37, 38, 39,  //  !"#$%&'
    40, 41, 42, 43, 44, 45, 46, 47,  // ()*+,-./
    132, 133, 134, 135, 136, 137, 138, 139,  // 01234567
    140, 141, 48, 49, 50, 51, 52, 53, 54,  // 89:;<=>?@
    142, 143, 144, 145, 146, 147, 148, 149,  // ABCDEFGH
    150, 151, 152, 153, 154, 155, 156, 157,  // IJKLMNOP
    158, 159, 160, 161, 162, 163, 164, 165,  // QRSTUVWX
    166, 167, 55, 56, 57, 58, 59, 60,  // YZ[\]^_`
    142, 143, 144, 145, 146, 147, 148, 149,  // abcdefgh
    150, 151, 152, 153, 154, 155, 156, 157,  // ijklmnop
    158, 159, 160, 161, 162, 163, 164, 165,  // qrstuvwx
    166, 167, 61, 62, 63, 64, 65, 66,  // yz{|}~
};


public static int rol(int x, int n) {
    // simulate a rotate shift left (>>> preserves the sign bit)
    return ((int)x << n) | ((int)((uint)x >> (32 - n)));
}

public static int checksum(string s) {
    int checksum = 0; 
    for (var i = 0; i < s.Length; i++) {
        checksum = rol(checksum, 4);

        var c = ((int)s[i]);
        int xorcode = 0;
        if (c < xorcodes.Length) {
            xorcode = xorcodes[c];
        } 
        checksum ^= xorcode;
    }
    return checksum;
}