web-dev-qa-db-ja.com

xが日付の場合、「(ORDER BY x RANGE BETWEEN n PRECEDING ...)」の意味は何ですか?

別のスレッドで:

https://stackoverflow.com/questions/37759659/db2-query-to-find-average-sale-for-each-item-1-year-previous

oPは過去365日間の移動平均を求めていました。 ROWS BETWEEN ...の使用は、1日に1回だけ発生することが保証されている場合は問題ありませんが、ここではそうではありません。 RANGE BETWEEN ...は適切なように見えますが、DB2でそれが何を意味するのかは明確ではありません。 db2にINTERVALタイプがないことが重要かどうかはわかりませんが、ラベル付きの期間でそれを模倣します。

ドキュメントは言う:( https://www.ibm.com/support/knowledgecenter/SSEPGG_10.5.0/com.ibm.db2.luw.sql.ref.doc/doc/r0023461.html

unsigned-constant PRECEDING

現在の行の前の行の範囲または数を指定します。 ROWSが指定されている場合、unsigned-constantはゼロまたは行数を示す正の整数でなければなりません。 RANGEが指定されている場合、unsigned-constantのデータ型は、window-order-clauseのsort-key-expressionの型と同等でなければなりません。 sort-key-expressionは1つしか存在できず、sort-key-expressionのデータ型は減算を許可する必要があります。 group-bound1がCURRENT ROWまたはunsigned-constant FOLLOWINGの場合、この句はgroup-bound2で指定できません。

符号なし定数

現在の行に続く行の範囲または数を指定します。 ROWSが指定されている場合、unsigned-constantはゼロまたは行数を示す正の整数でなければなりません。 RANGEが指定されている場合、unsigned-constantのデータ型は、window-order-clauseのsort-key-expressionの型と同等でなければなりません。 sort-key-expressionは1つしか存在できず、sort-key-expressionのデータ型は加算を許可する必要があります。

DB2では、次のような構成が可能です。

values current_date - 1

デフォルトの単位日は日なので、これは次のことを意味します。

values current_date - 1 day

これを考えると、この例が機能すると期待します:

create table test 
( d date not null
, x decimal(3,0) not null);

insert into test (d,x) 
values ('2016-01-01',10),('2016-01-07',20),('2016-01-12',30);

私はクエリを期待します:

select d, avg(x) over (order by d 
                       range between 30 preceding 
                                 and current row) 
from test 
order by d;

戻るには:

2016-01-01-00.00.00  10
2016-01-07-00.00.00  15
2016-01-12-00.00.00  20

またはエラーを生成する可能性がありますが、結果は次のとおりです。

2016-01-01-00.00.00  10
2016-01-07-00.00.00  20
2016-01-12-00.00.00  25

また、クエリに日を追加してみました。

select d, avg(x) over (order by d 
                       range between 30 days preceding

select d, avg(x) over (order by d 
                       range between (cast 30 as day) preceding

念のためですが、これらの試みは両方とも次の結果になります。

 SQL0104N  An unexpected token "day" was found following "y ...
 SQL0104N  An unexpected token "cast(30 as day)" was found following "r ...

最初の疑いは、単位が1日よりも小さいことですが、前の部分を300に増やすと同じ結果が返されます。おそらくさらに驚くべきことは、500に増やすと結果が次のように変わるということです。

 select d, avg(x) over (order by d 
                        range between 500 preceding 
                                  and current row) 
 from test 
 order by d

2016-01-01-00.00.00  10
2016-01-07-00.00.00  20
2016-01-12-00.00.00  30

クエリが与えられた場合:

 select d, avg(x) over (order by d 
                        range between n preceding 
                                  and current row) 

300 <= n <= 399の場合、最後の行の結果は25です。n<300またはn> 399の場合、最後の行の結果は30です。

Avg関数でどの行が表示されるかを理解できません。おそらく、フレーミング句に他の何かへの暗黙の日付キャストがあると思いますが、この仮定を証明または反証する方法はわかりません。誰かがこれに光を当てることができますか?

5
Lennart

まず、式_values current_date - 1_は、Oracle互換モードが有効な場合にのみ有効になります。これは、デフォルトの間隔が(潜在的に小数の)日で表されるOracleの日時演算を模倣します。

Oracleの互換性に関係なく、範囲の境界は整数として比較できるはずであり、DATE値を整数と比較すると予期しない結果が生じる可能性があると思います。 DATEsを過去のある日からの日数に変換すると、整数比較を使用できます。たとえば、JULIAN_DAY()を使用できます。

_select d, avg(x) over (order by julian_day(d) 
                   range between 30 preceding 
                             and current row) 
from test 
order by d
_

あなたが期待する結果を生み出します:

_D          2                                
---------- ---------------------------------
01/01/2016   10.0000000000000000000000000000
01/07/2016   15.0000000000000000000000000000
01/12/2016   20.0000000000000000000000000000

  3 record(s) selected.
_

10.5の最初のフィックスパックでは、日付の範囲を使用できましたが、結果は予測できませんでした。最近のフィックスパックではこれが許可されなくなったため、最近のフィックスパックを使用することで、問題の混乱の多くを回避できたはずです。

4
mustaccio