【问题标题】:Complex MySQL sort复杂的 MySQL 排序
【发布时间】:2017-05-27 23:32:18
【问题描述】:

我已经对这个问题进行了几乎完全的重写,因为我仍然卡住了......寻找一种无需使用 php 或其他一些数据解释器/编程来查看结果表中数据的方法以我希望的方式查看结果。

基本结果表是这样的(如果你愿意,可以把它弄得更乱):

 id1 |  id2 | name   | ord
 ------------------------------
 a1  | null | name1  |  0
 a2  | null | name2  |  1
 a3  | null | name3  |  2
 b1  |  a1  | name4  |  0
 b2  |  b1  | name5  |  0
 b3  |  a1  | name6  |  1
 b4  |  a1  | name7  |  2
 c1  |  a2  | name8  |  0
 c2  |  a2  | name9  |  1
 d1  |  a2  | name10 |  2
 d2  |  a2  | name11 |  3
 d3  |  a2  | name12 |  4
 d4  |  a3  | name13 |  0
 c3  |  d4  | name14 |  0
 c4  |  c3  | name15 |  1
 d5  |  b2  | name16 |  0

我要找的结果是这样的:

 id1 |  id2 | name   | ord
 -----------------------------
 a1  | null | name1  |  0  <--group header, signified by `id2` is null
 b1  |  a1  | name4  |  0  <--item that's parent to line below
 b2  |  b1  | name5  |  0  <--item that's parent to line below
 d5  |  b2  | name16 |  0  <--last child item
 b3  |  a1  | name6  |  1  <--special case where `ord` takes over
 b4  |  a1  | name7  |  2  <--`id2` is still the same, so `ord` sort
 a2  | null | name2  |  1  <--next group header
 c1  |  a2  | name8  |  0  <--`id2` is a2, so name2 is parent, `ord` sort
 c2  |  a2  | name9  |  1  <--same
 d1  |  a2  | name10 |  2  <--same
 d2  |  a2  | name11 |  3  <--same
 d3  |  a2  | name12 |  4  <--same
 a3  | null | name3  |  2  <--next group header
 d4  |  a3  | name13 |  0  <--`id2` is a3, so name3 is parent
 c3  |  d4  | name14 |  0  <--`id2` is d3, so name13 is parent
 c4  |  c3  | name15 |  1  <--`id2` is c3, so name14 is parent

我需要什么的冗长解释

基本上,我需要id2 成为按ord 排序的“组标题”。然后在他们每个人的下方,我需要以这样一种方式对行进行排序,即id2 等于其上方行的id1,并且任何时候id2 与上方行的id2 相同,它应按ord 排序。

id2 与最后一个id1 匹配的行用完时,应该出现下一个组标题,并且应该重新开始排序,下一个id2 匹配新组标题的id1

id2 为空的任何项目都是组标题,但在这些组中,ord 基于“父”行重新开始。其中一些行也是其他项目的父项,因此排序的复杂性

实际结果表中还有其他列,因此我将其简化为实际进行排序所需的列。

表格数据说明

实际的id 列是 char(36),name 是 varchar(1024),ord 是 int(11),表中的其他列运行范围...我还应该提到这些结果来自JOIN,这样做是为了限制找到此数据的主表的结果。

id1 是主键并且是唯一的,与排序顺序无关,除了 id2 等于给定 id1 组标题的所有行应该出现在它的下方。 id2 是对“父行 ID”的引用,因此对于组标题它为空;它们是最父的行,并且通过ord 有自己的排序顺序。 name 实际上是作为类别名称的组标题行所期望的项目名称。

例如,假设组标题是食物组,而其他条目是食物类型。所以一个组标题会是“水果”,下一个是“蔬菜”等。它实际上是 A/V 设备,但水果可能更容易理解。

我做了什么

我在这方面的尝试已经遍及整个地图。我尝试了各种order byfield()if(),并且我考虑过子查询,但我的技能还不能靠自己解决这个问题。我认为,如果我能指出正确的方向,我可以结合我必须得到的结果来处理。不幸的是,出于沮丧,我删除了所有半工作代码,但它可能只不过是一个 ORDER BY 和一个不工作的 CASE 声明。

要点:

  1. id2 为空时,它是一个组头,组头应按其ord 字段从低到高(0、1、2)排序。
  2. id1 等于另一行的id2 的任何行都是其父行。换言之,如果发现某行的id2 等于另一行的id1,则应将其放在其下方。
  3. 如果存在id2 等于id1 的多行,则排序应首先考虑#2,然后按ord 从低到高排序。

【问题讨论】:

  • 这听起来像hierarchical data。您可能需要在数据中添加一些“左”和“右”列,以便快速排序。
  • 这可能是看待它的好方法。也就是说,id1id2 可能是我的左/右,然后ord 将是叶节点......谢谢你,我一定会在时间允许的时候看看!如果结果是解决方案,我会发布答案。

标签: mysql sorting


【解决方案1】:

您描绘的数据是分层的(id2 指的是“父”),并且在 MySQL 中(直到东 5.7)没有特定的功能,例如递归 CTE 来处理层次结构。有一些变通方法,在这里,我们似乎知道我们可以为每个(级别 - 1)使用一个左连接的最大级别数(即,对于 4 个级别添加 3 个左连接)。一旦通过连接建立了层次结构,然后在各个列中使用 COALESCE(),由于左连接,其中许多现在可以为 NULL,我们可以排列数据以适应所需的排序顺序。 (好吧,“几乎”。如果您将想要的订单与下面查询显示的订单进行比较,则会有一些细微差别。)

也请参考这个SQL Fiddle

CREATE TABLE Table1
    (`id1` varchar(2), `id2` varchar(4), `name` varchar(6), `ord` int)
