FIND_IN_SET () против IN () - PullRequest
       36

FIND_IN_SET () против IN ()

117 голосов
/ 11 ноября 2010

У меня есть 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

Почему это так, почему первый запрос возвращает всекомпании, но второй запрос возвращает только первый?

Ответы [ 6 ]

92 голосов
/ 11 ноября 2010
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)
13 голосов
/ 11 ноября 2010

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

, когда вы используете где в

, поэтому, если comapnyid = 1:

companyID IN ('1,2,3')

это возвращаемое значение true

, но если число 1 не на первом месте

 companyID IN ('2,3,1')

, его возвращаемое значение false

3 голосов
/ 16 февраля 2016

Чтобы получить название всех связанных компаний, не основываясь на определенном идентификаторе

SELECT 
    (SELECT GROUP_CONCAT(cmp.cmpny_name) 
    FROM company cmp 
    WHERE FIND_IN_SET(cmp.CompanyID, odr.attachedCompanyIDs)
    ) AS COMPANIES
FROM orders odr
1 голос
/ 11 ноября 2010

, так как второй запрос ищет строки с идентификатором 1 ИЛИ 2 ИЛИ 3, первый запрос ищет одно из значений, разделенных запятыми, которые существуют в companyID,

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

Ваша проблема действительно существует со второй частью моегоответ.(с вашим вторым запросом)

0 голосов
/ 15 сентября 2017

Позвольте мне объяснить, когда использовать FIND_IN_SET и когда использовать IN.

Давайте возьмем таблицу A, в которой есть столбцы с именами "aid", "aname".Давайте возьмем таблицу B, в которой есть столбцы с именами "bid", "bname", "aids".

Теперь в таблице A и таблице B есть фиктивные значения, как показано ниже.

Таблица A

Помощь Aname

1 Яблоко

2 Банан

3 Манго

Таблица B

ставка BNAME СПИД

1 яблоко 1,2

2 банан 2,1

3 манго 3,1,2

enter code here

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

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

Case2: если вы хотите получить те записи из таблицы a, в столбцах помощи которых присутствует значение 1 или 2 или 3, вам нужно использовать IN.

Запрос: select * from A JOINB ON A.aid IN (b.aids);

Теперь вот вам то, что вам нужно с помощью запроса MySQL.

0 голосов
/ 21 октября 2016
SELECT o.*, GROUP_CONCAT(c.name) FROM Orders AS o , Company.c
    WHERE FIND_IN_SET(c.CompanyID , o.attachedCompanyIDs) GROUP BY o.attachedCompanyIDs
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...