按两列的最小值排序

时间:2015-04-13 08:15:53

标签: sql sql-server tsql sorting min

我使用SQL Server 2008 R2

我需要按两列的最小值对表进行排序。

表格如下:

ID: integer; 
Date1: datetime; 
Date2: datetime.

我希望我的数据按最少两个日期排序。

以这种方式对此表进行排序的最简单方法是什么?

13 个答案:

答案 0 :(得分:57)

非空列。您需要在 CASE 条款中添加 ORDER BY 语句,如下所示:

SELECT Id, Date1, Date2
FROM YourTable
ORDER BY CASE 
           WHEN Date1 < Date2 THEN Date1 
           ELSE Date2 
         END 

NULLABLE列。正如 Zohar Peled 在评论中写道,如果列可以为空,则可以使用ISNULL(但最好使用COALESCE代替ISNULL,因为它是ANSI SQL standard )在下面:

SELECT Id, Date1, Date2
FROM YourTable
ORDER BY CASE 
           WHEN COALESCE(Date1, '1753-01-01') < COALESCE(Date2, '1753-01-01') THEN Date1 
           ELSE Date2 
         END

您可以阅读 ANSI 标准日期格式1753-01-01 here

答案 1 :(得分:33)

CASE

中使用ORDER BY表达式
 ORDER BY case when date1 < date2 then date1 else date2 end

修改

如果需要考虑空值,请添加coalesce()

 ORDER BY case when date1 < date2 then date1 else coalesce(date2,date1) end

说明:

如果date1&lt; date2然后按date1排序。 (这里的两个日期都是非空的。)就像以前一样工作。

否则使用COALESCE()按date2(当date2不为null时)或date1(当date2为null时)或null(如果两个日期都为空)。

答案 2 :(得分:8)

最简单的方法是使用VALUES关键字,如下所示:

SELECT ID, Date1, Date2
FROM YourTable
ORDER BY (SELECT MIN(v) FROM (VALUES (Date1), (Date2)) AS value(v))

此代码适用于所有情况,即使使用可空列也是如此。

修改:

带有COALESCE关键字的

The solution不是通用的。它有重要的限制:

  • 如果列属于Date类型(如果您使用01/01/1753之前的日期)
  • ,它将无法工作
  • 如果其中一列为NULL,则无法工作。它解释了 NULL值为最小datetime值。但实际上是这样吗 真正?它甚至不是datetime,它什么都不是。
  • 如果我们使用两个以上列,IF表达式将会复杂得多。

根据问题:

  

以这种方式对此表进行排序的最简单方法是什么?

最短和最简单的解决方案是上述解决方案,因为:

  • 它没有采用大量编码来实现它 - 只需添加一行即可。
  • 不需要关心关于列是否可以为空。你只需使用代码即可。
  • 只需在逗号后添加一个即可扩展查询中的列数。
  • 使用Date有效,您无需修改​​代码。

编辑2:

Zohar Peled 建议采用以下方式:

  

我会按照这个规则对行进行排序:首先,当null为空时,第二个,当date1为null时,第三个,当date2为null时,第四个,min(date1,date2)

因此,对于这种情况,可以通过使用相同的方法来达到解决方案,如下所示:

SELECT ID, Date1, Date2
FROM YourTable
ORDER BY 
CASE WHEN Date1 IS NULL AND Date2 IS NULL THEN 0
     WHEN Date1 IS NULL THEN 1
     WHEN Date2 IS NULL THEN 2
     ELSE 3 END,
(SELECT MIN(v) FROM (VALUES ([Date1]), ([Date2])) AS value(v))

此代码的输出如下:

The output result for *Zohar's* way of order

COALESCE solution 不会以这种方式对表格进行排序。它弄乱至少有一个NULL值单元格的行。它的输出如下:

Weird ORDER BY of <code>COALESCE</code> solution

希望这有助于等待批评者。

答案 3 :(得分:5)

如果您不想在Case statement中使用Order By,那么这是另一种方法,只需将Case statement移至Select

SELECT Id, Date1, Date2 FROM 
 (SELECT Id, Date1, Date2
  ,CASE WHEN Date1 < Date2 THEN Date1 ELSE Date2 END as MinDate 
FROM YourTable) as T
ORDER BY MinDate

答案 4 :(得分:5)

这可能是一种替代解决方案,不需要像CASE WHEN这样的分支。这基于here所述的公式max(a,b)=1/2(a+b+|a−b|)。我们使用DATEDIFF和参考日期('1773-01-01')获得a和b的绝对值。

ORDER BY (DATEDIFF(d,'17730101' ,isnull(Startdate,enddate)) + DATEDIFF(d,'17730101' ,isnull(EndDate,Startdate)) 
    -  ABS(DATEDIFF(d,isnull(Startdate,enddate),isnull(EndDate,Startdate))))

测试数据

Create Table #DateData(ID int Identity, Name varchar(15),Startdate datetime,EndDate DateTime)
Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-04-17 18:48:27','2015-04-18 18:48:27')
Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-04-19 18:48:27','2015-04-18 18:48:27')
Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-04-20 18:48:27','2015-04-18 18:48:27')
Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-04-11 18:48:27','2015-04-22 18:48:27')
Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-05-09 18:48:27','2015-04-18 18:48:27')
Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-04-17 19:07:38','2015-04-17 18:55:38')
Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-04-17 19:07:38','2015-05-12 18:56:29')

完成查询

