如何在SQLite中将数据存储在下一个Null值列中?

问题描述 投票:0回答:1

我正在为锦标赛跟踪软件创建数据库,其中一部分涉及将用户的 ID 存储为稍后参考的值。为此,我使用了 2 个表; player(主键 player_ID),存储事件中玩家的信息,例如他们的 user_ID(不相关表的一部分)和他们参加的比赛的结果,以及 event,它存储所有事件详细信息,以及所有 16 个事件参与者的 player_ID。这些列名为 player1 -> player16

我的问题是试图找到一种方法来单独填充这些列。创建事件时,所有 16 列均以 Null 值开头,并将更改为用户注册事件时在 player 表中创建的 player_ID

我的第一个想法是为每个列创建单独的查询,以顺序检查它们是否包含非空值,然后更新第一列以匹配此条件并突破检查,但是拥有 32 个似乎效率非常低查询涵盖每个选项。有没有办法将其压缩到更合理的大小,如果是的话,我会怎么做?

sqlite sql-update
1个回答
0
投票

建议您考虑第三个表,这是一个将玩家映射到事件的地图(关联表、参考表、映射表和其他术语)。

这样的表支持多对多关系。也就是说,一个赛事可以有很多玩家,而一个玩家可以参加很多赛事。

这样的表格由 2 个主要列组成,一列用于引用事件,另一列用于引用玩家。主键通常应由这两列组成(因此玩家不能处于同一事件中)。

活动最初没有玩家,可以添加玩家。唯一的问题是一场赛事的玩家人数限制为 16 人,这意味着检查赛事中的玩家数量(演示 SELECT QUERY 包括获取计数)。

这是一个演示:-

/* just in case cleanup the environment */
DROP TABLE IF EXISTS event_participant;
DROP TABLE IF EXISTS player;
DROP TABLE IF EXISTS event;
CREATE TABLE IF NOT EXISTS player (player_id INTEGER PRIMARY KEY, player_name TEXT);
CREATE TABLE IF NOT EXISTS event (event_id INTEGER PRIMARY KEY, event_name TEXT /* etc */);
/* THE MAPPING TABLE */
CREATE TABLE IF NOT EXISTS event_participant (
    event_id_map INTEGER REFERENCES event(event_id) ON DELETE CASCADE ON UPDATE CASCADE, 
    player_id_map INTEGER REFERENCES player(player_id) ON DELETE CASCADE ON UPDATE CASCADE,
    PRIMARY KEY (event_id_map,player_id_map));
    /* Note FOREIGN KEY (aka REFERENCES) is optional but suggested as it enforces referential integrity */
        /* ON DELETE and ON UPDATE are optional but help to maintain referential integrity again suggested */
/* SUGGESTED INDEX ON the 2nd column, will liekly improve efficiency */
CREATE INDEX ix_event_participant_playermap ON event_participant (player_id_map);
/* ADD SOME DATA */
INSERT OR IGNORE INTO player (player_name) VALUES('Mary'),('Jane'),('Tom'),('Fred'),('Anne'),('Bert'),('Alan'),('Beth');
INSERT OR IGNORE INTO event (event_name) VALUES('E1'),('E2'),('E3'),('E4');
/* NOTHING SELECTED as there are currently no events with participants */
SELECT 
    event_name,
    group_concat(player_name,'::') AS players,
    count(*) AS num_of_players 
FROM event
    JOIN event_participant ON event.event_id = event_participant.event_id_map
    JOIN player ON player_id_map = player_id
GROUP BY event_id
ORDER BY num_of_players
/* Add some participants to some events */
;
INSERT OR IGNORE INTO event_participant VALUES
    (1,2),(1,4),(1,8) /* Jane, Fred and Beth in event 1*/,
    (2,1),(2,3),(2,5),(2,7) /* Mary, Tom, Anne and Alan in event 2 */,
    (3,1),(3,4),(3,8),(3,6),(3,2),(3,7),(3,5),(3,3) /* all in event 3 note any order */,
    /*!!!!!!!!!! OOOPS ON PURPOSE !!!!!!!!!!*/ (1,2),(1,4),(1,8) /* Jane, Fred and Beth in event 1??????????*/
    /* ooops because they already exist as participants in the event */ 
;
/* Now there are participants then the exact same query as above will yield results */
SELECT 
    event_name,
    group_concat(player_name,'::') AS players,
    count(*) AS num_of_players 
FROM event
    JOIN event_participant ON event.event_id = event_participant.event_id_map
    JOIN player ON player_id_map = player_id
GROUP BY event_id
ORDER BY num_of_players
;
/* Cleanup demo environment */
DROP TABLE IF EXISTS event_participant;
DROP TABLE IF EXISTS player;
DROP TABLE IF EXISTS event;

运行时消息日志显示:-

