MySQL(ERROR子查询返回超过1行) - 但我期待超过1行

时间:2015-08-17 05:32:37

标签: mysql

我期待多行:

SELECT * FROM OWNER 

    WHERE OWNER.ID IN(
            (
            SELECT 
                ID 
            FROM OWNER 
            WHERE 
                LAST_NAME LIKE '%pickles%'
                OR FIRST_NAME LIKE '%pickles%'
            ),

            (
            SELECT 
                OWNER_ID 
            FROM VISITOR 
            WHERE 
                NAME LIKE '%pickles%'
                OR SIZE LIKE '%pickles%'
            ),

            (
            SELECT 
                OWNER_ID 
            FROM BOARDING 
            WHERE 
                COMMENTS LIKE '%pickles%'
                OR PERSONAL_BELONGINGS LIKE '%pickles%'
            )

        )

第二个子查询

FROM VISITOR 
        WHERE 
            NAME LIKE '%pickles%'

导致错误。 但是我期待返回多行? 它应该从找到的行返回一些ID。 那么为什么这些多行有问题?

谢谢。

2 个答案:

答案 0 :(得分:2)

我认为您正在寻找union

SELECT * FROM OWNER 
WHERE OWNER.ID IN(

        SELECT ID 
        FROM OWNER 
        WHERE LAST_NAME LIKE '%pickles%'
            OR FIRST_NAME LIKE '%pickles%'

        UNION

        SELECT OWNER_ID 
        FROM VISITOR 
        WHERE NAME LIKE '%pickles%'
            OR SIZE LIKE '%pickles%'

        UNION

        SELECT OWNER_ID 
        FROM BOARDING 
        WHERE COMMENTS LIKE '%pickles%'
            OR PERSONAL_BELONGINGS LIKE '%pickles%'


    )

<强>解释

您的子查询将结果返回3 ,例如:

ID                     OWNER_ID               OWNER_ID1
-----------------------------------------------------------------
OWNER_Table_ids        Visitor_Table_ids      Boarding_Table_ids

使用UNION,它将返回如下:

ID   
-------------------
OWNER_table_ids
Visitor_table_ids
Boarding_table_ids

错误&#34;子查询返回的值超过1&#34;只是意味着&#34;超过1列&#34;。

答案 1 :(得分:0)

接受的答案确实提供了一个有效的解决方案,但错误地解释了为什么现有查询不能按照书面形式工作。

有多种类型的子查询,返回多种类型的结果。

在您的查询中:

WHERE expr IN (( /* subq 1 */ ), ( /* subq 2 */ ), ( /* subq 3 */ )) 

......等同于......

WHERE expr = ( /* subq 1 */ )
   OR expr = ( /* subq 2 */ )
   OR expr = ( /* subq 3 */ )

这是,而且只能是三个相等比较。以这种方式使用的子查询将必然在标量上下文中解释...并且标量子查询只能返回一行或零行,因为子查询的结果将是用作scalar operand。对于服务器来解释它,否则是不合逻辑的:你可以将单个值与两个值或更多值进行比较,并进行相等比较吗?没有。

您的查询似乎不是要求服务器构建子查询中所有结果的集合,并测试OWNER.ID是否为IN()设置。您的查询要求服务器构建一个包含最多三个标量值的集合,每个子查询一个,并确定OWNER.ID是否为IN() (可能小得多)设置。这些子查询中的任何一个返回多行的事实使查询无效。你很幸运它已经无法运作。如果您选择的数据只匹配一行,那么它现在可以正常运行,但是当您有更多匹配时,它会分解。

ERROR 1242 (21000): Subquery returns more than 1 row

如果您(人为地)将LIMIT 1添加到三个子查询中的每个子查询中,您会发现查询确实有效,这说明了我的观点。结果将是不完整的,但查询是有效的,这反驳了错误是关于&#34;列的断言。&#34;它是一套......但是,正如所写的那样,它只是一组标量,而且只能是一组标量。

使用UNION进行重构可以明确您对服务器的意图。

请注意,如果关于列的错误的断言是正确的,则消息将是不同的:

ERROR 1241 (21000): Operand should contain 1 column(s)

现在,在解释了错误的实际原因后,我还会提出另一种解决方案。如果你还没有,你应该在VISITOR和BOARDING都有一个索引(OWNER_ID)。此解决方案与UNION解决方案之间的性能可能会因各种表中的数据组成以及MySQL Server的版本而异。这两种解决方案都不是最佳的,因为%比较中的起始LIKE要求表或至少一个包含该列(如果有的话)的索引完整地被读取。这是全表扫描或全索引扫描,&#34;扫描&#34;逐行表示。结尾%很好;开头%代价很高,因为它从列的左侧取消锚定模式,这是索引完成工作的地方。如果可以,只要您的应用程序允许,就可以删除它,以获得更好的性能,并对搜索到的列进行索引;如果你不能删除前导%,至少要明白这是你将大规模看到的表现的贡献者。这是B-Tree索引的一般限制,而不是MySQL的限制。您最后也可能想要查看FULLTEXT search

SELECT o.* 
  FROM OWNER o
 WHERE o.LAST_NAME LIKE '%pickles%'
    OR o.FIRST_NAME LIKE '%pickles%'
    OR EXISTS(SELECT * 
                FROM VISITOR v 
               WHERE v.OWNER_ID = o.ID 
                 AND (v.NAME LIKE '%pickles%' OR v.SIZE LIKE '%pickles%')
             )
    OR EXISTS(SELECT *
                FROM BOARDING b 
               WHERE b.OWNER_ID = o.ID 
                 AND (COMMENTS LIKE '%pickles%' OR PERSONAL_BELONGINGS LIKE '%pickles%')
             );

请注意,EXISTS(SELECT * ...)yet another form of subquery, entirely different than anything we've considered so far。特别值得注意的是*只是一种惯例。您可以说SELECT 1SELECT TRUE并不重要,因为实际上没有选择任何数据,EXISTS()除了TRUE之外没有任何内容返回 - 子查询匹配目标表中的一个或更多行 - 或FALSE - 它没有。请注意,这些查询引用外部查询中的值o.ID,因此它们

在这里,我们检查所有者表格是否符合匹配条件,然后,如有必要,我们会检查VISITOR或BOARDING中是否存在符合条件的行。如果一个名叫&#34; Cat&#34;有一只名叫&#34; Cat&#34;并且你正在寻找&#34; Cat&#34;然后我们将在OWNER中找到它并且优化器可能能够避免为该用户搜索其他两个表,因为我们已经知道她是匹配的,无论出于何种原因。

另请参阅:https://dev.mysql.com/doc/refman/5.6/en/subquery-errors.html