From 21651541ce0b2cc0e777988f1e35040b4badfce1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Wed, 16 Mar 2016 01:57:59 +0200 Subject: [PATCH] Implemented avg() window function. It is based on the sum() function, thus much of the logic is shared. Considering that there are 2 counters stored within the function, one that handles the null value, while the other that handles the divisor for the avg computation, it is possible to remove the counter from the Item_sum_avg. I have not removed it in this patch as we may choose to refactor the whole code into a separate class. This remains to be dicussed. --- mysql-test/r/win_avg.result | 95 +++++++++++++++++++++++++++++++++++++ mysql-test/t/win_avg.test | 47 ++++++++++++++++++ sql/item_sum.cc | 10 ++++ sql/item_sum.h | 3 ++ sql/sql_window.cc | 1 + 5 files changed, 156 insertions(+) create mode 100644 mysql-test/r/win_avg.result create mode 100644 mysql-test/t/win_avg.test diff --git a/mysql-test/r/win_avg.result b/mysql-test/r/win_avg.result new file mode 100644 index 00000000000..7e539d933d8 --- /dev/null +++ b/mysql-test/r/win_avg.result @@ -0,0 +1,95 @@ +create table t1 ( +pk int primary key, +a int, +b int, +c real +); +insert into t1 values +(101 , 0, 10, 1.1), +(102 , 0, 10, 2.1), +(103 , 1, 10, 3.1), +(104 , 1, 10, 4.1), +(108 , 2, 10, 5.1), +(105 , 2, 20, 6.1), +(106 , 2, 20, 7.1), +(107 , 2, 20, 8.15), +(109 , 4, 20, 9.15), +(110 , 4, 20, 10.15), +(111 , 5, NULL, 11.15), +(112 , 5, 1, 12.25), +(113 , 5, NULL, 13.35), +(114 , 5, NULL, 14.50), +(115 , 5, NULL, 15.65), +(116 , 6, 1, NULL), +(117 , 6, 1, 10), +(118 , 6, 1, 1.1), +(119 , 6, 1, NULL), +(120 , 6, 1, NULL), +(121 , 6, 1, NULL), +(122 , 6, 1, 2.2), +(123 , 6, 1, 20.1), +(124 , 6, 1, -10.4), +(125 , 6, 1, NULL), +(126 , 6, 1, NULL), +(127 , 6, 1, NULL); +select pk, a, b, avg(b) over (partition by a order by pk ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) +from t1; +pk a b avg(b) over (partition by a order by pk ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) +101 0 10 10.0000 +102 0 10 10.0000 +103 1 10 10.0000 +104 1 10 10.0000 +105 2 20 20.0000 +106 2 20 20.0000 +107 2 20 16.6667 +108 2 10 15.0000 +109 4 20 20.0000 +110 4 20 20.0000 +111 5 NULL 1.0000 +112 5 1 1.0000 +113 5 NULL 1.0000 +114 5 NULL NULL +115 5 NULL NULL +116 6 1 1.0000 +117 6 1 1.0000 +118 6 1 1.0000 +119 6 1 1.0000 +120 6 1 1.0000 +121 6 1 1.0000 +122 6 1 1.0000 +123 6 1 1.0000 +124 6 1 1.0000 +125 6 1 1.0000 +126 6 1 1.0000 +127 6 1 1.0000 +select pk, a, c, avg(c) over (partition by a order by pk ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) +from t1; +pk a c avg(c) over (partition by a order by pk ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) +101 0 1.1 1.6 +102 0 2.1 1.6 +103 1 3.1 3.5999999999999996 +104 1 4.1 3.5999999999999996 +105 2 6.1 6.6 +106 2 7.1 7.116666666666667 +107 2 8.15 6.783333333333334 +108 2 5.1 6.625000000000001 +109 4 9.15 9.65 +110 4 10.15 9.65 +111 5 11.15 11.7 +112 5 12.25 12.25 +113 5 13.35 13.366666666666667 +114 5 14.5 14.5 +115 5 15.65 15.075 +116 6 NULL 10 +117 6 10 5.55 +118 6 1.1 5.55 +119 6 NULL 1.0999999999999996 +120 6 NULL NULL +121 6 NULL 2.1999999999999997 +122 6 2.2 11.15 +123 6 20.1 3.966666666666667 +124 6 -10.4 4.85 +125 6 NULL -10.400000000000002 +126 6 NULL NULL +127 6 NULL NULL +drop table t1; diff --git a/mysql-test/t/win_avg.test b/mysql-test/t/win_avg.test new file mode 100644 index 00000000000..23a3652d943 --- /dev/null +++ b/mysql-test/t/win_avg.test @@ -0,0 +1,47 @@ +create table t1 ( + pk int primary key, + a int, + b int, + c real +); + + +insert into t1 values +(101 , 0, 10, 1.1), +(102 , 0, 10, 2.1), +(103 , 1, 10, 3.1), +(104 , 1, 10, 4.1), +(108 , 2, 10, 5.1), +(105 , 2, 20, 6.1), +(106 , 2, 20, 7.1), +(107 , 2, 20, 8.15), +(109 , 4, 20, 9.15), +(110 , 4, 20, 10.15), +(111 , 5, NULL, 11.15), +(112 , 5, 1, 12.25), +(113 , 5, NULL, 13.35), +(114 , 5, NULL, 14.50), +(115 , 5, NULL, 15.65), +(116 , 6, 1, NULL), +(117 , 6, 1, 10), +(118 , 6, 1, 1.1), +(119 , 6, 1, NULL), +(120 , 6, 1, NULL), +(121 , 6, 1, NULL), +(122 , 6, 1, 2.2), +(123 , 6, 1, 20.1), +(124 , 6, 1, -10.4), +(125 , 6, 1, NULL), +(126 , 6, 1, NULL), +(127 , 6, 1, NULL); + + +--sorted_result +select pk, a, b, avg(b) over (partition by a order by pk ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) +from t1; + +--sorted_result +select pk, a, c, avg(c) over (partition by a order by pk ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) +from t1; + +drop table t1; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index b7490d02233..4b4ed0225d3 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1681,6 +1681,16 @@ bool Item_sum_avg::add() return FALSE; } +void Item_sum_avg::remove() +{ + Item_sum_sum::remove(); + if (!aggr->arg_is_null(true)) + { + DBUG_ASSERT(count > 0); + count--; + } +} + double Item_sum_avg::val_real() { DBUG_ASSERT(fixed == 1); diff --git a/sql/item_sum.h b/sql/item_sum.h index 6698a1b2169..b8f41030465 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -837,6 +837,8 @@ class Item_sum_count :public Item_sum_int class Item_sum_avg :public Item_sum_sum { public: + // TODO-cvicentiu given that Item_sum_sum now uses a counter of its own, in + // order to implement remove(), it is possible to remove this member. ulonglong count; uint prec_increment; uint f_precision, f_scale, dec_bin_size; @@ -855,6 +857,7 @@ public: } void clear(); bool add(); + void remove(); double val_real(); // In SPs we might force the "wrong" type with select into a declare variable longlong val_int() { return val_int_from_real(); } diff --git a/sql/sql_window.cc b/sql/sql_window.cc index f0d74b15e7c..efdb5aa91a0 100644 --- a/sql/sql_window.cc +++ b/sql/sql_window.cc @@ -1585,6 +1585,7 @@ bool JOIN::process_window_functions(List *curr_fields_list) case Item_sum::COUNT_FUNC: case Item_sum::SUM_BIT_FUNC: case Item_sum::SUM_FUNC: + case Item_sum::AVG_FUNC: { /* Frame-aware window function computation. It does one pass, but