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尝试在此公司中将其转换为整数
Haim Evgi 2010年

我认为这是最好的示例mysqltutorial.org/mysql-find_in_set
Shurvir Mori

Answers:


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
感谢您的解释。我没有意识到AttachedCompanyIDs字段已强制转换为INT。在MySQL中有什么方法可以解决此问题? FIND_IN_SET可以,但是不使用索引,并且Company表中的很多信息可能会变慢。
火箭Hazmat

1
你能解释一下这个更新吗?这样做到底是做什么的,因为它似乎有效。
火箭Hazmat

1
@Rocket:它将pos从的开头剥离项目,CVS并将其余部分转换为整数。
Quassnoi

9
赞(y)10 things in MySQL (that won’t work as expected)
NullPointer 2014年

@Quassnoi,你为什么写CROSS JOIN?它们在MySQL中不是都一样吗?
Pacerier

13

AttachedCompanyIDs是一个大字符串,因此mysql尝试在此类型中将其强制转换为整数

当您在哪里使用

因此,如果comapnyid = 1:

companyID IN ('1,2,3')

这是真的

但是如果数字1不在首位

 companyID IN ('2,3,1')

返回false


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或3的行,所以第一个查询正在寻找以逗号分隔的值中存在于companyID中的一个,

而这里的另一个问题是您没有将表连接到您所在位置的公共键上,因此您将获得一个行的突变,即= count(table1)* count(table2);

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


两个表中的行都比我显示的多。在这两个表中,都以您登录时所使用的用户的ID身份存在,是否可以加入该帮助?
火箭Hazmat

好吧,如果您的第一个查询未返回预期结果,则只需更改任何内容。如果第一个查询返回的是您想要的结果,那么实际上没有问题。我以为您只是好奇为什么2个显示的结果不一样。
2010年

@superfro,我很好奇为什么2个没有显示相同的结果。
火箭Hazmat

您的第二个查询使用的是where IN(值),其中“值”部分来自表及其字符串。该字符串被评估为布尔值true,其中true = 1,这就是为什么它仅显示第一行的原因。
2010年

1
如果您担心性能,则可能应该考虑更改数据库结构。您可以添加包含2个值的联合表order_ID和company_ID,而不是在订单表中使用逗号分隔列表。这样,您就可以从company.company_ID = order_companies.company_ID上的左公司order_order.order_ID = order.order_ID上左公司的左联接订单中选择公司的名称。这将使用索引。
superfro 2010年

-1

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

让我们看一下表A,其中的列名为“ aid”,“ aname”。让我们看一下表B,其中的列名为“ bid”,“ bname”,“ aids”。

现在,表A和表B中的伪值如下所示。

表A

援助名

1个苹果

2个香蕉

3芒果

表B

出价助教

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:如果您想从表a中获取那些在辅助列中具有1或2或3值的记录,则必须使用IN。

查询:从A.INID上的A JOIN B(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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.