SQL에서 난수로 열을 어떻게 채울 수 있습니까? 모든 행에서 동일한 값을 얻습니다.


84
UPDATE CattleProds
SET SheepTherapy=(ROUND((RAND()* 10000),0))
WHERE SheepTherapy IS NULL

그런 다음 SELECT를 수행하면 내 임의의 숫자가 모든 행에서 동일하다는 것을 알 수 있습니다. 고유 한 난수를 생성하는 방법에 대한 아이디어가 있습니까?

답변:


167

대신 결과의 각 행에 대해 다시 계산되는을 rand()사용 newid()하십시오. 일반적인 방법은 체크섬의 모듈로를 사용하는 것입니다. 주 checksum(newid())에 -2,147,483,648 및 원인 정수 오버 플로우를 생성 할 수 있습니다 abs()우리가 절대 값으로 변환하기 전에 검사 반환 값에 모듈을 사용할 필요가 있도록.

UPDATE CattleProds
SET    SheepTherapy = abs(checksum(NewId()) % 10000)
WHERE  SheepTherapy IS NULL

이것은 0에서 9999 사이의 임의의 숫자를 생성합니다.


1
이 질문 / 대답도 도움이 될 수 있습니다 stackoverflow.com/a/9039661/47226
아론 호프만

이것은 전혀 작동하지 않습니다. 열이 INT 여야합니까? 매번 오류 # 1064. ... 미친 알약에 도달
freeworlder

1
이것은 아름다움입니다! 잘 했어. 그것을 사랑하십시오. 약간 느린 성능이지만 여전히 훌륭합니다.
Arvin Amir

25

SQL Server 2008을 사용하는 경우 다음을 사용할 수도 있습니다.

 CRYPT_GEN_RANDOM(2) % 10000

다소 간단 해 보입니다 ( newid아래에 표시된대로 행당 한 번도 평가됨 )

DECLARE @foo TABLE (col1 FLOAT)

INSERT INTO @foo SELECT 1 UNION SELECT 2

UPDATE @foo
SET col1 =  CRYPT_GEN_RANDOM(2) % 10000

SELECT *  FROM @foo

반환 (아마도 다른 임의의 숫자 2 개 )

col1
----------------------
9693
8573

내가 생각할 수있는 유일한 합법적 인 이유는 설명되지 않은 반대표를 멀어지게하는 이유는 생성 된 임의의 숫자가 0-65535 사이이기 때문에 10,000으로 균등하게 나눌 수없는 일부 숫자가 약간 초과 표시된다는 것입니다. 이 문제를 해결하는 방법은 60,000이 넘는 숫자를 버리고 자신을 재귀 적으로 호출하여 대체 번호를 얻는 스칼라 UDF로 래핑하는 것입니다.

CREATE FUNCTION dbo.RandomNumber()
RETURNS INT
AS
  BEGIN
      DECLARE @Result INT

      SET @Result = CRYPT_GEN_RANDOM(2)

      RETURN CASE
               WHEN @Result < 60000
                     OR @@NESTLEVEL = 32 THEN @Result % 10000
               ELSE dbo.RandomNumber()
             END
  END  

1
@downvoter-특별한 이유가 있습니까? 위쪽 화살표를 누르려고했을 수도 있습니다.이 답변은 잘 작동합니다!
Martin Smith

모두가 놓치고있는 것은이 방법이 성능면에서 훨씬 더 좋다는 것입니다. 나는 NEWID ()의 대안을 찾고 있었는데 이것이 바로 그 자리입니다. 감사합니다!
Digs

원하는 범위를 쉽게 처리 할 수 ​​있습니다. 예를 들어 ABS (CAST (CRYPT_GEN_RANDOM (8) AS BIGINT) % 10001)은 0-10000의 숫자를 산출합니다. 이는 OP 코드가 원하는대로 작동했다면 생성했을 범위입니다.
bielawski

