2016-07-28 5 views
1

結果が2つのばらばらの範囲になると、範囲の差分演算が失敗する理由を理解します。しかし、私は回避策が何であるか分かりません。任意の範囲に含まれていない数字を見つける

単純な概念的な例は、私がカレンダーを保持していて、各会議の記録があり、会議の時間を格納するタイムスタンプの範囲フィールドがある場合です。人がその日に無料だった時間のリストを生成する簡単な方法は何でしょうか?

範囲とは基本的なやりとりのように思えますが、それほど複雑ではない方法を思いつくことはできません。

+0

[編集]あなたの質問とそのデータに基づいていくつかのサンプルデータと予想される出力を追加します。 _Formatted_ text please、[スクリーンショットなし](http://meta.stackoverflow.com/questions/285551/why-may-i-not-upload-images-of-code-on-so-when-asking-a-質問/ 285557#285557) –

+0

以下の両方の回答には、私が心に留めていたものと非常によく似た例がありますので、この時点で例を追加することはおそらく役に立ちません。しかし、私はその提案に感謝し、将来の質問でそれを行うでしょう。 –

答えて

1
create or replace function range_exclude(anyelement, anyelement) returns anyarray as $$ 
declare 
    r1 text; 
    r2 text; 
begin 
    -- Check input parameters 
    if not pg_typeof($1) in ('numrange'::regtype, 'int8range'::regtype, 'daterange'::regtype, 'tstzrange'::regtype) then 
    raise exception 'Function accepts only range types but got % type.', pg_typeof($1); 
    end if; 

    -- If result is single element 
    if ($1 &< $2 or $1 &> $2) then 
    return array[$1 - $2]; 
    end if; 

    -- Else build array of two intervals 
    if lower_inc($1) then r1 := '['; else r1 := '('; end if; 
    r1 := r1 || lower($1) || ',' || lower($2); 
    if lower_inc($2) then r1 := r1 || ')'; else r1 := r1 || ']'; end if; 

    if upper_inc($2) then r2 := '('; else r2 := '['; end if; 
    r2 := r2 || upper($2) || ',' || upper($1); 
    if upper_inc($1) then r2 := r2 || ']'; else r2 := r2 || ')'; end if; 
    return array[r1, r2]; 
end $$ immutable language plpgsql; 

create or replace function range_exclude(anyelement, anyarray) returns anyarray as $$ 
declare 
    i int; 
    j int; 
begin 
    -- Check input parameters 
    if not pg_typeof($1) in ('numrange'::regtype, 'int8range'::regtype, 'daterange'::regtype, 'tstzrange'::regtype) then 
    raise exception 'Function accepts only range types but got % type.', pg_typeof($1); 
    end if; 

    if array_length($2,1) is null then 
    return array[$1]; 
    end if; 

    $0 := range_exclude($1,$2[array_lower($2,1)]); 
    for i in array_lower($2,1) + 1 .. array_upper($2,1) loop 
    select array(select x from (select unnest(range_exclude(x,$2[i])) from unnest($0) as t(x)) as t(x) where not isempty(x)) into $0; 
    end loop; 
    return $0; 
end $$ immutable language plpgsql; 

select range_exclude(numrange(8,17), array[numrange(10,11), numrange(13,20)]); 

テスト:

select range_exclude(numrange(1,10), numrange(5,6)); 
select range_exclude(numrange(8,17), array[numrange(10,11), numrange(13,15)]); 

結果:タイムスタンプ用

{"[1,5)","[6,10)"} 
{"[8,10)","[11,13)","[15,17)"} 

と同じ:

select range_exclude(
    tstzrange('2016-07-28 8:00','2016-07-28 17:00'), 
    array[ 
    tstzrange('2016-07-28 10:00','2016-07-28 11:00'), 
    tstzrange('2016-07-28 13:00','2016-07-28 15:00')]); 

結果:

{"[\"2016-07-28 08:00:00+03\",\"2016-07-28 10:00:00+03\")","[\"2016-07-28 11:00:00+03\",\"2016-07-28 13:00:00+03\")","[\"2016-07-28 15:00:00+03\",\"2016-07-28 17:00:00+03\")"} 
+0

完璧に見えます、ありがとうございます。私はまだ機能を作っていないので、これは私の質問に答えるだけでなく、次回から必要なときに作業するモデルになるため、多くの役に立ちます。私は本当に助けに感謝します。 –

1

範囲は、窓関数lead()またはlag()互いに素使用している場合:

create table plan (duration tsrange); 
insert into plan values 
('[2015-01-01 10:00:00, 2015-01-01 12:00:00)'), 
('[2015-01-01 14:00:00, 2015-01-01 16:00:00)'), 
('[2015-01-01 18:00:00, 2015-01-01 20:00:00)'); 

select tsrange(upper(duration), lower(lead(duration) over (order by duration))) free_time 
from plan; 

        free_time     
----------------------------------------------- 
["2015-01-01 12:00:00","2015-01-01 14:00:00") 
["2015-01-01 16:00:00","2015-01-01 18:00:00") 
["2015-01-01 20:00:00",) 
(3 rows)  
+0

うん、それはほとんどのもので動作します。オーバーラップ範囲に問題があるようですが、2番目の会議が13:00〜16:00になるように例を変更すると、出力が上限よりも高い範囲の出力であるためエラーが発生しますバウンド。私はそれが私のデータのための問題だとは思わないので、これは私の特定の問題のための素敵な簡単な答えです - とても感謝します。 –

+0

私は厳密にあなたの質問に答えました。範囲が重複する場合は、クエリにいくつかの変更が必要であることは明らかです。 – klin

+0

合意して、それは良い答えです、私はそれをupvoteを与えた。それは私の頭の中にあったが、私の質問では綴られていないすべてのシナリオをカバーしていないあなたのせいではありません。 –

関連する問題