【问题标题】:Possible to create a mysql query that only displays things that are in descending order可以创建一个只显示降序的东西的mysql查询
【发布时间】:2011-10-11 10:06:59
【问题描述】:

首先,我想明确表示我并没有尝试按降序排列。

我希望按其他方式排序,但只有在其下方第 1 行的该列中的值小于其自身时,才通过在第二列中显示内容来进一步过滤。一旦它发现下一列较低,它就会停止。

例子:

Ordered by column-------------------Descending Column
353215                                    20
535325                                    15
523532                                    10
666464                                    30
473460                                    20

如果给定该数据,我希望它只返回 20、15 和 10。因为现在 30 高于 10,我们不关心它下面的内容。

我到处找,找不到解决办法。

【问题讨论】:

  • 感谢大家的帮助。很遗憾我不能这样做。我的生活变得更加艰难了xD

标签: mysql sorting sql-order-by


【解决方案1】:

您没有找到解决方案,因为这是不可能的。

SQL 只能在一行内工作,它不能查看它上面或下面的行。

您可以编写一个存储过程来执行此操作,本质上是一次循环一行并计算逻辑。

用前端语言编写它可能会更容易,无论你使用的是什么。

【讨论】:

  • @regilero 我只说这在 SQL 中是不可能的。您的解决方案很棒。
【解决方案2】:

恐怕你不能在 SQL 中做到这一点。关系数据库是为不同的目的而设计的,因此没有像下一行或上一行这样的抽象。以“包装”语言在 SQL 之外执行。

