私はこのようなテーブルを持っています:
Number Price Type Date Time
------ ----- ---- ---------- ---------
23456 0,665 SV 2014/02/02 08:00:02
23457 1,3 EC 2014/02/02 07:50:45
23460 0,668 SV 2014/02/02 07:36:34
ECごとに、前/次のSV価格が必要です。この場合、クエリは単純です。
Select Lag(price, 1, price) over (order by date desc, time desc),
Lead(price, 1, price) over (order by date desc, time desc)
from ITEMS
ただし、2つ以上の行がECタイプである特殊なケースがいくつかあります。
Number Price Type Date Time
------ ----- ---- ---------- ---------
23456 0,665 SV 2014/02/02 08:00:02
23457 1,3 EC 2014/02/02 07:50:45
23658 2,4 EC 2014/02/02 07:50:45
23660 2,4 EC 2014/02/02 07:50:48
23465 0,668 SV 2014/02/02 07:36:34
この場合、リード/ラグを使用できますか?そうでない場合、サブクエリを使用する必要がありましたか?
あなたの質問(そしてアノンの優れた答え)は 島とギャップのSQL の一部です。この回答では、「row_number()マジック」について詳しく調べてみます。
ボールゲームのイベントに基づいて簡単な例を作成しました。イベントごとに、前四半期と次の四半期に関連するメッセージを印刷します。
_create table TestTable (id int identity, event varchar(64));
insert TestTable values
('Start of Q1'),
('Free kick'),
('Goal'),
('End of Q1'),
('Start of Q2'),
('Penalty'),
('Miss'),
('Yellow card'),
('End of Q2');
_
「row_number()マジック」アプローチを示すクエリは次のとおりです。
_; with grouped as
(
select *
, row_number() over (order by id) as rn1
, row_number() over (
partition by case when event like '%of Q[1-4]' then 1 end
order by id) as rn2
from TestTable
)
, order_in_group as
(
select *
, rn1-rn2 as group_nr
, row_number() over (partition by rn1-rn2 order by id) as rank_asc
, row_number() over (partition by rn1-rn2 order by id desc)
as rank_desc
from grouped
)
select *
, lag(event, rank_asc) over (order by id) as last_event_of_prev_group
, lead(event, rank_desc) over (order by id) as first_event_of_next_group
from order_in_group
order by
id
_
row_number()
を計算します。 1つ目は、テーブルの各行の_1 2 3
_です。 2番目のrow_number()
は、一時停止のアナウンスを1つのリストに配置し、他のイベントを2番目のリストに配置します。 2つの違いである_rn1 - rn2
_は、ゲームのセクションごとに異なります。例の出力の違いを確認すると便利です。それは_group_nr
_列にあります。各値がゲームの1つのセクションに対応していることがわかります。1 2 3
_、降順の場合は_3 2 1
_です。lag()
とlead()
にジャンプする距離を指示するのに十分な知識があります。前のセクションの最後の行を見つけるには、_rank_asc
_行を遅らせる必要があります。次のセクションの最初の行を見つけるには、_rank_desc
_行をリードする必要があります。これがギャップと島の「魔法」を明らかにするのに役立つことを願っています。 これはSQL Fiddleでの実例です。
はい、LEAD/LAGを使用できます。少しのROW_NUMBER()マジックを使用して、ジャンプする距離を事前に計算する必要があります。
DECLARE @a TABLE ( number int, price money, type varchar(2),
date date, time time)
INSERT @a VALUES
(23456,0.665,'SV','2014/02/02','08:00:02'),
(23457,1.3 ,'EC','2014/02/02','07:50:45'),
(23658,2.4 ,'EC','2014/02/02','07:50:45'),
(23660,2.4 ,'EC','2014/02/02','07:50:48'),
(23465,0.668,'SV','2014/02/02','07:36:34');
; WITH a AS (
SELECT *,
ROW_NUMBER() OVER(ORDER BY [date] DESC, [time] DESC) x,
ROW_NUMBER() OVER(PARTITION BY
CASE [type] WHEN 'SV' THEN 1 ELSE 0 END
ORDER BY [date] DESC, [time] DESC) y
FROM @a)
, b AS (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY x-y ORDER BY x ASC) z1,
ROW_NUMBER() OVER(PARTITION BY x-y ORDER BY x DESC) z2
FROM a)
SELECT *,
CASE [type] WHEN 'SV' THEN
LAG(price,z1,price) OVER(PARTITION BY [type] ORDER BY x)
ELSE LAG(price,z1,price) OVER(ORDER BY x)
END,
CASE [type] WHEN 'SV' THEN
LEAD(price,z2,price) OVER(PARTITION BY [type] ORDER BY x)
ELSE LEAD(price,z2,price) OVER(ORDER BY x)
END
FROM b
ORDER BY x
同じ結果を達成するさらに別の方法がありますが、序数に対してウィンドウ処理された条件付き最大/最小関数を使用します。序数は、目的に合った列に基づいて設定できますが、この場合、OPはそれらをDate
およびTime
にすることを意図していると思います。
DROP TABLE IF EXISTS #t;
CREATE TABLE #t (
Number INT,
Price MONEY,
Type CHAR(2),
Date DATE,
Time TIME(0)
);
INSERT INTO #t VALUES
(23456, 0.666, 'SV', '2014/02/02', '10:00:02'),
(23457, 1.4 , 'EC', '2014/02/02', '09:50:45'),
(23658, 2.5 , 'EC', '2014/02/02', '09:50:45'),
(23660, 2.5 , 'EC', '2014/02/02', '09:50:48'),
(23465, 0.669, 'SV', '2014/02/02', '09:36:34'),
(23456, 0.665, 'SV', '2014/02/02', '08:00:02'),
(23457, 1.3 , 'EC', '2014/02/02', '07:50:45'),
(23658, 2.4 , 'EC', '2014/02/02', '07:50:45'),
(23660, 2.4 , 'EC', '2014/02/02', '07:50:48'),
(23465, 0.668, 'SV', '2014/02/02', '07:36:34'), -- which one of these?
(23465, 0.670, 'SV', '2014/02/02', '07:36:34'); --
WITH time_ordered AS (
SELECT *, DENSE_RANK() OVER (ORDER BY Date, Time) AS ordinal FROM #t
)
SELECT
*,
CASE WHEN Type = 'EC'
THEN MAX(CASE WHEN ordinal = preceding_non_EC_ordinal THEN Price END)
OVER (PARTITION BY preceding_non_EC_ordinal ORDER BY ordinal ASC) END AS preceding_price,
CASE WHEN Type = 'EC'
THEN MIN(CASE WHEN ordinal = following_non_EC_ordinal THEN Price END)
OVER (PARTITION BY following_non_EC_ordinal ORDER BY ordinal DESC) END AS following_price
FROM (
SELECT
*,
MAX(CASE WHEN Type <> 'EC' THEN ordinal END)
OVER (ORDER BY ordinal ASC) AS preceding_non_EC_ordinal,
MIN(CASE WHEN Type <> 'EC' THEN ordinal END)
OVER (ORDER BY ordinal DESC) AS following_non_EC_ordinal
FROM time_ordered
) t
ORDER BY Date, Time
OPによって与えられた例は、EC
の散在するシーケンスが意図した結果をもたらすことを示すために拡張されていることに注意してください。この場合、タイプSV
の最初の2つの連続する行によって導入されたあいまいさにより、最大値が選択されます。 Price
を含めるように序数を設定することは、この動作を変更するための可能な方法です。
SQLFiddleはここにあります: http://sqlfiddle.com/#!18/85117/1