我正在尝试找到一种在SQL中计算随机和唯一键的明确方法(使用SQL 2012)。
我有一个像这样定义的表:
CREATE TABLE Variables
(
...
KeyId bigint NOT NULL DEFAULT(dbo.GetKeyId())
)
UDF定义如下:
CREATE FUNCTION [dbo].[GetKeyId]()
RETURNS bigint
AS
BEGIN
DECLARE @maxAttempts INT = 100
DECLARE @MaxID bigint
DECLARE @NewKeyId bigint
SET @MaxID = POWER(36.0, 12)
;WITH CTE AS
(
SELECT FLOOR((SELECT rndvalue FROM rnd)*@MaxID) AS rn, 1 AS i
UNION ALL
SELECT FLOOR((SELECT rndvalue FROM rnd)*@MaxID) AS rn, i = i + 1
FROM CTE AS c
INNER JOIN Variables AS r ON c.rn = r.KeyId
WHERE (i < @maxAttempts)
AND NOT EXISTS(SELECT * FROM Variables WHERE KeyId=c.rn)
)
SELECT TOP 1 @NewKeyId = rn
FROM CTE
ORDER BY i DESC
RETURN @NewKeyId
END
当然还有以下观点
CREATE VIEW [dbo].[rnd]
AS
SELECT RAND() AS rndvalue
在小规模测试此解决方案后,我发现它不起作用。有时它会返回已存在的键值。使用UDF给我带来了许多限制的麻烦。 任何人都可以推荐一个有效的版本吗?
答案 0 :(得分:1)
经典方法之一:
e.g。
CREATE TABLE BulkOfRealRandoms (id int identity(1,1), rand_value BINARY(8)
填写像md5的db这样的东西。我的意思是这样做是一次性的工作。在OLTP场景中,在内部事务中不要做这样的事情。正如许多其他人所说 - 你的udf是性能杀手。
CREATE TABLE RandomReceiver (self_id int, rand_value_id int)
您可以使用IDENTITY或SEQUENCE生成rand_value_id
。这就是你如何快速,自信地获得随机值而没有共谋。
如果你试图通过分配随机公共id而不是顺序公钥来隐藏数据库中某些表的增长,那么你应该发明一个algorythm来生成这样的id,这可能是字符串数据类型。定性随机化不是一项微不足道的任务。如果这是您的情况(生成公共订单ID或类似信息),那么随机数字将变得非常舒适和有用。
答案 1 :(得分:0)
我会重新使用IDENTITY
或UNIQUEIDENTIFIER
,但如果您坚持这样做,请尝试以下操作:
1-创建你的功能:
CREATE FUNCTION GetKeyId ()
RETURNS bigint
AS
BEGIN
DECLARE @Value BIGINT;
ExixtsValue:
SET @Value = (CONVERT(BigInt, CRYPT_GEN_RANDOM(3)) % 1000000);
IF EXISTS (SELECT 1 FROM MyVariables WHERE KeyId = @Value)
GOTO ExixtsValue;
RETURN @Value
END
2-创建测试表:
CREATE TABLE MyVariables (
KeyId bigint NOT NULL DEFAULT(dbo.GetKeyId())
);
3-根据需要测试它:
INSERT INTO MyVariables VALUES (dbo.GetKeyId() );
答案 2 :(得分:0)
如果可以在UDF中使用CRYPT_GEN_RANDOM,那么Sami的答案也不会太糟糕。
我的最终解决方案是不使用UDF作为KeyId的默认值。然后,在表中预先生成密钥,并在插入变量表时(在执行插入的SP中)提取KeyId值。
因此Variables表定义如下:
package cit260.harrypotter.view;
import java.util.Scanner;
public abstract class View implements ViewInterface {
public View() {
@Override
public void display(){
boolean endView = false;
do {
String[] inputs = this.getInputs();
endView = doAction(inputs);
} while (!endView);
}
@Override
public String getInput(String promptMessage) {
String input;
boolean valid = false;
while(!valid) {
System.out.println(promptMessage);
Scanner keyboard = new Scanner(System.in);
input = keyboard.nextLine(); input.trim();
if (input.length != 0){
System.out.println("Please enter a valid input");
valid = true;
}
}
return input;
}
}
}
和PoolKeys表:
CREATE TABLE Variables
(
...
KeyId bigint NOT NULL
)
我定义了这个视图来获得一个大的随机数(OK伪随机):
CREATE TABLE [dbo].[PoolKeys](
[KeyId] [bigint] NOT NULL,
[Rank] [int] NOT NULL
)
然后这个SP生成新密钥:
CREATE VIEW [dbo].[rndBig]
AS
SELECT ABS(CAST(CRYPT_GEN_RANDOM(10) AS bigint)) AS rndvalue
和SP提取一个新的KeyId:
CREATE PROCEDURE [dbo].[GenerateNewKeys]
AS
BEGIN
SET NOCOUNT ON;
DECLARE @min int = 1
DECLARE @max bigint = POWER(36.0, 12)-1 -- needs to be an odd value
DECLARE @n INT = 1000
;WITH CTE AS (
SELECT TOP(@n) CONVERT(bigint, FLOOR((SELECT rndvalue FROM dbo.rndBig) * CONVERT(float, (@max - @min + 1) / @max))) % @max + @min AS KeyId,
ROW_NUMBER() OVER (ORDER BY s1.[object_id]) AS rnk
FROM sys.all_objects as s1
CROSS JOIN sys.all_objects as s2
)
INSERT INTO PoolKeys(KeyId, [Rank])
SELECT DISTINCT TOP (@n) s.KeyId, s.rnk
FROM CTE AS s
WHERE NOT EXISTS (SELECT * FROM Variables V WHERE V.KeyId = s.KeyId)
END
有了这一切,在执行INSERT INTO变量的SP中,我调用GetNewKey来获取我插入变量的下一个KeyId。 因此,每1000次调用一次,将花费几毫秒来生成另外1000个密钥。这在我的案例中是可以接受的。