有没有一种快速的方法可以在 T-SQL 中将 varchar(8) 二进制序列转换为整数?



我有一个DECLARE @binarySequence varchar(8) = '00000000';.我使用 STUFF 手动更改每个索引中的位值。不过,该代码与此问题无关。一旦我更改了所有必要的位,那么SELECT @binarySequence;返回'01001001'.有没有办法让我改变它,以便它返回该二进制序列的十进制表示形式 (73),而无需循环遍历每个字符? 欢迎所有建议以及任何答案/问题 谢谢你的时间!

符号量级表示二进制文件

这是我几年前写的。它使用 Tally 将值拆分为其单个字符,然后在相关POWER聚合每个值以获得最终结果:

CREATE FUNCTION [dbo].[SignedBinaryToDec] (@Binary varchar(64)) 
RETURNS table
AS RETURN
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT TOP (LEN(@Binary)-1) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2, N N3)
SELECT SUM(SS.C * POWER(CONVERT(decimal(2,0),2),T.I-1)) *
CASE LEFT(@Binary,1) WHEN 0 THEN 1
WHEN 1 THEN -1
END AS Dec
FROM Tally T
CROSS APPLY (VALUES(SUBSTRING(REVERSE(@Binary),T.I,1)))SS(C)
WHERE @Binary NOT LIKE '%[^0-1]%';
GO
SELECT Dec
FROM dbo.SignedBinaryToDec('01001001');
<小时 />

这是如何工作的

以上是一个内联表值函数。这意味着它返回一个数据集,而不是一个标量值,你在FROM中引用它。原因是多行标量函数的执行能力可能很差。在SQL Server 2019 +中,它可以内联用户定义的标量函数,但是,我不确定这样的查询是否会。因此,我不想冒险使用它,因为具有基于设置的逻辑的iTVF将具有性能。

让我们将其分为几个步骤:

理货

你将看到的查询的第一部分是 2 个公用表表达式 (CTE);NTally.N字面上只包含 4 行,值为NULL.为什么是4?我稍后会解释。为什么NULL?好吧,它可以是任意值,我只是使用NULL因为它的值毫无意义。

接下来我们有 CTETally.首先,您会注意到它在FROM中引用了N3 次;这意味着NCROSS JOIN(使用旧的 ANSI-89 语法,是的)3 次,导致(最多)64 行,4^3 = 64,这是(您会注意到)参数的长度,varchar(64)。这就是为什么我使用了 4 个NULL值。

SELECT中,我将要返回的行数限制为varchar-1的长度。-1因为二进制值是有符号的,所以字符串中的第一个数字不会用于确定聚合值(稍后完成),而是用于表示该值是正数还是负数。

最后,我们有了ROW_NUMBER,毫不奇怪地给每一行一个升序数字,从1开始。(SELECT NULL)在这里ORDER BY,因为我们(再次)需要一些任意值。

如果我们在这里停止,使用您的值,这将导致一个包含 7 行的数据集,其中值17.您可以使用以下内容进行测试

DECLARE @Binary varchar(64) = '01001001';
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT TOP (LEN(@Binary)-1) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2, N N3)
SELECT I
FROM Tally;

外部选择

我们先去FROM。显然FROM Tally返回我们刚刚讨论的 CTETally中的行。接下来,我们CROSS APPLYVALUES表构造。这将在单独的行中返回varchar的每个单独字符。请注意,我们REVERSE值,因为最低计价位于数字的右侧。 如果仅从FROM返回值的结果,则最终将得到以下数据集:

1
IC
1
20
30
41
50
60
71

现在我已经设法创建了一个将 varchar(8) 位序列转换为整数的函数。

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION  BinarySequenceToInt
(
-- Add the parameters for the function here
@BinarySequence varchar(64)
)
RETURNS int
AS
BEGIN
-- Declare the return variable here

DECLARE @bitIndex int = 1;
DECLARE @SequenceLength int = LEN(@BinarySequence)
DECLARE @powerInversionNumber int = @SequenceLength;
DECLARE @BitSequenceNumber int = 0;

WHILE @bitIndex <= @SequenceLength
BEGIN
IF(SUBSTRING(@BinarySequence , @bitIndex,1) = '1')
BEGIN
SET @BitSequenceNumber = @BitSequenceNumber + POWER(2,@powerInversionNumber-@bitIndex);
END
SET @bitIndex += 1;
END
return @BitSequenceNumber
END
GO

如果我传递它"01001000",则返回 73。 我知道,我可以采用传递的 varchar 的长度,而不是硬编码 8,所以我会努力。

最新更新