如何重用 2 个相同子查询检索到的值?
How to reuse the value retrieved by 2 identical subqueries?
我已经为我的问题准备了一个简单的SQL Fiddle。
在 PostgreSQL10 中有 2 个表保存用户 ID 和用户社交网络数据(如名字、照片):
CREATE TABLE words_users (
uid SERIAL PRIMARY KEY
);
CREATE TABLE words_social (
sid text NOT NULL,
social integer NOT NULL CHECK (0 < social AND social <= 64),
given text NOT NULL CHECK (given ~ '\S'),
uid integer NOT NULL REFERENCES words_users ON DELETE CASCADE,
PRIMARY KEY(sid, social)
);
还有2张桌子放着游戏和聊天信息:
CREATE TABLE words_games (
gid SERIAL PRIMARY KEY,
player1 integer REFERENCES words_users(uid) ON DELETE CASCADE NOT NULL CHECK (player1 <> player2),
player2 integer REFERENCES words_users(uid) ON DELETE CASCADE
);
CREATE TABLE words_chat (
cid BIGSERIAL PRIMARY KEY,
created timestamptz NOT NULL,
gid integer NOT NULL REFERENCES words_games ON DELETE CASCADE,
uid integer NOT NULL REFERENCES words_users ON DELETE CASCADE,
msg text NOT NULL
);
这里我用测试数据填表:
- Facebook (social=10) 上 uid=1 和 sid="1111" 的用户 "Alice"
- 用户 "Bob" 在 Google (social=20)
上具有 uid=2 和 sid="2222"
- gid=100 的游戏,两个用户都在聊天
SQL:
INSERT INTO words_users (uid) VALUES (1), (2);
INSERT INTO words_games (gid, player1, player2) VALUES (100, 1, 2);
INSERT INTO words_social (sid, social, given, uid) VALUES
('1111', 10, 'Alice', 1),
('2222', 20, 'Bob', 2);
INSERT INTO words_chat (created, gid, uid, msg) VALUES
(now() + interval '1 min', 100, 2, 'Hello, Alice'),
(now() + interval '2 min', 100, 1, 'Hello, Bob'),
(now() + interval '3 min', 100, 2, 'Nice to see you, Alice'),
(now() + interval '4 min', 100, 1, 'Nice to see you too, Bob'),
(now() + interval '5 min', 100, 2, 'Goodbye, Alice'),
(now() + interval '6 min', 100, 1, 'Goodbye, Bob');
我正在尝试创建一个 PHP 脚本,当给定 sid、social 和 gid 会 return 游戏 gid 的完整聊天。
我不只是使用 gid 参数来获取和显示聊天,因为我不希望其他用户监视他们不玩的游戏。
我也不能使用 uid 参数来识别调用我的脚本的用户,我必须使用 sid 和 social 参数代替(加上一个秘密,我在上面的测试用例中省略了)。
所以这是我的 SQL-获取聊天的语句:
SELECT uid, msg
FROM words_chat
WHERE gid=100
AND EXISTS (select 1 from words_games where
(select uid from words_social
where sid='1111' and social=10) in (player1, player2))
ORDER BY CREATED ASC;
它有效,但我还需要 1 个东西,这使它变得复杂 - 对于每个聊天 msg 我需要一个布尔值(而不是 uid) 来知道这是不是 "Alice" 的消息(这样我就可以用粗体画那条线)。
所以我正在尝试这样做,SQL 语句变得更加丑陋:
SELECT uid=(select uid from words_social
where sid='1111' and social=10) AS mine, msg
FROM words_chat
WHERE gid=100
AND EXISTS (select 1 from words_games where
(select uid from words_social
where sid='1111' and social=10) in (player1, player2))
ORDER BY CREATED ASC;
正如您在上面的屏幕截图中看到的那样有效,但如何改进此查询?
在 SQL 中有没有办法(即我不希望在我的 PHP 脚本中保存 uid 值或使用 pl/pgSQL) 在第二个子查询中重用 uid?
考虑使用 join
s 就容易多了:
select wc.*, (wc.uid = ws.uid) as mine
from words_chat wc join
words_games wg
on wc.gid = wg.gid join
words_social ws
on ws.uid in (wg.player1, player2)
where wc.gid = 100 and
ws.sid = '1111' and
ws.social = 10
order by created asc;
Here 是 SQL Fiddle.
我已经为我的问题准备了一个简单的SQL Fiddle。
在 PostgreSQL10 中有 2 个表保存用户 ID 和用户社交网络数据(如名字、照片):
CREATE TABLE words_users (
uid SERIAL PRIMARY KEY
);
CREATE TABLE words_social (
sid text NOT NULL,
social integer NOT NULL CHECK (0 < social AND social <= 64),
given text NOT NULL CHECK (given ~ '\S'),
uid integer NOT NULL REFERENCES words_users ON DELETE CASCADE,
PRIMARY KEY(sid, social)
);
还有2张桌子放着游戏和聊天信息:
CREATE TABLE words_games (
gid SERIAL PRIMARY KEY,
player1 integer REFERENCES words_users(uid) ON DELETE CASCADE NOT NULL CHECK (player1 <> player2),
player2 integer REFERENCES words_users(uid) ON DELETE CASCADE
);
CREATE TABLE words_chat (
cid BIGSERIAL PRIMARY KEY,
created timestamptz NOT NULL,
gid integer NOT NULL REFERENCES words_games ON DELETE CASCADE,
uid integer NOT NULL REFERENCES words_users ON DELETE CASCADE,
msg text NOT NULL
);
这里我用测试数据填表:
- Facebook (social=10) 上 uid=1 和 sid="1111" 的用户 "Alice"
- 用户 "Bob" 在 Google (social=20) 上具有 uid=2 和 sid="2222"
- gid=100 的游戏,两个用户都在聊天
SQL:
INSERT INTO words_users (uid) VALUES (1), (2);
INSERT INTO words_games (gid, player1, player2) VALUES (100, 1, 2);
INSERT INTO words_social (sid, social, given, uid) VALUES
('1111', 10, 'Alice', 1),
('2222', 20, 'Bob', 2);
INSERT INTO words_chat (created, gid, uid, msg) VALUES
(now() + interval '1 min', 100, 2, 'Hello, Alice'),
(now() + interval '2 min', 100, 1, 'Hello, Bob'),
(now() + interval '3 min', 100, 2, 'Nice to see you, Alice'),
(now() + interval '4 min', 100, 1, 'Nice to see you too, Bob'),
(now() + interval '5 min', 100, 2, 'Goodbye, Alice'),
(now() + interval '6 min', 100, 1, 'Goodbye, Bob');
我正在尝试创建一个 PHP 脚本,当给定 sid、social 和 gid 会 return 游戏 gid 的完整聊天。
我不只是使用 gid 参数来获取和显示聊天,因为我不希望其他用户监视他们不玩的游戏。
我也不能使用 uid 参数来识别调用我的脚本的用户,我必须使用 sid 和 social 参数代替(加上一个秘密,我在上面的测试用例中省略了)。
所以这是我的 SQL-获取聊天的语句:
SELECT uid, msg
FROM words_chat
WHERE gid=100
AND EXISTS (select 1 from words_games where
(select uid from words_social
where sid='1111' and social=10) in (player1, player2))
ORDER BY CREATED ASC;
它有效,但我还需要 1 个东西,这使它变得复杂 - 对于每个聊天 msg 我需要一个布尔值(而不是 uid) 来知道这是不是 "Alice" 的消息(这样我就可以用粗体画那条线)。
所以我正在尝试这样做,SQL 语句变得更加丑陋:
SELECT uid=(select uid from words_social
where sid='1111' and social=10) AS mine, msg
FROM words_chat
WHERE gid=100
AND EXISTS (select 1 from words_games where
(select uid from words_social
where sid='1111' and social=10) in (player1, player2))
ORDER BY CREATED ASC;
正如您在上面的屏幕截图中看到的那样有效,但如何改进此查询?
在 SQL 中有没有办法(即我不希望在我的 PHP 脚本中保存 uid 值或使用 pl/pgSQL) 在第二个子查询中重用 uid?
考虑使用 join
s 就容易多了:
select wc.*, (wc.uid = ws.uid) as mine
from words_chat wc join
words_games wg
on wc.gid = wg.gid join
words_social ws
on ws.uid in (wg.player1, player2)
where wc.gid = 100 and
ws.sid = '1111' and
ws.social = 10
order by created asc;
Here 是 SQL Fiddle.