2012-07-04 41 views
5

Tôi có bảng nàysql hiệu quả nhất để thu hồi

---------------- 
| X | Y | 
---------------- 
| a | 1 | 
| c | 6 | 
| e | 3 | 
| d | 6 | 
| c | 4 | 
| b | 1 | 
| a | 5 | 
| g | 1 | 
---------------- 

Khi tôi đưa ra một mảng [c, d] Tôi cần phải tìm "6" trong bảng trên. I E. cho mỗi tập hợp các phần tử tôi cần tìm giá trị Y được chia sẻ bởi tất cả các phần tử trong tập hợp nhưng chỉ khi không có phần tử nào khác (tức là phần tử không nằm trong mảng đã cho) chia sẻ giá trị đó. Số lượng các phần tử trong mảng không có giới hạn lý thuyết.

Ví dụ khác: cho [a, b, c] tôi không cần phải tìm gì cả. Đối với [a, b] tôi cũng không cần tìm gì cả (vì g cũng có mục nhập cho Y = 1, vì vậy đối với [a, b, g] tôi cần tìm "1").

Tôi có thể tất nhiên lặp qua mảng, truy vấn theo truy vấn, nhưng điều đó có vẻ như một cách không hiệu quả làm việc đó. Cách tốt nhất để làm điều này trong SQL là gì? Cảm ơn bạn.

+1

[Bạn đã thử làm gì?] (Http://whathaveyoutried.com) Bạn đang cố chuyển "mảng" vào truy vấn bằng danh sách IN? –

+0

Bạn đang sử dụng RMDB nào? –

+0

Tốt nhất là chủ quan. Nó phụ thuộc vào dữ liệu bạn có, cơ sở dữ liệu và phiên bản bạn sử dụng và những chỉ mục nào có sẵn. –

Trả lời

2

Đây là cách có các giá trị "truy vấn" của bạn trong một bảng riêng biệt.

create table t (x varchar(1), y int); 

insert into t (x, y) values ('a', 1); 
insert into t (x, y) values ('c', 6); 
insert into t (x, y) values ('e', 3); 
insert into t (x, y) values ('d', 6); 
insert into t (x, y) values ('c', 4); 
insert into t (x, y) values ('b', 1); 
insert into t (x, y) values ('a', 5); 
insert into t (x, y) values ('g', 1); 

create table q (x varchar(1)); 

insert into q (x) values ('a'); 
insert into q (x) values ('b'); 

select a.y from 
(
    select t.y 
    from t join q on (t.x = q.x) 
    group by t.y 
    having count(*) = (select count(*) from q) 
) a 
join t on (a.y = t.y) 
group by a.y 
having count(*) = (select count(*) from q) 

Here's an example SQLFiddle.

Giả định rằng bạn không thể có kết hợp trùng lặp.

Nếu bạn muốn thực hiện nó mà không có bảng thứ hai, bạn có thể thay thế select count(*) bằng số lượng giá trị mà bạn đang khớp trong danh sách IN và thay vì thực hiện kết nối trên truy vấn con bên trong, hãy sử dụng mệnh đề where.

select a.y from 
(
select t.y 
from t 
    where t.x in ('c', 'd') 
group by t.y 
having count(*) = 2 
) a 
join t on (a.y = t.y) 
group by a.y 
having count(*) = 2 
+0

Cần GROUP BY trong các truy vấn bên ngoài. Nhưng tốt đẹp bên suy nghĩ để làm điều đó với một bảng duy nhất. Tôi nghĩ rằng cách tiếp cận LEFT JOIN sẽ ít tốn kém hơn (một tham gia và một nhóm bởi, thay vì hai tham gia và hai nhóm), nhưng hiệu suất sẽ rất kém hoặc là cách vì vậy điều này có vẻ tốt với tôi! (+1) – MatBailie

+0

@Dems - Cảm ơn. MySQL không cần nhóm bởi vì nó ngầm nhóm, nhưng có lẽ nên bao gồm nó để dễ đọc. –

+0

@NWest: Tôi thực sự thích giải pháp thứ hai của bạn, đó là sắc sảo, dường như cũng hoạt động. Cảm ơn! – Vincent

2

Những loại truy vấn là bao giờ đặc biệt performant trên các bộ dữ liệu lớn và/hoặc nơi có nhiều giá trị cổ phiếu Y cùng các giá trị trong X.

Điều đó nói rằng, đây là phiên bản đơn giản của cách bình thường của tôi để làm việc đó ...

CREATE TEMPORARY TABLE params (
    item VARCHAR(16) 
) 
INSERT INTO params SELECT 'a' 
INSERT INTO params SELECT 'b' 
INSERT INTO params SELECT 'g' 


SELECT 
    yourTable.Y 
FROM 
    yourTable 
LEFT JOIN 
    params 
    ON yourTable.X = params.item 
GROUP BY 
    yourTable.Y 
HAVING 
    COUNT(DISTINCT yourTable.X) = COUNT(DISTINCT params.item) 


một tùy chọn khác mà không cần một bảng thông số, mặc dù tôi không nghĩ rằng đó là performant thêm ...

SELECT 
    y 
FROM 
    yourTable 
GROUP BY 
    y 
HAVING 
    COUNT(DISTINCT x) = COUNT(DISTINCT CASE WHEN x IN ('a', 'b', 'g') THEN x ELSE NULL END) 

Điều này không có tham gia, nhưng là chi phí thực hiện quét toàn bộ bảng.

+2

Điều đó cũng mang lại hiệu quả cho {a, b} count = 2. Ví dụ của anh ta với {a, b} (ví dụ: không có g) không nên mang lại bất cứ điều gì. (nếu tôi hiểu chính xác) BTW: điều này có vẻ như sudeku ... – wildplasser

+0

@ wildplasser - Tốt tại chỗ, tôi đã không đọc rằng bit *** oops ***. Cập nhật để LEFT JOIN trên một bảng và cập nhật HAVING để nói rằng cả hai bên phải có cùng một số mục riêng biệt. – MatBailie

+0

@ Dems, giải pháp này có lẽ là tốt vì vậy cảm ơn rất nhiều, nhưng tôi đang sử dụng symfony và DQL vì vậy tôi muốn tránh xa việc tạo ra các bảng param. Và tôi chắc chắn không muốn quét toàn bộ bảng. – Vincent

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