FIND_IN_SET () 대 IN ()


125

데이터베이스에 2 개의 테이블이 있습니다. 하나는 주문을위한 것이고 다른 하나는 회사를위한 것입니다.

주문의 구조는 다음과 같습니다.

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

그리고 회사는 다음과 같은 구조를 가지고 있습니다 :

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

주문 회사 이름을 얻기 위해 다음과 같은 쿼리를 수행 할 수 있습니다.

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

해당 쿼리는 제대로 작동하지만 다음 쿼리는 작동하지 않습니다.

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

첫 번째 쿼리는 왜 작동하지만 두 번째 쿼리는 작동하지 않습니까?

첫 번째 쿼리는 다음을 반환합니다.

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

두 번째 쿼리는 다음 만 반환합니다.

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

왜 이것이 첫 번째 쿼리가 모든 회사를 반환하지만 두 번째 쿼리는 첫 번째 쿼리 만 반환합니까?


3
attachedCompanyIDs 이렇게 정수로이 자사의 캐스트에 회사를 찾아보십시오 MySQL은, 하나의 큰 문자열
하임 Evgi

이것이 가장 좋은 예라고 생각합니다 mysqltutorial.org/mysql-find_in_set
Shurvir Mori

답변:


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

attachedCompanyIDsINT(유형 companyID) 으로 캐스팅 된 스칼라 값입니다 .

캐스트는 숫자가 아닌 첫 번째 숫자까지만 숫자를 반환합니다 (귀하의 경우 쉼표).

그러므로,

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

에서 PostgreSQL, 당신은 배열에 문자열을 주조 (또는 처음부터 배열로 저장) 수 :

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

그리고 이것도 인덱스를 사용합니다 companyID.

불행히도 MySQL후자는 배열을 지원하지 않기 때문에 작동 하지 않습니다.

이 기사가 흥미로울 수 있습니다 (참조 #2).

최신 정보:

쉼표로 구분 된 목록 (예 :) 이하의 값 수에 대한 합리적인 제한 5이있는 경우이 쿼리를 사용해보십시오.

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
설명 주셔서 감사합니다. attachedCompanyIDs 필드가 INT로 전송되었음을 알지 못했습니다. MySQL 에서이 문제를 해결할 방법이 있습니까? FIND_IN_SET작동하지만 인덱스를 사용하지 않으며 Company 테이블에 많은 정보가 있으면 느려질 수 있습니다.
로켓 Hazmat

1
그 업데이트를 설명 할 수 있습니까? 그것이 작동하는 것처럼 보이기 때문에 정확히 무엇을합니까?
로켓 Hazmat

1
@Rocket : pos시작 부분에서 항목을 제거 CVS하고 나머지를 정수로 캐스팅합니다.
Quassnoi

9
10 things in MySQL (that won’t work as expected)
NullPointer 님의

@Quassnoi, 왜 쓰 CROSS JOIN십니까? MySQL에서 모두 동일 하지 않습니까?
Pacerier

13

attachedCompanyIDs는 하나의 큰 문자열이므로 mysql은 정수로 캐스팅 된 회사를 찾으려고합니다.

어디에서 사용할 때

따라서 comapnyid = 1 인 경우 :

companyID IN ('1,2,3')

이것은 사실이다

그러나 숫자 1이 처음이 아닌 경우

 companyID IN ('2,3,1')

그 반환 거짓


3

특정 ID를 기반으로하지 않고 모든 관련 회사 이름을 가져옵니다.

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

1

두 번째 쿼리는 id가 1 또는 2 OR 3 인 행을 찾기 때문에 첫 번째 쿼리는 companyID에 존재하는 쉼표로 구분 된 값 중 하나를 찾고 있습니다.

그리고 여기서 또 다른 문제는 당신이있는 곳의 공통 키에서 테이블을 조인하지 않기 때문에 행의 돌연변이를 얻을 것입니다. = count (table1) * count (table2);

귀하의 문제는 실제로 제 답변의 2 부에 있습니다. (두 번째 쿼리 포함)


두 테이블에 표시되는 것보다 많은 행이 있습니다. 두 테이블 모두 로그인 한 사용자의 ID가 해당 도움말에 참여합니까?
로켓 Hazmat

첫 번째 쿼리가 예상 결과를 반환하지 않으면 아무것도 변경하면됩니다. 첫 번째 쿼리가 원하는 결과를 반환하면 실제로 문제가 없습니다. 나는 왜 2가 같은 결과를 보이지 않는지 궁금하다고 생각했습니다.
superfro

@ superfro, 나는 왜 2가 같은 결과를 보이지 않는지 궁금합니다.
로켓 Hazmat

두 번째 쿼리는 '값'부분이 테이블에서 나오는 where IN (값)과 문자열을 사용합니다. 문자열은 부울 true로 평가되며 = 1이므로 첫 번째 행만 표시합니다.
superfro

1
성능이 걱정되면 데이터베이스 구조 변경에 대해 생각해야합니다. 주문 테이블에서 쉼표로 구분 된 목록을 사용하는 대신 order_ID 및 company_ID라는 2 개의 값이 포함 된 연결 테이블을 추가 할 수 있습니다. 이를 통해 company.company_ID = order_companies.company_ID의 왼쪽 회사 주문 왼쪽 order_companies에서 order_companies의 이름을 선택할 수 있습니다. order_companies.order_ID = order.order_ID, orders.order_ID = 1; 이것은 인덱스를 사용합니다.
superfro

-1

FIND_IN_SET 사용시기 및 IN 사용시기를 설명하겠습니다.

"aid", "aname"이라는 열이있는 테이블 A를 보자. "bid", "bname", "aids"라는 열이있는 테이블 B를 보자.

이제 아래 표 A와 표 B에 더미 값이 있습니다.

표 A

이름을 돕다

사과 1 개

2 바나나

3 망고

표 B

입찰 이름 보조기구

사과 1,2

2 바나나 2,1

3 망고 3,1,2

enter code here

사례 1 : 보조 열에 1 값이있는 테이블 b에서 해당 레코드를 가져 오려면 FIND_IN_SET을 사용해야합니다.

쿼리 : select * from A JOIN B ON FIND_IN_SET (A.aid, b.aids) 여기서 A.aid = 1;

사례 2 : 보조 열에 1 OR 2 OR 3 값이있는 테이블 a에서 해당 레코드를 가져 오려면 IN을 사용해야합니다.

쿼리 : select * from A JOIN B ON A.aid IN (b.aids);

이제 mysql 쿼리를 통해 필요한 것을 얻을 수 있습니다.


이 질문은 이미 해결되었습니다. 또한 IN을 사용한 두 번째 예제는 효과가 없다고 생각합니다. 기본적으로 내가 처음에 해결하려고했던 문제였습니다.
로켓 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
SO에 오신 것을 환영합니다! 설명이없는 코드는 거의 도움이되지 않습니다. 이 경우 "왜 ...?"라는 질문에 대답하지도 않습니다. 또한이 특정 질문에는 이미 합격 (> 80 투표!) 답변을 제공하는 승인 된 답변이 있습니다. 새로운 사용자는 답이없는 질문에 집중하거나 좋은 질문을하는 것이 가장 좋습니다.
cfi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.