5

特定の組み合わせが存在するかどうかをデータベースで確認しようとしています。

テーブル:conversations

+----+
| id |
+----+
| 1  |
| 2  |
| 3  |
| 4  |
| 5  |
+----+

テーブル:conversations_users

+----+--------------+------+
| id | conversation | user |
+----+--------------+------+
| 1  | 1            | 1    |
| 2  | 1            | 2    |
| 3  | 2            | 1    |
| 4  | 2            | 2    |
| 5  | 2            | 3    |
| 6  | 2            | 4    |
| 7  | 3            | 2    |
| 8  | 3            | 3    |
| 9  | 4            | 2    |
| 10 | 4            | 4    |
+----+--------------+------+

次に、これらのユーザーが同じ会話に参加している会話を取得するクエリを作成します。

Users: 1,2,3,4 (Only them, no else)

それらだけが入っている会話がある場合、その会話のIDを取得したいのですが、そうでresultなければ0

このトリックを行う方法を知っている人はいますか?

4

9 に答える 9

3

アイデアは、特定の会話で個別のユーザーをカウントすることです。句で設定したユーザー数と一致する場合はIN、検索対象のユーザーのみが存在することになります。

SELECT id
FROM conversations_users
WHERE user in (1, 2, 3, 4)
GROUP BY id
HAVING COUNT(DISTINCT user) = 4

これは、4 人のユーザーのうち 3 人だけが表示される会話を出力しないことに注意してください。これらの会話も必要な場合は、次のようにします。

SELECT id
FROM conversations_users
WHERE user in (1, 2, 3, 4)
GROUP BY id
HAVING COUNT(DISTINCT user) <= 4
于 2013-04-30T13:22:18.233 に答える
2

クエリと結合はシンプルで読みやすいものにしてください。

ユーザー 5 が存在するため、探している会話に 4 人のユーザー (1、2、3、4) 全員が含まれている必要はなく、それらの任意の組み合わせのみを含む会話が必要であると想定しました。 4 ユーザー。

デモ

select distinct
    cu.conversation
from
    conversations_users cu
        left join
    conversations_users cu2 ON cu.conversation = cu2.conversation
where
    cu.user in (1 , 2, 3, 4)
        and cu2.user in (1 , 2, 3, 4)
        and cu.user != cu2.user /* include this clause if you need to exclude conversations of a user with themselves */

代わりに、4 人のユーザー全員だけが関与する会話が必要な場合はお知らせください。

他の会話を削除しますか? 「結果が 0 になるはずです」と言うとき、行数または会話 ID の値を参照していますか? 後者の場合は、次を使用します。

select distinct
    case
        when
            cu.user in (1 , 2, 3, 4)
                and cu2.user in (1 , 2, 3, 4)
        then
            cu.conversation
        else 0
    end conversation
from
    conversations_users cu
        left join
    conversations_users cu2 ON cu.conversation = cu2.conversation
where
    1 = 1 
            and cu.user != cu2.user /* include this clause if you need to exclude conversations of a user with themselves */
于 2013-04-30T14:16:30.630 に答える
2

私があなたの要件を正しく読んだ場合、会話の id が必要です。会話に参加しているのは (たとえば) 1、2、3、4 人だけであり、それらすべての人が参加しています。そうでない場合は、その会話に 0 を返します。

もしそうなら、このようなもの

SELECT CASE WHEN MatchCount = 4 AND UnMatchCount IS NULL THEN conversations.id ELSE 0 END
FROM conversations
LEFT OUTER JOIN (SELECT conversation, COUNT(DISTINCT user) AS MatchCount FROM conversations_users WHERE user IN (1,2,3,4) GROUP BY conversation) Sub1 ON conversations.id = Sub1.conversation
LEFT OUTER JOIN (SELECT conversation, COUNT(DISTINCT user) AS UnMatchCount FROM conversations_users WHERE user NOT IN (1,2,3,4) GROUP BY conversation) Sub2 ON conversations.id = Sub2.conversation

編集 - 上記のクエリの修正版は、4 人のユーザーのみが関与する会話の ID のみを戻すようにします。これをいじることは、それを行うためのかなり効率的な方法のようです。

SELECT conversations.id 
FROM conversations
LEFT OUTER JOIN (SELECT conversation, COUNT(DISTINCT user) AS MatchCount FROM conversations_users WHERE user IN (1,2,3,4) GROUP BY conversation) Sub1 ON conversations.id = Sub1.conversation
LEFT OUTER JOIN (SELECT conversation, COUNT(DISTINCT user) AS UnMatchCount FROM conversations_users WHERE user NOT IN (1,2,3,4) GROUP BY conversation) Sub2 ON conversations.id = Sub2.conversation
WHERE MatchCount = 4 
AND UnMatchCount IS NULL
于 2013-04-30T14:53:28.983 に答える
2
SELECT ID FROM CONVERSATIONS WHERE ID IN 
(SELECT CONVERSATIONS FROM CONVERSATION_USERS 
GROUP BY CONVERSATIONS HAVING COUNT(DISTINCT USER) >= 2)
于 2013-04-30T14:18:28.387 に答える
2

