web-dev-qa-db-ja.com

最も近い時間で2つのテーブルを結合する、PostgreSQL 9.6

テーブルは2つあります:tbl1、tbl2。

CREATE TABLE tbl1(time_1)
AS VALUES
  ( '2017-09-06 15:26:03'::timestamp ),
  ( '2017-09-06 15:26:02' ),
  ( '2017-09-06 15:28:01' ),
  ( '2017-09-06 15:40:00' );

CREATE TABLE tbl2(time_2)
AS VALUES
  ( '2017-09-06 15:29:01'::timestamp ),
  ( '2017-09-06 15:40:00' ),
  ( '2017-09-06 15:23:59' ),
  ( '2017-09-06 15:45:58' );

テーブルに参加したいので、tbl1のすべての行がtbl2の最も近い時間に一致します。出力は次のとおりです。

     time_1                     time_2
---------------------      --------------------
 2017-09-06 15:26:03      2017-09-06 15:23:59      
 2017-09-06 15:26:02      2017-09-06 15:23:59 
 2017-09-06 15:28:01      2017-09-06 15:29:01
 2017-09-06 15:40:00      2017-09-06 15:45:58

私は最も近い時間の単一の値を見つける方法を知っています:

SELECT * from tbl1 where time_1=INPUT_TIME ORDER BY case when time_1 > INPUT_TIME then time_1 - INPUT_TIME else INPUT_TIME - time_1 end limit 1;
4
delkov

btree_Gistおよび<->の使用

あなたは本当に<->が必要です。これは内部tstz_distを通じてGistインデックスで実際に機能します。あなたは本当に間隔を気にしないので、これはうまくいきます。まず、拡張子を追加する必要があります。

CREATE EXTENSION btree_Gist;

その後

SELECT DISTINCT ON (time_1) time_1, time_2
FROM tbl1
CROSS JOIN tbl2
ORDER BY time_1, time_1 <-> time_2;

行が多すぎてクロス結合を実行できない場合は、ラテラルソリューションの方がうまくいく場合があります。

SELECT time_1, time_2
FROM tbl1
CROSS JOIN LATERAL (
  SELECT time_2
  FROM tbl2
  ORDER BY time_1<->time_2
  LIMIT 1
) AS tbl2;

ベンチマークに関するレポートを確認することに関心があります。 =)特に、time_1とtime_2にGistインデックスがある場合。

4
Evan Carroll

次に、ラテラル結合を使用した1つの試みを示します。

select time_1, time_2
from tbl1 
cross join lateral (
    select time_2 
    from tbl2 
    order by case when time_1 > time_2 then age(time_1,time_2) 
                                       else age(time_2,time_1) 
             end 
    fetch first 1 rows only
) as t;

別の可能性は、サブクエリを使用することです:

select time_1, (
    select time_2 
    from tbl2 
    order by case when time_1 > time_2 then age(time_1,time_2) 
                                       else age(time_2,time_1) 
             end 
    fetch first 1 rows only
)
from tbl1;

タイムスタンプの差の絶対値を決定するより洗練された方法がおそらくあります。注目に値するのは、テーブルの順序を変更すると結果が異なることです。

1
Lennart
select dt1.time_1, 
       dt1.time_2 
from
(select row_number() over(partition by time_1 order by abs(extract('Epoch' from time_1) - extract('Epoch' from time_2)) asc) as rn,
        time_1,
        time_2
from    tbl1
cross
join    tbl2
) dt1
where dt1.rn = 1
order by dt1.time_1
  • 派生テーブル内dt1 ...
  • 2つのテーブルでcross join(別名デカルト積)を実行します...
  • Epoch回の差の絶対値を使用して、同じtime_1値を持つ行のグループの行番号付けを並べ替えます。その後...
  • 外部クエリでは、各dt1.rn = 1値の最初の行(time_1)に出力を制限します

結果:

time_1              | time_2             
------------------- | -------------------
2017-09-06 15:26:02 | 2017-09-06 15:23:59
2017-09-06 15:26:03 | 2017-09-06 15:23:59
2017-09-06 15:28:01 | 2017-09-06 15:29:01
2017-09-06 15:40:00 | 2017-09-06 15:40:00

注:最後のtime_1 = 2017-09-06 15:40:00については、質問で提案されたものとは異なるtime_2の一致が得られます。

-- suggested in the question:

     time_1                     time_2
---------------------      --------------------
 2017-09-06 15:40:00      2017-09-06 15:45:58

これが dbfiddle です

1
markp-fuso