SQL:是否有可能将数字(1,2,3,4…)转换为字母(a、B、C、D…)

SQL:是否有可能将数字(1,2,3,4…)转换为字母(a、B、C、D…),sql,sql-server,tsql,Sql,Sql Server,Tsql,是否有可能获得像a、B这样的字母而不是数字1、2,例如,由于在MS Sql中调用稠密的秩函数?提示:在Sql Enterprise manager中尝试此方法 select char(65), char(66), char(67) 对于等级高达17500或三个字母,ZZZ以下的完整解决方案为: select case When rnk < 703 Then '' else Char(64 + ((rnk-26) / 26 / 26)) End + case Whe

是否有可能获得像a、B这样的字母而不是数字1、2,例如,由于在MS Sql中调用稠密的秩函数?

提示:在Sql Enterprise manager中尝试此方法

  select char(65), char(66), char(67)
对于等级高达17500或三个字母,ZZZ以下的完整解决方案为:

select 
    case When rnk < 703 Then ''
 else Char(64 + ((rnk-26) / 26 / 26)) End +
    case When rnk < 27 Then '' 
   When rnk < 703 Then Char(64 + ((rnk-1)/ 26))
 else Char(65 + ((rnk-1)% 702 / 26)) End +
    Char(65 + ((rnk - 1) % 26))  
from (select Dense_Rank() 
     OVER (ORDER BY T.Sequence) rnk
      From YourTable t) z
试试这个:

SELECT
   Letters = Char(64 + T.Num),
   T.Col1,
   T.Col2
FROM
   dbo.YourTable T
;
只是要知道,当你到达Z轴27度时,事情会变得有趣,而不是有用

如果你想开始把字母对折,比如。。。十、 Y,Z,AA,AB,AC,AD。。。然后它会变得有点棘手。这适用于所有版本的SQL Server。SELECT子句只是CASE语句的替代语句,每个语句短2个字符

SELECT
   *,
   LetterCode =
      Coalesce((SELECT Char(65 + (N.Num - 475255) / 456976 % 26) WHERE N.Num >= 475255), '')
      + Coalesce((SELECT Char(65 + (N.Num - 18279) / 17576 % 26) WHERE N.Num >= 18279), '')
      + Coalesce((SELECT Char(65 + (N.Num - 703) / 676 % 26) WHERE N.Num >= 703), '')
      + Coalesce((SELECT Char(65 + (N.Num - 27) / 26 % 26) WHERE N.Num >= 27), '')
      + (SELECT Char(65 + (N.Num - 1) % 26))
FROM dbo.YourTable N
ORDER BY N.Num
;
SQL2008及以上版本的演示,请注意,我使用稠密的秩来模拟一系列数字

这将从A到ZZZZ工作,表示值1到12356630。上面所有的疯狂而不是一个更简单的表达式的原因是因为a在这里并不简单地表示0。在每个阈值之前,当序列转到前面添加的下一个字母A时,实际上有一个隐藏的空白数字,但不再使用。所以5个字母的长度不是26^5个组合,而是26+26^2+26^3+26^4+26^5

这需要一些真正的修修补补才能使代码正常工作。。。我希望你或其他人能欣赏它!只需添加另一个具有正确值的字母生成表达式,就可以轻松地将其扩展到更多字母

因为我现在在一个男子气概证明的中间,我做了一些性能测试。对我来说,WHILE循环不是比较性能的好方法,因为我的查询被设计为一次针对整个行集运行。对我来说,在一行上运行一百万次是没有意义的,这基本上迫使它进入虚拟的UDF区域,因为它可以在一百万行上运行一次,这是OP给出的针对大型行集执行此操作的用例场景。下面是测试1000000行的脚本测试脚本需要SQL Server 2005及以上版本

DECLARE
   @Buffer varchar(16),
   @Start datetime;

