正确的方法来排序SQL列

时间:2014-06-14 18:45:00

标签: php sql sql-order-by

我有一个表格列,如下所示:

id     |   date
1      |   January 1 2014 12:00 AM
2      |   February 1 2014 12:00 AM
3      |   March 1 2014 12:00 AM
4      |   April 1 2014 12:00 AM
5      |   May 1 2014 12:00 AM
6      |   June 1 2014 12:00 AM
7      |   July 15 2014 12:00 AM
8      |   July 15 2014 1:00 PM
9      |   July 15 2014 2:00 PM
10     |   July 15 2014 3:00 PM

由于已经传递了ID 1-6,我希望它们从底部排序,并且即将到来的最新一个应该在顶部排序。

期望的输出:

id     |   date
7      |   July 15 2014 12:00 AM
8      |   July 15 2014 1:00 PM
9      |   July 15 2014 2:00 PM
10     |   July 15 2014 3:00 PM
6      |   June 1 2014 12:00 AM
5      |   May 1 2014 12:00 AM
4      |   April 1 2014 12:00 AM
3      |   March 1 2014 12:00 AM
2      |   February 1 2014 12:00 AM
1      |   January 1 2014 12:00 AM

5 个答案:

答案 0 :(得分:1)

我能想到的最好的方法是首先对未来/历史日期进行排序,然后对历史日期进行相反的排序,以及通常的未来日期。

简单地将日期反转-1不起作用,因为MySQL无法与数字类型一起处理日期类型以进行排序。因此,在这两种情况下都需要将其转换为数字。

所以,就像这样:

ORDER BY
    (CASE WEN date1 < NOW() THEN 1 ELSE 0 END),
    (CASE WHEN date1 < NOW() THEN -1 * date1 ELSE 1 * date1 END)

小提琴,基于VBlades的小提琴:http://sqlfiddle.com/#!2/eb83b/17

答案 1 :(得分:0)

根据您的DBMS,日期算术略有不同。然而,诀窍是使用案例表达式将历史日期映射到未来。类似的东西:

order by date_col + case when date_col < now() then 100 else 0 end year

答案 2 :(得分:0)

以下是在SQL Server中执行此操作的方法,但应该可以轻松移植(http://sqlfiddle.com/#!3/89d35/24):

select
          id,
          date
from
  (select
            (select count(*) FROM table1 where id <= t1.id and date >= getdate()) as row,
            *,
            1 AS TableSort
  from
            table1 t1
  where
            t1.date >= getdate()

  union all

  select
            (select count(*) FROM table1 where id >= t2.id and date < getdate()) as row,
            *,
            2 AS TableSort
  from
            table1 t2
  where
            date < getdate()) t3
order by
          tablesort,
          row

答案 3 :(得分:0)

您的问题是这里基本上有两个查询,排序不同。如果您无法实际运行它们作为两个查询(即在您的应用程序层中),那么您需要了解它。

但是:

  • ORDER BY CASE...解决方案的问题在于(几乎)保证从不使用索引 - 优化程序无法通过索引向上/向下翻页,因为订单依赖在行值。
  • 与上述相同的限制适用于在所需列上进行各种类型的“数学运算”。加上你正在做的任何数学计算的额外开销。

如果您的结果集很小,则上述内容不是问题。但是,在任何实际大小的dbs上,这都很重要。所以......

如果要合并两个(半相关)查询,则使用UNION ALL(如果要删除重复项,则使用UNION)。问题是生成的集合以“未定义”的顺序拼接在一起(在文档中明确指出并不总是)。造成这种情况的原因各不相同,但是如果系统使用数据并行性,哪一方当前可能正在等待锁定,或者仅仅因为它是在Sunnydale的星期二,则会包含这些原因。所以我们需要ORDER BY ...

除了您的查询结果外,基本上需要两个(不同)ORDER BY。我们只能提供一个用于UNION (ALL)运算符的(因为排序将在组合集上执行)。幸运的是,可以为该子句的每列提供不同的排序! UNION允许我们提供“虚拟”空列 - 那么排序结果在哪里?

SELECT id, COALESCE(date, other) AS sortedDate
FROM (SELECT id, date, null AS other
      FROM <sourceTable>
      WHERE date >= CURRENT_TIMESTAMP
      UNION ALL
      SELECT id, null, date
      FROM <sourceTable>
      WHERE date < CURRENT_TIMESTAMP) Derived
ORDER BY date ASC, other DESC

<强> PostgreSQL Fiddle Demo

为什么这样做?在RDBMS中,null排序到范围的一端。对于PostgreSQL,Oracle和DB2,它们被排序为“最大”值。也就是说,生成一个如下所示的中间表:

id     |   date                       |  other
7      |   July 15 2014 12:00 AM      |  null
8      |   July 15 2014 1:00 PM       |  null
9      |   July 15 2014 2:00 PM       |  null
10     |   July 15 2014 3:00 PM       |  null
6      |   null                       |  June 1 2014 12:00 AM
5      |   null                       |  May 1 2014 12:00 AM
4      |   null                       |  April 1 2014 12:00 AM
3      |   null                       |  March 1 2014 12:00 AM
2      |   null                       |  February 1 2014 12:00 AM
1      |   null                       |  January 1 2014 12:00 AM

...你可以看到这与ORDER BY中表达的顺序相比,对吧?

不幸的是,对于MySQL和SQL Server,null最低值,因此我们必须更改声明!

SELECT id, COALESCE(date, other) AS sortedDate
FROM (SELECT id, 1 AS orderGroup, date, null AS other
      FROM <sourceTable>
      WHERE date >= CURRENT_DATE
      UNION ALL
      SELECT id, 2, null, date
      FROM <sourceTable>
      WHERE date < CURRENT_DATE) Derived
ORDER BY orderGroup ASC, date ASC, other DESC

<强> SQL Server Fiddle Demo

...这里,中间表看起来像这样:

id   |  og  |  date                       |  other
7    |  1   |  July 15 2014 12:00 AM      |  null
8    |  1   |  July 15 2014 1:00 PM       |  null
9    |  1   |  July 15 2014 2:00 PM       |  null
10   |  1   |  July 15 2014 3:00 PM       |  null
6    |  2   |  null                       |  June 1 2014 12:00 AM
5    |  2   |  null                       |  May 1 2014 12:00 AM
4    |  2   |  null                       |  April 1 2014 12:00 AM
3    |  2   |  null                       |  March 1 2014 12:00 AM
2    |  2   |  null                       |  February 1 2014 12:00 AM
1    |  2   |  null                       |  January 1 2014 12:00 AM

(很明显,即使null排序为“最大”,此版本也能正常工作)

那么db使用索引的能力呢?

我不知道。 (我没有好的盒子/样品套装可以测试......)

它取决于优化器的智能程度,以及它是否可以从SELECT列表中提取常量。如果是这样,我希望它会扫描相关指数两次 - 一旦上升,一次下降,从范围的开始。

答案 4 :(得分:0)

简单:

select  *
from test
order by case when date < now() then 1 else 0 end, date;