2012-05-07 37 views
24

Tôi có một cột ở một trong bảng của tôi, nơi tôi lưu trữ nhiều id được phân tách bằng dấu phẩy. Có cách nào để tôi có thể sử dụng giá trị của cột này trong mệnh đề "IN" của truy vấn hay không.Giá trị được phân tách bằng dấu phẩy trong mệnh đề "IN" của MySQL

Cột (city) có giá trị như 6,7,8,16,21,2

tôi cần phải sử dụng như

select * from table where e_ID in (Select city from locations where e_Id=?) 

Tôi hài lòng với câu trả lời của Crozin, nhưng tôi mở để gợi ý, quan điểm và các tùy chọn.

Hãy thoải mái chia sẻ quan điểm của bạn.

Trả lời

27

Xây dựng trên FIND_IN_SET() ví dụ từ @Jeremy Smith, bạn có thể thực hiện với tham gia để bạn không phải chạy truy vấn phụ.

SELECT * FROM table t 
JOIN locations l ON FIND_IN_SET(t.e_ID, l.city) > 0 
WHERE l.e_ID = ? 

này được gọi để thực hiện rất kém, vì nó đã làm bảng quét, đánh giá FIND_IN_SET() chức năng cho mỗi sự kết hợp của các hàng trong tablelocations. Nó không thể sử dụng một chỉ mục, và không có cách nào để cải thiện nó.

Tôi biết bạn đã nói rằng bạn đang cố gắng tạo ra thiết kế cơ sở dữ liệu tồi nhất, nhưng bạn phải hiểu mức độ nghiêm trọng của điều này.

Giải thích: Giả sử tôi yêu cầu bạn tra cứu tất cả mọi người trong danh bạ điện thoại có chữ cái đầu tiên, giữa hoặc cuối cùng là "J."Không có cách nào để thứ tự sắp xếp của cuốn sách giúp ích trong trường hợp này, vì bạn phải quét từng trang đơn lẻ.

Giải pháp LIKE được đưa ra bởi @fthiella có vấn đề tương tự về hiệu suất.

Cũng xem câu trả lời của tôi để Is storing a delimited list in a database column really that bad? cho những cạm bẫy khác của cách này lưu trữ dữ liệu denormalized

Nếu bạn có thể tạo một bảng bổ sung để lưu trữ một chỉ mục, bạn có thể lập bản đồ địa điểm để mỗi mục trong danh sách thành phố.:

CREATE TABLE location2city (
location INT, 
city INT, 
PRIMARY KEY (location, city) 
); 

Giả sử bạn có một bảng tra cứu cho tất cả các thành phố có thể (không chỉ những người được đề cập trong table), bạn có thể chịu đựng sự thiếu hiệu quả một thời gian để tạo ra các bản đồ:

INSERT INTO location2city (location, city) 
    SELECT l.e_ID, c.e_ID FROM cities c JOIN locations l 
    ON FIND_IN_SET(c.e_ID, l.city) > 0; 

Bây giờ bạn có thể chạy một truy vấn nhiều hiệu quả hơn để tìm các mục nhập trong số table:

SELECT * FROM location2city l 
JOIN table t ON t.e_ID = l.city 
WHERE l.e_ID = ?; 

Điều này có thể tận dụng chỉ mục. Bây giờ bạn chỉ cần quan tâm rằng bất kỳ INSERT/UPDATE/DELETE nào của các hàng trong locations cũng chèn các hàng ánh xạ tương ứng trong location2city.

+0

+1 cho giải thích rõ ràng như vậy –

+0

Tôi đã sử dụng tùy chọn FIND_IN_SET. Đây là câu trả lời tốt nhất có sẵn tại đây. Thay vì tham gia và truy vấn phụ, điều này phù hợp nhất. Nhưng trừ khi nó rất quan trọng không cố gắng lưu trữ các giá trị được phân cách bằng dấu phẩy vào một cột. Điều đó không phù hợp với RDBMS. Tôi đã sử dụng điều đó vì bảng và dữ liệu đã được tạo ra nhiều trước đây và có rất nhiều dữ liệu mà tôi không muốn mạo hiểm bởi Chuyển dữ liệu. – Vijay

+0

@Vijay: Cảm ơn rất nhiều vì gợi ý của bạn và Cảm ơn bạn rất nhiều Bill. –

25

Từ quan điểm của MySQL, bạn không lưu trữ nhiều id được phân tách bằng dấu phẩy - bạn đang lưu trữ giá trị văn bản, có cùng meaing giống như "Hello World" hoặc "Tôi thích bánh!" - tức là nó không có bất kỳ sự rên rỉ nào.

Điều bạn phải làm là tạo một bảng riêng biệt sẽ liên kết hai đối tượng từ cơ sở dữ liệu với nhau. Đọc thêm về mối quan hệ many-to-many hoặc one-to-many (tùy thuộc vào yêu cầu của bạn) trong cơ sở dữ liệu dựa trên SQL.

+0

Cảm ơn đề nghị của bạn và tôi chấp nhận rằng thiết kế là nghèo, nhưng tôi có một số trở ngại mà tôi không thể thay đổi. Có cách nào để thực hiện điều này .... –

+0

@Crozin, Rõ ràng là anh ấy yêu cầu một cách giải quyết .......... – Pacerier

5

IN có các hàng để lấy cột phân cách bằng dấu phẩy để tìm kiếm sẽ không làm những gì bạn muốn nhưng nếu bạn cung cấp dữ liệu như thế này ('1', '2', '3') thì nó sẽ hoạt động được. này trong lĩnh vực của bạn bất cứ điều gì bạn chèn vào cột nó sẽ mất toàn bộ điều như một chuỗi.

24

