sql - 保证随机插入

标签 sql sql-server

我正在尝试预生成一些字母数字字符串并将结果插入表中。字符串长度为 5。示例:a5r67。基本上我想为客户生成一些可读的字符串,以便他们可以访问他们的订单,例如 www.example.com/order/a5r67。现在我有一个选择语句:

;WITH 
    cte1 AS(SELECT * FROM (VALUES('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'),('a'),('b'),('c'),('d'),('e'),('f'),('g'),('h'),('i'),('j'),('k'),('l'),('m'),('n'),('o'),('p'),('q'),('r'),('s'),('t'),('u'),('v'),('w'),('x'),('y'),('z')) AS v(t)),
    cte2 AS(SELECT * FROM (VALUES('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'),('a'),('b'),('c'),('d'),('e'),('f'),('g'),('h'),('i'),('j'),('k'),('l'),('m'),('n'),('o'),('p'),('q'),('r'),('s'),('t'),('u'),('v'),('w'),('x'),('y'),('z')) AS v(t)),
    cte3 AS(SELECT * FROM (VALUES('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'),('a'),('b'),('c'),('d'),('e'),('f'),('g'),('h'),('i'),('j'),('k'),('l'),('m'),('n'),('o'),('p'),('q'),('r'),('s'),('t'),('u'),('v'),('w'),('x'),('y'),('z')) AS v(t)),
    cte4 AS(SELECT * FROM (VALUES('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'),('a'),('b'),('c'),('d'),('e'),('f'),('g'),('h'),('i'),('j'),('k'),('l'),('m'),('n'),('o'),('p'),('q'),('r'),('s'),('t'),('u'),('v'),('w'),('x'),('y'),('z')) AS v(t)),
    cte5 AS(SELECT * FROM (VALUES('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'),('a'),('b'),('c'),('d'),('e'),('f'),('g'),('h'),('i'),('j'),('k'),('l'),('m'),('n'),('o'),('p'),('q'),('r'),('s'),('t'),('u'),('v'),('w'),('x'),('y'),('z')) AS v(t))
INSERT INTO ProductHandles(ID, Used)
SELECT cte1.t + cte2.t + cte3.t + cte4.t + cte5.t, 0
FROM cte1
CROSS JOIN cte2
CROSS JOIN cte3
CROSS JOIN cte4
CROSS JOIN cte5

现在的问题是我需要编写这样的内容才能从表中获取值:

SELECT TOP 1 ID 
FROM ProductHandles
WHERE Used = 0

我将在Used列上建立索引,这样速度会很快。问题是它带有顺序:

00000
00001
00002
...

我知道我可以通过 NEWID() 进行订购,但这会慢得多。我知道除非我们指定 Order By 子句,否则无法保证排序。所需要的是相反的。我需要保证困惑,但不是每次客户创建订单时都通过 NEWID() 进行排序。

我将像这样使用它:

WITH cte as (
                SELECT TOP 1 * FROM ProductHandles WHERE Used = 0
                --I don't want to order by newid() here as it will be slow
            )
UPDATE cte 
SET Used = 1
OUTPUT INSERTED.ID

最佳答案

如果您向表中添加一个标识列,并在插入记录时使用 order by newid() (这会很慢,但据我了解,这是一次性离线完成的事情)然后您可以在 identity 列上使用 order by 来选择记录按照插入表的顺序

来自Limitations and Restrictions part of the INSERT page在 Microsoft 文档中:

INSERT queries that use SELECT with ORDER BY to populate rows guarantees how identity values are computed but not the order in which the rows are inserted.

这意味着,通过这样做,您可以有效地使 identity 列按照与 insert...select 语句中选择的行相同的随机顺序进行排序。

此外,无需重复相同的 cte 5 次 - 您已经在重复交叉应用:

CREATE TABLE ProductHandles(sort int identity(1,1), ID char(5), used bit)


;WITH 
    cte AS(SELECT * FROM (VALUES('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'),('a'),('b'),('c'),('d'),('e'),('f'),('g'),('h'),('i'),('j'),('k'),('l'),('m'),('n'),('o'),('p'),('q'),('r'),('s'),('t'),('u'),('v'),('w'),('x'),('y'),('z')) AS v(t))        
INSERT INTO ProductHandles(ID, Used)
SELECT a.t + b.t + c.t + d.t + e.t, 0
FROM cte a
CROSS JOIN cte b
CROSS JOIN cte c
CROSS JOIN cte d
CROSS JOIN cte e
ORDER BY NEWID()

然后,cte 可以有一个 order by 子句,保证与填充此表的 select 语句返回的行具有相同的随机顺序:

WITH cte as (
                SELECT TOP 1 * 
                FROM ProductHandles 
                WHERE Used = 0
                ORDER BY sort 
            )
UPDATE cte 
SET Used = 1
OUTPUT INSERTED.ID

You can see a live demo on rextester. (只有数字,否则会花费太长的时间)

关于sql - 保证随机插入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53102317/

相关文章:

mysql - 具有不同产品名称的计数字段的 SQL 总和一次

sql-server - 如何将选择列存储在变量中?

MySQL,替代使用 IN 哪个会优先考虑第一个值?

c# - FluentMigrator-在删除外键之前检查它是否存在

sql - 如何通过 OleDb 在 MS Access 上使用公用表表达式执行复杂的 SQL 查询

php - 如何自动将当前日期时间添加到我的数据库中

sql-server - 将具有相同 id 的字符串折叠到逗号分隔列表中

sql - 如何将单行与sql中列中的数字相乘

sql - 查找该结果的对应列的最小值和值

c# - 有什么方法可以将多个嵌套创建 foreach 循环展平为批量创建/插入吗?