我有一个 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 是彩票有效的最后一次抽奖日期。
我目前的尝试是在 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。这对我来说已经足够了。
感谢您的帮助
如果您愿意通过将数字列表拆分为子表来标准化数据,那么您可以通过以下方式轻松确定获胜者:
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.
我不是数据库专家,但我认为我想出了一个有点优雅的解决方案,不需要将数据重组到另一个表中。如果您使用数据透视表,您可以让 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
样本数据:
(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 列)返回的总行数,即与中奖号码匹配的总数数字。是的,查询并不真正漂亮,但它可以工作并且避免必须完成单独的表和行的所有工作。根据不同游戏的需要进行修改。