2011-12-16 29 views
0

我是一名初学SQL人员,我正在寻找更多的SQL经验,因此我决定设计一个程序来生成X数量的随机抽签。我所在地区的彩票允许您从1-27中选择5个1-47号码和1个“兆”号码。诀窍是“巨型”数字可以重复以前的5个数字,即1,2,3,4,5,巨型1.如何高效地生成更大的彩票号码集

我创建了以下过程以生成1000万个彩票选择,并且它花费了12小时和57分钟的过程完成。虽然我的朋友用java测试了相同的东西,但花了几秒钟。我想知道是否可以对代码进行任何改进,或者是否有我犯过的错误?我是新来的,因此我正在尝试学习更好的方法等,所有的评论欢迎。

USE lotto 
DECLARE 
@counter INT, 
@counter1 INT, 
@pm SMALLINT, 
@i1 SMALLINT, 
@i2 SMALLINT, 
@i3 SMALLINT, 
@i4 SMALLINT, 
@i5 SMALLINT, 
@sort int 

SET @counter1=0 

TRUNCATE TABLE picks 

WHILE @counter1<10000000 
BEGIN 
    TRUNCATE TABLE sort 
    SET @counter = 1 
     WHILE @counter < 6 
     BEGIN 
      INSERT INTO sort (pick) 
      SELECT CAST(((47+ 1) - 0) * RAND() + 1 AS TINYINT) 
      IF (SELECT count(distinct pick) FROM sort)<@counter 
       BEGIN 
       TRUNCATE TABLE sort 
       SET @counter=1 
       END 
      ELSE IF (SELECT COUNT(DISTINCT pick) FROM sort)[email protected] 
       BEGIN 
       SET @counter = @counter + 1 
      END 
     END 



    SET @sort = 0 
     WHILE @sort<5 
     BEGIN 
      UPDATE sort 
      SET [email protected] 
      WHERE pick = (SELECT min(pick) FROM sort WHERE sort is null) 
      SET @[email protected] + 1 
     END 

    SET @i1 = (SELECT pick FROM sort WHERE sort = 0) 
    SET @i2 = (SELECT pick FROM sort WHERE sort = 1) 
    SET @i3 = (SELECT pick FROM sort WHERE sort = 2) 
    SET @i4 = (SELECT pick FROM sort WHERE sort = 3) 
    SET @i5 = (SELECT pick FROM sort WHERE sort = 4) 
    SET @pm = (CAST(((27+ 1) - 0) * RAND() + 1 AS TINYINT)) 

    INSERT INTO picks(
     First, 
     Second, 
     Third, 
     Fourth, 
     Fifth, 
     Mega, 
     Sequence 
     ) 
    Values(
     @i1, 
     @i2, 
     @i3, 
     @i4, 
     @i5, 
     @pm, 
     @counter1 
     ) 
    SET @counter1 = @counter1+1 
END 
+0

提示...使用数字(理货)表... – 2011-12-16 01:36:21

+1

你说所有评论欢迎。 SQL是一个数据库,不是为计算而设计的。错误的工具。每次生成数据时,Java都会踢SQL的bytt。你可能有机会在.NET中击败java。 – Paparazzi 2011-12-16 02:52:53

回答

4

我在0秒内产生了10000行。我以另一种方式做到了。希望这将帮助你

;WITH Nbrs (n) AS (
     SELECT 1 UNION ALL 
     SELECT 1 + n FROM Nbrs WHERE n < 10000) 
SELECT 
    (ABS(CHECKSUM(NewId())) % 47 + 1) AS First, 
    (ABS(CHECKSUM(NewId())) % 47 + 1) AS Second, 
    (ABS(CHECKSUM(NewId())) % 47 + 1) AS Third, 
    (ABS(CHECKSUM(NewId())) % 47 + 1) AS Fourth, 
    (ABS(CHECKSUM(NewId())) % 47 + 1) AS Fifth, 
    (ABS(CHECKSUM(NewId())) % 27 + 1) AS Mega, 
    Nbrs.n AS Sequence 
FROM 
    Nbrs 
OPTION (MAXRECURSION 0) 

10000行0秒
100000行1秒
百万条13秒
千万行02分钟21秒

或者与交叉连接一起

WITH E00(N) AS (SELECT 1 UNION ALL SELECT 1), 
     E02(N) AS (SELECT 1 FROM E00 a, E00 b), 
     E04(N) AS (SELECT 1 FROM E02 a, E02 b), 
     E08(N) AS (SELECT 1 FROM E04 a, E04 b), 
     E16(N) AS (SELECT 1 FROM E08 a, E08 b), 
     E32(N) AS (SELECT 1 FROM E16 a, E16 b), 
    Nbrs(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY N) FROM E32) 
SELECT 
    (ABS(CHECKSUM(NewId())) % 47 + 1) AS First, 
    (ABS(CHECKSUM(NewId())) % 47 + 1) AS Second, 
    (ABS(CHECKSUM(NewId())) % 47 + 1) AS Third, 
    (ABS(CHECKSUM(NewId())) % 47 + 1) AS Fourth, 
    (ABS(CHECKSUM(NewId())) % 47 + 1) AS Fifth, 
    (ABS(CHECKSUM(NewId())) % 27 + 1) AS Mega, 
    Nbrs.n AS Sequence 
    FROM Nbrs 
    WHERE N <= 10000000; 

