Php 对同一子查询表达式的多个联接
我最近一直在自学SQL,并一直在做一个玩具项目来做到这一点。下面是一个示例模式:Php 对同一子查询表达式的多个联接,php,mysql,sql,Php,Mysql,Sql,我最近一直在自学SQL,并一直在做一个玩具项目来做到这一点。下面是一个示例模式: CREATE TABLE user ( id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, name VARCHAR(50) ); INSERT INTO user(name) VALUES ("User 1"), ("User 2"), ("User 3"), ("User 4"), ("User 5"); CREATE TABLE friend
CREATE TABLE user (
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
name VARCHAR(50)
);
INSERT INTO user(name) VALUES
("User 1"),
("User 2"),
("User 3"),
("User 4"),
("User 5");
CREATE TABLE friendship (
uid_1 INT,
uid_2 INT,
accepted_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (uid_1, uid_2),
CONSTRAINT fk_uid_1 FOREIGN KEY (uid_1) REFERENCES user (id),
CONSTRAINT fk_uid_2 FOREIGN KEY (uid_2) REFERENCES user (id)
);
INSERT INTO friendship(uid_1, uid_2) VALUES
(1, 2),
(2, 1);
CREATE TABLE event (
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
name VARCHAR(50),
owner_id INT,
CONSTRAINT fk_owner_id FOREIGN KEY (owner_id) REFERENCES user (id)
);
INSERT INTO event (name, owner_id) VALUES
("Event 1", 1),
("Event 2", 2),
("Event 3", 3),
("Event 4", 4),
("Event 5", 5),
("Event 6", 1);
CREATE TABLE invite (
event_id INT NOT NULL,
sent_from_id INT NOT NULL,
sent_to_id INT NOT NULL,
PRIMARY KEY (event_id, sent_to_id),
CONSTRAINT fk_event_id FOREIGN KEY (event_id) REFERENCES event (id),
CONSTRAINT fk_sent_from_id FOREIGN KEY (sent_from_id) REFERENCES user (id),
CONSTRAINT fk_sent_to_id FOREIGN KEY (sent_to_id) REFERENCES user (id)
);
INSERT INTO invite(event_id, sent_from_id, sent_to_id) VALUES
(1, 2, 3);
作为这个项目的一部分,我有一个查询,它获取一个用户列表,其中包含与当前经过身份验证的用户相关的信息
查询的简化版本如下所示:
$select_users_query = "
SELECT
user.id AS id,
user.name AS name,
friendship.accepted_time AS friend_since
FROM user
LEFT JOIN friendship
ON friendship.uid_1 = user.id AND friendship.uid_2 = $relative_to_id
";
然后,在某些端点,我想返回具有一个或多个用户作为子对象的对象。为了做到这一点,我一直将表作为子查询连接到上述查询中,但是当返回的对象有多个用户(例如,事件邀请有一个发送用户、一个接收用户和一个拥有相关事件的用户)时,生成的查询可能会非常重复:
$select_invites_query = "
SELECT
event.id AS event_id,
event.name AS event_name,
owner.id AS owner_id,
owner.name AS owner_name,
owner.friend_since AS owner_friend_since,
sent_to.id AS sent_to_id,
sent_to.name AS sent_to_name,
sent_to.friend_since AS sent_to_friend_since,
sent_from.id AS sent_from_id,
sent_from.name AS sent_from_name,
sent_from.friend_since AS sent_from_friend_since,
FROM invite
INNER JOIN event
ON event.id = invite.event_id
INNER JOIN ($select_users_query) owner
ON event.owner_id = owner.id
INNER JOIN ($select_users_query) sent_from
ON invite.sent_from_id = sent_from.id
INNER JOIN ($select_users_query) sent_to
ON invite.sent_to_id = sent_to.id
";
我的问题是:
内部联接只在一行上匹配,那么在执行过程中重复这样的子查询是性能问题吗
$select\u查询
所需的额外解析是否是一个值得关注的问题(尤其是当$select\u用户\u查询
变得庞大和复杂时)EXPLAIN
的检查来看,MySQL似乎能够非常有效地处理这些JOIN
s,但是定义变量会迫使MySQL在JOIN
ing之前将未过滤的结果集拉入内存吗参见SQL FaldSchema。
,因为您似乎需要同一个查询的自联接,请考虑一个(在MySQL 8 +中可用)和PHP参数化。下面以面向对象和过程的方式演示如何使用PHP的API:
$select_invites_query = "
WITH sub AS (
SELECT u.id AS id, u.`name` AS `name`,
f.accepted_time AS friend_since
FROM user
LEFT JOIN friendship f
ON f.uid_1 = u.id AND f.uid_2 = ?
)
SELECT event.id AS event_id, event.`name` AS event_name,
owner.id AS owner_id, owner.`name` AS owner_name,
owner.friend_since AS owner_friend_since,
sent_to.id AS sent_to_id, sent_to.`name` AS sent_to_name,
sent_to.friend_since AS sent_to_friend_since,
sent_from.id AS sent_from_id, sent_from.`name` AS sent_from_name,
sent_from.friend_since AS sent_from_friend_since
FROM invite
INNER JOIN event
ON event.id = invite.event_id
INNER JOIN sub owner
ON event.owner_id = owner.id
INNER JOIN sub sent_from
ON invite.sent_from_id = sent_from.id
INNER JOIN sub sent_to
ON invite.sent_to_id = sent_to.id";
// OBJECT-ORIENTED STYLE
$conn = new mysqli("my_host", "my_user", "my_pwd", "my_db");
$stmt = $conn->prepare($select_invites_query))
$stmt->bind_param("i", $relative_to_id);
$stmt->execute();
...
// PROCEDURAL STYLE
$conn = mysqli_connect("my_host", "my_user", "my_pwd", "my_db");
$stmt = mysqli_prepare($conn, $select_invites_query);
mysqli_stmt_bind_param($stmt, "i", $relative_to_id);
mysqli_stmt_execute($stmt);
...
因为您需要自己加入相同的查询,请考虑一个(在MySQL 8 +中可用)和PHP参数化。下面以面向对象和过程的方式演示如何使用PHP的API:
$select_invites_query = "
WITH sub AS (
SELECT u.id AS id, u.`name` AS `name`,
f.accepted_time AS friend_since
FROM user
LEFT JOIN friendship f
ON f.uid_1 = u.id AND f.uid_2 = ?
)
SELECT event.id AS event_id, event.`name` AS event_name,
owner.id AS owner_id, owner.`name` AS owner_name,
owner.friend_since AS owner_friend_since,
sent_to.id AS sent_to_id, sent_to.`name` AS sent_to_name,
sent_to.friend_since AS sent_to_friend_since,
sent_from.id AS sent_from_id, sent_from.`name` AS sent_from_name,
sent_from.friend_since AS sent_from_friend_since
FROM invite
INNER JOIN event
ON event.id = invite.event_id
INNER JOIN sub owner
ON event.owner_id = owner.id
INNER JOIN sub sent_from
ON invite.sent_from_id = sent_from.id
INNER JOIN sub sent_to
ON invite.sent_to_id = sent_to.id";
// OBJECT-ORIENTED STYLE
$conn = new mysqli("my_host", "my_user", "my_pwd", "my_db");
$stmt = $conn->prepare($select_invites_query))
$stmt->bind_param("i", $relative_to_id);
$stmt->execute();
...
// PROCEDURAL STYLE
$conn = mysqli_connect("my_host", "my_user", "my_pwd", "my_db");
$stmt = mysqli_prepare($conn, $select_invites_query);
mysqli_stmt_bind_param($stmt, "i", $relative_to_id);
mysqli_stmt_execute($stmt);
...
谢谢,稍后将提供。与其重复使用查询作为子查询,不如为用户和友谊表创建一个连接。3次重复相同的查询意味着通过加入这2个表来做相同的事情3次肯定会有性能改进。@NandanRana您能澄清一下您的确切意思吗?您将如何更改加入策略?@草莓添加了一个指向SQL Fiddle设置的链接。您的MySQL版本是什么?使用8+,您可以利用CTEs。谢谢,稍后将提供。与其重复使用查询作为子查询,不如为用户和友谊表创建连接。3次重复相同的查询意味着通过加入这2个表来做相同的事情3次肯定会有性能改进。@NandanRana您能澄清一下您的确切意思吗?您将如何更改加入策略?@草莓添加了一个指向SQL Fiddle设置的链接。您的MySQL版本是什么?使用8+,您可以利用CTE。