我为我的问题准备了一个SQL Fiddle -
在一个 2 人文字游戏中,我将玩家和他们的游戏存储在 2 个表中:
CREATE TABLE players (
uid SERIAL PRIMARY KEY,
name text NOT NULL
);
CREATE TABLE games (
gid SERIAL PRIMARY KEY,
player1 integer NOT NULL REFERENCES players ON DELETE CASCADE,
player2 integer NOT NULL REFERENCES players ON DELETE CASCADE
);
放置移动的字母块以及生成的单词和分数存储在另外 2 个表中:
CREATE TABLE moves (
mid BIGSERIAL PRIMARY KEY,
uid integer NOT NULL REFERENCES players ON DELETE CASCADE,
gid integer NOT NULL REFERENCES games ON DELETE CASCADE,
played timestamptz NOT NULL,
tiles jsonb NOT NULL
);
CREATE TABLE scores (
mid bigint NOT NULL REFERENCES moves ON DELETE CASCADE,
uid integer NOT NULL REFERENCES players ON DELETE CASCADE,
gid integer NOT NULL REFERENCES games ON DELETE CASCADE,
word text NOT NULL CHECK(word ~ '^[A-Z]{2,}$'),
score integer NOT NULL CHECK(score >= 0)
);
在这里,我用包含一个游戏和 2 个玩家(Alice 和 Bob)的测试数据填充上面的表格:
INSERT INTO players (name) VALUES ('Alice'), ('Bob');
INSERT INTO games (player1, player2) VALUES (1, 2);
他们的互换招式如下,有时一个招式可以产生2个字:
INSERT INTO moves (uid, gid, played, tiles) VALUES
(1, 1, now() + interval '1 min', '[{"col": 7, "row": 12, "value": 3, "letter": "A"}, {"col": 8, "row": 12, "value": 10, "letter": "B"}, {"col": 9, "row": 12, "value": 1, "letter": "C"}, {"col": 10, "row": 12, "value": 2, "letter": "D"}]
'::jsonb),
(2, 1, now() + interval '2 min', '[{"col": 7, "row": 12, "value": 3, "letter": "X"}, {"col": 8, "row": 12, "value": 10, "letter": "Y"}, {"col": 9, "row": 12, "value": 1, "letter": "Z"}]
'::jsonb),
(1, 1, now() + interval '3 min', '[{"col": 7, "row": 12, "value": 3, "letter": "K"}, {"col": 8, "row": 12, "value": 10, "letter": "L"}, {"col": 9, "row": 12, "value": 1, "letter": "M"}, {"col": 10, "row": 12, "value": 2, "letter": "N"}]
'::jsonb),
(2, 1, now() + interval '4 min', '[]'::jsonb),
(1, 1, now() + interval '5 min', '[{"col": 7, "row": 12, "value": 3, "letter": "A"}, {"col": 8, "row": 12, "value": 10, "letter": "B"}, {"col": 9, "row": 12, "value": 1, "letter": "C"}, {"col": 10, "row": 12, "value": 2, "letter": "D"}]
'::jsonb),
(2, 1, now() + interval '6 min', '[{"col": 7, "row": 12, "value": 3, "letter": "P"}, {"col": 8, "row": 12, "value": 10, "letter": "Q"}]
'::jsonb);
INSERT INTO scores (mid, uid, gid, word, score) VALUES
(1, 1, 1, 'ABCD', 40),
(2, 2, 1, 'XYZ', 30),
(2, 2, 1, 'XAB', 30),
(3, 1, 1, 'KLMN', 40),
(3, 1, 1, 'KYZ', 30),
(5, 1, 1, 'ABCD', 40),
(6, 2, 1, 'PQ', 20),
(6, 2, 1, 'PABCD', 50);
正如您在上面看到的,该tiles
列始终是一个 JSON 对象列表。
但我只需要检索对象的单个属性:letter
.
所以这是我的 SQL 代码(用于在特定游戏中显示玩家移动的 PHP 脚本):
SELECT
STRING_AGG(x->>'letter', ''),
STRING_AGG(y, ', ')
FROM (
SELECT
JSONB_ARRAY_ELEMENTS(m.tiles) AS x,
FORMAT('%s (%s)', s.word, s.score) AS y
FROM moves m
LEFT JOIN scores s
USING (mid)
WHERE m.gid = 1
GROUP BY mid, s.word, s.score
ORDER BY played ASC
) AS z;
不幸的是,它没有按预期工作。
尽管我试图GROUP BY mid
:
有没有办法用mid
(又名移动ID)分割结果字符串?
更新:
我的问题不在于排序。我的问题是我得到了 2 个大字符串,而我希望有多个字符串,每个移动 id 一对(又名mid
)。
这是我的预期输出,有人对如何实现它有建议吗?
mid "concatenated 'letter' from JSON" "concatenated words and scores"
1 'ABCD' 'ABCD (40)'
2 'XYZ' 'XYZ (30), XAB (30)'
3 'KLMN' 'KLMN (40), KYZ (30)'
5 'ABCD' 'ABCD (40)'
6 'PQ' 'PQ (20), PABCD (50)'
更新#2:
我遵循了 Laurenz 的建议(谢谢!这里是SQL Fiddle):
SELECT
mid,
STRING_AGG(x->>'letter', '') AS tiles,
STRING_AGG(y, ', ') AS words
FROM (
SELECT
mid,
JSONB_ARRAY_ELEMENTS(m.tiles) AS x,
FORMAT('%s (%s)', s.word, s.score) AS y
FROM moves m
LEFT JOIN scores s
USING (mid)
WHERE m.gid = 1
) AS z
GROUP BY mid
ORDER BY mid;
但是由于某种原因,“单词(分数)”条目被乘以: