FIND_IN_SET () so với IN ()


125

Tôi có 2 bảng trong cơ sở dữ liệu của mình. Một là cho đơn đặt hàng, và một là cho các công ty.

Đơn đặt hàng có cấu trúc này:

OrderID     |     attachedCompanyIDs
------------------------------------
   1                     1,2,3
   2                     2,4

Và Công ty có cấu trúc này:

CompanyID      |        name
--------------------------------------
    1                 Company 1
    2                 Another Company
    3                 StackOverflow
    4                 Nothing

Để có được tên công ty của một đơn đặt hàng, tôi có thể thực hiện một truy vấn như sau:

SELECT name FROM orders,company
WHERE orderID = 1 AND FIND_IN_SET(companyID, attachedCompanyIDs)

Truy vấn đó hoạt động tốt, nhưng truy vấn sau thì không.

SELECT name FROM orders,company
WHERE orderID = 1 AND companyID IN (attachedCompanyIDs)

Tại sao truy vấn đầu tiên hoạt động mà không phải là truy vấn thứ hai?

Truy vấn đầu tiên trả về:

name
---------------
Company 1
Another Company
StackOverflow

Truy vấn thứ hai chỉ trả về:

name
---------------
Company 1

Tại sao lại như vậy, tại sao truy vấn đầu tiên trả về tất cả các công ty, nhưng truy vấn thứ hai chỉ trả về truy vấn đầu tiên?



Tôi nghĩ rằng đây là ví dụ tốt nhất mysqltutorial.org/mysql-find_in_set
Shurvir Mori

Câu trả lời:


100
SELECT  name
FROM    orders,company
WHERE   orderID = 1
        AND companyID IN (attachedCompanyIDs)

attachedCompanyIDslà một giá trị vô hướng được đúc thành INT(loạicompanyID ).

Các diễn viên chỉ trả về các số lên đến chữ số đầu tiên (dấu phẩy trong trường hợp của bạn).

Như vậy

companyID IN ('1,2,3')  companyID IN (CAST('1,2,3' AS INT))  companyID IN (1)

Trong PostgreSQL, bạn có thể truyền chuỗi thành mảng (hoặc lưu nó dưới dạng một mảng ở vị trí đầu tiên):

SELECT  name
FROM    orders
JOIN    company
ON      companyID = ANY (('{' | attachedCompanyIDs | '}')::INT[])
WHERE   orderID = 1

và điều này thậm chí sẽ sử dụng một chỉ mục trên companyID .

Thật không may, điều này không hoạt động trong MySQL vì sau này không hỗ trợ mảng.

