FIND_IN_SET()vs IN()

时间:2010-11-11 15:13:11

标签: mysql

我的数据库中有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 个答案:

答案 0 :(得分:91)

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

attachedCompanyIDs是一个标量值,投放到INTcompanyID的类型)。

演员表仅返回第一个非数字的数字(在您的情况下为逗号)。

因此,

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)

答案 1 :(得分:13)

attachedCompanyIDs是一个大字符串,所以mysql尝试在其中找到公司将其强制转换为整数

当你在

中使用时

所以如果comapnyid = 1:

companyID IN ('1,2,3')

这是真的

但如果数字1不在第一位

 companyID IN ('2,3,1')

返回false

答案 2 :(得分: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

答案 3 :(得分:1)

因为第二个查询正在查找id为1 OR 2 OR 3的行, 第一个查询正在寻找一个以逗号分隔的值存在于companyID

这里的另一个问题是你没有加入你所在位置的公共密钥表,所以你会得到一个行变异= count(table1)* count(table2);

我的答案的第2部分确实存在您的问题。 (使用您的第二个查询)

答案 4 :(得分:-1)

让我解释何时使用FIND_IN_SET和何时使用IN。

让我们选择表A,其中包含名为&#34; aid&#34;,&#34; aname&#34;的列。 让我们选择表B,其中包含名为&#34; bid&#34;,&#34; bname&#34;,&#34; aids&#34;。

现在表A和表B中有虚拟值,如下所示。

表A

援助aname

1 Apple

2香蕉

3芒果

表B

bid bname aids

1 Apple 1,2

2香蕉2,1

3芒果3,1,2

enter code here

案例1:如果你想从表b中获取那些在aids列中有1个值的记录,那么你必须使用FIND_IN_SET。

查询:选择*来自A JOIN B ON FIND_IN_SET(A.aid,b.aids),其中A.aid = 1;

案例2:如果您想从表a中获取那些在援助列中存在1 OR 2 OR 3值的记录,那么您必须使用IN。

查询:从A JOIN B ON A.aid IN(b.aids)中选择*;

现在通过mysql查询了解你所需要的东西。

答案 5 :(得分:-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