我正在为一个锦标赛跟踪软件创建一个数据库,其中一部分涉及将用户的ID存储为稍后参考的值。为了做到这一点,我使用了两张table;player(主键 player_ID),存储事件中玩家的信息,例如他们的 user_ID(不相关表的一部分)和他们所玩的比赛的结果,以及 event,存储所有事件细节,以及所有16个事件参与者的 player_ID。这些列被命名为 * player 1 * -> * player 16 *
我的问题是试图找到一种方法来分别填充这些列。当事件被创建时,所有16列开始都以值“0”开始,并且当用户注册事件时将被更改为在 player 表中创建的 player_ID。
我的第一个想法是为每个列创建单独的查询,以顺序检查它们是否包含非重复的值,然后更新第一列以匹配此条件并打破检查,但是使用32个查询来覆盖每个选项似乎非常低效。有没有办法把它压缩到一个更合理的尺寸,如果有,我会怎么做?
1条答案
按热度按时间axr492tv1#
建议您考虑第三个表,这是一个Map(关联表,参考表,Map表和其他术语),用于将玩家Map到事件,并且在将另一个玩家添加到事件时没有问题/并发症,而不是限制为每个事件16个(相对简单)。
这样的表支持many-many关系。这就是说,一个事件可以有许多球员,一个球员可以在许多事件。
这样的表由2个主要列组成,一个引用事件,另一个引用玩家。主键通常应该由这两列组成(因此玩家不能处于同一事件中)。
一个事件最初将没有球员,球员可以添加。唯一的问题是限制16名球员参加一个事件,这只是意味着检查事件中的球员数量(演示SELECT QUERY包括获得计数)。
下面是一个demo:
当运行时,消息日志显示:-
第一个查询的结果(即无):-
第二次查询的结果(即当事件有参与者时):-
首先有3行,因为event_id上有
GROUP BY
子句。即,具有相同event_id值的所有行被分组在一起作为单个输出行。可以看出,行按事件中参与者的数量进行
ORDER
艾德,较低的数字首先输出(默认顺序是ASC
结束)。玩家数量就是这个数字,并使用SQLite聚合
count
函数。请参阅https://www.sqlite.org/lang_aggfunc.html#countplayers列再次使用SQLite聚合函数,这次使用的是
group_concat
函数,该函数将传递给它的表达式中的值与组中每行的所有其他值连接起来。请参阅https://www.sqlite.org/lang_aggfunc.html#group_concatSELECT
中的JOIN
演示了指定列的两种形式。一种形式是用句点将表名与列名分隔开,从而完全限定列名。第二种形式只是列名。后一种形式更短,更容易**,但**容易产生歧义。id
,则使用列名id将是歧义的并且导致错误。交替
另一种不使用Map表的方法是使用CASE WHEN THEN END子句。
考虑这个演示,它逐步填充事件表中的空列。* 注意,整个过程中使用了完全相同的
UPDATE
,但播放器通过了(硬编码,但在调用时会被传递/绑定(就像WHERE子句中使用的事件的id一样)):按照SELECT查询的顺序,结果是:
最后在尝试添加第七个之后,玩家为666(即,该事件已满),该球员是不适用:-
稍微复杂一点,但更紧凑一点又名多CTE
这个例子进一步使用多个CTE,所以更复杂,但不太容易出现编码错误(使用
IS NULL
/IS NOT NULL
编码)。该示例基于与先前使用和加载的相同的2个表event和player。
SQL是:
第一个CTE(通过eid列更新的事件和设置相应列的播放器id)和第三个CTE(用于确定要更新哪个列(如果有的话))用于驱动更新。
使用多个调用来逐步填充事件(类似于前面的例子),在每个结果之后使用SELECT(注意,使用的玩家ID是不同的,但是在所有列都被填充之后仍然使用666)。选择的结果是:
在创建偶数表之后和UPDATE
之前立即执行
第一次更新后
第二次更新后
x 1c 13d1xx 1c 14d1xx 1c 15d1xx 1c 16d1x
所有列都已设置,因此尝试溢出:-
由于事件已经满,所以没有应用播放器ID 666。
这里有一个DB Fiddle的链接,其中包含最后一个示例的完整SQL代码-https://www.db-fiddle.com/f/i8BtXgUwBX6T5wEJpbYWYd/0