0

假设我有一组固定长度的字母字符标识符,例如总是五个字母,并且它们的分配方式总是按顺序递增(GGGGZ --> GGGHA 等)。现在,如果我到达 ZZZZZ,由于长度是固定的,我必须“翻转”到 AAAAA。我可能有一个从 ZZZAA 到 AAAAM 的连续街区。我想写一个存储过程,它会给我“下一个”标识符,在这种情况下是 AAAAN。

如果我没有这个“翻转”问题,当然,我只会按 DESC 排序并获得最佳结果。但是我现在有点不知所措——SQL 不是我最擅长的语言,这根本没有帮助。

如果必须,我可以将其移至我的 C# 调用代码,但存储过程会更合适。

ETA:我想避免更改架构(新列新表);我宁愿只是能够“弄清楚”。我什至可能更喜欢蛮力(例如从最低值开始并增加直到我找到一个“洞”),即使这可能会变得昂贵。如果您有一个不修改架构的答案,那将是满足我需求的更好解决方案。

4

7 回答 7

1

这是我认为可以为您提供 Next 价值的代码。我创建了 3 个函数。该表只是我使用您的 alpha id 对 table.column 的模拟(我使用了 MyTable.AlphaID)。我假设它正如您所暗示的那样,并且有一个由五个字符的大写字母字符串 (AlphaID) 组成的连续块:

IF OBJECT_ID('dbo.MyTable','U') IS NOT NULL
    DROP TABLE dbo.MyTable
GO
CREATE TABLE dbo.MyTable (AlphaID char(5) PRIMARY KEY)
GO
-- Play with different population scenarios for testing
INSERT dbo.MyTable VALUES ('ZZZZY')
INSERT dbo.MyTable VALUES ('ZZZZZ')
INSERT dbo.MyTable VALUES ('AAAAA')
INSERT dbo.MyTable VALUES ('AAAAB')
GO
IF OBJECT_ID('dbo.ConvertAlphaIDToInt','FN') IS NOT NULL
    DROP FUNCTION dbo.ConvertAlphaIDToInt
GO
CREATE FUNCTION dbo.ConvertAlphaIDToInt (@AlphaID char(5))
RETURNS int
AS
BEGIN
RETURN 1+ ASCII(SUBSTRING(@AlphaID,5,1))-65
              + ((ASCII(SUBSTRING(@AlphaID,4,1))-65) * 26)
              + ((ASCII(SUBSTRING(@AlphaID,3,1))-65) * POWER(26,2))
              + ((ASCII(SUBSTRING(@AlphaID,2,1))-65) * POWER(26,3))
              + ((ASCII(SUBSTRING(@AlphaID,1,1))-65) * POWER(26,4))
END
GO 

IF OBJECT_ID('dbo.ConvertIntToAlphaID','FN') IS NOT NULL
    DROP FUNCTION dbo.ConvertIntToAlphaID
GO
CREATE FUNCTION dbo.ConvertIntToAlphaID (@ID int)
RETURNS char(5)
AS
BEGIN
RETURN CHAR((@ID-1) / POWER(26,4) + 65)
      + CHAR ((@ID-1) % POWER(26,4) / POWER(26,3) + 65)
      + CHAR ((@ID-1) % POWER(26,3) / POWER(26,2) + 65)
      + CHAR ((@ID-1) % POWER(26,2) / 26 + 65)
      + CHAR ((@ID-1) % 26 + 65)

END
GO 
IF OBJECT_ID('dbo.GetNextAlphaID','FN') IS NOT NULL
    DROP FUNCTION dbo.GetNextAlphaID
GO
CREATE FUNCTION dbo.GetNextAlphaID ()
RETURNS char(5)
AS
BEGIN
    DECLARE @MaxID char(5), @ReturnVal char(5)
    SELECT @MaxID = MAX(AlphaID) FROM dbo.MyTable
    IF @MaxID < 'ZZZZZ'
        RETURN dbo.ConvertIntToAlphaID(dbo.ConvertAlphaIDToInt(@MaxID)+1)
    IF @MaxID IS NULL
        RETURN 'AAAAA'
    SELECT @MaxID = MAX(AlphaID) 
    FROM dbo.MyTable 
    WHERE AlphaID < dbo.ConvertIntToAlphaID((SELECT COUNT(*) FROM dbo.MyTable))
    IF @MaxID IS NULL
        RETURN 'AAAAA'
    RETURN dbo.ConvertIntToAlphaID(dbo.ConvertAlphaIDToInt(@MaxID)+1)
END
GO

SELECT * FROM dbo.MyTable ORDER BY dbo.ConvertAlphaIDToInt(AlphaID)
GO
SELECT  dbo.GetNextAlphaID () AS 'NextAlphaID'

顺便说一句,如果您不想假设连续,您可以按照您的建议进行操作,并且(如果有“ZZZZZ”行)使用序列中的第一个间隙。用这个替换最后一个函数:

IF OBJECT_ID('dbo.GetNextAlphaID_2','FN') IS NOT NULL
    DROP FUNCTION dbo.GetNextAlphaID_2
