web-dev-qa-db-ja.com

ネストされたサブクエリのユーザー親テーブルID

SELECT users.*,(SELECT COUNT(user_id) AS mutual_connection FROM 
                (SELECT user_id 
                       FROM (
                                SELECT sender_id AS user_id 
                                FROM `connections`
                                WHERE receiver_id=users.id AND status='2' 
                                UNION 
                                SELECT receiver_id AS user_id 
                                FROM `connections` 
                                WHERE sender_id=users.id AND status='2'
                        ) tempUser
                        WHERE user_id IN (
                                  SELECT sender_id AS user_id
                                  FROM `connections` 
                                  WHERE receiver_id='4' AND status='2'
                                  UNION 
                                  SELECT receiver_id AS user_id 
                                  FROM `connections` WHERE sender_id='4' AND status='2') 
                         GROUP BY user_id) 
                as mutualConnection)

     FROM users

エラー:

#1054-「where句」の不明な列「users.id」

値をサブクエリに使用する方法

MySQLは、1レベルのネストよりも深い外部レベルの列を参照することを禁止しています。ただし、クエリはusers.idを3レベル深く参照しています。

したがって、ネストされたクエリを使用しても、外部レベルとの相関がネストされないように、相関サブクエリを次のように書き直す必要があります。

(
  SELECT
    COUNT(*)
  FROM
    (
      SELECT ...
    ) AS mutualConnection
  WHERE
   ... = users.id
)

サブクエリがメインクエリと相関しているように見えるため、タスクは非常に困難です。私がそれを正しく理解すると、ロジックは次のようになります。

すべてのユーザーについて、特定の他のユーザー(この場合はユーザーID='4')にも接続している個別の接続(ユーザー)の数を見つけます。

したがって、2つのconnectionssender_idreceiver_idのどちらかから収集された列を取得しています。これは、もう一方users.idに一致します。取得後、取得したsender_idまたはreceiver_idがユーザー4の接続の中にあることを確認しています。最後に、すべてのdistinct結果の列の出現(これは繰り返しますが、sender_idreceiver_idの混合です)。

これは、多くのネストレベルを使用せずにすべての相関を同じレベルに維持することなく、これを行う方法です。

SELECT
  u.*,
  (
    SELECT
      COUNT(DISTINCT CASE u.id WHEN c.sender_id THEN c.receiver_id ELSE c.sender_id END)
    FROM
      connections AS c
    WHERE
      c.status = '2'
      AND u.id IN (c.sender_id, c.receiver_id)
      AND (CASE u.id WHEN c.sender_id THEN c.receiver_id ELSE c.sender_id END)
      IN  (
            SELECT sender_id AS user_id
            FROM   connections 
            WHERE  receiver_id = '4' AND status = '2'
            UNION 
            SELECT receiver_id AS user_id 
            FROM   connections 
            WHERE  sender_id = '4' AND status= '2'
          )
  ) AS mutual_connection_count
FROM
  users AS u
;

CASE式は、クエリのtempUser派生テーブルのuser_id列です。 COUNT関数およびWHERE句(IN述語)で使用されます。通常、このようなコードの繰り返しは、入れ子にすることで解消されます。ただし、この投稿の冒頭で述べたMySQLの制限のため、ここではネストを使用できません。したがって、コードの繰り返しは、コードを回避するために支払わなければならない代償です。幸いなことに、この特定のケースではそれほど多くありません。

9
Andriy M

WHERE user_id = users.id句を最初の相関サブクエリに追加します。

create table connections(user_id int, sender_id int, receiver_id int, status int);
create table users(id int);
SELECT users.*,
       (SELECT sender_id
        FROM   connections
        WHERE  receiver_id = users.id
       ) 1_level
FROM users;
 id | 1_level 
-:| ------:
SELECT users.*,
       (SELECT sender_id
        FROM   (SELECT receiver_id as sender_id
                FROM   connections
                WHERE  sender_id = users.id
               ) 2_level 
       ) 1_level
FROM users;
「where句」の不明な列「users.id」
SELECT users.*,
       (SELECT COUNT(user_id) AS mutual_connection 
        FROM 
             (SELECT user_id 
              FROM 
                   (SELECT sender_id AS user_id 
                    FROM   connections
                    WHERE  status = '2' 
                    UNION 
                    SELECT receiver_id AS user_id 
                    FROM   connections 
                    WHERE  status = '2'
                   ) tempUser
              WHERE user_id IN (SELECT sender_id AS user_id
                                FROM   connections 
                                WHERE  receiver_id = '4' AND status = '2'
                                UNION 
                                SELECT receiver_id AS user_id 
                                FROM   connections 
                                WHERE  sender_id = '4' AND status= '2') 
              GROUP BY user_id) as mutualConnection
        WHERE user_id = users.id  #<<<-------------- Here
       ) uid
FROM users
 id | uid 
-:| -:

dbfiddle ここ

1
McNets