SQL Server MSSQL将基于十进制的字符串转换为varbinary(10),其中8字节部分超过bigint

sq1bmfud  于 2023-01-25  发布在  其他
关注(0)|答案(1)|浏览(118)

序言

我有启用了CDC的MSSQL 2016示例。我按Debezium kafka connector流更改。日志和主题中的连接器以000000aa:af9c3f2d:0f5e1的形式引用Log Sequence Number (LSN)。在MSSQL中,我有cdc.dbo_Entity_cdc_CT这样的表,其中显示列__$start_lsn__$start_lsn__$end_lsn,但它的类型为varbinary(10)
我会匹配价值观。
主要基于Day 11 of 31 Days of Disaster: Converting LSN Formats article中的信息,提示它应该在chainconvert to binary style 1-〉bigint中转换,我为此编写了帮助函数:www.example.comhttps://github.com/Hubbitus/sql/commit/4ccf96492f2aba9796ccaecb1b74e9ba53a45895#diff-e495aa591f98a29c28153735379ad6fd
它大部分工作。包含描述,甚至测试转换做正确的。
但有一个问题我现在解决不了。

8字节部分超出bigint

当我转换输入varbinary(16)值时,我将其分为两部分:2字节和8字节长度,分别转换为bigint,然后转换为varchar,例如:

DECLARE @lsn VARBINARY(10) ...
-- Fill by \0 at left
SET @lsn = CONVERT(varbinary, REPLICATE(char(0), 10 - LEN(@lsn))) + @lsn
DECLARE @lsn_s varchar(max) = CONVERT(varchar, CONVERT(bigint, SUBSTRING(@lsn, 1, 2), 1)) + CONVERT(varchar, CONVERT(bigint, SUBSTRING(@lsn, 3, 8), 1))

它在大多数情况下有效(例如,对于测试表,我只看到4条记录有错误),例如,对于如下值:

DECLARE @lsn varbinary(10) = CONVERT(varbinary(10), '0x025D020F35B80001', 1)

您可以像这样检查:

SELECT dbo.__tmp_td_cdc_decode_check(CONVERT(varbinary(10), '0x025D020F35B80001', 1))

但是失败了,说:

DECLARE @lsn varbinary(10) = CONVERT(varbinary(10), '0x0043275D010200440020', 1)
SELECT dbo.__tmp_td_cdc_decode_check(@sql)

事实上,MSSQL have not unsigned datatypes.

主要问题是

    • 我不知道如何在以下行中解决此问题**:
DECLARE @lsn_s varchar(max) = CONVERT(varchar, CONVERT(bigint, SUBSTRING(@lsn, 1, 2), 1)) + CONVERT(varchar, CONVERT(bigint, SUBSTRING(@lsn, 3, 8), 1))
    • 在任何情况下,如何正确地将varbinary(10)转换为十进制数字形式的varchar?**

什么我也不明白,有时我得到错误"数据截断"的转换,但有时它默默发生。请看:

DECLARE @lsn varbinary(10) = CONVERT(varbinary(10), '0x0043275D010200440020', 1) -- Bigint overflow
SELECT
    dbo.__tmp_td_cdc_decode_check(@lsn) as ToCheck
    ,CONVERT(varchar, @lsn, 1) as orig_lsn_hex
    ,CONVERT(bigint, @lsn, 1) as orig_lsn_bigint -- In many cases it will lead overflow!
    ,dbo.__tmp_td_cdc_binary_decode_to_string(@lsn) as string
    ,dbo.__tmp_td_cdc_string_decode_to_binary_DECSTRING(dbo.__tmp_td_cdc_binary_decode_to_string(@lsn)) as decimal_string_reverse

输出:

┌─────────┬────────────────────────┬─────────────────────┬─────────────────────────┬────────────────────────┐
│ ToCheck │      orig_lsn_hex      │   orig_lsn_bigint   │         string          │ decimal_string_reverse │
├─────────┼────────────────────────┼─────────────────────┼─────────────────────────┼────────────────────────┤
│ Error   │ 0x0043275D010200440020 │ 2836424448419299360 │ 000012e6:2f707ba4:02580 │ 4838079590083609600    │
└─────────┴────────────────────────┴─────────────────────┴─────────────────────────┴────────────────────────┘

十六进制值0x0043275D010200440020应该是1_238_768_277_386_959_257_632,而不是从CONVERT(bigint, @lsn, 1)获得的2_836_424_448_419_299_360
P.S.也有趣和有用的材料:

ozxc1zmp

ozxc1zmp1#

如果你必须像使用“普通”十六进制值那样转换值,你需要更“聪明”一点。这是一个想法,使用计数:

USE Sandbox;
GO

SELECT V.YourHex,
       SUM(CONVERT(decimal(25,0),CASE SS.Char WHEN 'A' THEN 10
                                              WHEN 'B' THEN 11
                                              WHEN 'C' THEN 12
                                              WHEN 'D' THEN 13
                                              WHEN 'R' THEN 14
                                              WHEN 'F' THEN 15
                                              ELSE CONVERT(int,SS.Char)
                                 END) * POWER(CONVERT(decimal(25,0),16),20-pos)) AS YourInt
FROM (VALUES(0x0043275D010200440020))V(YourHex)
     CROSS APPLY (VALUES(CONVERT(varchar(20),V.YourHex,2)))C(YourString)
     CROSS APPLY (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),(20))I(I)
     CROSS APPLY (VALUES(SUBSTRING(C.YourString,I.I, 1),I.I))SS(Char, Pos)
GROUP BY V.YourHex;

相关问题