在下文中,“Ticket[Number]”应该被理解为“Selected Set of Lottery Numbers”。记住Set(a,b,c)
等于Set(c,b,a)
。
我会这样:
Purchase
-PersonID // associate Person (one person can have many purchases)
-TicketID // associate Ticket (a purchase is for one "ticket",
// which can be purchased many times)
-DisplayTicketNumber // for Human Display
Ticket
-TicketNumber
那是,Purchase:M-1:Ticket
是用户选择的DisplayTicketNumber
数字,例如“3,1,2”,而另一方面,TicketNumber
是标准化的票号,其中小值放在首位。最终形式因此min,..,max
或相似。也就是说DisplayTicketNumbers
,具有相同值集(以任何顺序)的任意数量将具有相同的TicketNumber
:
DisplayTicketNumber TicketNumber
1,2,3 1,2,3
2,3,1 1,2,3
3,2,1 1,2,3
3,2,1,4 1,2,3,4 .. and etc
然后放一个索引,TicketNumber
这样一个简单的索引WHERE TicketNumber = @normalizedTicketNumber
将是一个非常快的索引。
我实际上认为这是一个可以接受的规范化设计,TicketNumber(连同一个抽奖号码)形成一个密钥。因此,我对此的论点是:
TicketNumber 是一个不透明的值,它唯一地标识一张票(每个抽奖)。无需了解数据库模型内部的“细节”。(在某些情况下可能需要,但这里不是。)
DisplayTicketNumber 是用户输入的产物;然而多个 DisplayTicketNumber 可以代表同一个 TicketNumber。虽然这确实代表了可能的“重复”,但重要的是要意识到这是一个友好显示值,它代表一个列表(比一组信息更多)选择的数字。
在这种情况下,我会使用触发器使DisplayTicketNumber
(and TicketNumber
) 不可变,以便在创建后不会在此处引入数据库不一致。
如果可以计算 FK,则可以强制执行 DisplayTicketNumber 和 TicketNumber 之间的约束而不会保持不变。
(我省略了各种细节,例如为不同的莱佛士设置不同的 TicketNumbers 等。我还TicketId
为 FK 显示了一个,但我也暗示这RaffleId,TicketNumber
是一个可接受的 [非代理] 键。)
此外,Ticket 表可以被删除:因为很少有彩票号码集将被共享,所以如果没有额外的关联 Ticket 信息,那么删除它可能是一种可接受的非规范化。这样做的一个优点是TicketNumber
可以将其移动到Purchase
表中,然后转换为标准化 Ticket 值的计算列(仍被索引)。
And, if MySQL allows using a computed column in a FK then using the relationship PK(Ticket.TicketNumber)
-> FK(Purchase.TicketNumber)
, where Purchase.TicketNumber
is computed, could be used to increase model integrity without eliminating the Ticket table. (I do not use MySQL, however, so I cannot say if this is viable or not.)
Happy coding.