【问题标题】:MySQL SELECT order by values of 2 columnsMySQL SELECT 按 2 列的值排序
【发布时间】:2017-01-06 15:35:11
【问题描述】:

我有一张这样的桌子:

CREATE TABLE rows(
    UniqueID VARCHAR(225),
    Previous VARCHAR(225),
    Next VARCHAR(225)
);

内容如下:

+----------+-----------+-----------+
| UniqueID | Previous  | Next      |
+----------+-----------+-----------+
|    676   | undefined |       219 |
|    890   |       219 | undefined |
|    219   |       676 |       890 |
+----------+-----------+-----------+

如您所见,行具有 UID,PreviousNext 列引用。

我现在想要的是编写一个 SELECT * 语句,它将按 PreviousNext 字段对所有结果进行排序。 undefined 值标记结束元素。我怎么能做到这一点?在上面显示的表格的情况下,我想要的顺序是那里显示的,最后 2 行位置交换,所以 X 行的 Next 指向 Y 行的 UID,它有一个 Previous指向 X 行的 UID。等

【问题讨论】:

  • 请添加预期结果
  • order by previous, next?
  • 未定义的元素如何排序?
  • juergend 在 OP 中解释了更多内容。 jens,基于值大小的排序,所以 1; 2; 3等,这里不是这种情况。 tim-biegeleisen 没有Previous 的是第一个元素,没有Next 的是最后一个。

标签: mysql


【解决方案1】:

您要创建的是递归查询。不幸的是,MySQL 并不容易做到这一点。如果父母总是有一个大于孩子的指数,那么有相对简单的解决方案,但这里不是这种情况。有几个问题讨论此类问题。以下问题的答案探索了尝试此类查询的不同方法,包括使用存储过程。

How to do the Recursive SELECT query in MySQL?

使用存储过程的想法,您可以尝试以下方法:

CREATE PROCEDURE getInOrder()
  BEGIN
    DECLARE child_id VARCHAR(256);
    DECLARE prev_id VARCHAR(256);
    SELECT UniqueID INTO prev_id FROM rows WHERE Previous = 'undefined';
    SELECT `Next` INTO child_id
    FROM rows WHERE UniqueID = prev_id;
    CREATE TEMPORARY TABLE IF NOT EXISTS temp_table AS (SELECT * FROM rows WHERE 1=0);
    TRUNCATE TABLE temp_table;
    WHILE child_id <> 'undefined' DO
      INSERT INTO temp_table SELECT * FROM rows WHERE UniqueID = prev_id;
      SET prev_id = child_id;
      SELECT `Next` INTO child_id
      FROM rows WHERE UniqueID = prev_id;
    END WHILE;
    INSERT INTO temp_table SELECT * FROM rows WHERE UniqueID = prev_id;
    SELECT * FROM temp_table;
  END;

然后您可以调用存储过程来按顺序检索表。

工作示例:http://sqlfiddle.com/#!9/085dec/2

【讨论】:

  • 嘿@Surberus,我目前正在使用您的方法,但是我开始注意到,表格中只有一行,整个过程就挂起。在这种情况下是否有一种解决方法可以只返回该行?
  • 嗨@RandoHinn。如果表没有符合要求的数据,存储过程将挂起。下面的小提琴显示只要该行将PreviousNext 设置为“未定义”,该过程就可以使用一行。 sqlfiddle.com/#!9/40317/1 while 循环要求最终提供一行,其中 Next 未定义,否则它将永远循环。您可以放入一个安全计数器,以确保您循环的次数永远不会超过表中的行数。这也可以防止一组数据自行循环。
  • 嘿,@Surberus 现在将上一个和下一个设置为“未定义”。仍然挂起..当表为空时,它工作,当它有 1 行时,它挂起..:/
  • 如果没有看到您的代码,很难说出发生了什么。此外,对文本“未定义”的要求是基于您的原始问题。如果这会使事情变得更容易,您总是可以用 NULL 替换所有内容(在数据库和存储过程中)。如果你想发布一个小提琴,它会更容易排除故障。
  • 我开始制作小提琴......只是发现 sqlFiddle 使用 MySQL 5.6,并且它在那里工作,而我的环境使用 5.7......我正在降级 MySQL 看看是否有区别。很快就会回来报告...
【解决方案2】:
ORDER BY IFNULL(prev, ''),         -- some value lower than the rest
         IFNULL(next, 'zzzzz')     -- some value higher than all values

(从技术上讲,第一部分可能只是prev,没有IFNULL。)

如果 id 确实是数字,则应使用数字数据类型,例如 INT UNSIGNED。如果它们真的是字符串,你需要225吗?

这假设 prev next 来根据UniqueId 加载下一行,代码要复杂得多。

【讨论】:

    【解决方案3】:

    我认为这个请求缺乏细节。

    但是,你希望最终的结果是这样的吗?

    +----------+-----------+-----------+
    | UniqueID | Previous  | Next      |
    +----------+-----------+-----------+
    |    676   | undefined |       219 |
    |    219   |       676 |       890 |
    |    890   |       219 | undefined |
    +----------+-----------+-----------+
    

    如果我是对的,您可以使用(我将表命名为demo):

    SELECT d.* FROM (
        SELECT UniqueID, IF(Previous IS NULL, -1, Previous) AS Previous, IF(Next   IS NULL, 999999999999, Next) as Next
        FROM demo
    )t
    JOIN demo d ON d.UniqueID = t.UniqueID
    ORDER BY t.Next, t.Previous
    ;
    

    所以,当PreviousNULL 时,你把它加上-1 以确保他是列表中的第一个,当NextNULL 时,你把它放在一个非常高的值以确保它会是列表中的最后一个...那么您只需按PreviousNext 对查询进行排序。

    我必须强调,这个解决方案专注于呈现的数据。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-29
      • 1970-01-01
      • 2014-07-24
      • 1970-01-01
      • 2010-12-21
      • 1970-01-01
      • 2011-01-12
      相关资源
      最近更新 更多