Làm cách nào để điền vào một cột với các số ngẫu nhiên trong SQL? Tôi nhận được cùng một giá trị trong mọi hàng


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

Nếu sau đó tôi thực hiện một CHỌN, tôi thấy rằng số ngẫu nhiên của tôi giống hệt nhau trong mọi hàng . Bất kỳ ý tưởng làm thế nào để tạo ra các số ngẫu nhiên duy nhất?

Câu trả lời:


167

Thay vì rand(), hãy sử dụng newid(), được tính toán lại cho mỗi hàng trong kết quả. Cách thông thường là sử dụng modulo của tổng kiểm tra. Lưu ý rằng checksum(newid())có thể tạo ra -2,147,483,648 và gây tràn số nguyên abs(), vì vậy chúng ta cần sử dụng modulo trên giá trị trả về tổng kiểm tra trước khi chuyển đổi nó thành giá trị tuyệt đối.

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

Điều này tạo ra một số ngẫu nhiên từ 0 đến 9999.


1
Đây câu hỏi / câu trả lời cũng có thể hữu ích: stackoverflow.com/a/9039661/47226
Aaron Hoffman

Điều này không hiệu quả với tôi cả. Cột có phải là INT không? Lỗi # 1064 mọi lúc. Tiếp cận với những viên thuốc điên rồ ...
freeworlder

1
Đây là một điều của vẻ đẹp! Làm tốt. Yêu nó. Hiệu suất hơi chậm một chút, nhưng vẫn tuyệt vời.
Arvin Amir

25

Nếu bạn đang sử dụng SQL Server 2008, bạn cũng có thể sử dụng

 CRYPT_GEN_RANDOM(2) % 10000

Điều này có vẻ đơn giản hơn một chút (nó cũng được đánh giá một lần trên mỗi hàng newid- được hiển thị bên dưới)

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

Trả về (2 số ngẫu nhiên có thể khác nhau )

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

Bỏ phiếu giảm giá không giải thích được, lý do chính đáng duy nhất mà tôi có thể nghĩ đến là vì số ngẫu nhiên được tạo ra nằm trong khoảng từ 0-65535 không chia hết cho 10.000 nên một số số sẽ hơi bị đại diện. Một cách để giải quyết vấn đề này là bọc nó trong một UDF vô hướng loại bỏ bất kỳ số nào trên 60.000 và tự gọi đệ quy để nhận số thay thế.

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 - Có lý do cụ thể nào không? Có thể bạn muốn nhấn vào mũi tên lên, câu trả lời này hoạt động tốt!
Martin Smith

Điều mà mọi người dường như còn thiếu là phương pháp này hiệu suất tốt hơn RẤT NHIỀU. Tôi đã tìm kiếm một giải pháp thay thế cho NEWID () và điều này là đúng, cảm ơn!
Đào

Bất kỳ phạm vi mong muốn nào đều được xử lý dễ dàng. Ví dụ: ABS (CAST (CRYPT_GEN_RANDOM (8) AS BIGINT)% 10001) mang lại một số từ 0-10000, là phạm vi mà mã của OP sẽ tạo ra nếu nó hoạt động theo cách họ hy vọng.
bielawski

Vấn đề 'tương tự' nào? Công thức tạo ra các giá trị mới cho mỗi hàng (vấn đề của op đã được giải quyết) và kết quả nằm trong phạm vi nhưng chúng sẽ không bị lệch vì có 64 bit hạt giống và chỉ 14 bit kết quả nên không thể phát hiện được bất kỳ sai lệch tiềm năng nào. Ngay cả khi bạn tạo ra 10 ^ 15 kết quả bất kỳ sai lệch nào mà bạn có thể nghĩ rằng bạn đang phát hiện vẫn nằm trong phạm vi sai số. Có nghĩa là bạn cần tạo ra 2 ^ 19 kết quả để chứng minh rằng xiên thực sự tồn tại.
bielawski

9

Mặc dù tôi rất thích sử dụng CHECKSUM, nhưng tôi cảm thấy rằng cách tốt hơn là sử dụng NEWID(), chỉ vì bạn không phải trải qua một phép toán phức tạp để tạo ra các số đơn giản.

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

