我期待多行:
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。 那么为什么这些多行有问题?
谢谢。
答案 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 1
或SELECT 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