/* just in case cleanup the environment */
DROP TABLE IF EXISTS event_participant
> OK
> Time: 0.323s


DROP TABLE IF EXISTS player
> OK
> Time: 0.041s


DROP TABLE IF EXISTS event
> OK
> Time: 0.024s


CREATE TABLE IF NOT EXISTS player (player_id INTEGER PRIMARY KEY, player_name TEXT)
> OK
> Time: 0.024s


CREATE TABLE IF NOT EXISTS event (event_id INTEGER PRIMARY KEY, event_name TEXT /* etc */)
> OK
> Time: 0.024s


/* THE MAPPING TABLE */
CREATE TABLE IF NOT EXISTS event_participant (
    event_id_map INTEGER REFERENCES event(event_id) ON DELETE CASCADE ON UPDATE CASCADE, 
    player_id_map INTEGER REFERENCES player(player_id) ON DELETE CASCADE ON UPDATE CASCADE,
    PRIMARY KEY (event_id_map,player_id_map))
> OK
> Time: 0.024s


/* Note FOREIGN KEY (aka REFERENCES) is optional but suggested as it enforces referential integrity */
        /* ON DELETE and ON UPDATE are optional but help to maintain referential integrity again suggested */
/* SUGGESTED INDEX ON the 2nd column, will liekly improve efficiency */
CREATE INDEX ix_event_participant_playermap ON event_participant (player_id_map)
> OK
> Time: 0.024s


/* ADD SOME DATA */
INSERT OR IGNORE INTO player (player_name) VALUES('Mary'),('Jane'),('Tom'),('Fred'),('Anne'),('Bert'),('Alan'),('Beth')
> Affected rows: 8
> Time: 0.024s


INSERT OR IGNORE INTO event (event_name) VALUES('E1'),('E2'),('E3'),('E4')
> Affected rows: 4
> Time: 0.024s


/* NOTHING SELECTED as there are currently no events with participants */
SELECT 
    event_name,
    group_concat(player_name,'::') AS players,
    count(*) AS num_of_players 
FROM event
    JOIN event_participant ON event.event_id = event_participant.event_id_map
    JOIN player ON player_id_map = player_id
GROUP BY event_id
ORDER BY num_of_players
/* Add some participants to some events */
> OK
> Time: 0s


INSERT OR IGNORE INTO event_participant VALUES
    (1,2),(1,4),(1,8) /* Jane, Fred and Beth in event 1*/,
    (2,1),(2,3),(2,5),(2,7) /* Mary, Tom, Anne and Alan in event 2 */,
    (3,1),(3,4),(3,8),(3,6),(3,2),(3,7),(3,5),(3,3) /* all in event 3 note any order */,
    /*!!!!!!!!!! OOOPS ON PURPOSE !!!!!!!!!!*/ (1,2),(1,4),(1,8) /* Jane, Fred and Beth in event 1??????????*/
    /* ooops because they already exist as participants in the event */
> Affected rows: 15
> Time: 0.024s


/* Now there are participants then the exact same query as above will yield results */
SELECT 
    event_name,
    group_concat(player_name,'::') AS players,
    count(*) AS num_of_players 
FROM event
    JOIN event_participant ON event.event_id = event_participant.event_id_map
    JOIN player ON player_id_map = player_id
GROUP BY event_id
ORDER BY num_of_players
> OK
> Time: 0s


/* Cleanup demo environment */
DROP TABLE IF EXISTS event_participant
> OK
> Time: 0.023s


DROP TABLE IF EXISTS player
> OK
> Time: 0.024s


DROP TABLE IF EXISTS event
> OK
> Time: 0.052s

第一个查询的结果(即什么都没有):-

第二次查询的结果(即当事件有参与者时):-

首先有 3 行,因为 event_id 上有

GROUP BY
子句。即具有相同 event_id 值的所有行都分组在一起作为单个输出行。

可以看出,行按事件中的参与者数量进行

ORDER
排列,数字较小的先输出(因为默认顺序是
ASC
结束)。

玩家数量正是如此,并利用 SQLite 聚合

count
函数。请参阅https://www.sqlite.org/lang_aggfunc.html#count

players 列再次使用 SQLite 聚合函数,这次是使用 group_concat

 函数,它将传递给它的表达式中的值与组中每行的所有其他值连接起来。请参阅
https://www.sqlite.org/lang_aggfunc.html#group_concat

  • 请注意,

    JOIN

    中的
    SELECT
    演示了指定列的两种形式。一种形式是用句点将表名与列名分隔开,从而完全限定列名。第二种形式只是列名。后一种形式更短、更容易 
    但是 容易产生歧义。

  • 例如如果“event_id”和“player_id”列仅命名为

    id

    ,那么使用列名 id 将产生歧义并导致错误。

© www.soinside.com 2019 - 2024. All rights reserved.