使用SQL检查彩票中奖票

时间:2011-03-28 18:06:06

标签: sql sql-server-2008 performance

我有一个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的好主意,这些SQL将返回所有LotteryTickets并获得抽奖日期的奖金? ValidTill是票证有效的最后抽奖日期。

我目前的尝试是在C#中使用Linq2Sql并且具有冰上河马的速度,所以我真的需要一些SQL专业知识。

服务器是Microsoft SQL Server 2008 R2,如果重要的话。

更新:在调整了Mark B的答案后,我最终得到了以下查询。我需要通过添加一个新的表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 个答案:

答案 0 :(得分:4)

如果您愿意通过将数字列表拆分为子表来规范化数据,那么您可以通过以下方式轻松确定获胜者:

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

所以,假设你有一张2011年4月1日抽奖的票,号码为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。你已经匹配了5个抽奖号码中的3个,赢了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.

答案 1 :(得分:3)

我不是数据库专家,但我想我想出了一个不太优雅的解决方案,不需要将数据重组到另一个表中。如果使用数据透视表,则可以获取SQL以返回每个数字的正确计数。

首先是数据透视表(不要指定数据透视表,因为它会导致查询中出现MS SQL Server错误)。它只是一个包含int,primary key类型的列的表。它包含从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

数据透视表允许查询为每个匹配中奖号码的列返回一行,然后按ID对其进行分组,并计算数据透视表(第i列)返回的总行数,即匹配的总数中奖号码。是的,查询并不是真正的漂亮,但它可以工作,并避免必须完成单独的表和行的所有工作。根据需要修改不同的游戏。