【问题标题】:Trying to simplify code to get results from 2 tables excluding some from table 1 and some from table 2尝试简化代码以从 2 个表中获取结果,不包括表 1 中的一些和表 2 中的一些
【发布时间】:2017-11-06 00:44:08
【问题描述】:

更新为公关。下方 cmets 中的@philipxy 建议
(感谢输入)

我在 MySQL 数据库中有 2 个表,结构如下

论坛主题

| id |  date  | dateEdit | userID |  title  |  summary  |  randomString  |
--------------------------------------------------------------------------
| 1  | <date> |  <date>  |   1    | Welcome |  Welcome  |  jhdsa76dfasi  |
|    |        |          |        |         |  message  |                |
--------------------------------------------------------------------------
| 2  | <date> |  <date>  |   5    | LEFT    | How to    |  oiasud88ashk  |
|    |        |          |        | JOIN    | LEFT JOIN |                |
--------------------------------------------------------------------------
| 3  | <date> |  <date>  |   6    | Not the | How to do |  lkdsajlkjdf7  |
|    |        |          |        | right   | it right  |                |
|    |        |          |        | way     |           |                |
--------------------------------------------------------------------------
.
.
.
--------------------------------------------------------------------------
| n  | <date> |  <date>  | <user> | title-n | summary-n | randomString-n |
--------------------------------------------------------------------------

(id 为 1 的用户写了 id 为 1 的主题)
(ID 为 5 的用户写了 ID 为 2 的主题)
(ID 为 6 的用户写了 ID 为 3 的主题)
等等

论坛主题视图

-----------------------------
| id |  userID  |  topicID  |
-----------------------------
| 1  |    1     |     2     |
-----------------------------
| 2  |    5     |     1     |
-----------------------------
| 3  |    5     |     3     |
-----------------------------
| 4  |    6     |     1     |
-----------------------------
.
.
.
-----------------------------
| n  | <userID> | <topicID> |
-----------------------------

(上表中 id 为 1 的用户观看了 id 为 2 的主题)
(上表中 id 为 5 的用户观看了 id 为 1 的主题)
(上表中 id 为 5 的用户观看了 id 为 3 的主题)
(上表中 id 为 6 的用户观看了 id 为 1 的主题)
等等

我要做什么
我正在尝试获取所有主题的信息,这些主题不是由登录用户编写的,也没有被登录用户查看过

所以对于用户 1:
用户 1 写了主题 1 并查看了主题 2
SELECT 语句应该显示所有不是 1 和 2(即 3)的主题的信息

对于用户 5:
用户 5 写了主题 2 并查看了主题 1 和 3
然后 SELECT 语句应该显示所有不是 1、2 和 3 的主题的信息(即没有结果)

对于用户 6:
用户 6 写了主题 3 并查看了主题 1
然后 SELECT 语句应该显示所有不是 1 和 3(即 2)的主题的信息

如何在单个 SELECT 语句中执行此操作?

connection.php

try {

    $servername = "localhost";
    $username = "******";
    $password = "**************";
    $database = "*****";
    $charset = 'utf8';

    $dsn = "mysql:host=$servername;dbname=$database;charset=$charset";

    $opt = [
        PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES   => false,
    ];

    $pdo = new PDO ( $dsn , $username , $password , $opt );

} catch ( PDOException $e ) {

    file_put_contents ( 'PDOError.txt', $e->getMessage(), FILE_APPEND );
    echo "Failed to connect to database!";
    die ();

}

有效但繁琐的代码:

functions.php

require ( '../scripts/connection.php' );

function getTopic ($pdo) {

    // Set userID for user logged in
    if ( isset ( $_SESSION['id'] ) ) {

        $userID = $_SESSION['id'];

    }

    try {

        // Find all topics not written by user logged in
        $prepareTopic = $pdo->prepare( "SELECT id , date , title , summary , randomString FROM forumtopic WHERE userID != ? ORDER BY date DESC" );
        $prepareTopic->execute([$userID]);
        $getTopic = $prepareTopic->fetchAll();

    } catch ( PDOException $e ) {

        file_put_contents ( 'error.txt', $e->getMessage(), FILE_APPEND );

    }

    // If found topics not written by user logged in
    if ( $getTopic ) {

        foreach ( $getTopic as $row ) {

            // Set topicID and use it to see if user logged in has already viewed it
            $topicID = $row['id'];

            try {

                // Find if specific topic viewed by user logged in
                $prepareTopicview = $pdo->prepare( "SELECT id FROM forumtopicview WHERE ( userID = ? AND topicID = ? ) LIMIT 1" );
                $prepareTopicview->execute([$userID,$topicID]);
                $getTopicView = $prepareTopicview->fetch();

            } catch ( PDOException $e ) {

                file_put_contents ( 'error.txt', $e->getMessage(), FILE_APPEND );

            }

            // Only if specific topic not viewed by user logged in
            if ( !$getTopicView ) {

                // Set needed variables
                $dateTopic[] = $row['date'];
                $titleTopic[] = $row['title'];
                $summaryTopic[] = $row['summary'];
                $randomStringTopic[] = $row['randomString'];

            }

        }

    }

}

