ãã®èšäºã§ã¯ã2ã€ã®ã·ã¹ãã ã§ã«ã¹ã¿ã éèšé¢æ°ããã³ãŠã£ã³ããŠïŒOracleã®çšèªã§ã¯åæïŒé¢æ°ãäœæããæ¹æ³ã説æããŸãã æ§æã®éããšäžè¬ã«æ¡åŒµæ§ã®ã¢ãããŒãã«éããããã«ããããããããããã®é¢æ°ã®ã¡ã«ããºã ã¯éåžžã«äŒŒãŠããŸãã ããããéãããããŸãã
確ãã«ãçµã¿èŸŒã¿ã®éèšé¢æ°ãšãŠã£ã³ããŠé¢æ°ã¯éåžžã«ãŸãã§ãã äžè¬ã«ããŠã£ã³ããŠé¢æ°ã¯ãäœããã®çç±ã§ãé«åºŠãªãSQLãšããŠäŒçµ±çã«åé¡ãããŠãããç解ããã³ç¿åŸãé£ãããšèããããŠããŸãã ããã§ã¯ãDBMSã§æ¢ã«äœ¿çšå¯èœãªæ©èœãåŠçããŸãã
ãªããã®åé¡ãæãäžããã®ã§ããããïŒ ããã€ãã®çç±ãæããããšãã§ããŸãã
- ãŠã£ã³ããŠé¢æ°ã¯éåžžã®éçŽé¢æ°ããã客芳çã«è€éã§ããããããã«è¶
è¶çãªãã®ã¯ãããŸããã SQLéçºè
ã«ãšã£ãŠçµ¶å¯Ÿã«äžå¯æ¬ ãªããŒã«ã§ãã ãŸããç¬èªã®ãŠã£ã³ããŠé¢æ°ãäœæããããšã§ãéåžžã«åçŽãªå Žåã§ããæšæºã®ãŠã£ã³ããŠé¢æ°ãã©ã®ããã«æ©èœããããããããç解ã§ããŸãã
- ãŠã£ã³ããŠé¢æ°ãšéèšé¢æ°ã¯ãæç¶ãååŠçãšå®£èšåããžãã¯ãçµã¿åãããã®ã«æé©ãªæ¹æ³ã§ãã å Žåã«ãã£ãŠã¯ã1ã€ã®SQLã¯ãšãªã§åé¡ã解決ããããã®ãã©ãã€ã ã®ãã¬ãŒã ã¯ãŒã¯å
ã«ãšã©ãŸããè€éãªã¢ã¯ã·ã§ã³ãå®è¡ããããšãå€æããŸãã
- ã¯ããèå³æ·±ããããã¯ã§ããã2ã€ã®ã·ã¹ãã ãæ¯èŒããã®ã¯ããã«èå³æ·±ããããã¯ã§ãã
ãã¬ãŒãã³ã°ã®äŸã¯ãæ°å€ã®ã¿ã€ãïŒOracleã®æ°å€ïŒã®æšæºçãªavgé¢æ°ã®é¡äŒŒç©ã§ããå¹³åã®èšç®ã§ãã ãã®ãããªé¢æ°ãäœæããéçŽã¢ãŒãããã³ãŠã£ã³ããŠã¢ãŒãã§ã©ã®ããã«æ©èœããããè€æ°ã®äžŠåããã»ã¹ã§èšç®ã§ãããã©ããã確èªããŸãã çµè«ãšããŠãå®ç掻ã®äŸãèŠãŠã¿ãŸãããã
éèšé¢æ°
åçŽãªãã®ããè€éãªãã®ã«ç§»è¡ããPostgreSQLãšOracleãåãæ¿ããŸãã
ãŸããããã€ãã®äžè¬çãªèæ
®äºé
ã éèšé¢æ°ã¯ãããŒãã«ã®åè¡ã«å¯ŸããŠé çªã«åŒã³åºãããæçµçã«ãã¹ãŠãåŠçããŸãã åŒã³åºãéã§ã圌女ã¯å®è¡ã®ã³ã³ããã¹ããå®çŸ©ããå
éšç¶æ
ãç¶æããå¿
èŠããããŸãã äœæ¥ã®æåŸã«ã圌女ã¯æçµå€ãè¿ãå¿
èŠããããŸãã
ãããã£ãŠã4ã€ã®ã³ã³ããŒãã³ããå¿
èŠã§ãã
- ç¶æ
ïŒã³ã³ããã¹ãïŒã
- 次ã®è¡ãåŠçããæ©èœã
- æçµçµæãçºè¡ããæ©èœã
- åã®3ã€ã®æ®µèœãéçŽé¢æ°ãæ§æããããšã®è¡šç€ºã
PostgreSQL
ç¶æ
ãä¿åããã«ã¯ãé©åãªããŒã¿åãéžæããå¿
èŠããããŸãã æšæºã䜿çšã§ããŸãããç¬èªã«å®çŸ©ããããšãã§ããŸãã å¹³åãèšç®ããé¢æ°ã®å Žåãå€ãåå¥ã«åèšãããã®æ°ãåå¥ã«èšç®ããå¿
èŠããããŸãã ãããã£ãŠã2ã€ã®ãã£ãŒã«ããæã€è€ååãäœæããŸãã
CREATE TYPE average_state AS (
accum numeric,
qty numeric
);
次ã®å€ãåŠçããé¢æ°ãå®çŸ©ããŸãã PostgreSQLã§ã¯ãé·ç§»é¢æ°ãšåŒã°ããŸãïŒ
CREATE OR REPLACE FUNCTION average_transition (
state average_state,
val numeric
) RETURNS average_state AS $$
BEGIN
RAISE NOTICE '%(%) + %', state.accum, state.qty, val;
RETURN ROW(state.accum+val, state.qty+1)::average_state;
END;
$$ LANGUAGE plpgsql;
ãã®é¢æ°ã¯çŸåšã®ç¶æ
ãšæ¬¡ã®å€ãååŸããæ°ããç¶æ
ãè¿ããŸããå€ãå ç®ãããæ°éã«1ãå ç®ãããŸãã
ããã«ãïŒRAISE NOTICEïŒé¢æ°ãã©ã¡ãŒã¿ãŒãåºåããŸã-ããã«ãããäœæ¥ãã©ã®ããã«è¡ããããã確èªã§ããŸãã å€ãè¯ããããã°PRINTãããªãããè¯ããã®ã¯ãããŸããã
次ã®æ©èœã¯ãæçµå€ãè¿ãããšã§ãã
CREATE OR REPLACE FUNCTION average_final (
state average_state
) RETURNS numeric AS $$
BEGIN
RAISE NOTICE '= %(%)', state.accum, state.qty;
RETURN CASE WHEN state.qty > 0 THEN
trim(trailing '0' from ( state.accum/state.qty )::text)::numeric
END;
END;
$$ LANGUAGE plpgsql;
é¢æ°ã¯ç¶æ
ãååŸããçµæã®æ°å€ãè¿ããŸãã ãããè¡ãã«ã¯ã环ç©éé¡ãéé¡ã§å²ããŸãã ããããéããŒãã®å ŽåãNULLãè¿ããŸãïŒå¹³åãè¿ããŸãïŒã
ããªã æ©èœã䜿çšããæ¥å°ã¯ãæ£ç¢ºãªåºåã®ããã«ã®ã¿å¿
èŠã§ãããã®æ¹æ³ã§ã¯ããããªããã°ç»é¢ãæ··ä¹±ãããç¥èŠã劚ãããããªéèŠã§ãªããŒããåãé€ããŸãã ãã®ãããªãã®ïŒ
SELECT 1::numeric / 2::numeric;
?column?
------------------------
0.50000000000000000000
(1 row)
å®éã«ã¯ããããã®ããªãã¯ã¯ãã¡ããå¿
èŠãããŸããã
æåŸã«ãéèšé¢æ°èªäœã決å®ããŸãã ãããè¡ãã«ã¯ãç¹å¥ãªCREATE AGGREGATEã³ãã³ãã䜿çšããŸãã
CREATE AGGREGATE average(numeric) (
sfunc = average_transition ,
stype = average_state ,
finalfunc = average_final ,
initcond = '(0,0)'
);
ãã®ã³ãã³ãã¯ãç¶æ
ã®ããŒã¿åïŒstypeïŒã2ã€ã®é¢æ°ïŒsfuncããã³finalfuncïŒãããã³åæç¶æ
å€ïŒinitcondïŒãæååå®æ°ã®åœ¢åŒã§ç€ºããŸãã
è©Šãããšãã§ããŸãã ãã®èšäºã®ã»ãšãã©ãã¹ãŠã®äŸã§ã¯ã5è¡ïŒ1ã2ã3ã4ã5ïŒã®åçŽãªããŒãã«ã䜿çšããŸãã ãã¹ãããŒã¿ãçæããããã«äžå¯æ¬ ãªããŒã«ã§ããgenerate_seriesé¢æ°ã䜿çšããŠããã®å Žã§ããŒãã«ãäœæããŸãã
SELECT average(gx) FROM generate_series(1,5) AS g(x);
NOTICE: 0(0) + 1
NOTICE: 1(1) + 2
NOTICE: 3(2) + 3
NOTICE: 6(3) + 4
NOTICE: 10(4) + 5
NOTICE: = 15(5)
average
---------
3
(1 row)
çµæã¯æ£ãããé¢æ°ã®åºåã«ããé²è¡ç¶æ³ã远跡ã§ããŸãã
- ç¶æ
ã¯ïŒ0,0ïŒã«èšå®ããã
- average_transitioné¢æ°ã¯5åé£ç¶ããŠåŒã³åºãããåŸã
ã«ç¶æ
ãå€åãã
- æçµçã«ãaverage_finalé¢æ°ãåŒã³åºããã3 = 15/5ã«ãªããŸããã
å¥ã®ãã§ãã¯ã¯ç©ºã®ã»ããã«å¯ŸããŠè¡ãããŸãïŒ
SELECT average(gx) FROM generate_series(1,0) AS g(x);
NOTICE: = 0(0)
average
---------
(1 row)
ãªã©ã¯ã«
Oracleã§ã¯ããã¹ãŠã®æ¡åŒµæ§ãããŒã¿ã«ãŒããªããžãšã³ãžã³ã«ãã£ãŠæäŸãããŸãã ç°¡åã«èšãã°ãéèšã«å¿
èŠãªã€ã³ã¿ãŒãã§ã€ã¹ãå®è£
ãããªããžã§ã¯ãã¿ã€ããäœæããå¿
èŠããããŸãã ã³ã³ããã¹ãã¯ããã®ãªããžã§ã¯ãã®å±æ§ã«ãã£ãŠèªç¶ã«è¡šãããŸãã
CREATE OR REPLACE TYPE AverageImpl AS OBJECT(
accum number,
qty number,
STATIC FUNCTION ODCIAggregateInitialize (actx IN OUT AverageImpl)
RETURN number,
MEMBER FUNCTION ODCIAggregateIterate (self IN OUT AverageImpl, val IN number
RETURN number,
MEMBER FUNCTION ODCIAggregateMerge (self IN OUT AverageImpl, ctx2 IN AverageImpl)
RETURN number,
MEMBER FUNCTION ODCIAggregateTerminate (self IN OUT AverageImpl, returnValue OUT number, flags IN number)
RETURN number
);
/
ããã§ã¯ãã³ã³ããã¹ãã®åæå€ã¯å®æ°ã§ã¯ãªããåå¥ã®ïŒéçãã€ãŸããªããžã§ã¯ãã®ç¹å®ã®ã€ã³ã¹ã¿ã³ã¹ã«é¢é£ä»ããããŠããªãïŒé¢æ°ODCIAggregateInitializeã«ãã£ãŠæ±ºå®ãããŸãã
åè¡ã«å¯ŸããŠåŒã³åºãããé¢æ°ã¯ODCIAggregateIterateã§ãã
çµæã¯ODCIAggregateTerminateé¢æ°ã«ãã£ãŠè¿ãããããã€ãã®ãã©ã°ãæž¡ãããããšã«æ³šæããŠãã ãããããã«ã€ããŠã¯åŸã§æ±ããŸãã
ã€ã³ã¿ãŒãã§ã€ã¹ã«ã¯ãODCIAggregateMergeãšããå¥ã®å¿
é æ©èœãå«ãŸããŠããŸãã ç§ãã¡ã¯ãããå®çŸ©ããŸã-ã©ããžè¡ãã¹ãã§ãããä»ã®ãšããã¯ããã«ã€ããŠã®äŒè©±ã延æããŸãã
次ã«ããªã¹ããããã¡ãœããã®å®è£
ã䜿çšããŠãªããžã§ã¯ãæ¬äœãäœæããŸãã
CREATE OR REPLACE TYPE BODY AverageImpl IS
STATIC FUNCTION ODCIAggregateInitialize (actx IN OUT AverageImpl)
RETURN number IS
BEGIN
actx := AverageImpl(0,0);
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateIterate (self IN OUT AverageImpl, val IN number)
RETURN number IS
BEGIN
dbms_output.put_line(self.accum||'('||self.qty||') + '||val);
self.accum := self.accum + val;
self.qty := self.qty + 1;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateMerge (self IN OUT AverageImpl, ctx2 IN AverageImpl)
RETURN number IS
BEGIN
dbms_output.put_line(self.accum||'('||self.qty||') & '||ctx2.accum||'('||ctx2.qty||')');
self.accum := self.accum + ctx2.accum;
self.qty := self.qty + ctx2.qty;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateTerminate (self IN OUT AverageImpl, returnValue OUT number, flags IN number)
RETURN number IS
BEGIN
dbms_output.put_line('= '||self.accum||'('||self.qty||') flags:'||flags);
returnValue := CASE WHEN self.qty > 0 THEN self.accum / self.qty END;
RETURN ODCIConst.Success;
END;
END;
/
å®è£
ã¯ãã»ãšãã©ã®éšåã§ãPostgreSQLã«å¯ŸããŠè¡ã£ããã¹ãŠãç¹°ãè¿ããŸãããæ§æã¯ãããã«ç°ãªããŸãã
æ»ãå€ã®åšãã®ããªã ãã³ã¹ã¯äžââèŠã§ããOracleã¯ãå€ãåºåãããšãã«ãéèŠã§ãªããŒããåå¥ã«ã«ããããŸãã
ãã¹ãŠã®é¢æ°ã¯æåã瀺ãå€ïŒODCIConst.Successå€ïŒãè¿ããã»ãã³ãã£ãã¯å€ã¯OUTããã³IN OUTãã©ã¡ãŒã¿ãŒãä»ããŠæž¡ãããããšã«æ³šæããŠãã ããïŒPL / SQLã§ã¯ãPL / pgSQLã®ããã«å®éã®æ»ãå€ã«é¢é£ä»ããããŸããïŒã ç¹ã«ãODCIAggregateTerminateãå«ããã¹ãŠã®é¢æ°ã¯ããªããžã§ã¯ãã®å±æ§ãå€æŽã§ããŸããæåã®ãã©ã¡ãŒã¿ãŒïŒselfïŒã§æž¡ããããªã³ã¯ã
éçŽé¢æ°ã®å®çŸ©ã¯æ¬¡ã®ãšããã§ãã
CREATE OR REPLACE FUNCTION average(val number) RETURN number
AGGREGATE USING AverageImpl;
/
確èªããŸãã å€ãçæããã«ã¯ãååž°ã¯ãšãªCONNECT BYã¬ãã«ã§æ
£çšçãªæ§æã䜿çšããŸãã
SELECT average(level) FROM dual CONNECT BY level <= 5;
AVERAGE(LEVEL)
--------------
3
0(0) + 1
1(1) + 2
3(2) + 3
6(3) + 4
10(4) + 5
= 15(5) flags:0
PostgreSQLã®ã¡ãã»ãŒãžåºåãçµæã®åã«è¡šç€ºãããOracleã®åŸã«åºåãããããšã«æ³šæããŠãã ããã ããã¯ãRAISE NOTICEãéåæã§åäœããdbms_outputãã±ãããåºåããããã¡ãªã³ã°ããããã§ãã
ã芧ã®ãšãããODCIAggregateTerminateé¢æ°ã«nullãã©ã°ãæž¡ãããŸããã ã€ãŸããã³ã³ããã¹ãã¯äžèŠã«ãªããå¿
èŠã«å¿ããŠå¿ããããšãã§ããŸãã
ãããŠã空ã®ã»ããã確èªããŸãã
SELECT average(rownum) FROM dual WHERE 1 = 0;
AVERAGE(ROWNUM)
---------------
= 0(0) flags:0
ãŠã£ã³ããŠé¢æ°ïŒOVERïŒïŒ
幞ããªããšã«ãç§ãã¡ãæžããéçŽé¢æ°ã¯ããŠã£ã³ããŠïŒåæïŒãšããŠå€æŽããããšãªãæ©èœããŸãã
ãŠã£ã³ããŠé¢æ°ã¯ãéžæã1ã€ã®ïŒéçŽïŒè¡ã«æããããŸãªããšããç¹ã§éçŽãšç°ãªããŸãããè¡ããšã«åå¥ã«èšç®ãããŸãã æ§æçã«ã¯ããŠã£ã³ããŠé¢æ°åŒã³åºãã¯ãåŠçã®ããã®å€ãã®è¡ãå®çŸ©ãã
ãã¬ãŒã ã®æ瀺ã䌎ãOVERæ§é ã®ååšã«ãã£ãŠåºå¥ãããŸãã æãåçŽãªã±ãŒã¹ã§ã¯ãOVERïŒïŒã®ããã«èšè¿°ãããŸããããã¯ãé¢æ°ããã¹ãŠã®è¡ãåŠçããå¿
èŠãããããšãæå³ããŸãã çµæã¯ãéåžžã®éèšé¢æ°ãèšç®ããåãµã³ãã«è¡ã®å察åŽã«çµæïŒåããã®ïŒãæžã蟌ãã ãã®ããã«ãªããŸãã
ã€ãŸãããã¬ãŒã ã¯éçã§ããããã¹ãŠã®è¡ã«ãŸããã£ãŠããŸãã
1. 2. 3. 4. 5ã
+ --- + + --- + + --- + + --- --- + + --- +
| 1 | | 1 | | 1 | | 1 | | 1 |
| 2 | | 2 | | 2 | | 2 | | 2 |
| 3 | | 3 | | 3 | | 3 | | 3 |
| 4 | | 4 | | 4 | | 4 | | 4 |
| 5 | | 5 | | 5 | | 5 | | 5 |
+ --- + + --- + + --- + + --- --- + + --- +
PostgreSQL
è©ŠããŠã¿ãŸãããïŒ
SELECT gx, average(gx) OVER ()
FROM generate_series(1,5) as g(x);
NOTICE: 0(0) + 1
NOTICE: 1(1) + 2
NOTICE: 3(2) + 3
NOTICE: 6(3) + 4
NOTICE: 10(4) + 5
NOTICE: = 15(5)
x | average
---+---------
1 | 3
2 | 3
3 | 3
4 | 3
5 | 3
(5 rows)
NOTICEã®çµè«ã¯ãéåžžã®éçŽé¢æ°ãèšç®ãããšãããã¹ãŠã以åãšãŸã£ããåãããã«çºçããããšã瀺ããŠããŸãã average_finalé¢æ°ããçµæãåãåã£ãåŸãPostgreSQLã¯åè¡ã«çµæãé
眮ããŸãã
ãªã©ã¯ã«
SELECT average(level) OVER() average
FROM dual CONNECT BY level <= 5;
LEVEL AVERAGE
---------- -----------
1 3
2 3
3 3
4 3
5 3
0(0) + 1
1(1) + 2
3(2) + 3
6(3) + 4
10(4) + 5
= 15(5) flags:1
= 15(5) flags:1
= 15(5) flags:1
= 15(5) flags:1
= 15(5) flags:1
= 15(5) flags:0
æå€ãšã çµæã1åèšç®ãã代ããã«ãOracleã¯ODCIAggregateTerminateé¢æ°ãN + 1ååŒã³åºããŸããæåã¯ãã©ã°1ã®åè¡ã«å¯ŸããŠïŒã³ã³ããã¹ãããŸã æå¹ã§ããããšãæå³ããŸãïŒãæåŸã«ããäžåºŠåŒã³åºããŸãã æåŸã®åŒã³åºãããååŸããå€ã¯åã«ç¡èŠãããŸãã
çµè«ã¯æ¬¡ã®ãšããã§ããODCIAggregateTerminateé¢æ°ãèšç®çã«è€éãªããžãã¯ã䜿çšããå Žåãåããžã§ããæ°åå®è¡ããªãããšãèããå¿
èŠããããŸãã
ãŠã£ã³ããŠé¢æ°ïŒOVERïŒPARTITION BYïŒ
ãã¬ãŒã å®çŸ©ã®PARTITION BYå¥ã¯ãéåžžã®GROUP BYéèšæ§é ã«äŒŒãŠããŸãã PARTITION BYã瀺ããŠã£ã³ããŠé¢æ°ã¯è¡ã®ã°ã«ãŒãããšã«åå¥ã«èšç®ãããçµæã¯éžæç¯å²ã®åè¡ã«å²ãåœãŠãããŸãã
ãã®å®æœåœ¢æ
ã§ã¯ããã¬ãŒã ãéçã§ããããã°ã«ãŒãããšã«ç°ãªãã ããšãã°ã2ã€ã®ã°ã«ãŒãã®ã©ã€ã³ãå®çŸ©ãããŠããå ŽåïŒ1çªç®ãã2çªç®ã3çªç®ãã5çªç®ïŒããã¬ãŒã ã¯æ¬¡ã®ããã«æ³åã§ããŸãã
1. 2. 3. 4. 5ã
+ --- + + --- +
| 1 | | 1 |
| 2 | | 2 | + --- + + --- + + --- +
+ --- + + --- + | 3 | | 3 | | 3 |
| 4 | | 4 | | 4 |
| 5 | | 5 | | 5 |
+ --- + + --- + + --- +
PostgreSQL
SELECT gx/3 part,
gx,
average(gx) OVER (PARTITION BY gx/3)
FROM generate_series(1,5) as g(x);
NOTICE: 0(0) + 1
NOTICE: 1(1) + 2
NOTICE: = 3(2)
NOTICE: 0(0) + 3
NOTICE: 3(1) + 4
NOTICE: 7(2) + 5
NOTICE: = 12(3)
part | x | average
------+---+---------
0 | 1 | 1.5
0 | 2 | 1.5
1 | 3 | 4
1 | 4 | 4
1 | 5 | 4
(5 rows)
èšç®ã¯åã³é£ç¶ããŠè¡ãããŸãããçŸåšãå¥ã®ã°ã«ãŒãã®è¡ã«ç§»åãããšãç¶æ
ã¯åæå€ïŒinitcondïŒã«ãªã»ãããããŸãã
ãªã©ã¯ã«
SELECT trunc(level/3) part,
level,
average(level) OVER(PARTITION BY trunc(level/3)) average
FROM dual CONNECT BY level <= 5;
PART LEVEL AVERAGE
---------- ---------- ----------
0 2 1.5
0 1 1.5
1 4 4
1 5 4
1 3 4
0(0) + 2
2(1) + 1
= 3(2) flags:1
= 3(2) flags:1
0(0) + 4
4(1) + 5
9(2) + 3
= 12(3) flags:1
= 12(3) flags:1
= 12(3) flags:1
= 12(3) flags:0
èå³æ·±ãããšã«ãOracleã¯ã©ã€ã³ã亀æããããšã«ããŸããã ããã¯å®è£
ã®è©³çŽ°ã«ã€ããŠäœããèšããããããŸããããã©ããªå Žåã§ãæš©å©ãæã£ãŠããŸãã
ãŠã£ã³ããŠé¢æ°ïŒOVERïŒORDER BYïŒ
ãœãŒãé ã瀺ãORDER BYå¥ããã¬ãŒã ã®å®çŸ©ã«è¿œå ãããå Žåãé¢æ°ã¯
æé ã¢ãŒãã§åäœãéå§ããŸãïŒsumé¢æ°ã®å Žåã
环ç©åèšã§ ïŒã
æåã®è¡ã§ã¯ããã¬ãŒã ã¯ãã®1è¡ã§æ§æãããŸãã 第äº-第äžãšç¬¬äºãã; 3çªç®ã®å Žå-1çªç®ã2çªç®ã3çªç®ãªã©ããã ã€ãŸãããã¬ãŒã ã«ã¯æåããçŸåšãŸã§ã®è¡ãå«ãŸããŸãã
å®éã次ã®ããã«æžãããšãã§ããŸãïŒOVERïŒORDER BY ...æªçµåã®åè¡ãšçŸåšè¡ã®éã®è¡ïŒããã ãããã®åé·æ§ã¯ããã©ã«ãã§æ瀺ãããŠãããããéåžžã¯çç¥ãããŸãã
ãã®ããããã¬ãŒã ã¯éæ¢ããªããªããŸããé ãäžã«ç§»åããå°Ÿãæå®ã®äœçœ®ã«æ®ããŸãã
1. 2. 3. 4. 5ã
+ --- + + --- + + --- + + --- --- + + --- +
| 1 | | 1 | | 1 | | 1 | | 1 |
+ --- + | 2 | | 2 | | 2 | | 2 |
+ --- + | 3 | | 3 | | 3 |
+ --- + | 4 | | 4 |
+ --- + | 5 |
+ --- +
PostgreSQL
SELECT gx, average(gx) OVER (ORDER BY gx)
FROM generate_series(1,5) as g(x);
NOTICE: 0(0) + 1
NOTICE: = 1(1)
NOTICE: 1(1) + 2
NOTICE: = 3(2)
NOTICE: 3(2) + 3
NOTICE: = 6(3)
NOTICE: 6(3) + 4
NOTICE: = 10(4)
NOTICE: 10(4) + 5
NOTICE: = 15(5)
x | average
---+---------
1 | 1
2 | 1.5
3 | 2
4 | 2.5
5 | 3
(5 rows)
ã芧ã®ãšãããè¡ã¯äžåºŠã«1ã€ãã€ã³ã³ããã¹ãã«è¿œå ãããŸãããåè¿œå åŸã«average_finalé¢æ°ãåŒã³åºãããäžéçµæãåŸãããŸãã
ãªã©ã¯ã«
SELECT level, average(level) OVER(ORDER BY level) average
FROM dual CONNECT BY level <= 5;
LEVEL AVERAGE
---------- ----------
1 1
2 1.5
3 2
4 2.5
5 3
0(0) + 1
= 1(1) flags:1
1(1) + 2
= 3(2) flags:1
3(2) + 3
= 6(3) flags:1
6(3) + 4
= 10(4) flags:1
10(4) + 5
= 15(5) flags:1
= 15(5) flags:0
ä»åã¯ãäž¡æ¹ã®ã·ã¹ãã ãåãããã«æ©èœããŸãã
ãŠã£ã³ããŠé¢æ°ïŒOVERïŒPARTITION BY ORDER BYïŒ
PARTITION BYå¥ãšORDER BYå¥ã¯çµã¿åãããããšãã§ããŸãã 次ã«ãåè¡ã°ã«ãŒãå
ã§ãé¢æ°ã¯äžæã¢ãŒãã§åäœããã°ã«ãŒãããã°ã«ãŒãã«ç§»åãããšãç¶æ
ã¯åæç¶æ
ã«ãªã»ãããããŸãã
1. 2. 3. 4. 5ã
+ --- + + --- +
| 1 | | 1 |
+ --- + | 2 | + --- + + --- + + --- +
+ --- + | 3 | | 3 | | 3 |
+ --- + | 4 | | 4 |
+ --- + | 5 |
+ --- +
PostgreSQL
SELECT gx/3 part,
gx,
average(gx) OVER (PARTITION BY gx/3 ORDER BY gx)
FROM generate_series(1,5) as g(x);
NOTICE: 0(0) + 1
NOTICE: = 1(1)
NOTICE: 1(1) + 2
NOTICE: = 3(2)
NOTICE: 0(0) + 3
NOTICE: = 3(1)
NOTICE: 3(1) + 4
NOTICE: = 7(2)
NOTICE: 7(2) + 5
NOTICE: = 12(3)
part | x | average
------+---+---------
0 | 1 | 1
0 | 2 | 1.5
1 | 3 | 3
1 | 4 | 3.5
1 | 5 | 4
(5 rows)
ãªã©ã¯ã«
SELECT trunc(level/3) part,
level,
average(level) OVER(PARTITION BY trunc(level/3) ORDER BY level) average
FROM dual CONNECT BY level <= 5;
PART LEVEL AVERAGE
---------- ---------- ----------
0 1 1
0 2 1.5
1 3 3
1 4 3.5
1 5 4
0(0) + 1
= 1(1) flags:1
1(1) + 2
= 3(2) flags:1
0(0) + 3
= 3(1) flags:1
3(1) + 4
= 7(2) flags:1
7(2) + 5
= 12(3) flags:1
= 12(3) flags:0
ã¹ã©ã€ãã£ã³ã°ãã¬ãŒã ãåãããŠã£ã³ããŠæ©èœ
ç§ãã¡ãèŠããã¹ãŠã®äŸã§ããã¬ãŒã ã¯éçã§ããããé ã®ã¿ã移åããŠããŸããïŒORDER BYå¥ã䜿çšããŠããå ŽåïŒã ããã«ãããç¶æ
ãé çªã«èšç®ãã1è¡ãã€ã³ã³ããã¹ãã«è¿œå ããããšãã§ããŸããã
ãã ãããŠã£ã³ããŠé¢æ°ã®ãã¬ãŒã ã¯ãããŒã«ãã·ããããããã«èšå®ããããšãã§ããŸãã ãã®äŸã§ã¯ãããã¯
移åå¹³åã®æŠå¿µã«å¯Ÿå¿ã
ãŸãã ããšãã°ãOVERïŒROWS BETWEEN 2 PRECEDING AND CURRENT ROWïŒãæå®ãããšãçŸåšã®å€ãš2ã€ã®åã®å€ãçµæã®è¡ããšã«å¹³ååãããŸãã
1. 2. 3. 4. 5ã
+ --- +
| | + --- +
| | | | + --- +
| 1 | | 1 | | 1 | + --- +
+ --- + | 2 | | 2 | | 2 | + --- +
+ --- + | 3 | | 3 | | 3 |
+ --- + | 4 | | 4 |
+ --- + | 5 |
+ --- +
ãã®å Žåãçªé¢æ°ãèšç®ã§ããŸããïŒ å¹æçã§ã¯ãããŸãããã圌ã¯ã§ããããšãããããŸããã ããããããã«ã³ãŒããæžãããšã§ãç¶æ³ãæ¹åã§ããŸãã
PostgreSQL
èŠãŠã¿ãŸãããïŒ
SELECT gx,
average(gx) OVER (ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
FROM generate_series(1,5) as g(x);
NOTICE: 0(0) + 1
NOTICE: = 1(1)
NOTICE: 1(1) + 2
NOTICE: = 3(2)
NOTICE: 3(2) + 3
NOTICE: = 6(3)
NOTICE: 0(0) + 2
NOTICE: 2(1) + 3
NOTICE: 5(2) + 4
NOTICE: = 9(3)
NOTICE: 0(0) + 3
NOTICE: 3(1) + 4
NOTICE: 7(2) + 5
NOTICE: = 12(3)
x | average
---+---------
1 | 1
2 | 1.5
3 | 2
4 | 3
5 | 4
(5 rows)
3è¡ç®ãŸã§ã¯ãããŒã«ãå®éã«ç§»åããªãããããã¹ãŠãããŸããããŸããæ¢åã®ã³ã³ããã¹ãã«æ¬¡ã®å€ãè¿œå ããã ãã§ãã ããããã³ã³ããã¹ãããå€ãåé€ããæ¹æ³ãããããªãããã4è¡ç®ãš5è¡ç®ã§ã¯ããã¹ãŠãå®å
šã«åã«ãŠã³ããããæ¯ååæç¶æ
ã«æ»ããŸãã
ãã®ãã
ã次ã®å€ã
è¿œå ããæ©èœã ãã§ãªããç¶æ
ããå€ã
åé€ããæ©èœããããš
䟿å©ã§ãã å®éããã®ãããªé¢æ°ã¯äœæã§ããŸãïŒ
CREATE OR REPLACE FUNCTION average_inverse (state average_state, val numeric)
RETURNS average_state AS $$
BEGIN
RAISE NOTICE '%(%) - %', state.accum, state.qty, val;
RETURN ROW(state.accum-val, state.qty-1)::average_state;
END;
$$ LANGUAGE plpgsql;
ãŠã£ã³ããŠé¢æ°ã§äœ¿çšã§ããããã«ããã«ã¯ã次ã®ããã«éçŽãåäœæããå¿
èŠããããŸãã
DROP AGGREGATE average(numeric);
CREATE AGGREGATE average(numeric) (
--
sfunc = average_transition ,
stype = average_state ,
finalfunc = average_final ,
initcond = '(0,0)',
-- ââ
msfunc = average_transition ,
minvfunc = average_inverse ,
mstype = average_state ,
mfinalfunc = average_final ,
minitcond = '(0,0)'
);
ãã§ãã¯ïŒ
SELECT gx,
average(gx) OVER (ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
FROM generate_series(1,5) as g(x);
NOTICE: 0(0) + 1
NOTICE: = 1(1)
NOTICE: 1(1) + 2
NOTICE: = 3(2)
NOTICE: 3(2) + 3
NOTICE: = 6(3)
NOTICE: 6(3) - 1
NOTICE: 5(2) + 4
NOTICE: = 9(3)
NOTICE: 9(3) - 2
NOTICE: 7(2) + 5
NOTICE: = 12(3)
x | average
---+---------
1 | 1
2 | 1.5
3 | 2
4 | 3
5 | 4
(5 rows)
ããã§ãã¹ãŠãæŽããŸããã4è¡ç®ãš5è¡ç®ã§ã¯ãç¶æ
ããããŒã«å€ãåé€ããæ°ããå€ãè¿œå ããŸãã
ãªã©ã¯ã«
ããã§ãç¶æ³ã¯äŒŒãŠããŸãã äœæãããããŒãžã§ã³ã®åæé¢æ°ã¯æ©èœããŸãããéå¹ççã§ãã
SELECT level,
average(level) OVER(ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) average
FROM dual CONNECT BY level <= 5;
LEVEL AVERAGE
---------- ----------
1 1
2 1.5
3 2
4 3
5 4
0(0) + 1
= 1(1) flags:1
1(1) + 2
= 3(2) flags:1
3(2) + 3
= 6(3) flags:1
0(0) + 2
2(1) + 3
5(2) + 4
= 9(3) flags:1
0(0) + 3
3(1) + 4
7(2) + 5
= 12(3) flags:1
= 12(3) flags:0
ã³ã³ããã¹ãããå€ãåé€ããé¢æ°ã¯ã次ã®ããã«å®çŸ©ãããŸãã
MEMBER FUNCTION ODCIAggregateDelete (self IN OUT AverageImpl, val IN number)
RETURN number IS
BEGIN
dbms_output.put_line(self.accum||'('||self.qty||') - '||val);
self.accum := self.accum - val;
self.qty := self.qty - 1;
RETURN ODCIConst.Success;
END;
ã³ããŒã¢ã³ãããŒã¹ãã®å®å
šãªã³ãŒãCREATE OR REPLACE TYPE AverageImpl AS OBJECT(
accum number,
qty number,
STATIC FUNCTION ODCIAggregateInitialize (actx IN OUT AverageImpl) RETURN number,
MEMBER FUNCTION ODCIAggregateIterate (self IN OUT AverageImpl, val IN number) RETURN number,
MEMBER FUNCTION ODCIAggregateMerge (self IN OUT AverageImpl, ctx2 IN AverageImpl) RETURN number,
MEMBER FUNCTION ODCIAggregateTerminate (self IN OUT AverageImpl, returnValue OUT number, flags IN number) RETURN number,
MEMBER FUNCTION ODCIAggregateDelete (self IN OUT AverageImpl, val IN number) RETURN number
);
/
CREATE OR REPLACE TYPE BODY AverageImpl IS
STATIC FUNCTION ODCIAggregateInitialize (actx IN OUT AverageImpl)
RETURN number IS
BEGIN
actx := AverageImpl(0,0);
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateIterate (self IN OUT AverageImpl, val IN number)
RETURN number IS
BEGIN
dbms_output.put_line(self.accum||'('||self.qty||') + '||val);
self.accum := self.accum + val;
self.qty := self.qty + 1;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateMerge (self IN OUT AverageImpl, ctx2 IN AverageImpl)
RETURN number IS
BEGIN
dbms_output.put_line(self.accum||'('||self.qty||') & '||ctx2.accum||'('||ctx2.qty||')');
self.accum := self.accum + ctx2.accum;
self.qty := self.qty + ctx2.qty;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateTerminate (self IN OUT AverageImpl, returnValue OUT number, flags IN number)
RETURN number IS
BEGIN
dbms_output.put_line('= '||self.accum||'('||self.qty||') flags:'||flags);
returnValue := CASE WHEN self.qty > 0 THEN self.accum / self.qty END;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateDelete (self IN OUT AverageImpl, val IN number)
RETURN number IS
BEGIN
dbms_output.put_line(self.accum||'('||self.qty||') - '||val);
self.accum := self.accum - val;
self.qty := self.qty - 1;
RETURN ODCIConst.Success;
END;
END;
/
é¢æ°èªäœãåäœæããå¿
èŠã¯ãããŸããã ãã§ãã¯ïŒ
SELECT level,
average(level) OVER(ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) average
FROM dual CONNECT BY level <= 5;
LEVEL AVERAGE
---------- ----------
1 1
2 1.5
3 2
4 3
5 4
0(0) + 1
= 1(1) flags:1
1(1) + 2
= 3(2) flags:1
3(2) + 3
= 6(3) flags:1
6(3) - 1
5(2) + 4
= 9(3) flags:1
9(3) - 2
7(2) + 5
= 12(3) flags:1
= 12(3) flags:0
å¹³è¡åºŠ
PostgreSQLãšOracleïŒEnterprise EditionïŒã®äž¡æ¹ã¯ãéèšé¢æ°ã䞊è¡ããŠèšç®ã§ããŸãã ããã«ãå䞊åããã»ã¹ã¯äœæ¥ã®äžéšãå®è¡ããäžéç¶æ
ã圢æããŸãã 次ã«ãã¡ã€ã³ã®èª¿æŽããã»ã¹ããããã®ããã€ãã®ç¶æ
ãåãåãããããã1ã€ã®æçµç¶æ
ã«çµåããå¿
èŠããããŸãã
ãããè¡ãã«ã¯ããã1ã€ã®
unioné¢æ°ãå¿
èŠã§ãã ãã®å ŽåãåçŽã«åèšãšå€ã®æ°ãåèšããŸãã
PostgreSQL
æ©èœã¯æ¬¡ã®ãšããã§ãã
CREATE OR REPLACE FUNCTION average_combine (state1 average_state, state2 average_state)
RETURNS average_state AS $$
BEGIN
RAISE NOTICE '%(%) & %(%)', state1.accum, state1.qty, state2.accum, state2.qty;
RETURN ROW(state1.accum+state2.accum, state1.qty+state2.qty)::average_state;
END;
$$ LANGUAGE plpgsql;
ãŸããaverage_transitioné¢æ°ãããããã°åºåãåé€ããŸãã 䞊åå®è¡ã§ã¯ã5ã€ã®å€ãéèšããã®ã§ã¯ãªããããã«å€ãã®å€ãéèšããããããããè¡ããªããšãç¡é§ãªæ
å ±ã倧éã«åŸãããŸãã
åºåãåé€ãããããæç¶ãåèšèªã䜿çšããå¿
èŠããããŸãããé¢æ°ãçŽç²ãªSQLã§èšè¿°ããŸãã
CREATE OR REPLACE FUNCTION average_transition (state average_state, val numeric)
RETURNS average_state AS $$
SELECT ROW(state.accum+val, state.qty+1)::average_state;
$$ LANGUAGE sql;
æ°ããæ©èœãèæ
®ããŠãŠããããåäœæãã䞊åã¢ãŒãã§å®å
šã«äœ¿çšã§ããããšã瀺ãããšã¯æ®ããŸãã
DROP AGGREGATE average(numeric);
CREATE AGGREGATE average(numeric) (
--
sfunc = average_transition ,
stype = average_state ,
finalfunc = average_final ,
combinefunc = average_combine ,
initcond = '(0,0)',
-- ââ
msfunc = average_transition ,
minvfunc = average_inverse ,
mstype = average_state ,
mfinalfunc = average_final ,
minitcond = '(0,0)',
--
parallel = safe
);
次ã«ãããŒãã«ãäœæããããŒã¿ãå
¥åããŸãã æ°åè¡ã§ååã§ãã
CREATE TABLE t(n) AS SELECT generate_series(1,1000)::numeric;
ããã©ã«ãèšå®ã§ã¯ãPostgreSQLã¯ãã®ãããªããŒãã«ã®äžŠåãã©ã³ãæ§ç¯ããŸãã-ããã¯å°ããããŸã-ãããããã説åŸããããšã¯é£ãããããŸããïŒ
SET parallel_setup_cost=0;
SET min_parallel_table_scan_size=0;
EXPLAIN(costs off) SELECT average(n) FROM t;
QUERY PLAN
------------------------------------------
Finalize Aggregate
-> Gather
Workers Planned: 2
-> Partial Aggregate
-> Parallel Seq Scan on t
ãªã¯ãšã¹ãã«é¢ããŠã¯ã次ã®ããšãããããŸãã
- éšåéçŽïŒéšåéçŽïŒãå®è¡ãã2ã€ã®èšç»ãããã¯ãŒã¯ãããŒã
- æ
å ±ãåéããããŒããåéãã
- æåŸã®ç¶æ
ã®éçŽïŒFinalize AggregateïŒã
ãã§ãã¯ïŒ
SELECT average(n) FROM t;
NOTICE: 0(0) & 281257(678)
NOTICE: 281257(678) & 127803(226)
NOTICE: 409060(904) & 91440(96)
NOTICE: = 500500(1000)
average
---------
500.5
(1 row)
average_combineé¢æ°ã2åã§ã¯ãªã3ååŒã³åºãããã®ã¯ãªãã§ããïŒ äºå®ãPostgreSQLã§ã¯ã調æŽããã»ã¹ãããã€ãã®äœæ¥ãè¡ããŸãã ãããã£ãŠã2ã€ã®äœæ¥ããã»ã¹ãéå§ãããŸããããå®éã«ã¯3ã€ã®äœæ¥ããã»ã¹ãå®è¡ãããŸããã ãã®ãã¡ã®1ã€ã¯678è¡ãä»ã®226è¡ãš3è¡ç®-96è¡ãåŠçããŸããïŒãã ãããããã®çªå·ã¯äœãæå³ãããç°ãªãèµ·åã§ã¯ç°ãªãå ŽåããããŸãïŒã
ãªã©ã¯ã«
èŠããŠããã°ãODCIAggregateMergeé¢æ°ã¯Oracleã§å¿
é ã§ãããããéåžžã«æåã«äœæããŸããã ææžã§ã¯ããã®é¢æ°ã¯äžŠåæäœã ãã§ãªãé 次æäœã«ãå¿
èŠã§ãããšäž»åŒµããŠããŸã-çç±ãç解ããã®ã¯é£ãããšæããŸãïŒå®éã«ã¯ãé 次åŠçäžã«ãã®å®è£
ãæ±ãå¿
èŠã¯ãããŸããã§ããïŒ
ããšã¯ã䞊åæäœã«å¯ŸããŠå®å
šãªé¢æ°ã宣èšããã ãã§ãã
CREATE OR REPLACE FUNCTION average(val number) RETURN number
PARALLEL_ENABLE
AGGREGATE USING AverageImpl;
/
ããŒãã«ãäœæããŸãã
CREATE TABLE t(n) AS SELECT to_number(level) FROM dual CONNECT BY level <= 1000;
Oracleã説åŸããã®ã¯PostgreSQLãããç°¡åã§ã-ãã³ããæžãã ãã§ãã èšç»ã¯æ¬¡ã®ãšããã§ãïŒç°¡åã«ããããã«ãåºåã¯å€§å¹
ã«èª¿æŽãããŠããŸãïŒã
EXPLAIN PLAN FOR SELECT /*+ PARALLEL(2) */ average(n) FROM t;
SELECT * FROM TABLE(dbms_xplan.display);
---------------------------------
| Id | Operation |
---------------------------------
| 0 | SELECT STATEMENT |
| 1 | SORT AGGREGATE |
| 2 | PX COORDINATOR |
| 3 | PX SEND QC (RANDOM) |
| 4 | SORT AGGREGATE |
| 5 | PX BLOCK ITERATOR |
| 6 | TABLE ACCESS FULL |
---------------------------------
èšç»ã«ã¯ä»¥äžãå«ãŸããŸãã
- éšåéçŽïŒ4ïŒã
- éšåçãªã³ã³ããã¹ããåãåãã³ãŒãã£ããŒã¿ãŒïŒ2ïŒã
- ã³ã³ããã¹ãã®çµæã®çµã¿åããïŒ1ïŒã
SELECT /*+ PARALLEL(2) */ average(n) FROM t;
AVERAGE(N)
----------
500.5
0(0) & 216153(657)
216153(657) & 284347(343)
= 500500(1000) flags:0
Oracleã§ã¯ãã³ãŒãã£ããŒã¿ãŒã¯éšåéçŽã«é¢äžããŠããŸããã ãããã£ãŠã2ã€ã®ã³ã³ããã¹ãã®ã¿ãçµåãããåãçç±ã§ãODCIAggregateMergeé¢æ°ã®åºåã®ã¿ã衚瀺ãããŸãã
ããã¥ã¡ã³ã
DBMSã«æ¢ã«å«ãŸããŠããéçŽé¢æ°ããŠã£ã³ããŠé¢æ°ãªã©ã®ããã¥ã¡ã³ããžã®ãªã³ã¯ãæäŸãããšããæ¥ãŸããã ããã«ã¯å€ãã®èå³æ·±ããã®ããããŸãã
PostgreSQLïŒ
OracleïŒ
ã»ã³ãã®äžžãã«é¢ããäŸ
ãããŠã人çããçŽæãããäŸã ãã®æ©èœãæãã€ããã®ã¯ãRASïŒãã·ã¢ã®äŒèšèŠåïŒã®äžã§åããŠãäŒèšçšã®ã¬ããŒããäœæããå¿
èŠããã£ããšãã§ãã
åæšäºå
¥ãçºçããæãåçŽãªã¿ã¹ã¯ã¯ãç·è²»çšïŒããšãã°100ã«ãŒãã«ïŒãããååïŒããšãã°çããïŒã«åŸã£ãŠéšéïŒããšãã°3åïŒã«åé
ããããšã§ãã
WITH depts(name) AS (
VALUES ('A'), ('B'), ('C')
), report(dept,amount) AS (
SELECT name, 100.00 / count(*) OVER() FROM depts
)
SELECT dept, round(amount,2) FROM report;
dept | round
------+-------
A | 33.33
B | 33.33
C | 33.33
(3 rows)
ãã®ã¯ãšãªã¯åé¡ã瀺ããŠããŸããéé¡ã¯äžžããå¿
èŠããããŸããããããŒã¯å€±ãããŸãã ããããRASã¯ãããèš±ããŸããã
åé¡ã¯ããŸããŸãªæ¹æ³ã§è§£æ±ºã§ããŸãããç§ã®å¥œã¿ã§ã¯ãæããšã¬ã¬ã³ããªæ¹æ³ã¯ãŠã£ã³ããŠé¢æ°ã§ãããŠã£ã³ããŠé¢æ°ã¯å¢å ã¢ãŒãã§åäœãããããŒãšã®ãã¹ãŠã®æŠããåããŸãã
WITH depts(name) AS (
VALUES ('A'), ('B'), ('C')
), report(dept,amount) AS (
SELECT name, 100.00 / count(*) OVER() FROM depts
)
SELECT dept, round2(amount) OVER (ORDER BY dept) FROM report;
dept | round2
------+--------
A | 33.33
B | 33.34
C | 33.33
(3 rows)
ãã®ãããªé¢æ°ã®ç¶æ
ã«ã¯ãäžžã誀差ïŒr_errorïŒãšçŸåšã®äžžããããå€ïŒéïŒãå«ãŸããŸãã 次ã®å€ãåŠçããããã®é¢æ°ã¯äžžã誀差ãå¢ãããæ¢ã«1ãããŒã®ååãè¶
ããŠããå Žåã¯ãäžžããããéé¡ã«1ãããŒãè¿œå ããŸãã
state.r_error := state.r_error + val - round(val,2);
state.amount := round(val,2) + round(state.r_error,2);
state.r_error := state.r_error - round(state.r_error,2);
ãããŠãçµæãçæããé¢æ°ã¯ãåã«æ¢è£œã®state.amountãè¿ããŸãã
ç§ã¯é¢æ°ã®å®å
šãªã³ãŒããæäŸããŸããïŒæ¢ã«äžããããäŸã䜿çšããŠããããæžãããšã¯é£ãããããŸããã
ç¬èªã®éèšé¢æ°ãŸãã¯ãŠã£ã³ããŠé¢æ°ã䜿çšããèå³æ·±ãäŸã«åºããããå Žåã¯ãã³ã¡ã³ãã§å
±æããŠãã ããã