Bạn có thể thấy bài viết này thú vị (xem #2):

Cập nhật:

Nếu có một số giới hạn hợp lý về số lượng giá trị trong danh sách được phân tách bằng dấu phẩy (giả sử, không quá 5), vì vậy bạn có thể thử sử dụng truy vấn này:

SELECT  name
FROM    orders
CROSS JOIN
        (
        SELECT  1 AS pos
        UNION ALL
        SELECT  2 AS pos
        UNION ALL
        SELECT  3 AS pos
        UNION ALL
        SELECT  4 AS pos
        UNION ALL
        SELECT  5 AS pos
        ) q
JOIN    company
ON      companyID = CAST(NULLIF(SUBSTRING_INDEX(attachedCompanyIDs, ',', -pos), SUBSTRING_INDEX(attachedCompanyIDs, ',', 1 - pos)) AS UNSIGNED)

3
Cảm ơn đã giải thích. Tôi đã không nhận ra trường đính kèm công cụ được gắn vào INT. Có cách nào khác trong điều này trong MySQL không? FIND_IN_SEThoạt động, nhưng không sử dụng chỉ mục và có thể chậm với nhiều thông tin trong bảng Công ty.
Tên lửa Hazmat

1
Bạn có thể giải thích bản cập nhật đó? Chính xác thì nó làm gì, bởi vì nó dường như hoạt động.
Rocket Hazmat

1
@Rocket: nó postách các mục từ đầu CVSvà biến phần còn lại thành số nguyên.
Quassnoi

9
Thumbs up (y) cho10 things in MySQL (that won’t work as expected)
NullPulum

@Quassnoi, tại sao bạn viết CROSS JOIN? Không phải tất cả đều giống nhau trong MySQL sao?
Pacerier

13

AttachCompanyIDs là một chuỗi lớn, vì vậy mysql cố gắng tìm công ty trong chuỗi này thành số nguyên

khi bạn sử dụng ở đâu trong

vì vậy nếu comapnyid = 1:

companyID IN ('1,2,3')

đây là sự thật

nhưng nếu số 1 không ở vị trí đầu tiên

 companyID IN ('2,3,1')

nó trở lại sai


3

Để có được tất cả các tên công ty liên quan, không dựa trên Id cụ thể.

SELECT 
    (SELECT GROUP_CONCAT(cmp.cmpny_name) 
    FROM company cmp 
    WHERE FIND_IN_SET(cmp.CompanyID, odr.attachedCompanyIDs)
    ) AS COMPANIES
FROM orders odr

1

bởi vì truy vấn thứ hai đang tìm kiếm các hàng có id 1 OR 2 OR 3, nên truy vấn đầu tiên đang tìm kiếm một trong các giá trị được phân cách bằng dấu phẩy để tồn tại trong companyID,

và một vấn đề khác ở đây là bạn không tham gia các bảng trên một khóa chung ở nơi bạn sẽ nhận được một sự đột biến của các hàng = Count (bảng1) * Count (bảng2);

Vấn đề của bạn thực sự tồn tại với phần 2 của câu trả lời của tôi. (với truy vấn thứ hai của bạn)


Có nhiều hàng trong cả hai bảng hơn tôi đang hiển thị. Trong cả hai bảng, có ID của người dùng mà bạn đã đăng nhập, sẽ tham gia vào trợ giúp đó?
Tên lửa Hazmat

Vâng, bạn chỉ cần thay đổi bất cứ điều gì nếu truy vấn đầu tiên của bạn không trả về kết quả mong đợi. Nếu truy vấn đầu tiên trả về kết quả mà bạn muốn thì thực sự không có vấn đề gì. Tôi nghĩ rằng bạn chỉ tò mò tại sao 2 không hiển thị kết quả tương tự.
superfro

@superfro, tôi tò mò về lý do tại sao 2 không hiển thị kết quả tương tự.
Tên lửa Hazmat

truy vấn thứ hai của bạn đang sử dụng một IN (giá trị) trong đó phần 'giá trị' đến từ bảng và chuỗi của nó. Chuỗi đang được đánh giá là bool true which = 1, đó là lý do tại sao nó chỉ hiển thị hàng đầu tiên.
superfro

1
Nếu bạn lo lắng về hiệu suất, có lẽ bạn nên nghĩ về việc thay đổi cấu trúc cơ sở dữ liệu của mình. Bạn có thể thêm một bảng chung chứa 2 giá trị, order_ID và company_ID thay vì sử dụng danh sách được phân cách bằng dấu phẩy trong bảng thứ tự. Điều này sẽ cho phép bạn chọn tên từ công ty bên trái tham gia order_compiances trên company.company_ID = order_compiances.company_ID bên trái tham gia đơn hàng trên order_compiances.order_ID = order.order_ID trong đó order.order_ID = 1; Điều này sẽ sử dụng các chỉ mục.
superfro

-1

Hãy để tôi giải thích khi nào nên sử dụng FIND_IN_SET và Khi nào nên sử dụng IN.

Hãy lấy bảng A có các cột có tên là "viện trợ", "aname". Hãy lấy bảng B có các cột có tên là "giá thầu", "bname", "aids".

Bây giờ có các giá trị giả trong Bảng A và Bảng B như dưới đây.

Bảng A

hỗ trợ aname

1 quả táo

2 quả chuối

3 quả xoài

Bảng B

hỗ trợ tên thầu

1 quả táo 1,2

2 quả chuối 2.1

3 Xoài 3,1,2

enter code here

Case1: nếu bạn muốn lấy các bản ghi đó từ bảng b có 1 giá trị hiện diện trong các cột hỗ trợ thì bạn phải sử dụng FIND_IN_SET.

Truy vấn: chọn * từ A THAM GIA B TRÊN FIND_IN_SET (A.aid, b.aids) trong đó A.aid = 1;

Case2: nếu bạn muốn lấy các bản ghi đó từ bảng a có giá trị 1 HOẶC 2 HOẶC 3 trong các cột trợ giúp thì bạn phải sử dụng IN.

Truy vấn: chọn * từ A THAM GIA B ON A.aid IN (b.aids);

Bây giờ ở đây cho bạn biết những gì bạn cần thông qua truy vấn mysql.


Câu hỏi này đã được giải quyết. Ngoài ra tôi không nghĩ ví dụ thứ hai của bạn, với IN, hoạt động ... về cơ bản đó là vấn đề tôi đã cố gắng giải quyết lúc đầu.
Tên lửa Hazmat

-2
SELECT o.*, GROUP_CONCAT(c.name) FROM Orders AS o , Company.c
    WHERE FIND_IN_SET(c.CompanyID , o.attachedCompanyIDs) GROUP BY o.attachedCompanyIDs

6
Chào mừng đến với SO! Mã mà không giải thích là hiếm khi hữu ích. Trong trường hợp này, nó thậm chí không cố gắng trả lời câu hỏi "Tại sao ...?". Ngoài ra, xin lưu ý rằng câu hỏi cụ thể này đã có câu trả lời được chấp nhận, câu trả lời được đón nhận (> 80 phiếu!). Là người dùng mới, tốt nhất nên tập trung vào các câu hỏi chưa được trả lời và / hoặc tự hỏi những câu hỏi hay.
cfi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.