usersテーブルもあると仮定します。

SELECT id
FROM conversations AS c
WHERE NOT EXISTS
      ( SELECT *
        FROM users AS u
        WHERE u.id IN (1, 2, 3, 4)
          AND NOT EXISTS
              ( SELECT *
                FROM conversations_users AS cu 
                WHERE cu.user = u.id
                  AND cu.conversation = c.id
              )
      ) 
  AND NOT EXISTS 
      ( SELECT *
        FROM conversations_users AS co        -- and only them
        WHERE co.conversation = c.id 
          AND co.user NOT IN (1, 2, 3, 4)
      ) ;

テーブルを持っていないusersか、使いたくない場合 (理由はわかりますがとにかく)、この部分を置き換えることができます。

WHERE NOT EXISTS
      ( SELECT *
        FROM users AS u
        WHERE u.id IN (1, 2, 3, 4)
          AND NOT EXISTS

と:

WHERE NOT EXISTS
      ( SELECT *
        FROM (SELECT 1 AS id UNION SELECT 2 UNION
              SELECT 3       UNION SELECT 4) AS u
        WHERE NOT EXISTS

上記のクエリは一般的なものですが、MySQL ではあまり効率的ではありません (二重のネストと単純なオプティマイザーのせいです)。このGROUP BY / COUNT方法はおそらくより効率的なレベルですが、データでテストしてください。また、この種の質問に答える方法は他にも (10 以上) あります。この回答では、次の回答を参照してください: How to filter SQL results in has-many-through relationship そこにあるクエリ 5 と 6 は、MySQL で非常に効率的であると期待しています (group by クエリよりも効率的なレベルです)。

あなたのケースには違いがあります。質問/回答は(単純な)関係分割についてですが、正確な関係分割が必要なので、次のように5を書くことができます:

SELECT id
FROM conversations AS c
WHERE  EXISTS (SELECT * FROM conversations_users AS cu
               WHERE  cu.conversation = c.id AND cu.user = 1)
AND    EXISTS (SELECT * FROM conversations_users AS cu
               WHERE  cu.conversation = c.id AND cu.user = 2)
AND    EXISTS (SELECT * FROM conversations_users AS cu
               WHERE  cu.conversation = c.id AND cu.user = 3)
AND    EXISTS (SELECT * FROM conversations_users AS cu
               WHERE  cu.conversation = c.id AND cu.user = 4)
AND    NOT EXISTS (SELECT * FROM conversations_users AS cu
                   WHERE  cu.conversation = c.id AND cu.user NOT IN (1,2,3,4))
于 2013-04-30T13:45:53.380 に答える
2

これは、「set-within-sets」クエリの例です。これらについては、次の句を使用group byするのが好きです。having

select conversation
from conversation_users cu
group by conversation
having SUM(user = 1) > 0 and
       sum(user = 2) > 0 and
       sum(user = 3) > 0 and
       sum(user = 4) > 0 and
       sum(user not in (1, 2, 3, 4)) = 0

having 句の各条件は、質問で指定された 5 つの条件のいずれかに対応します。

  • ユーザー 1 が会話中
  • ユーザー 2 が会話中
  • ユーザー 3 が会話中
  • ユーザー 4 が会話中
  • 他のユーザーは会話に参加していません
于 2013-04-30T14:30:48.857 に答える
2

あなたの質問を正しく理解できれば、これを使用できます:

SELECT
  conversation
FROM
  conversations_users
GROUP BY
  conversation
HAVING
  COUNT(
    DISTINCT CASE WHEN user IN (1,2,3,4) THEN user END
  )=4 AND
  COUNT(DISTINCT user)=4
于 2013-04-30T13:43:04.660 に答える
2
SELECT
  cs.conversation,
  IF(csl.total = 4,'yes','no') AS AllIn
FROM conversations_users AS cs
  LEFT JOIN (
                SELECT 
                    conversation , 
                    COUNT(DISTINCT user) AS total 
                FROM conversations_users 
                WHERE user IN (1,2,3,4) 
                GROUP BY conversation
            ) AS csl
    ON csl.conversation = cs.conversation
GROUP BY cs.conversation

SQL フィドルのデモ

出力

| CONVERSATION | ALLIN |
------------------------
|            1 |    no |
|            2 |   yes |
|            3 |    no |
|            4 |    no |

これにより、すべての会話 ID とそのステータスが表示されます

修正済み

| CONVERSATION | ALLIN |
------------------------
|            1 |     0 |
|            2 |     2 |
|            3 |     0 |
|            4 |     0 |
于 2013-04-30T13:32:14.480 に答える