어떤 '동일한'문제입니까? 이 수식은 행당 새 값을 생성하고 (운영 문제 해결됨) 결과는 범위 내에 있지만 64 비트의 시드와 14 비트의 결과 만 있으므로 잠재적 인 왜곡을 감지 할 수 없기 때문에 왜곡되지 않습니다. 10 ^ 15 결과를 생성하더라도 감지하고 있다고 생각할 수있는 왜곡이 여전히 오류 범위 내에있을 것입니다. 스큐가 실제로 존재했음을 증명하려면 2 ^ 19 개의 결과를 생성해야합니다.
bielawski

9

나는 CHECKSUM을 사용하는 것을 좋아하지만 NEWID(), 단순한 숫자를 생성하기 위해 복잡한 수학을 할 필요가 없기 때문에를 사용하는 것이 더 좋은 방법이라고 생각합니다 .

ROUND( 1000 *RAND(convert(varbinary, newid())), 0)

1000제한으로 설정하려는 숫자로 바꿀 수 있으며 항상 더하기 기호를 사용하여 범위를 만들 수 있습니다. 100와 사이의 임의의 숫자를 원한다고 가정 해 보겠습니다 200. 다음과 같이 할 수 있습니다.

100 + ROUND( 100 *RAND(convert(varbinary, newid())), 0)

쿼리에 함께 넣으십시오.

UPDATE CattleProds 
SET SheepTherapy= ROUND( 1000 *RAND(convert(varbinary, newid())), 0)
WHERE SheepTherapy IS NULL

1

저는 각각 100,000,000 개의 행을 생성하여 RAND ()에 대해 2 개의 세트 기반 무작위 화 방법을 테스트했습니다. 필드를 평준화하기 위해 출력은 RAND ()를 모방하기 위해 0-1 사이의 부동 소수점입니다. 대부분의 코드는 인프라를 테스트하므로 여기에 알고리즘을 요약합니다.

-- Try #1 used
(CAST(CRYPT_GEN_RANDOM(8) AS BIGINT)%500000000000000000+500000000000000000.0)/1000000000000000000 AS Val
-- Try #2 used
RAND(Checksum(NewId()))
-- and to have a baseline to compare output with I used
RAND() -- this required executing 100000000 separate insert statements

CRYPT_GEN_RANDOM을 사용하는 것은 10 ^ 18 숫자 집합에서 10 ^ 8 숫자를 뽑을 때 1 개의 중복을 볼 확률이 .000000001 %에 불과하기 때문에 가장 무작위였습니다. IOW 우리는 어떤 중복도 보지 말았어야했는데 이것도 없었습니다! 이 세트는 랩톱에서 생성하는 데 44 초가 걸렸습니다.

Cnt     Pct
-----   ----
 1      100.000000  --No duplicates

SQL Server 실행 시간 : CPU 시간 = 134795ms, 경과 시간 = 39274ms.

IF OBJECT_ID('tempdb..#T0') IS NOT NULL DROP TABLE #T0;
GO
WITH L0   AS (SELECT c FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS D(c))  -- 2^4  
    ,L1   AS (SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B)    -- 2^8  
    ,L2   AS (SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B)    -- 2^16  
    ,L3   AS (SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B)    -- 2^32  