SET @Start = GetDate();
WITH A (N) AS (SELECT 1 FROM (VALUES (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) A (N)),
B (N) AS (SELECT 1 FROM A, A X),
C (N) AS (SELECT 1 FROM B, B X),
D (N) AS (SELECT 1 FROM C, B X),
N (Num) AS (SELECT Row_Number() OVER (ORDER BY (SELECT 1)) FROM D)
SELECT @Buffer = dbo.HinkyBase26(N.Num)
FROM N
;
SELECT [HABO Elapsed Milliseconds] = DateDiff( ms, @Start, GetDate());

SET @Start = GetDate();
WITH A (N) AS (SELECT 1 FROM (VALUES (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) A (N)),
B (N) AS (SELECT 1 FROM A, A X),
C (N) AS (SELECT 1 FROM B, B X),
D (N) AS (SELECT 1 FROM C, B X),
N (Num) AS (SELECT Row_Number() OVER (ORDER BY (SELECT 1)) FROM D)
SELECT
   @Buffer =
      Coalesce((SELECT Char(65 + (N.Num - 475255) / 456976 % 26) WHERE N.Num >= 475255), '')
      + Coalesce((SELECT Char(65 + (N.Num - 18279) / 17576 % 26) WHERE N.Num >= 18279), '')
      + Coalesce((SELECT Char(65 + (N.Num - 703) / 676 % 26) WHERE N.Num >= 703), '')
      + Coalesce((SELECT Char(65 + (N.Num - 27) / 26 % 26) WHERE N.Num >= 27), '')
      + (SELECT Char(65 + (N.Num - 1) % 26))   
FROM N
;
SELECT [ErikE Elapsed Milliseconds] = DateDiff( ms, @Start, GetDate());
结果是:

UDF: 17093 ms
ErikE: 12056 ms
原始查询

最初,我用一种有趣的方式来实现这一点,即每个字母生成一行,并使用XML进行轴心连接,但尽管这确实很有趣,但它的速度却很慢。以下是Dense_排名所需的posterity SQL 2005及更高版本,但在SQL 2000中仅用于将数字转换为字母:

WITH Ranks AS (
   SELECT
      Num = Dense_Rank() OVER (ORDER BY T.Sequence),
      T.Col1,
      T.Col2
   FROM
      dbo.YourTable T
)
SELECT
   *,
   LetterCode =
      (
         SELECT Char(65 + (R.Num - X.Low) / X.Div % 26)
         FROM
            (
               SELECT 18279, 475254, 17576
               UNION ALL SELECT 703, 18278, 676
               UNION ALL SELECT 27, 702, 26
               UNION ALL SELECT 1, 26, 1
            ) X (Low, High, Div)      
         WHERE R.Num >= X.Low
         FOR XML PATH(''), TYPE
      ).value('.[1]', 'varchar(4)')
FROM Ranks R
ORDER BY R.Num
;

可以使用自定义项将值转换为偏移量base-26:

编辑:修正功能

create function dbo.HinkyBase26( @Value as BigInt ) returns VarChar(15) as
  begin
  -- Notes: 'A' = 0.  Negative numbers are not handled.
  declare @Result as VarChar(15) = '';

  if @Value = 0
    select @Result = 'A';
  else
    set @Value += 1;
  while @Value > 0
    select @Value -= 1, @Result = Char( ASCII( 'A' ) + @Value % 26 ) + @Result, @Value /= 26;
  return @Result;
  end;
样本值:

select Arabic, dbo.HinkyBase26( Arabic ) as Alpha
  from ( values ( 0 ), ( 1 ), ( 25 ), ( 26 ), ( 51 ), ( 52 ),
    ( 27 * 26 - 1 ), ( 27 * 26 ),
    ( 33685567531 ) ) as Foo( Arabic );
在埃里克的建议下,我在笔记本上进行了一次快速性能测试。UDF与XML解决方案的1000000次迭代:

declare @Count as Int;
declare @Buffer as VarChar(16);
declare @Start as DateTime;

select @Count = 1000000, @Start = GetDate();
while @Count > 0
  select @Buffer = dbo.HinkyBase26( @Count ), @Count -= 1;
select DateDiff( ms, @Start, GetDate() ) as 'Elapsed Milliseconds'; -- 14,583    
select @Count = 1000000, @Start = GetDate();
while @Count > 0
  select @Buffer =
      (
         SELECT Char( ASCII( 'A' ) + (@Count - X.Low) / X.Div % 26)
         FROM
            (
               SELECT 18279, 475254, 17576
               UNION ALL SELECT 703, 18278, 676
               UNION ALL SELECT 27, 702, 26
               UNION ALL SELECT 1, 26, 1
            ) X (Low, High, Div)      
         WHERE @Count >= X.Low
         FOR XML PATH(''), TYPE
      ).value('.[1]', 'varchar(4)'), @Count -= 1;
select DateDiff( ms, @Start, GetDate() ) as 'Elapsed Milliseconds'; -- 47,256

UDF的速度快了3倍多一点。

这不是一个直接的答案-但是如果有人有3个字符的字母转换要求,下面是我正在做的

/*                    
Function Desc: Convert integer value to 3 character alpha-numeric
--Note: 1. This will return unique values from 0 to 17575, after that it startes again from AAA.
        2. Returns NULL If less than 0.

--Test Values
    select dbo.udfGetBase26CharacterValue(0) --AAA
    select dbo.udfGetBase26CharacterValue(17575) --ZZZ
    select dbo.udfGetBase26CharacterValue(17576) --AAA
    select dbo.udfGetBase26CharacterValue(NULL) --NULL
    select dbo.udfGetBase26CharacterValue(-1) --NULL
*/


CREATE FUNCTION [dbo].udfGetBase26CharacterValue
(    
    @id INT
)    
RETURNS CHAR(3)   
AS    
BEGIN    

IF ((@id < 0) OR (@id IS NULL))
BEGIN
    Return NULL
END

--Convert to base 26
Return  char(@id / power(26,2) % 26 + 65) +
            char(@id / 26 % 26 + 65) + 
            char(@id % 26 + 65)    

END

我使用它作为函数的基础,将整数转换为base26字符串

DECLARE @Input integer  = 3000

DECLARE @Value     integer  
DECLARE @Quotient  integer      = 0
DECLARE @Remainder integer      = 0
DECLARE @Output    varchar(max) = ''

DECLARE @BASE char(26) = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

SET @Value = @Input

WHILE @Value > 0 BEGIN

  SET @Quotient  = @Value / 26
  SET @Remainder = @Value % 26

  SET @Output = substring(@BASE,@Remainder,1) + @Output

  SELECT @Value = @Quotient

END

SELECT @Output --- DKJ

我想说的是你的表情只在26到701之间起作用。。。我现在正在编写一个适用于0-475253的表达式。@ErikE-为什么不创建一个接受BIGINT并返回偏心base-26等效字符串的UDF呢?只需执行常规的mod/div洗牌即可更改基数。或者使用一个好的、缓慢的递归CTE。@Erike,我也在尝试同样的方法,但所花的时间比我想象的要长。。。我将把它留给你…@HABO这是非常复杂的,因为说,我们希望达到ZZZZ,你不要从AAAA开始,其中A代表0!实际上,在某些情况下,前3位可能有27个数字,而在其他情况下,前3位可能有26个,因为例如,对于A-Z,前3位是空白的,这是A-Z之外的一个额外值。最多有三个字母。。。。当T.序列的密级高于顺序<703时,选择case,然后选择“Else Char64+密级高于顺序T.序列-26/26/26结束+当T.序列的密级高于顺序T.序列<27时,选择case,然后当T.序列的密级低于顺序T.序列<703时,然后选择Char64+密级高于顺序T.序列-1/26 Else Char65+密级”按T顺序排列-1%702/26 End+Char65+densite_Rank按T顺序排列-1%26来自dbo。YourTable T考虑到将数字转换为字母的问题与这些行的排列完全不同-这个问题与densite_Rank有关没有具体的原因。感谢您指出使用XML-I的缺陷我的解决方案很大一部分其实是玩闪亮的玩具。我现在已经全力以赴,拿出了我本来应该有的解决方案。请查看我的新结果答案,UDF 17093ms,我的查询12056ms。如果你在发出响亮的声音后更新你的代码很容易,并且没有26/27的噪音,那么我也可以更新我的代码!顺便说一句,我的老问题停止了@
DECLARE @Input integer  = 3000

DECLARE @Value     integer  
DECLARE @Quotient  integer      = 0
DECLARE @Remainder integer      = 0
DECLARE @Output    varchar(max) = ''

DECLARE @BASE char(26) = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

SET @Value = @Input

WHILE @Value > 0 BEGIN

  SET @Quotient  = @Value / 26
  SET @Remainder = @Value % 26

  SET @Output = substring(@BASE,@Remainder,1) + @Output

  SELECT @Value = @Quotient

END

SELECT @Output --- DKJ