【讨论】:

    【解决方案3】:

    我猜你可能会(按顺序)将它们选择到一个临时表中,该表也有一个自动递增列,然后从临时表中选择,根据自动递增列(id)加入到自身, 但是 where t1.id = t2.id + 1,然后使用 where 条件(以及适当的 order by 和 limit 1)来查找 t2 中降序列大于 t1 中的行的 t1.id。之后,您可以从临时表中选择id小于或等于您刚刚找到的id。虽然它并不漂亮! :)

    【讨论】:

    • 如果我这样做,我的老板会杀了我
    【解决方案4】:

    我不确定这些是否符合您的要求,而且它们可能太慢了:

    SELECT t1.col1, t1.col2
      FROM tbl t1
      WHERE t1.col2 = (SELECT MIN(t2.col2) FROM tbl t2 WHERE t2.col1 <= t1.col1)
    

    或者

    SELECT t1.col1, t1.col2
      FROM tbl t1
      INNER JOIN tbl t2 ON t2.col1 <= t1.col1
      GROUP BY t1.col1, t1.col2
      HAVING t1.col2 = MIN(t2.col2)
    

    【讨论】:

      【解决方案5】:

      其实是可以的,只是性能不好优化。如果 Col1 是有序的并且 Col2 是降序列:

      首先,您创建每行与下一行的自联接(请注意,这仅在列值唯一时才有效,否则您需要在唯一值上联接)。

      (Select Col1, (Select Min(Col2) as A2 from MyTable as B Where B.A2>A.Col1)  As Col1FromNextRow From MyTable As A) As D
      INNER JOIN
      (Select Col1 As C1,Col2 From MyTable As C On C.C1=D.Col1FromNextRow)
      

      然后你实现“一直到第一个升值”位:

      Select Col2 FROM
      (
      (Select Col1, (Select Min(Col2) as A2 from MyTable as B Where B.A2>A.Col1)  As Col1FromNextRow From MyTable As A) As D
      INNER JOIN
      (Select Col1 As C1,Col2 From MyTable As C On C.C1=D.Col1FromNextRow)
      ) As E
      WHERE NOT EXISTS
      (SELECT Col1 FROM MyTable As Z Where z.COL1<E.Col1 and Z.Col2 < E.Col2)
      

      我没有测试这个的环境,所以它可能有错误。我很抱歉,但希望这个想法是半清楚的。

      我仍然会尝试在 SQL 之外进行。

      【讨论】:

        【解决方案6】:

        编辑: 删除了大数字 init,并在 ifnull 测试中编辑了计数器,因此它适用于纯 MySQL:ifnull(@prec,counter) 而不是 ifnull(@prec,999999)

        如果您的起始表是 t1 并且基本请求是:

        select id,counter from t1 order by id;
        

        然后使用 mysql 变量 你可以完成这项工作:

        SET @prec=NULL;
        select * from (
            select id,counter,@prec:= if(
               ifnull(@prec,counter)>=counter,
               counter,
               -1) as prec
            from t1 order by id
         ) t2 where prec<>-1;
        

        除了 这里我需要 99999 作为列的最大值,并且可能有一种方法可以在第一个请求中将 @prec 的初始化设置为 NULL。

        这里的 prec 列包含第 1 行值计数器,如果每行的计数器值小于前一行的值,则为每行的计数器值,如果为 false,则为 -1。

        更新

        如果在WHERE子句中完成变量赋值,则可以完全去掉外层选择:

        SELECT @prec := NULL;
        SELECT
          id,
          counter
        FROM t1
        WHERE
          (@prec := IF(
            IFNULL(@prec, counter) >= counter,
            counter,
            -1
          )) IS NOT NULL
          AND @prec <> -1
        ORDER BY id;
        

        regilero 编辑: 我可以通过这种方式使用 1 行的临时表(左连接)删除第一个初始化查询:但这可能会减慢查询速度。

        (...)
        FROM t1 
        LEFT JOIN (select @prec:=NULL as nullinit limit 1) as tmp1 ON tmp1.nullinit is null
        (..)
        

        正如@Mike 所说,使用简单的 UNION 查询甚至:

        (...)
        FROM t1 , (select @prec:=NULL) tmp1 
        (...)
        

        如果你想避免第一个查询更好。

        所以最后最好的解决方案是:

        SELECT NULL AS id, NULL AS counter FROM dual WHERE (@prec := NULL) 
        UNION
        SELECT   id,   counter
        FROM t1  
        WHERE   (
           @prec := IF(
              IFNULL(@prec, counter) >= counter,
              counter,
              -1   )) IS NOT NULL
        AND @prec <> -1
        ORDER BY id;
        
        +--------+---------+
        | id     | counter |
        +--------+---------+
        | 353215 |      20 |
        | 523532 |      10 |
        | 535325 |      15 |
        +--------+---------+
        

        EXPLAIN SELECT 输出:

        +----+--------------+------------+------+---------------+------+---------+------+------+------------------+
        | id | select_type  | table      | type | possible_keys | key  | key_len | ref  | rows | Extra            |
        +----+--------------+------------+------+---------------+------+---------+------+------+------------------+
        |  1 | PRIMARY      | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL | Impossible WHERE | 
        |  2 | UNION        | t1         | ALL  | NULL          | NULL | NULL    | NULL |    6 | Using where      | 
        | NULL | UNION RESULT | <union1,2> | ALL  | NULL          | NULL | NULL    | NULL | NULL | Using filesort   | 
        +----+--------------+------------+------+---------------+------+---------+------+------+------------------+
        

        【讨论】:

        • 可以移动WHERE子句中的变量赋值,完全去掉外层SELECT。我已经编辑了您的帖子以包含一个示例-希望您不介意!见Advanced MySQL user variable techniques
        • @Mike:谢谢,没问题。你有什么想法删除第一个选择,我用它来防止在同一个会话上多次运行以获取 prec 变量已经在 -1。
        • 您可以在FROM t1 之后添加选择,就像这样FROM t1, (SELECT @prec := NULL) var,但我认为这会创建一个子查询,这可能会影响性能。看看两者的 EXPLAIN SELECT 结果。我想实现同样的目标,花了将近一个小时试图解决它,但最终放弃了!
        • 我刚刚又试了一次。尝试通过在主 SELECT 之前添加这个来创建一个 UNION:SELECT NULL AS id, NULL AS counter FROM dual WHERE (@prec := NULL) IS NOT NULL UNION ... 这对我有用。 WHERE 会产生一个Impossible WHERE,这意味着它不会返回任何行,但会重置@prec
        • 其实你不需要IS NOT NULL,这个也可以:SELECT NULL AS id, NULL AS counter FROM dual WHERE (@prec := NULL) UNION ...
        猜你喜欢
        • 1970-01-01
        • 2019-03-30
        • 2011-03-14
        • 2021-02-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多