SELECT TOP 100000000 (CAST(CRYPT_GEN_RANDOM(8) AS BIGINT)%500000000000000000+500000000000000000.0)/1000000000000000000 AS Val
  INTO #T0
  FROM L3;

 WITH x AS (
     SELECT Val,COUNT(*) Cnt
      FROM #T0
     GROUP BY Val
)
SELECT x.Cnt,COUNT(*)/(SELECT COUNT(*)/100 FROM #T0) Pct
  FROM X
 GROUP BY x.Cnt;

거의 15 자릿수 정도 덜 무작위로이 방법은 2 배나 빠르지 않았으며 1 억 개의 숫자를 생성하는 데 23 초 밖에 걸리지 않았습니다.

Cnt  Pct
---- ----
1    95.450254    -- only 95% unique is absolutely horrible
2    02.222167    -- If this line were the only problem I'd say DON'T USE THIS!
3    00.034582
4    00.000409    -- 409 numbers appeared 4 times
5    00.000006    -- 6 numbers actually appeared 5 times 

SQL Server 실행 시간 : CPU 시간 = 77156ms, 경과 시간 = 24613ms.

IF OBJECT_ID('tempdb..#T1') IS NOT NULL DROP TABLE #T1;
GO
WITH L0   AS (SELECT c FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS D(c))  -- 2^4  
    ,L1   AS (SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B)    -- 2^8  
    ,L2   AS (SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B)    -- 2^16  
    ,L3   AS (SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B)    -- 2^32  
SELECT TOP 100000000 RAND(Checksum(NewId())) AS Val
  INTO #T1
  FROM L3;

WITH x AS (
    SELECT Val,COUNT(*) Cnt
     FROM #T1
    GROUP BY Val
)
SELECT x.Cnt,COUNT(*)*1.0/(SELECT COUNT(*)/100 FROM #T1) Pct
  FROM X
 GROUP BY x.Cnt;

RAND ()만으로는 집합 기반 생성에 쓸모가 없으므로 임의성을 비교하기위한 기준선을 생성하는 데 6 시간 이상이 걸렸으며 최종적으로 올바른 수의 출력 행을 얻으려면 여러 번 다시 시작해야했습니다. 또한 체크섬 (newid ())을 사용하여 각 행을 다시 시드하는 것보다 낫지 만 임의성이 많이 남아있는 것 같습니다.

Cnt  Pct
---- ----
1    99.768020
2    00.115840
3    00.000100  -- at least there were comparitively few values returned 3 times

재시작으로 인해 실행 시간을 캡처 할 수 없습니다.

IF OBJECT_ID('tempdb..#T2') IS NOT NULL DROP TABLE #T2;
GO
CREATE TABLE #T2 (Val FLOAT);
GO
SET NOCOUNT ON;
GO
INSERT INTO #T2(Val) VALUES(RAND());
GO 100000000

WITH x AS (
    SELECT Val,COUNT(*) Cnt
     FROM #T2
    GROUP BY Val
)
SELECT x.Cnt,COUNT(*)*1.0/(SELECT COUNT(*)/100 FROM #T2) Pct
  FROM X
 GROUP BY x.Cnt;

추신 : 재시동이 중복의 일부를 설명 할 수 있다고 생각하면서 거의 6-1 / 2 분 걸리는 3M 행만 빠르게 테스트했습니다. 나는 2101 dups를 얻었고 2 개의 값이 3 번 나타났다 (각각 .07 %와 .000067 %)는 재시작이 아마도 역할을했지만 무작위성은 여전히 ​​별과는 거리가 멀다는 것을 나타냅니다.
bielawski

varbinary로 변환 된 newid로 시드 된 다른 답변을 발견 했으므로 시도했습니다. 체크섬을 사용하는 것보다 빠르지 않을뿐만 아니라 해당 테스트에서 하나의 값이 8 번 나타납니다. 공정하게 말하면, 여전히 95.447319 % 고유 한 것으로 내 테스트에서 RAND (Checksum (NewId ()))의 95.450254 %보다 거의 나쁩니다. 두 번째 실행은 1 억 개의 행을 테스트 할 때에도 YMMV이므로 5 번 나타나는 3 개의 숫자와 95.452929 %의 최악의 경우를 산출했습니다.
bielawski

-2
require_once('db/connect.php');

//rand(1000000 , 9999999);

$products_query = "SELECT id FROM products";
$products_result = mysqli_query($conn, $products_query);
$products_row = mysqli_fetch_array($products_result);
$ids_array = [];

do
{
    array_push($ids_array, $products_row['id']);
}
while($products_row = mysqli_fetch_array($products_result));

/*
echo '<pre>';
print_r($ids_array);
echo '</pre>';
*/
$row_counter = count($ids_array);

for ($i=0; $i < $row_counter; $i++)
{ 
    $current_row = $ids_array[$i];
    $rand = rand(1000000 , 9999999);
    mysqli_query($conn , "UPDATE products SET code='$rand' WHERE id='$current_row'");
}

))) 아마 정확하고 easylest 방법하지만 작동하지
VASO Nadiradze

1
답변을 시작하기 전에 질문을주의 깊게 읽으십시오. 그건 그렇고, 각각의 모든 행에 대해 개별적으로 UPDATE 쿼리를 보내는 것은 적당한 수의 행이라도 UPDATE해야 할 때 매우 나쁜 생각입니다.
darlove
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.