Php 对同一子查询表达式的多个联接

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

我最近一直在自学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 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。