2

PostgreSQL 8.3.8 を使用しています。

time_boundaries テーブルに、時間境界 (日付別) のリストがあります。

CREATE TABLE role_times_boundaries
(
  role_date DATE,
  time_boundary TIME
);

INSERT INTO role_times_boundaries (role_date, time_boundary) VALUES ('2013-04-24'::date, '09:00:00'::time);
INSERT INTO role_times_boundaries (role_date, time_boundary) VALUES ('2013-04-24'::date, '10:00:00'::time);
INSERT INTO role_times_boundaries (role_date, time_boundary) VALUES ('2013-04-25'::date, '07:00:00'::time);
INSERT INTO role_times_boundaries (role_date, time_boundary) VALUES ('2013-04-25'::date, '08:50:00'::time);
INSERT INTO role_times_boundaries (role_date, time_boundary) VALUES ('2013-04-25'::date, '09:00:00'::time);
INSERT INTO role_times_boundaries (role_date, time_boundary) VALUES ('2013-04-25'::date, '12:00:00'::time);
INSERT INTO role_times_boundaries (role_date, time_boundary) VALUES ('2013-04-25'::date, '13:00:00'::time);
INSERT INTO role_times_boundaries (role_date, time_boundary) VALUES ('2013-04-25'::date, '16:00:00'::time);
INSERT INTO role_times_boundaries (role_date, time_boundary) VALUES ('2013-04-25'::date, '17:30:00'::time);
INSERT INTO role_times_boundaries (role_date, time_boundary) VALUES ('2013-04-25'::date, '20:00:00'::time);

そして、私はこのテーブルコンテンツを持っています:

 role_date  | time_boundary 
------------+---------------
 2013-04-24 | 09:00:00
 2013-04-24 | 10:00:00
 2013-04-25 | 07:00:00
 2013-04-25 | 08:50:00
 2013-04-25 | 09:00:00
 2013-04-25 | 12:00:00
 2013-04-25 | 13:00:00
 2013-04-25 | 16:00:00
 2013-04-25 | 17:30:00
 2013-04-25 | 20:00:00

目標

各 time_boundary を「start_time」として、次の time_boundary (順序による) を同じ日付として、「role_times_boundaries」で自己内部結合を行うことにより、「タイム スライス リスト」テーブルを作成したいと思います。目標は、その結果を得ることです:

 role_date  | start_time | end_time 
------------+------------+----------
 2013-04-24 | 09:00:00   | 10:00:00
 2013-04-25 | 07:00:00   | 08:50:00
 2013-04-25 | 08:50:00   | 09:00:00
 2013-04-25 | 09:00:00   | 12:00:00
 2013-04-25 | 12:00:00   | 13:00:00
 2013-04-25 | 13:00:00   | 16:00:00
 2013-04-25 | 16:00:00   | 17:30:00
 2013-04-25 | 17:30:00   | 20:00:00

仮の

このSQLクエリを介して希望の結果を取得しようとしました

SELECT role_times_boundaries.role_date,
       role_times_boundaries.time_boundary AS start_time,
       end_time_boundaries.time_boundary AS end_time
FROM role_times_boundaries
INNER JOIN (
             SELECT role_date,
                    time_boundary
             FROM role_times_boundaries
           ) AS end_time_boundaries ON (
                                       role_times_boundaries.role_date = end_time_boundaries.role_date
                                       AND end_time_boundaries.time_boundary = (
                                                                                  SELECT MIN(a_list_of_end_boundaries.time_boundary)
                                                                                  FROM role_times_boundaries AS a_list_of_end_boundaries
                                                                                  WHERE a_list_of_end_boundaries.time_boundary > role_times_boundaries.time_boundary
                                                                                )
                                     )

結果は次のとおりです。

 role_date  | start_time | end_time 
------------+------------+----------
 2013-04-24 | 09:00:00   | 10:00:00
 2013-04-25 | 07:00:00   | 08:50:00
 2013-04-25 | 08:50:00   | 09:00:00
 2013-04-25 | 12:00:00   | 13:00:00
 2013-04-25 | 13:00:00   | 16:00:00
 2013-04-25 | 16:00:00   | 17:30:00
 2013-04-25 | 17:30:00   | 20:00:00

よく見ると、09:00:00 から 12:00:00までのタイム スライスが欠落しています。しかし、私はまだ理由を理解しておらず、まだエラーを見つけていません。

4

2 に答える 2

3

PostgreSQL 8.4 以降にアップグレードすると、、 、、などのウィンドウ関数 (Oracle 用語では「分析関数」) を使用できます。rank()row_number()lead()lag()