10000行0秒
100000行1秒
个 百万行14秒
千万行03分钟29秒

我还要提到的原因,我使用

(ABS(CHECKSUM(NewId())) % 47 + 1) 

是返回每行一个随机数。

CAST(((47+ 1) - 0) * RAND() + 1 AS TINYINT) 

如果您一次选择它们,则会为每一行返回相同的随机数。要测试此运行此示例:

;WITH Nbrs (n) AS (
     SELECT 1 UNION ALL 
     SELECT 1 + n FROM Nbrs WHERE n < 5) 
SELECT 
    CAST(((47+ 1) - 0) * RAND() + 1 AS TINYINT) AS Random, 
    (ABS(CHECKSUM(NewId())) % 47 + 1) AS RadomCheckSum, 
    Nbrs.n AS Sequence 
FROM Nbrs 

好的。所以我确实看到了你的评论,我也有一个解决方案。如果你真的想要订购这些数字。算法的复杂性提高,这也意味着算法的时间增加。但我仍然认为这是可行的。但不是以同样的方式。

--Yeah declaring a temp table for just the random order number 
DECLARE @tbl TABLE(value int) 

--The same function but with the number of the random numbers 
;WITH Nbrs (n) AS (
     SELECT 1 UNION ALL 
     SELECT 1 + n FROM Nbrs WHERE n < 5) 
INSERT INTO @tbl 
(
    value 
) 
SELECT 
    Nbrs.n AS Sequence 
FROM Nbrs 

;WITH Nbrs (n) AS (
     SELECT CAST(1 as BIGINT) UNION ALL 
     SELECT 1 + n FROM Nbrs WHERE n < 100000) 
SELECT 
    tblOrderRandomNumbers.[1] AS First, 
    tblOrderRandomNumbers.[2] AS Second, 
    tblOrderRandomNumbers.[3] AS Third, 
    tblOrderRandomNumbers.[4] AS Fourth, 
    tblOrderRandomNumbers.[5] AS Fifth, 
    (ABS(CHECKSUM(NewId())) % 27 + 1) AS Mega, 
    Nbrs.n AS Sequence 
FROM 
    Nbrs 
    --This cross join. Joins with the declared table 
    CROSS JOIN 
     (
      SELECT 
       [1], [2], [3], [4], [5] 
      FROM 
      (
      SELECT 
       Random, 
       ROW_NUMBER() OVER(ORDER BY tblRandom.Random ASC) AS RowNumber 
      FROM 
       (
       SELECT 
        (ABS(CHECKSUM(NewId())) % 47 + 1) AS Random 
       FROM 
        @tbl AS tblNumbers 
       ) AS tblRandom 

      )AS tblSortedRadom 
      --A pivot makes the rows to columns. Using the row index over order of the random number 
      PIVOT 
      (
       AVG(Random) 
      FOR RowNumber IN ([1], [2], [3], [4],[5]) 
      ) as pivottable 
     ) AS tblOrderRandomNumbers 
OPTION (MAXRECURSION 0) 

但我仍然设法做到在一点时间
10000行:0秒
100000行数:4秒
百万行数:43秒
千万行数:7分9秒

我希望这个帮助

0

我刚刚写出这个脚本出于好奇。它应该比你的脚本做得更好,但我无法确定。

请注意,我使用了一个声明的表,如果使用真正的表,在生成大量行时性能应该会更好。

我在大约13秒的时间内产生了10000行,计数到大约3.5小时以产生10 000 000行。比你描述的Java更糟糕。

set nocount on 
go 

declare @i int = 1 

declare @t table(nr1 int, nr2 int, nr3 int, nr4 int, nr5 int, mega int, seq int) 

while @i <= 10000 
begin 

;with numbers(nr) 
as 
(
select 1 
union all 
select nr+1 
from numbers 
where nr < 47 
) 
,mega(nr) 
as 
(
select 1 
union all 
select nr+1 
from mega 
where nr < 27 
) 
,selectednumbers(nr) 
as 
(
select top 5 nr 
from numbers 
order by newid() 
) 
,selectedmega(mega) 
as 
(
select top 1 nr 
from mega 
order by newid() 
) 
,tmp 
as 
(
select * 
     ,row_number() over(order by nr) as rownr 
from selectednumbers 
) 
insert into @t 
select max(nr1) as nr1 
     ,max(nr2) as nr2 
     ,max(nr3) as nr3 
     ,max(nr4) as nr4 
     ,max(nr5) as nr5 
     ,(select mega from selectedmega) as mega 
     ,@i as seq 
from (
     select case when rownr = 1 then nr else 0 end as nr1 
       ,case when rownr = 2 then nr else 0 end as nr2 
       ,case when rownr = 3 then nr else 0 end as nr3 
       ,case when rownr = 4 then nr else 0 end as nr4 
       ,case when rownr = 5 then nr else 0 end as nr5 
     from tmp 
    ) x 

set @i = @i + 1 
end 

select * from @t