2011-03-28 93 views
5

我有一个SQL效率问题。这涉及挪威国家彩票。他们画出七个数字和三个奖金球。使用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都有很好的想法,它会返回所有LotteryTicket的抽奖日期的奖品? 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。这对我来说已经足够了。

感谢您的帮助

+0

一张票上的12个数字不是一张票上的12个数字,而是12个数字范围内的7个数字的组合。你不需要全部储存12 – cairnz 2011-03-28 18:09:11

+0

@ cairnz你是对的。据此编辑。 – Paaland 2011-03-28 18:16:53

+0

@Paaland:你在桌子上定义了哪些索引? – 2011-03-28 18:17:29

回答

3

如果你愿意通过拆分号码列表为子表数据标准化的,那么你可以平凡确定的东西,如获奖者:

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'是你用来标准化你的表格。而不是一个单一monolitic“票”记录的,你把它分成两个表:

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。你已经匹配了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. 
+0

我遇到了很大的问题让它运行(甚至试图找出你想要达到的目标)。首先,GROUP_CONCAT不是可识别的关键字(SQL 2008 R2)。第二,LotteryTicketNumbers从哪里来? – Paaland 2011-03-28 18:45:28

3

我不是一个数据库专家,但我觉得我想出了不需要的数据重组到另一个表中的几分优雅的解决方案。如果您使用数据透视表,你可以得到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)

Query/Stored pr ocedure: [传递像一个中奖号码:1-2-3-20-30-33或采用默认值PARAMS(本例中)

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)返回的总的行是匹配的总数到获奖号码。是的,查询不是很漂亮,但它的工作原理,并避免了必须做一个单独的表和行的所有工作。根据需要修改不同的游戏。