SELECT tb.role_date AS role_date
        , tb.time_boundary AS start_time
        , LEAD (time_boundary) OVER www AS end_time
FROM role_times_boundaries tb
WINDOW www AS (PARTITION BY tb.role_date ORDER BY tb.time_boundary)
        ;

または上記のクエリの別の同等物:

SELECT tb.role_date AS role_date
        , tb.time_boundary AS start_time
        , LEAD (time_boundary) OVER ( PARTITION BY tb.role_date ORDER BY tb.time_boundary) AS end_time
FROM role_times_boundaries tb;

次の結果セットが得られます。

 role_date  | start_time | end_time 
------------+------------+----------
 2013-04-24 | 09:00:00   | 10:00:00
 2013-04-24 | 10:00:00   | 
 2013-04-25 | 07:00:00   | 08:50:00
 2013-04-25 | 08:50:00   | 09:00:00
 2013-04-25 | 09:00:00   | 12:00:00
 2013-04-25 | 12:00:00   | 13:00:00
 2013-04-25 | 13:00:00   | 16:00:00
 2013-04-25 | 16:00:00   | 17:30:00
 2013-04-25 | 17:30:00   | 20:00:00
 2013-04-25 | 20:00:00   | 
(10 rows)

を持たないピリオドを削除するには、end_timeこれをサブクエリにまとめることができます。

SELECT role_date , start_time , end_time
FROM (
        SELECT tb.role_date AS role_date
        , tb.time_boundary AS start_time
        , LEAD (time_boundary) OVER ( PARTITION BY tb.role_date ORDER BY tb.time_boundary) AS end_time
        FROM role_times_boundaries tb
        ) sq
WHERE sq.start_time <= sq.end_time;

これにより、次の結果が得られます。

 role_date  | start_time | end_time 
------------+------------+----------
 2013-04-24 | 09:00:00   | 10:00:00
 2013-04-25 | 07:00:00   | 08:50:00
 2013-04-25 | 08:50:00   | 09:00:00
 2013-04-25 | 09:00:00   | 12:00:00
 2013-04-25 | 12:00:00   | 13:00:00
 2013-04-25 | 13:00:00   | 16:00:00
 2013-04-25 | 16:00:00   | 17:30:00
 2013-04-25 | 17:30:00   | 20:00:00
(8 rows)

NOT EXISTS更新:キーワードを使用して問題を解決する WINDOW 関数の使用を回避する別の代替クエリ:

SELECT lo.role_date
        , lo.time_boundary AS start_time
        , hi.time_boundary AS end_time
FROM role_times_boundaries lo
JOIN role_times_boundaries hi
    ON lo.role_date = hi.role_date
    AND lo.time_boundary < hi.time_boundary
    AND NOT EXISTS ( -- eliminate the men in the middle ...
        SELECT * FROM role_times_boundaries nx
        WHERE   nx.role_date = hi.role_date
        AND nx.time_boundary > lo.time_boundary
        AND nx.time_boundary < hi.time_boundary
        );
于 2013-04-27T15:38:40.067 に答える
2

解決

では、まずクエリを少し単純化しましょう。

SELECT
  l.role_date,
  l.time_boundary AS start_time,
  r.time_boundary AS end_time
FROM role_times_boundaries l
INNER JOIN role_times_boundaries AS r ON ( -- You don't need that inner query, it's redundant
  l.role_date = r.role_date
  AND r.time_boundary = (
    SELECT MIN(r2.time_boundary)
    FROM role_times_boundaries AS r2
    WHERE r2.time_boundary > l.time_boundary))

ここでの問題は、役割の日付によって制限されているものではなく、r2 のすべて のを比較しているため、修正されたクエリは次のようになります。time_boundarie

SELECT
  l.role_date,
  l.time_boundary AS start_time,
  r.time_boundary AS end_time
FROM role_times_boundaries l
INNER JOIN role_times_boundaries AS r ON (
  l.role_date = r.role_date
  AND r.time_boundary = (
    SELECT MIN(r2.time_boundary)
    FROM role_times_boundaries AS r2
    -- Note the added restriction:
    WHERE r2.time_boundary > l.time_boundary and r2.role_date = l.role_date))

代替クエリ

以下もユースケースで機能し、より読みやすい場合があります

select
  l.role_date as role_date,
  l.time_boundary as start_time,
  min(r.time_boundary) as end_time
from role_times_boundaries l
join role_times_boundaries r on
  r.role_date = l.role_date
  and r.time_boundary > l.time_boundary
group by l.role_date, l.time_boundary
order by l.role_date, l.time_boundary
于 2013-04-27T15:04:31.857 に答える