Bạn có thể thay thế 1000bằng bất kỳ số nào bạn muốn đặt làm giới hạn và bạn luôn có thể sử dụng dấu cộng để tạo phạm vi, giả sử bạn muốn một số ngẫu nhiên giữa 100200, bạn có thể làm như sau:

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

Đặt nó lại với nhau trong truy vấn của bạn:

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

1

Tôi đã thử nghiệm 2 phương pháp ngẫu nhiên hóa dựa trên bộ dựa trên RAND () bằng cách tạo 100.000.000 hàng với mỗi phương pháp. Để cân bằng trường, đầu ra là một dấu nổi giữa 0-1 để bắt chước RAND (). Hầu hết mã là cơ sở hạ tầng thử nghiệm, vì vậy tôi tóm tắt các thuật toán ở đây:

-- 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

Sử dụng CRYPT_GEN_RANDOM rõ ràng là ngẫu nhiên nhất vì chỉ có 0,000000001% cơ hội nhìn thấy thậm chí 1 bản sao khi lấy 10 ^ 8 số TỪ tập hợp 10 ^ 18 số. IOW chúng tôi không nên thấy bất kỳ bản sao và điều này không có! Tập hợp này mất 44 giây để tạo trên máy tính xách tay của tôi.

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

Thời gian thực thi SQL Server: thời gian CPU = 134795 ms, thời gian đã trôi qua = 39274 ms.

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;

Với gần 15 lệnh có độ lớn ít ngẫu nhiên hơn, phương pháp này không nhanh gấp đôi, chỉ mất 23 giây để tạo ra 100 triệu số.

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 

Thời gian thực thi SQL Server: thời gian CPU = 77156 ms, thời gian đã trôi qua = 24613 ms.

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;

Riêng RAND () là vô dụng đối với việc tạo dựa trên bộ vì vậy việc tạo đường cơ sở để so sánh độ ngẫu nhiên mất hơn 6 giờ và phải được khởi động lại nhiều lần để cuối cùng có được số hàng đầu ra phù hợp. Cũng có vẻ như sự ngẫu nhiên để lại rất nhiều điều mong muốn mặc dù tốt hơn là sử dụng tổng kiểm tra (newid ()) để gửi lại từng hàng.

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

Do quá trình khởi động lại, không thể nắm bắt được thời gian thực hiện.

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;

PS Nghĩ rằng các lần khởi động lại có thể có một số bản sao, tôi nhanh chóng kiểm tra chỉ 3M hàng mất gần 6-1 / 2 phút. Tôi đã nhận được 2101 lần lỗi và 2 giá trị xuất hiện 3 lần (tương ứng .07% và .000067%) cho thấy việc khởi động lại có thể đóng một phần nhưng tính ngẫu nhiên vẫn còn xa.
bielawski

Nhận thấy một câu trả lời khác vừa được gieo bằng newid được chuyển đổi thành varbinary nên tôi cũng đã thử. Nó không chỉ không nhanh hơn so với việc sử dụng tổng kiểm tra mà một giá trị xuất hiện 8 lần trong bài kiểm tra đó. Công bằng mà nói, nó vẫn là 95,447319% duy nhất, chỉ kém hơn một chút so với 95,450254% của RAND (Checksum (NewId ())) trong thử nghiệm của tôi. Lần thực thi thứ hai mang lại trường hợp xấu nhất là 3 số xuất hiện 5 lần và 95,452929% khác biệt nên YMMV ngay cả khi kiểm tra 100 triệu hàng.
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'");
}

có thể nó không chính xác và cách easylest nhưng nó hoạt động)))
VASO Nadiradze

1
Vui lòng đọc kỹ câu hỏi trước khi bắt đầu trả lời. Nhân tiện, việc gửi một truy vấn CẬP NHẬT cho từng và từng hàng riêng biệt là một Ý TƯỞNG RẤT RẤT RẤT XẤU khi người ta phải CẬP NHẬT ngay cả một số hàng khiêm tốn.
darlove
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.