select *
from #DateData order by (DATEDIFF(d,'17730101' ,isnull(Startdate,enddate)) + DATEDIFF(d,'17730101' ,isnull(EndDate,Startdate)) 
-  ABS(DATEDIFF(d,isnull(Startdate,enddate),isnull(EndDate,Startdate))))

答案 5 :(得分:4)

我更喜欢这种方式来处理可空列:

SELECT Id, Date1, Date2
FROM YourTable
ORDER BY 
   CASE 
     WHEN Date1 < Date2 OR Date1 IS NULL THEN Date1 
     ELSE Date2 
   END 

答案 6 :(得分:4)

Code for max

我正在使用CROSS APPLY,我不确定效果,但CROSS APPLY通常会有更好的表现。

CREATE TABLE #Test (ID INT, Date1 DATETIME, Date2 DATETIME)
INSERT INTO #Test SELECT 1, NULL, '1/1/1';INSERT INTO #Test SELECT 2, NULL, NULL;INSERT INTO #Test SELECT 3, '2/2/2', '3/3/1';INSERT INTO #Test SELECT 4, '3/3/3', '11/1/1'

SELECT t.ID, Date1, Date2, MinDate
FROM #TEST t
    CROSS APPLY (SELECT MIN(d) MinDate FROM (VALUES (Date1), (Date2)) AS a(d)) md
ORDER BY MinDate

DROP TABLE #Test

答案 7 :(得分:2)

我认为,当您要对date2ORDER BY这两个字段进行排序时,您应该在SELECT * FROM aTable ORDER BY CASE WHEN date1 < date2 THEN date1 ELSE date2 END, CASE WHEN date1 < date2 THEN date2 ELSE date1 END 部分中同时使用这两个字段,如下所示:

date1      | date2      
-----------+------------
2015-04-25 | 2015-04-21
2015-04-26 | 2015-04-21
2015-04-25 | 2015-04-22
2015-04-22 | 2015-04-26

结果可以是这样的:

Null

要获得具有SELECT * FROM aTable ORDER BY CASE WHEN date1 IS NULL THEN NULL WHEN date1 < date2 THEN date1 ELSE date2 END ,CASE WHEN date2 IS NULL THEN date1 WHEN date1 IS NULL THEN date2 WHEN date1 < date2 THEN date2 ELSE date1 END 值的完美结果,请使用:

date1      | date2      
-----------+------------
NULL       | NULL
NULL       | 2015-04-22
2015-04-26 | NULL
2015-04-25 | 2015-04-21
2015-04-26 | 2015-04-21
2015-04-25 | 2015-04-22

结果将如下:

cout << ch1 << '\t' << ch2 << ch3;

答案 8 :(得分:2)

我将焦点从如何转移到为什么你需要这个 - 并建议改变架构。经验法则是:如果您需要拉动特技来访问您的数据,那么设计决定就会很糟糕。

正如您所见,此任务对于SQL来说非常不典型,因此尽管可能,但与普通ORDER BY相比,所有提议的方法都非常缓慢。

  • 如果您需要经常这样做,那么两个日期中的最小日期必须对您的应用具有一些独立的物理意义。
    • 这证明了一个单独的列(或者可能是替换其中一个列的列) - 由触发器维护,或者甚至是手动的,如果含义足够独立,在某些情况下列可能都不是。

答案 9 :(得分:1)

我会按照这条规则订购行:

  1. 当两者都为null
  2. 当date1为空时
  3. 当日期2为空时
  4. min(date1,date2)
  5. 为此,嵌套的案例将简单有效(除非表格非常大)according to this post

    SELECT ID, Date1, Date2
    FROM YourTable
    ORDER BY 
    CASE 
      WHEN Date1 IS NULL AND Date2 IS NULL THEN 0
      WHEN Date1 IS NULL THEN 1
      WHEN Date2 IS NULL THEN 2
      ELSE 3 END,
    CASE 
      WHEN Date1 < Date2 THEN Date1
      ELSE Date2
    END
    

答案 10 :(得分:1)

还有另一种选择。您可以根据需要的逻辑计算结果列,并按列排序选择外部列。在这种情况下,代码如下:

select ID, x.Date1, x.Date2
from
(
    select
        ID,
        Date1,
        Date2, 
        SortColumn = case when Date1 < Date2 then Date1 else Date2 end
    from YourTable
) x
order by x.SortColumn

此解决方案的好处是您可以添加必要的过滤查询(在内部选择中),但索引仍然有用。

答案 11 :(得分:0)

您可以在min子句中使用order by函数:

select * 
from [table] d
order by ( select min(q.t) from (
           select d.date1 t union select d.date2) q
         )

你也可以在case子句中使用order by语句,但是你知道比较(><)任何值(null或none null)的结果即使您已将true设置为ansi_nulls,null也不会为off。因此,为了保证您想要的排序,您需要处理null,如case子句中when true的结果为when然后进一步{{1}没有评估语句,所以你可以说:

select * from [table]
order by case 
           when date1 is null then date2
           when date2 is null then date1 
           when date1<date2 then date1 -- surely date1 and date2 are not null here
           else date2 
         end

如果您的场景不同,这里还有一些其他解决方案,也许您可​​以评估在分离字段内比较多列(或计算)的结果,最后按计算字段排序,而不使用order by子句中的任何条件。

答案 12 :(得分:0)

SELECT ID, Date1, Date2
FROM YourTable
ORDER BY (SELECT TOP(1) v FROM (VALUES (Date1), (Date2)) AS value(v) ORDER BY v)

非常类似@dyatchenko答案,但没有NULL问题