Проверка на выигрышные билеты в лотерее с использованием SQL - PullRequest
5 голосов
/ 28 марта 2011

У меня есть вопрос эффективности SQL. Это касается норвежской национальной лотереи. Они рисуют семь номеров и три бонусных шара.

У меня есть база данных со всеми чертежами и множеством билетов. Вопрос в том, какова наиболее эффективная структура стола и способ получить все выигрышные билеты в розыгрыше.

Это мои две основные таблицы:

LotteryDraw
   DrawId (int, PK)
   DrawDate (datetime)
   MainNumbers (varchar)
   BonusNumbers (varchar)
   Main1 (smallint)
   Main2 (smallint)
   Main3 (smallint)
   Main4 (smallint)
   Main5 (smallint)
   Main6 (smallint)
   Main7 (smallint)
   Bonus1 (smallint)
   Bonus2 (smallint)
   Bonus3 (smallint)

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

Похоже, у меня есть:

LotteryTicket
   TicketId (int, PK)
   UserId (int, FK)
   ValidTill (datetime)
   MainNumbers (varchar)
   Main1 (smallint)
   Main2 (smallint)
   Main3 (smallint)
   Main4 (smallint)
   Main5 (smallint)
   Main6 (smallint)
   Main7 (smallint)

Вы получаете призы за 4 + 1, 5, 6, 6 + 1 и 7 правильных номеров (правильные основные номера + бонусные номера). У кого-нибудь есть отличные идеи о том, как написать эффективный SQL, который будет возвращать все лотерейные билеты с призом за дату розыгрыша? ValidTill - это последняя дата розыгрыша, когда билет был действительным.

Моя текущая попытка - использовать Linq2Sql в C # и иметь скорость бегемота на льду, поэтому мне действительно нужны некоторые знания SQL.

Сервер - это Microsoft SQL Server 2008 R2, если это имеет значение.

Обновление: после настройки ответа от Марка Б. я получил следующий запрос. Мне нужно было немного нормализовать базу данных, добавив новую таблицу LotteryTicketNumber (ticketid, number).

SELECT LotteryTicket.TicketID, count(LotteryTicket.Numbers) AS MainBalls, (
    SELECT top 1 ltn.Number
    FROM LotteryTicketNumber ltn
    WHERE ltn.Number IN (2,4,6)
    AND ltn.TicketId = LotteryTicket.TicketId
) As BonusBall
FROM LotteryTicket
LEFT JOIN LotteryTicketNumber ON LotteryTicket.TicketId = LotteryTicketNumber.TicketId
WHERE LotteryTicketNumber.Number IN (13,14,16,23,26,27,30)
GROUP BY LotteryTicket.TicketID
HAVING count(LotteryTicketNumber.Number) >= 4

Приведенный выше запрос возвращает все билеты по крайней мере с 4 правильными основными номерами. Также поле Bonusball! = NULL, если в одном и том же билете есть один или несколько бонусных шаров. Этого мне достаточно.

Спасибо за помощь

Ответы [ 2 ]

4 голосов
/ 28 марта 2011

Если вы хотите нормализовать данные, разбив список чисел на подстатью, вы можете тривиально определить победителей, например:

SELECT LotteryTicket.TicketID, GROUP_CONCAT(LotteryTicketNumbers.number), COUNT(LotteryTicketNumbers.number) AS cnt
FROM LotteryTicket
LEFT JOIN LotterYTicketNumbers ON (LotteryTicketNumbers.number IN (winning, numbers, here))
GROUP BY LotteryTicket.TicketID
HAVING cnt >= 3;

, где «3» представляет минимальное количество совпавших чисел, необходимых для выигрыша любого приза. Это не будет обрабатывать «бонусные» номера, если таковые имеются, хотя вы можете повторить тот же запрос и пометить любые розыгрыши, где бонусный номер присутствует, с производным полем.

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


комментарий:

GROUP_CONCAT - это специфичное для mysql расширение sql. Вы можете разорвать это, так как может показаться, что вы используете SQLserver.

«LottoTicketNumbers» - это то, что вы использовали бы для нормализации ваших таблиц. Вместо одной монолитной записи «билета» вы разбиваете ее на две таблицы:

LottoTicket:  ticketID, drawDate
LottoTicketNumbers: ticketID, drawNumber

Допустим, у вас есть билет на розыгрыш 1/2011 апреля с номерами 1,12,23,44,55, в результате вы получите что-то вроде:

LottoTicket: ticketID = 1, drawDate = Apr 1/2011
LottoTicketNumbers: (1,1), (1,12), (1,23), (1,44), (1,55)

Структурирование таблиц таким образом заставляет запрос работать, используя некоторую базовую теорию множеств и мощь реляционной базы данных. Исходная структура таблицы делает почти невозможным проведение сравнений, необходимых для определения всех возможных комбинаций выигрышных чисел, в результате вы получите некую отвратительную конструкцию, такую ​​как

select ...
where (number1 in (winning, numbers here), number2 in (winning, numbers, here), number3 in (winning, numbers,here), etc....

и не сообщит вам, какой именно приз вы выиграли (соответствует 3, 5 + бонус и т. Д.).

Пример результатов запроса:

Допустим, номера ничьих 10,20,30,40,50, а у вас есть билет с 10,20,30,42,53. Вы выбрали 3 из 5 номеров розыгрыша и выиграли 10 долларов. Используя приведенную выше структуру нормализованных таблиц, вы получите такие таблицы:

LottoTicket: id #203, drawDate: Apr 1/2011
LottoTicketNumbers: (203, 10), (203, 20), (203, 30), (203, 42), (203, 53)

И запрос будет

SELECT LottoTicket.TicketID, COUNT(LottoTicketNumbers.number) AS cnt
FROM LottoTicket
LEFT JOIN LottoTicketNumbers ON (LottoTicketNumbers.number IN (10,20,30,40,50))
GROUP BY LottoTicket.TicketID
HAVING CNT >= 3

Вы получите (не сгруппированные) результаты

203, 10
203, 20
203, 30

и с функциями группировки / агрегирования:

203, 3   // ticket #203 matched 3 numbers.
3 голосов
/ 16 октября 2011

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

Сначала сводная таблица (не называйте сводную, потому что это вызывает ошибку MS SQL Server в запросе). Это просто таблица с одним столбцом типа int, первичный ключ). Он содержит данные, у которых есть строка от 1 до 100. Вам действительно нужно столько же номеров, сколько и ваш самый большой номер лотереи. Больше в порядке.

PVT Structure: i(int,primary key)

PVT Data: (1) (2) (3) .... (100)

Я делаю этот пример для лотереи Флорида 6 номеров, без пауэрбола, 53 номера.

У вас есть стол LotteryTicket, что-то вроде

LotteryTicket: ID, Number, N1, N2, N3, N4, N5, N6

SampleData:

(1), (1-2-3-4-5-6), (1), (2), (3), (4), (5), (6)

(2), (1-2-3-15-18-52), (1), (2), (3), (15), (18), (52)

Запрос / Хранимая процедура: [передать выигрышный номер лотереи, например: 1-2-3-20-30-33, или оставить параметры по умолчанию (в этом примере)]

MatchFloridaLottery
    (
        @p1 int = 1,
        @p2 int = 2,
        @p3 int = 3, 
        @p4 int = 4,
        @p5 int = 5,
        @p6 int = 6,
        @minmatches int = 2 
    )

AS



SELECT t.id, COUNT(p.i) numbermatch
FROM LotteryTicket t, pvt p
WHERE 
(n1 IN (@p1,@p2,@p3,@P4,@p5,@p6) AND t.n1=p.i)
or 
(n2 IN (@p1,@p2,@p3,@P4,@p5,@p6) AND t.n2=p.i)
or 
(n3 IN (@p1,@p2,@p3,@P4,@p5,@p6) AND t.n3=p.i)
or 
(n4 IN (@p1,@p2,@p3,@P4,@p5,@p6) AND t.n4=p.i)
or 
(n5 IN (@p1,@p2,@p3,@P4,@p5,@p6) AND t.n5=p.i)
or 
(n6 IN (@p1,@p2,@p3,@P4,@p5,@p6) AND t.n6=p.i)

group by n.id
HAVING COUNT(p.i) > @minmatches

Для моего примера в LotteryTickets я получаю:

ID     NumberMatch (count of numbers that matched)

1           6

2           3

Сводная таблица позволяет запросу возвращать строку для каждого столбца, которая соответствует выигрышному номеру, который вы затем группируете по идентификатору, и подсчитывает общее количество строк, возвращаемых сводной таблицей (столбец i), которое представляет собой общее количество совпадений с выигрышный номер. Да, запрос не очень красивый, но он работает и позволяет избежать необходимости выполнять всю работу с отдельной таблицей и строками. Изменяйте по мере необходимости для разных игр.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...