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
attachCompanyIDs - одна велика струна, тому mysql намагайся знайти компанію в цьому складі для цілого числа
Хаїм Евгі

Я думаю, що це найкращий приклад mysqltutorial.org/mysql-find_in_set
Шурвір Морі

Відповіді:


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

attachedCompanyIDs- скалярне значення, яке вкидається INT(тип 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
Дякую за пояснення. Я не розумів, що приєднане полеCompanyIDs було передано на INT. Чи існує спосіб обходу цього в MySQL? FIND_IN_SETпрацює, але не використовує індекси та може бути повільним з великою кількістю інформації в таблиці компанії.
Ракета 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

attachCompanyIDs - це одна велика рядок, тому mysql спробуйте знайти компанію в цьому складі для цілого числа

коли ви використовуєте де в

тому якщо comapnyid = 1:

companyID IN ('1,2,3')

це повернення правдиве

але якщо число 1 не на першому місці

 companyID IN ('2,3,1')

її повернення помилкове


3

Щоб отримати назву всіх пов’язаних компаній, а не на основі конкретного ідентифікатора.

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

1

оскільки другий запит шукає рядки з ідентифікатором 1 АБО 2 АБО 3, перший запит шукає одне з знаків, розділених комами, щоб існувати в companyID,

і ще одна проблема полягає в тому, що ви не приєднуєтесь до таблиць на загальному ключі у вашому місці, де ви отримаєте мутацію рядків, що = count (table1) * count (table2);

Ваша проблема дійсно існує в частині моєї відповіді. (зі своїм другим запитом)


В обох таблицях є більше рядків, ніж я показую. В обох таблицях існує ідентифікатор користувача, на якого ви зареєстровані, приєднавшись до цієї допомоги?
Ракета Hazmat

Добре, вам потрібно що-небудь змінити, якщо ваш перший запит не повертає очікуваних результатів. Якщо перший запит повертає потрібні результати, то насправді це не проблема. Я думав, що вам просто цікаво, чому два не показують однакового результату.
суперфро

@superfro, мені цікаво, чому два не показують однакового результату.
Ракета Hazmat

ваш другий запит використовує де IN (значення), де частина 'значень' походить з таблиці, а її рядок. Рядок оцінюється як істинний bool, який = 1, тому він показує лише перший рядок.
суперфро

1
Якщо ви турбуєтесь про ефективність роботи, ви, ймовірно, повинні подумати про зміну структури вашої бази даних. Ви можете додати спільну таблицю, яка містить 2 значення, order_ID та company_ID, замість того, щоб використовувати список обмежених комами в таблиці замовлень. Це дозволить вам вибрати ім’я з компанії left left order_companies на company.company_ID = order_companies.company_ID ліві замовлення приєднання на order_companies.order_ID = order.order_ID, де order.order_ID = 1; Це використовувало б індекси.
суперфро

-1

Дозвольте мені пояснити, коли використовувати FIND_IN_SET та коли використовувати IN.

Візьмемо таблицю А, яка містить стовпці з назвою "допомога", "анам". Візьмемо таблицю B, яка містить стовпці з назвою "ставка", "bname", "aids".

Тепер у таблиці А та Таблиці Б є наведені нижче дані.

Таблиця А

допоміжний анам

1 Яблуко

2 Банан

3 Манго

Таблиця В

посібники зі ставки

1 Яблуко 1,2

2 Банан 2,1

3 Манго 3,1,2

enter code here

Випадок1: якщо ви хочете отримати ті записи з таблиці b, у якій 1 стовпчик присутній у стовпцях посібників, вам доведеться використовувати FIND_IN_SET.

Запит: виберіть * з A JOIN B ON FIND_IN_SET (A.aid, b.aids), де A.aid = 1;

Випадок2: якщо ви хочете отримати ці записи з таблиці а, яка має значення 1 АБО 2 АБО 3 в стовпцях допомоги, вам доведеться використовувати IN.

Запит: виберіть * з 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.