Как эффективно генерировать большие наборы лотерейных номеров - PullRequest
0 голосов
/ 16 декабря 2011

Я новичок в SQL, и я искал больше опыта с SQL, поэтому я решил разработать процедуру для генерации X количества случайных лотов. Лотерея здесь, в моей области, позволяет вам выбрать 5 номеров от 1 до 47 и 1 «мега» число от 1 до 27. Трюк в том, что «мега» число может повторяться с 5 числами ранее, то есть 1, 2, 3, 4, 5, мега 1.

Я создал следующую процедуру для генерации 10 миллионов лотерейных отборов, и процесс занял 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)=@counter 
                BEGIN
                SET @counter = @counter + 1
            END
        END



    SET @sort = 0
        WHILE @sort<5
        BEGIN
            UPDATE sort
            SET sort=@sort
            WHERE pick = (SELECT min(pick) FROM sort WHERE sort is null)
            SET @sort=@sort + 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

Ответы [ 2 ]

4 голосов
/ 16 декабря 2011

Я сгенерировал 10000 строк за 0 секунд.Я сделал это по-другому.Надеюсь, что это вам поможет

;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 с
1000000 строк 13 с
10000000 строк 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 с
1000000 строк 14 с
10000000 строк 03 мин 29 с

IСледует также отметить, что причина, по которой я использую

(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

Ok.Так что я увидел ваш комментарий, и у меня есть решение для этого.Если вы действительно хотите заказать номера.Сложность алгоритма повышается, и это также означает, что время алгоритма увеличивается.Но я все еще думаю, что это выполнимо.Но не таким аккуратным образом.

--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 сек
1000000 Строк:43 сек
10000000 Строки: 7 мин 9 сек

Надеюсь, эта помощь

0 голосов
/ 16 декабря 2011

Я написал этот сценарий просто из любопытства.Он должен работать лучше, чем ваш скрипт, но я не могу сказать наверняка.

Остерегайтесь того, что я использую объявленную таблицу, и если вы используете реальную таблицу, производительность должна быть лучше при генерации большого количества строк.

Я сгенерировал 10000 строк примерно за 13 секунд, что составляет около 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...