2013-04-30 29 views
5

Tôi đang cố gắng kiểm tra cơ sở dữ liệu nếu có một kết hợp cụ thể tồn tại.CHỌN Ở ĐÂU MỘT cách nâng cao

Bảng: conversations

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

Bảng: 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 | 
+----+--------------+------+ 

Sau đó, tôi muốn thực hiện một truy vấn để có được các cuộc đàm thoại nơi những người dùng đang ở trong cuộc trò chuyện giống nhau:

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

Nếu có một cuộc trò chuyện mà chỉ có những người đang ở trong, tôi muốn có được id của th tại cuộc hội thoại và một số khác là result sẽ trở thành 0

Bất kỳ ai có ý tưởng nào về cách thực hiện thủ thuật này?

+1

Sẽ dễ dàng hơn cho chúng tôi nếu bạn thiết lập [SQL Fiddle] (http://sqlfiddle.com/) –

+1

Vì sự tồn tại của người dùng 5, tôi đã giả định rằng bạn không yêu cầu TẤT CẢ bốn người dùng (1,2,3,4) trong các cuộc hội thoại bạn đang tìm kiếm, nhưng thay vào đó, bạn đang tìm kiếm bất kỳ cuộc trò chuyện nào bao gồm bất kỳ sự kết hợp nào của (1,2,3,4) người dùng. Bạn có thể làm rõ điều này? – gillyspy

Trả lời

2

Đây là một ví dụ về truy vấn "set-trong-bộ".Đối với những, tôi muốn sử dụng group by với having khoản:

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 

Mỗi điều kiện của việc có điều khoản tương ứng với một trong năm điều kiện quy định trong câu hỏi:

  • dùng 1 là trong cuộc trò chuyện
  • dùng 2 là trong cuộc trò chuyện
  • sử dụng 3 là trong cuộc trò chuyện
  • sử dụng 4 là trong cuộc trò chuyện
  • không có người khác đang trong cuộc trò chuyện
+0

Làm việc như một sự quyến rũ. Cảm ơn rất nhiều. –

3

Ý tưởng là tính số người dùng riêng biệt trong một cuộc trò chuyện cụ thể. Nếu nó phù hợp với số lượng người dùng bạn thiết lập trong mệnh đề IN, sau đó bạn chắc chắn rằng chỉ có những người bạn tìm kiếm:

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

Lưu ý rằng điều này không nói chuyện không đầu ra mà chỉ có 3 trong số 4 người dùng xuất hiện. Nếu bạn cần những cuộc nói chuyện quá, sau đó:

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

Xin lỗi vì trả lời muộn. Tôi đã bị bệnh, vì vậy không phải ở nơi làm việc. Vâng, điều này hoạt động rất tốt, nhưng vấn đề duy nhất là nếu tôi có những điều sau đây: http://sqlfiddle.com/#!2/859360/1 vấn đề là nó cũng chọn cuộc hội thoại, nơi có nhiều người dùng hơn, vì vậy không chỉ được chọn. Nếu bạn hiểu. –

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 Fiddle Demo

Output

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

này sẽ cung cấp cho bạn tất cả các id trò chuyện và tình trạng của họ

Modified

| CONVERSATION | ALLIN | 
------------------------ 
|   1 |  0 | 
|   2 |  2 | 
|   3 |  0 | 
|   4 |  0 | 
+0

Cảm ơn rất nhiều. Điều này làm việc cho tôi. –

+0

Trong khi một giải pháp tốt, sự hiểu biết của tôi là bạn muốn loại trừ các cuộc hội thoại có người khác ngoài 4 được chỉ định của bạn? Hay tôi đã đọc câu hỏi ban đầu của bạn không chính xác? – Kickstart

2

Tôi nghĩ rằng đây là những gì bạn đang tìm kiếm:

SELECT cu.conversation 
FROM (select conversation, count(distinct user) usercnt 
     from conversations_users 
    group by conversation) t 
    JOIN conversations_users cu on t.conversation = cu.conversation 
WHERE cu.user in (1, 2, 3, 4) AND t.usercnt = 4 
GROUP BY cu.conversation 
HAVING COUNT(DISTINCT cu.user) = 4 

SQL Fiddle Demo

này sử dụng một subquery để xác định tổng số người sử dụng kết hợp với mỗi cuộc nói chuyện. Điều này là cần thiết để đảm bảo bạn không có nhiều người dùng hơn trong cuộc hội thoại hơn là 1,2,3 và 4.

2

Nếu tôi hiểu câu hỏi của bạn một cách chính xác, bạn có thể sử dụng này:

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 
2

Giả sử bạn có một bảng users cũng như:

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) 
    ) ; 