Thay vì sử dụng IN trên truy vấn của bạn, sử dụng FIND_IN_SET (docs):

SELECT * FROM table 
WHERE 0 < FIND_IN_SET(e_ID, (
      SELECT city FROM locations WHERE e_ID=?)) 

Các hãy cẩn thận thông thường về hình thức bình thường đầu tiên được áp dụng (cơ sở dữ liệu không nên lưu trữ nhiều giá trị trong một cột duy nhất), nhưng nếu bạn đang mắc kẹt với nó, sau đó tuyên bố trên sẽ giúp đỡ.

+0

Cảm ơn câu trả lời của bạn.Điều này rất hữu ích. Bất kỳ ánh sáng nào về hiệu suất? –

+2

Hiệu suất sẽ tương đối kém, vì 'FIND_IN_SET' không thể sử dụng bất kỳ chỉ mục nào. Nếu bạn lo lắng về hiệu suất (và có quyền làm như vậy), bạn thực sự sẽ cần phải chia cột 'locations'.'city' thành một bảng khác bằng cách sử dụng 1nf và sau đó bạn có thể sử dụng các chỉ mục để giúp hiệu suất của bạn. –

+1

Khi @JeremySmyth nói, lý thuyết lưu trữ một danh sách các số được phân tách bằng dấu phẩy trong một cột vi phạm quy tắc cơ bản về thiết kế cơ sở dữ liệu, quy tắc nguyên tử (một ô, một giá trị). Vì vậy, nó sẽ là tốt hơn để thiết kế lại điều đó. NHƯNG, nếu các ràng buộc không cho phép điều đó, hãy đi với 'FIND_IN_SET'. – inigomedina

21

này không sử dụng tại khoản, nhưng nó phải làm những gì bạn cần:

Select * 
from table 
where 
    CONCAT(',', (Select city from locations where e_Id=?), ',') 
    LIKE 
    CONCAT('%,', e_ID, ',%') 

nhưng bạn phải chắc chắn rằng e_ID không chứa bất kỳ dấu phẩy hoặc bất kỳ nhân vật vui vẻ.

ví dụ:

CONCAT(',', '6,7,8,16,21,2', ',') returns ',6,7,8,16,21,2,' 

e_ID=1 --> ',6,7,8,16,21,2,' LIKE '%,1,%' ? FALSE 
e_ID=6 --> ',6,7,8,16,21,2,' LIKE '%,6,%' ? TRUE 
e_ID=21 --> ',6,7,8,16,21,2,' LIKE '%,21,%' ? TRUE 
e_ID=2 --> ',6,7,8,16,21,2,' LIKE '%,2,%' ? TRUE 
e_ID=3 --> ',6,7,8,16,21,2,' LIKE '%,3,%' ? FALSE 
etc. 
+0

+1 @fthiella đơn giản nhất của tất cả làm cho asnwers tốt nhất :) và tôi thích mẫu mã trong câu trả lời và câu hỏi: D – bonCodigo

+0

có ... chuyển đổi trường so sánh từ một int thành một chuỗi, và sử dụng như thế nào. Những công việc này. Tôi biết vấn đề là do thiết kế DB xấu, nhưng tiếc là đôi khi chúng tôi phải làm việc với thiết kế DB xấu này đôi khi không có đủ thời gian để tái cấu trúc nó. –

7

này trong cho oracle ..here chuỗi nối được thực hiện bằng wm_concat

select * from table where e_ID in (Select wm_concat(city) from locations where e_Id=?) 

yes i đồng ý với raheel shan .. để đặt này "trong" khoản chúng ta cần phải làm cho điều đó cột vào hàng dưới đây mã một làm công việc đó.

select * from table where to_char(e_ID) 
in (
    select substr(city,instr(city,',',1,rownum)+1,instr(city,',',1,rownum+1)-instr(city,',',1,rownum)-1) from 
    (
    select ','||WM_CONCAT(city)||',' city,length(WM_CONCAT(city))-length(replace(WM_CONCAT(city),','))+1 CNT from locations where e_Id=?) TST 
    ,ALL_OBJECTS OBJ where TST.CNT>=rownum 
    ) ; 
6

bạn nên sử dụng

FIND_IN_SET Returns vị trí của giá trị trong chuỗi các giá trị bằng dấu phẩy

mysql> SELECT FIND_IN_SET('b','a,b,c,d'); 
    -> 2 
6

Không biết nếu điều này là những gì bạn muốn đạt được. Với MySQL có tính năng để nối các giá trị từ một nhóm GROUP_CONCAT

Bạn có thể thử một cái gì đó như thế này:

select * from table where e_ID in (Select GROUP_CONCAT(city SEPARATOR ',') from locations where e_Id=?) 
5

Bạn cần phải "TÁCH" các giá trị cột thành phố. Nó sẽ như thế nào:

SELECT * 
    FROM table 
WHERE e_ID IN (SELECT TO_NUMBER(
           SPLIT_STR(city /*string*/ 
              , ',' /*delimiter*/ 
              , 1 /*start_position*/ 
              ) 
           ) 
        FROM locations); 

Bạn có thể đọc thêm về split_str MySQL chức năng ở đây: http://blog.fedecarg.com/2009/02/22/mysql-split-string-function/

Ngoài ra, tôi đã sử dụng chức năng TO_NUMBER của Oracle đây. Vui lòng thay thế bằng chức năng MySQL thích hợp.

2

Bạn có thể tạo một tuyên bố chuẩn bị động như this

set @sql = concat('select * from city where city_id in (', 
        (select cities from location where location_id = 3), 
        ')'); 
prepare in_stmt from @sql; 
execute in_stmt; 
deallocate prepare in_stmt; 
Các vấn đề liên quan