我正在尝试根据搜索者的输入进行查询以获取数据库中的产品。将搜索者输入的单词设置为“堆栈溢出”。
查询如下:
SELECT * FROM
(SELECT * FROM products t1
INNER JOIN products_references t2 ON t1.id = t2.id
INNER JOIN products_photo t3 ON t2.id = t3.id
WHERE
(t2.name LIKE '%stack%' COLLATE UTF8_UNICODE_CI
OR t3.photo_name LIKE '%stack%' COLLATE UTF8_UNICODE_CI
)
AND
(
t2.name LIKE '%overflow%' COLLATE UTF8_UNICODE_CI
OR t3.photo_name LIKE '%overflow%' COLLATE UTF8_UNICODE_CI
)
ORDER BY
CASE WHEN (t2.name LIKE '%stack%') THEN 0 ELSE 1 END,
CASE WHEN (t3.photo_name LIKE '%stack%' OR t3.photo_name LIKE '%overflow%' ) THEN 0 ELSE 1 END
) as a
UNION DISTINCT
SELECT * FROM
(SELECT * FROM products t1
INNER JOIN products_references t2 ON t1.id = t2.id
INNER JOIN products_photo t3 ON t2.id = t3.id
WHERE
(t2.name LIKE '%stack%' COLLATE UTF8_UNICODE_CI
OR t3.photo_name LIKE '%stack%' COLLATE UTF8_UNICODE_CI
)
ORDER BY
CASE WHEN (t2.name LIKE '%stack%' ) THEN 0 ELSE 1 END,
CASE WHEN (t3.photo_name LIKE '%stack%' ) THEN 0 ELSE 1 END
) as b LIMIT 0,10
我要存档的内容:
示例:
表A结果
表B结果
最终结果
我现在得到的结果是没有按我的要求排序。有什么想法吗?
修改
我尝试过spencer7593解决方案:
SELECT *
FROM products t1
JOIN products_references t2 ON t1.id = t2.id
JOIN products_photo t3 ON t2.id = t3.id
WHERE t2.name LIKE '%stack%' COLLATE UTF8_UNICODE_CI
OR t3.photo_name LIKE '%stack%' COLLATE UTF8_UNICODE_CI
ORDER
BY CASE WHEN (t2.name LIKE '%stack%' ) THEN 0 ELSE 1 END,
CASE WHEN (t3.photo_name LIKE '%stack%' OR t3.photo_name LIKE '%overflow%' ) THEN 0 ELSE 1 END,
CASE WHEN (t3.photo_name LIKE '%stack%' ) THEN 0 ELSE 1 END
这里的主要问题是两个查询按第一个单词排序(上例中的'%stack%')。由于第一个查询在某种程度上是第二个的子集,因此一起排序会改变顺序。如何加入这两个查询以保持给定的相同顺序,因为它们是单独的查询?
答案 0 :(得分:1)
当外部查询中没有ORDER BY时,MySQL会优化内联视图中的ORDER BY子句。 (如果视图查询包含LIMIT子句,则内联视图中的ORDER BY子句不会被优化;但是不保证外部查询返回的行的顺序。)
如果要按特定顺序返回行,请在最外面的查询中添加ORDER BY。
参考:https://dev.mysql.com/doc/refman/5.7/en/union.html
对单个SELECT语句使用ORDER BY并不意味着行在最终结果中出现的顺序,因为UNION默认生成一组无序行。因此,在此上下文中使用ORDER BY通常与LIMIT结合使用,因此它用于确定要为SELECT检索的所选行的子集,即使它不一定影响这些行的顺序。最终的UNION结果。如果在SELECT中没有LIMIT出现ORDER BY,它会被优化掉,因为它无论如何都没有效果。
要使用ORDER BY或LIMIT子句对整个UNION结果进行排序或限制,请为各个SELECT语句加上括号,并将ORDER BY或LIMIT置于最后一个之后。
就外部查询中的行排序而言,我们可以包含一个“discriminator列”,它告诉我们哪个查询返回了一行。在此示例中,我们在第一个查询返回的行上返回文字值“1”,在第二个查询返回的行上返回文字值“2”:
( SELECT '1' AS src, f.* FROM ... ORDER BY ... )
UNION ALL
( SELECT '2' AS src, s.* FROM ... ORDER BY ... )
ORDER BY src, ...
但是使用这些不同的值,我们将无法使用DISTINCT删除重复项,或者优先保留第一个查询中的行。
<强>后续强>
我在联合编辑的两个查询之间看到的唯一区别是ORDER BY子句,以及WHERE子句的这一部分
AND
(
t2.name LIKE '%overflow%' COLLATE UTF8_UNICODE_CI
OR t3.photo_name LIKE '%overflow%' COLLATE UTF8_UNICODE_CI
)
看起来第一个查询返回第二个返回的行的子集。
所以,我不明白为什么这需要两个单独的查询。
我们不能只运行一个查询,其中包括第二个查询返回的行,然后返回第一个查询首先返回的行。
我们可以从WHERE子句中移动该条件,然后按顺序排序;将它作为ORDER BY子句中的第一个表达式。
SELECT *
FROM products t1
JOIN products_references t2 ON t1.id = t2.id
JOIN products_photo t3 ON t2.id = t3.id
WHERE t2.name LIKE '%stack%' COLLATE UTF8_UNICODE_CI
OR t3.photo_name LIKE '%stack%' COLLATE UTF8_UNICODE_CI
ORDER
BY CASE WHEN (t2.name LIKE '%overflow%' OR t3.photo_name LIKE '%overflow%') THEN 0 ELSE 1 END
, CASE WHEN (t2.name LIKE '%stack%' OR t3.photo_name LIKE '%overflow%') THEN 0 ELSE 1 END
, CASE WHEN (t3.photo_name LIKE '%stack%' OR t3.photo_name LIKE '%overflow%') THEN 0 ELSE 1 END
, CASE WHEN (t2.name LIKE '%stack%' ) THEN 0 ELSE 1 END
, CASE WHEN (t3.photo_name LIKE '%stack%' ) THEN 0 ELSE 1 END
ORDER BY子句中的第一个表达式仅对第一个查询返回的行计算为TRUE。
如果我们想要删除重复的行,我们可以在SELECT之后添加DISTINCT关键字,或者添加一个合适的GROUP BY子句。
并添加回所需的任何COLLATE UTF8_UNICODE_CI
。