web-dev-qa-db-ja.com

2列の最小値で並べ替え

私が使う SQL Server 2008 R2

2つの列の最小値でテーブルをソートする必要があります。

テーブルは次のようになります。

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

データを最低2つの日付で並べ替えたい。

このテーブルをそのように並べ替える最も簡単な方法は何ですか?

51
Vasiliy Volkov

NOT NULLカラム[〜#〜] case [〜#〜]ステートメントをORDER BY句に追加する必要があります:

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

NULLABLE列Zohar Peledが列がnull可能である場合にコメントに書き込んだように、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-01here について読むことができます。

_ORDER BY_でCASE式を使用します。

_ ORDER BY case when date1 < date2 then date1 else date2 end
_

編集:

null値を考慮する必要がある場合は、coalesce()を追加します。

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

説明:

Date1 <date2の場合、date1で並べ替えます。 (ここではどちらの日付もnullではありません。)以前と同じように機能します。

そうでない場合は、COALESCE()を使用して、date2(date2がnullでない場合)、またはdate1(date2がnullの場合)、またはnull(両方の日付がnullの場合)の順に並べます。

35
jarlh

最も簡単な方法は、次のように VALUES キーワードを使用することです。

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

このコードは、nullable列でも、すべてのケースで機能します。

編集:

ソリューションCOALESCEキーワードを使用すると、普遍的ではありません。これには重要な制限があります。

  • 列がDateタイプの場合は機能しません(01/01/1753より前の日付を使用する場合)
  • 列の1つがNULLの場合は機能しません。 NULL値を最小のdatetime値として解釈します。しかし、それは本当ですか? datetimeでもなく、何もありません。
  • 2つ以上列を使用すると、IF式はさらに複雑になります。

質問によると:

このテーブルをそのようにソートする最も簡単な方法は何ですか?

最短かつ最も簡単な解決策は、上記の解決策です。

  • それを実装するのに多くのコーディングは必要ありません-単に1行追加するだけです。
  • 列がnull入力可能かどうかを気にする必要はありません。コードを使用するだけで機能します。
  • カンマの後ろに列を追加するだけで、クエリの列数を拡張できます
  • これはDate列で機能します。コードを変更する必要はありません。

編集2:

Zohar Peledは、次の順序を提案しました。

私はこのルールで行を並べ替えます:最初に、両方がnullの場合、2番目、date1がnullの場合、3番目、date 2がnullの場合、4番目、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

COALESCEsolutionは、この方法でテーブルをソートしません。それはmesses upNULL値の少なくとも1つのセルがある行。その出力は次のとおりです。

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

これが役に立ち、批評家を待つことを願っています。

8
dyatchenko

これは、_CASE WHEN_のように分岐を必要としない代替ソリューションの可能性があります。これは、式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
Arun Gairola

Case statementOrder Byを使用しない場合は、Case statementSelectに移動するだけの別のアプローチです。

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
5
pjobs

最大のコード

私は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
4
EarlOfEnnui

私はこの方法でnull許容列を処理することを好みます。

SELECT Id, Date1, Date2
FROM YourTable
ORDER BY 
   CASE 
     WHEN Date1 < Date2 OR Date1 IS NULL THEN Date1 
     ELSE Date2 
   END 
4
Jesús López

howからフォーカスをwhyに変更します。これが必要です-代わりにスキーマを変更することを提案します。経験則は次のとおりです。データにアクセスするためにスタントをプルする必要がある場合、設計上の決定は不適切です。

ご覧のように、このタスクはSQLでは非常に一般的ではないため、可能ではありますが、提案されたすべてのメソッドは通常のORDER BY

  • これを頻繁に行う必要がある場合は、2つの日付の最小値がアプリケーションにとって独立した物理的な意味を持つ必要があります。
    • これは、別の列(またはおそらく2つのうちの1つを置き換える列)を正当化します-場合によっては列がおそらくどちらでもないほど十分に独立している場合、トリガーまたは手動でさえ維持されます。
2
ivan_pozdeev

date1date2の両方のフィールドで並べ替える場合は、次のようにORDER 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
2
shA.t

私はこのルールで行を並べます:

  1. 両方がnullの場合
  2. date1がnullの場合
  3. 日付2がnullの場合
  4. 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,
CASE 
  WHEN Date1 < Date2 THEN Date1
  ELSE Date2
END
1
Zohar Peled

別のオプションがあります。必要なロジックによって結果列を計算し、列による順序付けで外部列による選択をカバーできます。この場合、コードは次のようになります。

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

このソリューションの利点は、必要なフィルタリングクエリを(内部選択で)追加でき、それでもインデックスが役立つことです。

1
Sandr

_order by_句でmin関数を使用できます。

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

_order by_句でcaseステートメントを使用することもできますが、(_>_および_<_)の任意の値(nullまたはnone null)とnullを比較した結果はtrueではありません。 _ansi_nulls_をoffに設定しました。したがって、目的の並べ替えを保証するには、null句でわかっているように、caseの結果がwhenである場合はtrueステートメントを評価しないため、whensを処理する必要があります。

_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句内の条件を使用せずに、その計算済みフィールドで並べ替えます。

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

@dyatchenkoの回答に非常に似ていますが、NULLの問題はありません

0
AlexK