但这是很多代码的方式

我想做的事

functions.php

if ( isset ( $_SESSION['id']; ) ) {

    $userID = $_SESSION['id'];

}

try {

    $prepareTopic = $pdo->( "SELECT..." );
    $prepareTopic->execute([$userID]);
    $getTopic = $prepareTopic->fetchAll();

} catch ( PDOException $e ) {

    file_put_contents ( 'error.txt', $e->getMessage(), FILE_APPEND );

}

if ( $getTopic ) {

    foreach ( $getTopic as $row ) {

        $dateTopic[] = $row['date'];
        $titleTopic[] = $row['title'];
        $summaryTopic[] = $row['summary'];
        $randomStringTopic[] = $row['randomString'];

    }

}

插入单个 SELECT 语句

而且我知道可以在单个 SELECT 语句中完成所有这些操作 - 但我已经退出“游戏”有一段时间了,我似乎无法让它发挥作用

应该按照建议的解决方案工作

SELECT <select_list> FROM forumtopic t LEFT JOIN forumtopicview v ON t.userID = v.userID AND t.id = v.topicID WHERE t.userID <> ? AND v.id IS NULL

它不包括所有由登录用户编写的主题 (t.userID ?)
但不排除已登录用户查看的那些(t.userID = v.userID AND t.id = v.topicID)

希望有人能帮忙

【问题讨论】:

  • 顺便说一句:你的 sql 中有一个错误(有 2 个 where 子句)
  • 如果您仍想得到答案,请不要接受该问题。或者,如果它导致您更改问题,请发布新问题。另外,请不要添加“编辑”,编辑您的问题,以使其成为您所问问题的最佳措辞。另请阅读minimal reproducible example 并采取行动。请同时格式化您的代码,以便 SQL 也被格式化。
  • left join 返回由inner join 执行的行加上由nulls 扩展的不匹配的左表行。 onwhere 绑定得更紧密,但其他方面是相同的。
  • 你不清楚。例如,您说建议的答案“有效”,但没有奏效。例如:告诉我们你的查询做了什么代数运算没有什么意义;我们知道它的作用,我们需要知道您认为沿途表格中有哪些行。因此,经过我的一些编辑后,您的规范“[ir] 用户编写的 [not] 主题 [not] 和 [ir] 用户 [不] 查看的 [forumtopic rows]”很有帮助。 1“[ir] 用户未编写的 [forumtopic rows] 主题”相同。但 2 和 3 不清楚。根据基本关系中的哪些行或到目前为止的行,告诉我们您想要/期望哪些行。
  • PS 你不知道你的基表中的行说什么。除非您这样做,否则没有人可以使用数据库。 forumtopic 中的一行关于它的 id、userID 等说明了什么。目前尚不清楚您所说的“不是由用户编写的主题”是什么意思。

标签: php mysql join left-join


【解决方案1】:

所有非用户撰写且用户未查看的主题

SELECT
      t.id
    , t.date
    , t.dateEdit
    , t.userID
    , t.title
    , t.summary
    , t.randomString
FROM forumtopic t
LEFT JOIN forumtopicview v ON t.userID = v.userID AND t.id = v.topicID
WHERE t.userID <> ? 
AND v.id IS NULL

注意:连接表时,在所有列引用中包含表名或表别名变得非常重要。这样可以避免由常见的列名引起的错误导致歧义。

编辑

SELECT
      t.id
    , t.date
    , t.dateEdit
    , t.userID
    , t.title
    , t.summary
    , t.randomString
FROM forumtopic t
WHERE t.userID <> ? 
AND NOT EXISTS (
        SELECT NULL
        FROM forumtopicview v where t.id = v.topicID
        AND v.userID = ?
        )

【讨论】:

  • 你在符号上都是正确的,当然不应该有 2 个 WHERE 子句 - 第一个 WHERE 子句应该附加到第二个子句并且它有效 - 所以谢谢
  • 只有一个WHERE 子句。另一个是ON 子句。有细微的差别。 stackoverflow.com/questions/2722795/…
  • @Randall Left join 返回内部连接执行的行加上由空值扩展的不匹配的左表行。 onwhere 绑定得更紧密,但其他方面是相同的。这不是“微妙的”。在糟糕的描述中,这些差异可能会模糊,但这是描述不佳的属性,而不是左与右连接或 on vs where。
  • @Randall 2 where 子句存在于问题内的查询中。 APM 指的是我在该问题下发表的评论。
  • @Used_By_Already 很抱歉,我先接受了,然后又收回了接受——当时是凌晨 2 点,我从没有得到结果到你的解决方案得到了结果——代码看起来就像我做的那样想要-所以我开枪了-对此感到抱歉-您的解决方案似乎采用了所有不是用户编写的主题(正确),但没有删除任何已查看的主题-抱歉造成混淆-我非常感谢您的努力-您有任何想法如何更改您的代码,以便它会做我想做的事情? - 它可能非常小,但我似乎找不到它 - 再次感谢您的帮助
猜你喜欢
  • 2015-04-28
  • 2021-01-03
  • 1970-01-01
  • 1970-01-01
  • 2015-08-15
  • 2020-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多