GO
CREATE FUNCTION dbo.GetNextAlphaID_2 ()
RETURNS char(5)
AS
BEGIN
    DECLARE @MaxID char(5), @ReturnVal char(5)
    SELECT @MaxID = MAX(AlphaID) FROM dbo.MyTable
    IF @MaxID < 'ZZZZZ'
        RETURN dbo.ConvertIntToAlphaID(dbo.ConvertAlphaIDToInt(@MaxID)+1)
    IF @MaxID IS NULL
        RETURN 'AAAAA'
    SELECT TOP 1 @MaxID=M1.AlphaID
    FROM dbo.Mytable M1
    WHERE NOT EXISTS (SELECT 1 FROM dbo.MyTable M2 
                      WHERE AlphaID = dbo.ConvertIntToAlphaID(dbo.ConvertAlphaIDToInt(M1.AlphaID) + 1 )
                     )
    ORDER BY M1.AlphaID
    IF @MaxID IS NULL
        RETURN 'AAAAA'
    RETURN dbo.ConvertIntToAlphaID(dbo.ConvertAlphaIDToInt(@MaxID)+1)
END
GO
于 2009-04-02T21:08:46.477 回答
0

您必须在序列中存储最后分配的标识符。

例如,将其存储在另一个具有一列一行的表中。

CREATE TABLE CurrentMaxId (
    Id CHAR(6) NOT NULL
);

INSERT INTO CurrentMaxId (Id) VALUES ('AAAAAA');

每次你分配一个新的标识符时,你都会在那个小表中获取值,增加它,然后将该值存储在你的主表中,并更新CurrentMaxId.

通常的警告适用于并发、表锁定等。

于 2009-04-02T16:52:59.260 回答
0

我想我会尝试将序列存储为整数,然后将其转换为字符串。或者存储一个与 alpha 值同时递增的并行整数列。无论哪种方式,您都可以对整数列进行排序。

于 2009-04-02T16:53:25.467 回答
0

这里的一个问题是,除非有更多关于如何删除旧条目的详细信息,否则您无法从数据中真正分辨出“最后一个”条目在哪里。

如果我理解正确,您将在序列结束时回绕,这意味着您必须删除一些旧数据以腾出空间。但是,如果数据没有以完全统一的方式删除,您最终会得到片段,如下所示:

ABCD   HIJKL NOPQRS   WXYZ

您会注意到没有明显的下一个值...D 可能是最后创建的值,但也可能是 L 或 S。

充其量您可以查找第一个或最后一个缺失元素(使用存储过程执行 x+1 检查,就像在整数序列中查找缺失元素一样),但它不会为rolled 提供任何特殊结果-over 列表。

于 2009-04-02T17:16:52.620 回答
0

因为我不想编写代码来增加字母,所以我会创建一个包含所有有效 ID(AAAAAA 到 ZZZZZZ)的表,其中这些 ID 的整数从 1 到 X。然后,您可以使用以下内容:

SELECT @max_id = MAX(id) FROM Possible_Silly_IDs

SELECT
    COALESCE(MAX(PSI2.silly_id), 'AAAAAA')
FROM
    My_Table T1
INNER JOIN Possible_Silly_IDs PSI1 ON
    PSI1.silly_id = T1.silly_id
INNER JOIN Possible_Silly_IDs PSI2 ON
    PSI2.id = CASE WHEN PSI1.id = @max_id THEN 1 ELSE PSI1.id + 1 END
LEFT OUTER JOIN My_Table T2 ON
    T2.silly_id = PSI2.silly_id
WHERE
    T2.silly_id IS NULL

如果表为空,则 COALESCE 就在那里。为了真正强大,您应该计算 'AAAAAA' (SELECT @min_silly_id = silly_id WHERE id = 1),以防您的“编号”算法发生变化。

如果你真的想把事情做对,你会按照建议重做数据库设计。

于 2009-04-02T18:23:40.913 回答
0

要返回ID给定的下一个ID(带有翻转),请使用:

SELECT  COALESCE
        (
        (
        SELECT  TOP 1 id
        FROM    mytable
        WHERE   id > @id
        ORDER BY
                id
        ),
        (
        SELECT  TOP 1 id
        FROM    mytable
        ORDER BY
                id
        )
        ) AS nextid

此查询搜索ID给定的下一个。如果没有ID,则返回第一个ID

结果如下:

WITH mytable AS
        (
        SELECT  'AAA' AS id
        UNION ALL
        SELECT  'BBB' AS id
        UNION ALL
        SELECT  'CCC' AS id
        UNION ALL
        SELECT  'DDD' AS id
        UNION ALL
        SELECT  'EEE' AS id
        )
SELECT  mo.id,
        COALESCE
        (
        (
        SELECT  TOP 1 id
        FROM    mytable mi
        WHERE   mi.id > mo.id
        ORDER BY
                id
        ),
        (
        SELECT  TOP 1 id
        FROM    mytable mi
        ORDER BY
                id
        )
        ) AS nextid
FROM    mytable mo

id      nextid
-----   ------
AAA     BBB
BBB     CCC
CCC     DDD
DDD     EEE
EEE     AAA

, 即它返回BBBfor AAA, CCCforBBB等, 最后, AAAforEEE是表中的最后一个。

于 2009-04-03T17:53:15.910 回答
0

我认为对我的需求影响最小的解决方案是添加一个标识列。我可以保证的一件事是,排序将首先添加应该“首先出现”的条目——我永远不会添加带有标识符 BBBB 的条目,然后再返回并稍后添加 BBBA。如果我没有那个约束,显然它不会起作用,但就目前而言,我可以按标识列排序并获得我想要的排序。

我会继续考虑其他建议——也许如果它们在我脑海中“点击”,它们看起来会是一个更好的选择。

于 2009-04-09T13:18:25.723 回答