【问题标题】:Mysql two select statments union with orderMysql 两个 select 语句 union with order
【发布时间】:2017-04-28 14:14:14
【问题描述】:

我正在尝试根据搜索者的输入进行查询以从我的数据库中获取产品。假设在搜索器上输入的单词是“堆栈溢出”。

查询如下:

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 结果

  • mysql 堆栈溢出
  • jquery 堆栈溢出

表 B 结果

  • 堆叠一些东西
  • jquery 堆栈溢出
  • 全部堆叠

最终结果

  • mysql 堆栈溢出
  • jquery 堆栈溢出
  • 堆栈溢出一些东西
  • 全部堆叠

我现在得到的结果是没有按我想要的顺序排列。有什么想法吗?

编辑

我已经尝试过 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%' )。由于第一个查询在某种程度上是第二个查询的子集,因此将所有查询放在一起会改变顺序。如何加入这两个查询以保持与单个查询相同的顺序?

【问题讨论】:

  • 如果两个都存在结果,在B记录之前显示A记录?由于外部选择没有排序,因此可以按引擎所需的任何顺序返回结果。子查询的排序仅在您使用限制时才重要。这似乎不是这里的情况,所以它们除了减慢结果之外没有任何作用。要获得所需的结果,您必须订购外部查询。
  • 是的。我会尝试更好地解释它。我想先显示 A 的记录,然后再显示所有 B 记录。如果 A 上存在记录,我不想再次显示它们。我确实对外部查询使用了限制,这有关系吗?

标签: mysql sql-order-by union


【解决方案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 结果。如果 ORDER BY 在 SELECT 中出现时没有 LIMIT,则它会被优化掉,因为无论如何它都没有效果。

要使用 ORDER BY 或 LIMIT 子句对整个 UNION 结果进行排序或限制,请将各个 SELECT 语句括起来,并将 ORDER BY 或 LIMIT 放在最后一个语句之后。


就外部查询中的行排序而言,我们可以包含一个“鉴别器列”,它告诉我们哪个查询返回了一行。在此示例中,我们在第一个查询返回的行上返回字面值“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。

【讨论】:

  • 我的代码有错字。如果两个查询的第一个顺序子句只过滤第一个单词怎么办?获取所有 '%stack%' 行将导致不需要的顺序,因为第二个查询将提供更多行。
  • @Sergi:我想我回答了您提出的部分问题...如果您将两个查询与 UNION/UNION ALL 运算符组合在一起,并且您希望结果排序...添加 ORDER BY在外部查询上。要区分第一个查询返回的行和第二个查询返回的行,请添加一个鉴别器列,这是一个额外的列,用于告知哪个查询返回了特定行。要从第二个查询中排除行,请添加谓词(条件)以排除这些行。
  • @Sergi:在跟进中,我正在解决一个不同的问题,一个不同的问题......如何在不使用两个查询的 UNION/UNION ALL 的情况下返回指定的结果集,而是使用 single 查询,并在 ORDER BY 子句中使用将按所需顺序返回行的表达式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-30
  • 2016-06-08
  • 2023-03-24
相关资源
最近更新 更多