;

INSERT INTO Table1
    (`id1`, `id2`, `name`, `ord`)
VALUES
    ('a1', NULL, 'name1', 0),
    ('a2', NULL, 'name2', 1),
    ('a3', NULL, 'name3', 2),
    ('b1', 'a1', 'name4', 0),
    ('b2', 'b1', 'name5', 0),
    ('b3', 'a1', 'name6', 1),
    ('b4', 'a1', 'name7', 2),
    ('c1', 'a2', 'name8', 0),
    ('c2', 'a2', 'name9', 1),
    ('d1', 'a2', 'name10', 2),
    ('d2', 'a2', 'name11', 3),
    ('d3', 'a2', 'name12', 4),
    ('d4', 'a3', 'name13', 0),
    ('c3', 'd4', 'name14', 0),
    ('c4', 'c3', 'name15', 1),
    ('d5', 'b2', 'name16', 0)
;

查询

select
      coalesce(p1.id1, p2.id1, p3.id1, p4.id1) id1s
    , coalesce(p1.id2, p2.id2, p3.id2, p4.id2) id2s
    , coalesce(p1.name, p2.name, p3.name, p4.name) names
    , coalesce(p1.ord, p2.ord, p3.ord, p4.ord) ords
    #, coalesce(p4.id1, p3.id1, p2.id1, p1.id1) ord1
    #, coalesce(p4.id2, p3.id2, p2.id2, p1.id2) ord2
    #, coalesce(p4.ord, p3.ord, p2.ord, p1.ord) ord3
from        table1 p1
left join   table1 p2 on p1.id2 = p2.id1
left join   table1 p3 on p2.id2 = p3.id1 
left join   table1 p4 on p3.id2 = p4.id1 
order by
      coalesce(p4.id1, p3.id1, p2.id1, p1.id1)
    , coalesce(p4.id2, p3.id2, p2.id2, p1.id2)
    , coalesce(p4.ord, p3.ord, p2.ord, p1.ord)
    , id2s

Result

| id1s |   id2s |  names | ords |
|------|--------|--------|------|
|   a1 | (null) |  name1 |    0 |
|   b3 |     a1 |  name6 |    1 |
|   b4 |     a1 |  name7 |    2 |
|   b1 |     a1 |  name4 |    0 |
|   b2 |     b1 |  name5 |    0 |
|   d5 |     b2 | name16 |    0 |
|   a2 | (null) |  name2 |    1 |
|   d3 |     a2 | name12 |    4 |
|   c2 |     a2 |  name9 |    1 |
|   d1 |     a2 | name10 |    2 |
|   d2 |     a2 | name11 |    3 |
|   c1 |     a2 |  name8 |    0 |
|   a3 | (null) |  name3 |    2 |
|   d4 |     a3 | name13 |    0 |
|   c4 |     c3 | name15 |    1 |
|   c3 |     d4 | name14 |    0 |

想要

# id1 |  id2 | name   | ord
# -----------------------------
# a1  | null | name1  |  0  <--group header, signified by `id2` is null
# b1  |  a1  | name4  |  0  <--item that's parent to line below
# b2  |  b1  | name5  |  0  <--item that's parent to line below
# d5  |  b2  | name16 |  0  <--last child item
# b3  |  a1  | name6  |  1  <--special case where `ord` takes over
# b4  |  a1  | name7  |  2  <--`id2` is still the same, so `ord` sort
# a2  | null | name2  |  1  <--next group header
# c1  |  a2  | name8  |  0  <--`id2` is a2, so name2 is parent, `ord` sort
# c2  |  a2  | name9  |  1  <--same
# d1  |  a2  | name10 |  2  <--same
# d2  |  a2  | name11 |  3  <--same
# d3  |  a2  | name12 |  4  <--same
# a3  | null | name3  |  2  <--next group header
# d4  |  a3  | name13 |  0  <--`id2` is a3, so name3 is parent
# c3  |  d4  | name14 |  0  <--`id2` is d3, so name13 is parent
# c4  |  c3  | name15 |  1  <--`id2` is c3, so name14 is parent

【讨论】:

    【解决方案2】:

    所以,已经有一段时间了。老实说,我已经忘记了这意味着什么,但我相信我正在尝试对应用程序中出现的行项目进行排序。假设是这种情况,我从我们的一位开发人员那里得到了答案。我们最终使用了COALESCE()LPAD() 的组合。

    假设这些是列表中的订单项,但有些是嵌套的,它们都位于同一个表中。唯一的关系是知道父行的idordinal。我们想出了这个:

    SELECT CONCAT_WS('-', LPAD(COALESCE(item5.ordinal),4,'0'),`
       LPAD(COALESCE(item4.ordinal),4,'0'),
       LPAD(COALESCE(item3.ordinal),4,'0'),
       LPAD(COALESCE(item2.ordinal),4,'0'),
       LPAD(COALESCE(item.ordinal),4,'0')) AS line_order
    FROM main_lines as main
    LEFT JOIN sub_line as item on item.id = main.id
    LEFT JOIN sub_line as item2 on item2.id = item.parent_line_id
    LEFT JOIN sub_line as item3 on item3.id = item2.parent_line_id
    LEFT JOIN sub_line as item4 on item4.id = item3.parent_line_id
    LEFT JOIN sub_line as item5 on item5.id = item4.parent_line_id
    
    ORDER BY line_order;
    

    COALESCE 允许我们对行进行空值保护,LPAD 允许CONCAT_WS 提供正确的排序顺序并在每个结果中使用 0。

    编辑:感谢Paul MaxwellSQL Fiddle 中证明这一点的灵感。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-30
      • 2015-08-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多