Nếu bạn không có một bảng users hoặc bạn không thích sử dụng nó (có thể biết tại sao nhưng dù sao), bạn có thể thay thế phần này:

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

với:

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

Truy vấn trên, trong khi được chung sẽ không thể rất hiệu quả trong MySQL (đổ lỗi cho các tổ đôi và tôi ưu hoa ngây thơ). Cách GROUP BY/COUNT có thể là một mức hiệu quả hơn - nhưng hãy kiểm tra với dữ liệu của bạn. Bạn cũng có thể tìm thấy nhiều cách khác (hơn 10) để trả lời loại câu hỏi này, trong câu trả lời này: How to filter SQL results in a has-many-through relation Một số trong số chúng không hoạt động trong MySQL mà nhiều người làm. Tôi hy vọng các truy vấn 5 và 6 ở đó khá hiệu quả trong MySQL (một mức độ hiệu quả hơn nhóm theo truy vấn).

trường hợp của bạn có một sự khác biệt, bạn muốn phân chia quan hệ chính xác trong khi đó câu hỏi/câu trả lời là về (đơn giản) bộ phận quan hệ, vì vậy bạn có thể viết 5 như thế này:

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)) 
+0

+1 bộ phận quan hệ 1.0. Bước tiếp theo: tìm (và liệt kê) các nhóm người dùng tương đương chia sẻ cùng một tập hợp các cuộc hội thoại ;-) – wildplasser

+0

Cảm ơn rất nhiều, nhưng bảng 'người dùng' là không cần thiết cho việc này. Vì ID của người dùng nằm trong bảng 'converstations_users'. –

+0

Tôi không nói là cần thiết. Nhưng nếu bạn có bảng người dùng, truy vấn sẽ dễ viết hơn. –

2

Giữ truy vấn và tham gia đơn giản và dễ đọc.

Vì sự tồn tại của người dùng 5, tôi đã giả định rằng bạn không yêu cầu TẤT CẢ 4 người dùng (1,2,3,4) trong cuộc hội thoại mà bạn đang tìm kiếm, nhưng thay vào đó, bất kỳ cuộc trò chuyện nào bao gồm bất kỳ kết hợp nào của 4 người dùng đó.

DEMO

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 */ 

Hãy cho tôi biết nếu bạn thay vì muốn các cuộc đàm thoại mà chỉ có tất cả 4 người có liên quan.

Bạn có muốn xóa các cuộc hội thoại khác không? Khi bạn nói "kết quả sẽ trở thành 0" là bạn đề cập đến số hàng hoặc giá trị của id cuộc hội thoại? Nếu đó là thứ hai sau đó sử dụng:

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 */ 
2
SELECT ID FROM CONVERSATIONS WHERE ID IN 
(SELECT CONVERSATIONS FROM CONVERSATION_USERS 
GROUP BY CONVERSATIONS HAVING COUNT(DISTINCT USER) >= 2) 
+1

Hãy cho tôi biết nếu điều này hoạt động trên mặt của bạn. cổ vũ .. – Androidz

+0

Cảm ơn. Nhưng điều này không bao gồm ID của người dùng ở bất cứ đâu. Vì vậy, nó chỉ có thể chọn tất cả các cuộc hội thoại, nơi có 2 người dùng trong? –

2

Nếu tôi đọc yêu cầu của bạn một cách chính xác bạn muốn có một id của bất kỳ cuộc nói chuyện mà những người duy nhất trong đó là (ví dụ) 1,2,3 và 4, và tất cả những người đó đều ở trong đó. Nếu không, bạn muốn 0 quay lại cuộc trò chuyện đó.

Nếu vậy thì cái gì đó như

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 

EDIT này - thay đổi phiên bản của các truy vấn trên chỉ mang lại id của cuộc hội thoại mà chỉ có những 4 người đã tham gia. Chơi xung quanh điều này có vẻ là một cách khá hiệu quả để làm điều đó.

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 
+0

Có lẽ tôi đã làm điều gì đó sai, nhưng khi tôi sử dụng điều này và in ra đầu ra, tôi chỉ nhận được những điều sau đây: '[CASE WHEN MatchCount = 4 VÀ UnMatchCount IS NULL THEN conversation.id ELSE 0 END] => 0' –

+1

Chỉ cần double kiểm tra nó và nó hoạt động. Nó đặt ra id nếu chỉ có 4 người trong cuộc trò chuyện, nếu không thì không. Bạn có thể dễ dàng đặt tên bí danh cho cột được trả về nếu bạn muốn. – Kickstart

Các vấn đề liên quan