これはRedditに関する質問で出てきたばかりで、
PARTITIONED OUTER JOIN
とは何ですか? (定義)PARTITIONED OUTER JOIN
を欠くPostgreSQLまたは標準SQLの観点から、それをどのように記述しますか? (等価){1}パーティション化された外部結合:定義
... "このような結合は、クエリで定義された各論理パーティションに外部結合を適用することにより、従来の外部結合構文を拡張します。Oracleは、PARTITION BY句で指定した式に基づいて、クエリの行を論理的に分割します。パーティション化された外部結合は、論理的にパーティション化されたテーブル内の各パーティションの外部結合と、結合の反対側にあるテーブルとのUNIONです。」 ( ドキュメント )
{2}簡単な例
「データは通常スパースな形式で格納されます。つまり、特定のディメンション値の組み合わせに値が存在しない場合、ファクトテーブルには行が存在しません。ただし、データを密に表示したい場合があります。フォーム。ファクトデータが存在しない場合でも、ディメンション値のすべての組み合わせの行が表示されます。
...「たとえば、特定の期間中に製品が販売されなかった場合でも、その期間の製品の横に売上高がゼロの製品を表示したい場合があります。」 ( documentation からの引用)
テストテーブルとデータ(INSERT)
-- Oracle 12c
create table sales (
date_ date
, location_ varchar2( 16 )
, qty_ number
);
create table locations (
name varchar2( 16 )
);
-- dates for locations are "gappy":
-- none of the locations has entries for all 3 dates
-- ( date range: 2019-01-15 - 2019-01-17 )
insert into sales ( date_, location_, qty_ )
values ( date '2019-01-17', 'London', 11 ) ;
insert into sales ( date_, location_, qty_ )
values ( date '2019-01-15', 'London', 10 ) ;
insert into sales ( date_, location_, qty_ )
values ( date '2019-01-16', 'Paris', 20 ) ;
insert into sales ( date_, location_, qty_ )
values ( date '2019-01-17', 'Boston', 31 ) ;
insert into sales ( date_, location_, qty_ )
values ( date '2019-01-16', 'Boston', 30 ) ;
-- locations
insert into locations ( name ) values ( 'London' );
insert into locations ( name ) values ( 'Paris' );
insert into locations ( name ) values ( 'Boston' );
必要な出力
date_ location_ qty_
2019-01-15 London 10
2019-01-15 Paris 0 -- not INSERTed!
2019-01-15 Boston 0 -- not INSERTed!
2019-01-16 London 0 -- not INSERTed!
2019-01-16 Paris 20
2019-01-16 Boston 30
2019-01-17 London 11
2019-01-17 Paris 0 -- not INSERTed!
2019-01-17 Boston 31
クエリ(パーティション化された外部結合)
select S.date_, S.qty_, L.name
from sales S partition by ( date_ )
right join locations L on S.location_ = L.name
;
-- result
DATE_ QTY_ NAME
15-JAN-19 NULL Boston
15-JAN-19 10 London
15-JAN-19 NULL Paris
16-JAN-19 30 Boston
16-JAN-19 NULL London
16-JAN-19 20 Paris
17-JAN-19 31 Boston
17-JAN-19 11 London
17-JAN-19 NULL Paris
クエリ(バージョン2、同じ結合)
-- same as above, using NVL(), column aliases, and ORDER BY ...
select S.date_, nvl( S.qty_, 0 ) as sold, L.name as location
from sales S partition by ( date_ )
right join locations L on S.location_ = L.name
order by S.date_, L.name
;
DATE_ SOLD LOCATION
--------- ---------- ----------------
15-JAN-19 0 Boston
15-JAN-19 10 London
15-JAN-19 0 Paris
16-JAN-19 30 Boston
16-JAN-19 0 London
16-JAN-19 20 Paris
17-JAN-19 31 Boston
17-JAN-19 11 London
17-JAN-19 0 Paris
{3}同等
次のクエリは、PARTITION BY(date_)外部結合とほぼ同じ働きをします。 CROSS JOIN(内部SELECT)とLEFT OUTER JOINの組み合わせを使用しています。 (NULLから0への変換は省略)
オラクル
select SL.*, S.qty_
from
(
select *
from (
select unique date_ from sales
) , (
select unique name from locations
)
) SL left join (
select date_, location_, qty_ from sales
) S on SL.name = S.location_ and SL.date_ = S.date_
order by SL.date_, SL.name
;
DATE_ NAME QTY_
15-JAN-19 Boston NULL
15-JAN-19 London 10
15-JAN-19 Paris NULL
16-JAN-19 Boston 30
16-JAN-19 London NULL
16-JAN-19 Paris 20
17-JAN-19 Boston 31
17-JAN-19 London 11
17-JAN-19 Paris NULL
PostgreSQL 10( dbfiddle )
-- DDL and INSERTs
create table sales (
date_ date
, location_ varchar( 16 )
, qty_ number
);
create table locations (
name varchar( 16 )
);
insert into sales ( date_, location_, qty_ ) values
( '2019-01-17', 'London', 11 )
, ( '2019-01-15', 'London', 10 )
, ( '2019-01-16', 'Paris', 20 )
, ( '2019-01-17', 'Boston', 31 )
, ( '2019-01-16', 'Boston', 30 )
insert into locations ( name ) values
( 'London' ), ( 'Paris' ), ( 'Boston' );
クエリ(Postgres)
-- SL: all date_ <-> location combinations
-- S: all location_ and qty_ values of table sales
select SL.*, S.qty_
from
(
select *
from (
select distinct date_ from sales
) S_ cross join (
select distinct name from locations
) L_
) SL left join (
select date_, location_, qty_ from sales
) S on SL.name = S.location_ and SL.date_ = S.date_
order by SL.date_, SL.name
;
date_ name qty_
2019-01-15 Boston
2019-01-15 London 10
2019-01-15 Paris
2019-01-16 Boston 30
2019-01-16 London
2019-01-16 Paris 20
2019-01-17 Boston 31
2019-01-17 London 11
2019-01-17 Paris