4

クライアントから私に与えられた、少し奇妙な質問があります。

彼はデータのリストを持っており、括弧の間に日付があります。

Foo (14/08/2012)
Bar (15/08/2012)
Bar (16/09/2012)
Xyz (20/10/2012)

ただし、彼はリストを次のように表示することを望んでいます。

Foo (14/08/2012)
Bar (16/09/2012)
Bar (15/08/2012)
Foot (20/10/2012)

(2番目のバーが1つ上に移動したことに注意してください)

したがって、その背後にあるロジックは、2つの行が同じ名前(「バー」)である場合を除いて、リストを日付の昇順でソートする必要があるということです。それらが同じ名前である場合は、他の並べ替え順序のままで、最新の日付を先頭にして並べ替える必要があります。

これはリモートでも可能ですか?私はたくさんのORDER BY句を試しましたが、正しい句を見つけることができませんでした。誰かアイデアがありますか?

このデータがSQLサーバーデータベースのテーブルからのものであることを指定する必要があります(名前と日付は2つの異なる列にあります)。だから私は私が望むソートを行うことができるSQLクエリを探しています。

(私はこの例をかなり呟いたので、より多くのコンテキストが必要な場合は、遠慮なく質問してください)

4

5 に答える 5

4

これはうまくいくと思います

declare @t table (data varchar(50), date datetime)

insert @t 
values
('Foo','2012-08-14'),
('Bar','2012-08-15'),
('Bar','2012-09-16'), 
('Xyz','2012-10-20')

select t.*
from @t t
    inner join (select data, COUNT(*) cg, MAX(date) as mg from @t group by data) tc
        on t.data = tc.data
order by case when cg>1 then mg else date end, date desc

を生成します

data       date
---------- -----------------------
Foo        2012-08-14 00:00:00.000
Bar        2012-09-16 00:00:00.000
Bar        2012-08-15 00:00:00.000
Xyz        2012-10-20 00:00:00.000
于 2012-08-14T14:58:16.717 に答える
2

他の投稿された回答よりも優れたパフォーマンスを実現する方法は、 or を使用ORDER BYせずに完全に実行することです。JOINCTE

DECLARE @t TABLE (myData varchar(50), myDate datetime)

INSERT INTO @t VALUES 
('Foo','2012-08-14'),
('Bar','2012-08-15'),
('Bar','2012-09-16'), 
('Xyz','2012-10-20')

SELECT *
FROM @t t1
ORDER BY (SELECT MIN(t2.myDate) FROM @t t2 WHERE t2.myData = t1.myData), T1.myDate DESC

これはまさにあなたが要求したことを行い、任意のインデックスで機能し、他のどの回答よりも大量のデータではるかに優れています.

さらに、結合の複雑さで実際のロジックを隠したり、結合されたアイテムの数をチェックしたりするよりも、ここで実際に何をしようとしているのかがより明確になります。

于 2012-08-15T06:48:27.647 に答える
1

これは分析関数を使用して並べ替えを実行します。テーブルからの SELECT は 1 つだけ必要です。

内側のクエリは、名前が変わるギャップを見つけます。これらのギャップは、次のクエリでグループを識別するために使用され、外側のクエリはこれらのグループによる最終的な並べ替えを行います。

ここ(SQL Fiddle)で拡張テストデータを試してみました。

SELECT name, dat
FROM (
  SELECT name, dat, SUM(gap) over(ORDER BY dat, name) AS grp
  FROM (
    SELECT name, dat,
          CASE WHEN LAG(name) OVER (ORDER BY dat, name) = name THEN 0 ELSE 1 END AS gap
    FROM t
  ) x
) y
ORDER BY grp, dat DESC

拡張テストデータ

('Bar','2012-08-12'),
('Bar','2012-08-11'),
('Foo','2012-08-14'),
('Bar','2012-08-15'),
('Bar','2012-08-16'),
('Bar','2012-09-17'),
('Xyz','2012-10-20')

結果

Bar     2012-08-12
Bar     2012-08-11
Foo     2012-08-14
Bar     2012-09-17
Bar     2012-08-16
Bar     2012-08-15
Xyz     2012-10-20
于 2012-08-16T06:14:38.203 に答える
0

私がコメントで尋ねたケースを含め、これはうまくいくと思います:

declare @t table (data varchar(50), [date] datetime)

insert @t 
values
('Foo','20120814'),
('Bar','20120815'),
('Bar','20120916'), 
('Xyz','20121020')

; With OuterSort as (
    select *,ROW_NUMBER() OVER (ORDER BY [date] asc) as rn from @t
)
--Now we need to find contiguous ranges of the same data value, and the min and max row number for such a range
, Islands as (
    select data,rn as rnMin,rn as rnMax from OuterSort os where not exists (select * from OuterSort os2 where os2.data = os.data and os2.rn = os.rn - 1)
    union all
    select i.data,rnMin,os.rn
    from
        Islands i
            inner join
        OuterSort os
            on
                i.data = os.data and
                i.rnMax = os.rn-1
), FullIslands as (
    select
        data,rnMin,MAX(rnMax) as rnMax
    from Islands
    group by data,rnMin
)
select
    *
from
    OuterSort os
        inner join
    FullIslands fi
        on
            os.rn between fi.rnMin and fi.rnMax
order by
    fi.rnMin asc,os.rn desc

OuterSort最初にCTEの初期順序を計算することによって機能します。次に、2 つの CTE (IslandsおよびFullIslands) を使用して、同じデータ値が隣接する行に現れる順序の部分を計算します。これを行うと、すべての隣接する値が持つ任意の値 (それらが属する「島」の最小行番号など) によって最終的な順序付けを計算できます。次に、「島」内で、次の逆を使用します。最初に計算されたソート順。

ただし、これは大規模なデータ セットにはあまり効率的ではない場合があることに注意してください。サンプル データでは、ベース テーブルの 4 つのテーブル スキャンとスプールが必要であることが示されています。

于 2012-08-15T06:35:14.057 に答える
-2

次のようなものを試してください...

ORDER BY CASE date
    WHEN '14/08/2012' THEN 1
    WHEN '16/09/2012' THEN 2
    WHEN '15/08/2012' THEN 3
    WHEN '20/10/2012' THEN 4
END

MySQL では、次のことができます。

ORDER BY FIELD(date, '14/08/2012', '16/09/2012', '15/08/2012', '20/10/2012')

Postgres では、関数 FIELD を作成して以下を実行できます。

CREATE OR REPLACE FUNCTION field(anyelement, anyarray) RETURNS numeric AS $$
  SELECT
    COALESCE((SELECT i
              FROM generate_series(1, array_upper($2, 1)) gs(i)
              WHERE $2[i] = $1),
             0);
$$ LANGUAGE SQL STABLE

CASE を使用したくない場合は、FIELD 関数の SQL Server への実装を探すことができます。

于 2012-08-14T14:59:51.240 に答える