mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
MDEV-9676: RANGE-type frames for window functions
- Handle ORDER BY DESC in window definitions. - Fix an issue in Frame_range_current_row_top
This commit is contained in:
@ -91,6 +91,18 @@ pk a rank() over (order by a)
|
|||||||
7 2 5
|
7 2 5
|
||||||
9 4 9
|
9 4 9
|
||||||
10 4 9
|
10 4 9
|
||||||
|
select pk, a, rank() over (order by a desc) from t2;
|
||||||
|
pk a rank() over (order by a desc)
|
||||||
|
1 0 9
|
||||||
|
2 0 9
|
||||||
|
3 1 7
|
||||||
|
4 1 7
|
||||||
|
8 2 3
|
||||||
|
5 2 3
|
||||||
|
6 2 3
|
||||||
|
7 2 3
|
||||||
|
9 4 1
|
||||||
|
10 4 1
|
||||||
drop table t2;
|
drop table t2;
|
||||||
#
|
#
|
||||||
# Try DENSE_RANK() function
|
# Try DENSE_RANK() function
|
||||||
@ -252,6 +264,23 @@ pk c CNT
|
|||||||
8 2 2
|
8 2 2
|
||||||
9 2 2
|
9 2 2
|
||||||
10 2 1
|
10 2 1
|
||||||
|
# Check ORDER BY DESC
|
||||||
|
select
|
||||||
|
pk, c,
|
||||||
|
count(*) over (partition by c order by pk desc
|
||||||
|
rows between 2 preceding and 2 following) as CNT
|
||||||
|
from t1;
|
||||||
|
pk c CNT
|
||||||
|
1 1 3
|
||||||
|
2 1 4
|
||||||
|
3 1 4
|
||||||
|
4 1 3
|
||||||
|
5 2 3
|
||||||
|
6 2 4
|
||||||
|
7 2 5
|
||||||
|
8 2 5
|
||||||
|
9 2 4
|
||||||
|
10 2 3
|
||||||
drop table t0,t1;
|
drop table t0,t1;
|
||||||
#
|
#
|
||||||
# Resolution of window names
|
# Resolution of window names
|
||||||
@ -817,6 +846,22 @@ pk a cnt
|
|||||||
9 72 9
|
9 72 9
|
||||||
select
|
select
|
||||||
pk, a,
|
pk, a,
|
||||||
|
count(a) over (ORDER BY a DESC
|
||||||
|
RANGE BETWEEN UNBOUNDED PRECEDING
|
||||||
|
AND 10 FOLLOWING) as cnt
|
||||||
|
from t1;
|
||||||
|
pk a cnt
|
||||||
|
1 1 9
|
||||||
|
2 2 9
|
||||||
|
3 4 9
|
||||||
|
4 8 9
|
||||||
|
5 26 5
|
||||||
|
6 27 5
|
||||||
|
7 40 3
|
||||||
|
8 71 2
|
||||||
|
9 72 2
|
||||||
|
select
|
||||||
|
pk, a,
|
||||||
count(a) over (ORDER BY a
|
count(a) over (ORDER BY a
|
||||||
RANGE BETWEEN UNBOUNDED PRECEDING
|
RANGE BETWEEN UNBOUNDED PRECEDING
|
||||||
AND 1 FOLLOWING) as cnt
|
AND 1 FOLLOWING) as cnt
|
||||||
@ -849,6 +894,22 @@ pk a cnt
|
|||||||
9 72 7
|
9 72 7
|
||||||
select
|
select
|
||||||
pk, a,
|
pk, a,
|
||||||
|
count(a) over (ORDER BY a DESC
|
||||||
|
RANGE BETWEEN UNBOUNDED PRECEDING
|
||||||
|
AND 10 PRECEDING) as cnt
|
||||||
|
from t1;
|
||||||
|
pk a cnt
|
||||||
|
1 1 5
|
||||||
|
2 2 5
|
||||||
|
3 4 5
|
||||||
|
4 8 5
|
||||||
|
5 26 3
|
||||||
|
6 27 3
|
||||||
|
7 40 2
|
||||||
|
8 71 0
|
||||||
|
9 72 0
|
||||||
|
select
|
||||||
|
pk, a,
|
||||||
count(a) over (ORDER BY a
|
count(a) over (ORDER BY a
|
||||||
RANGE BETWEEN UNBOUNDED PRECEDING
|
RANGE BETWEEN UNBOUNDED PRECEDING
|
||||||
AND 1 PRECEDING) as cnt
|
AND 1 PRECEDING) as cnt
|
||||||
@ -881,6 +942,22 @@ pk a cnt
|
|||||||
9 72 2
|
9 72 2
|
||||||
select
|
select
|
||||||
pk, a,
|
pk, a,
|
||||||
|
count(a) over (ORDER BY a DESC
|
||||||
|
RANGE BETWEEN 1 PRECEDING
|
||||||
|
AND CURRENT ROW) as cnt
|
||||||
|
from t1;
|
||||||
|
pk a cnt
|
||||||
|
1 1 2
|
||||||
|
2 2 1
|
||||||
|
3 4 1
|
||||||
|
4 8 1
|
||||||
|
5 26 2
|
||||||
|
6 27 1
|
||||||
|
7 40 1
|
||||||
|
8 71 2
|
||||||
|
9 72 1
|
||||||
|
select
|
||||||
|
pk, a,
|
||||||
count(a) over (ORDER BY a
|
count(a) over (ORDER BY a
|
||||||
RANGE BETWEEN 1 FOLLOWING
|
RANGE BETWEEN 1 FOLLOWING
|
||||||
AND 3 FOLLOWING) as cnt
|
AND 3 FOLLOWING) as cnt
|
||||||
@ -895,6 +972,39 @@ pk a cnt
|
|||||||
7 40 0
|
7 40 0
|
||||||
8 71 1
|
8 71 1
|
||||||
9 72 0
|
9 72 0
|
||||||
|
# Try CURRENT ROW with[out] DESC
|
||||||
|
select
|
||||||
|
pk, a,
|
||||||
|
count(a) over (ORDER BY a
|
||||||
|
RANGE BETWEEN CURRENT ROW
|
||||||
|
AND 1 FOLLOWING) as cnt
|
||||||
|
from t1;
|
||||||
|
pk a cnt
|
||||||
|
1 1 2
|
||||||
|
2 2 1
|
||||||
|
3 4 1
|
||||||
|
4 8 1
|
||||||
|
5 26 2
|
||||||
|
6 27 1
|
||||||
|
7 40 1
|
||||||
|
8 71 2
|
||||||
|
9 72 1
|
||||||
|
select
|
||||||
|
pk, a,
|
||||||
|
count(a) over (order by a desc
|
||||||
|
range between current row
|
||||||
|
and 1 following) as cnt
|
||||||
|
from t1;
|
||||||
|
pk a cnt
|
||||||
|
1 1 1
|
||||||
|
2 2 2
|
||||||
|
3 4 1
|
||||||
|
4 8 1
|
||||||
|
5 26 1
|
||||||
|
6 27 2
|
||||||
|
7 40 1
|
||||||
|
8 71 1
|
||||||
|
9 72 2
|
||||||
insert into t1 select 22, pk, a from t1;
|
insert into t1 select 22, pk, a from t1;
|
||||||
select
|
select
|
||||||
part_id, pk, a,
|
part_id, pk, a,
|
||||||
|
@ -82,6 +82,7 @@ insert into t2 values
|
|||||||
(10 , 4);
|
(10 , 4);
|
||||||
|
|
||||||
select pk, a, rank() over (order by a) from t2;
|
select pk, a, rank() over (order by a) from t2;
|
||||||
|
select pk, a, rank() over (order by a desc) from t2;
|
||||||
|
|
||||||
drop table t2;
|
drop table t2;
|
||||||
|
|
||||||
@ -158,6 +159,13 @@ select
|
|||||||
rows between current row and 1 following) as CNT
|
rows between current row and 1 following) as CNT
|
||||||
from t1;
|
from t1;
|
||||||
|
|
||||||
|
--echo # Check ORDER BY DESC
|
||||||
|
select
|
||||||
|
pk, c,
|
||||||
|
count(*) over (partition by c order by pk desc
|
||||||
|
rows between 2 preceding and 2 following) as CNT
|
||||||
|
from t1;
|
||||||
|
|
||||||
drop table t0,t1;
|
drop table t0,t1;
|
||||||
|
|
||||||
--echo #
|
--echo #
|
||||||
@ -514,6 +522,13 @@ select
|
|||||||
AND 10 FOLLOWING) as cnt
|
AND 10 FOLLOWING) as cnt
|
||||||
from t1;
|
from t1;
|
||||||
|
|
||||||
|
select
|
||||||
|
pk, a,
|
||||||
|
count(a) over (ORDER BY a DESC
|
||||||
|
RANGE BETWEEN UNBOUNDED PRECEDING
|
||||||
|
AND 10 FOLLOWING) as cnt
|
||||||
|
from t1;
|
||||||
|
|
||||||
select
|
select
|
||||||
pk, a,
|
pk, a,
|
||||||
count(a) over (ORDER BY a
|
count(a) over (ORDER BY a
|
||||||
@ -528,6 +543,13 @@ select
|
|||||||
AND 10 PRECEDING) as cnt
|
AND 10 PRECEDING) as cnt
|
||||||
from t1;
|
from t1;
|
||||||
|
|
||||||
|
select
|
||||||
|
pk, a,
|
||||||
|
count(a) over (ORDER BY a DESC
|
||||||
|
RANGE BETWEEN UNBOUNDED PRECEDING
|
||||||
|
AND 10 PRECEDING) as cnt
|
||||||
|
from t1;
|
||||||
|
|
||||||
select
|
select
|
||||||
pk, a,
|
pk, a,
|
||||||
count(a) over (ORDER BY a
|
count(a) over (ORDER BY a
|
||||||
@ -543,6 +565,13 @@ select
|
|||||||
AND CURRENT ROW) as cnt
|
AND CURRENT ROW) as cnt
|
||||||
from t1;
|
from t1;
|
||||||
|
|
||||||
|
select
|
||||||
|
pk, a,
|
||||||
|
count(a) over (ORDER BY a DESC
|
||||||
|
RANGE BETWEEN 1 PRECEDING
|
||||||
|
AND CURRENT ROW) as cnt
|
||||||
|
from t1;
|
||||||
|
|
||||||
select
|
select
|
||||||
pk, a,
|
pk, a,
|
||||||
count(a) over (ORDER BY a
|
count(a) over (ORDER BY a
|
||||||
@ -550,6 +579,22 @@ select
|
|||||||
AND 3 FOLLOWING) as cnt
|
AND 3 FOLLOWING) as cnt
|
||||||
from t1;
|
from t1;
|
||||||
|
|
||||||
|
--echo # Try CURRENT ROW with[out] DESC
|
||||||
|
select
|
||||||
|
pk, a,
|
||||||
|
count(a) over (ORDER BY a
|
||||||
|
RANGE BETWEEN CURRENT ROW
|
||||||
|
AND 1 FOLLOWING) as cnt
|
||||||
|
from t1;
|
||||||
|
|
||||||
|
select
|
||||||
|
pk, a,
|
||||||
|
count(a) over (order by a desc
|
||||||
|
range between current row
|
||||||
|
and 1 following) as cnt
|
||||||
|
from t1;
|
||||||
|
|
||||||
|
|
||||||
# Try with partitions
|
# Try with partitions
|
||||||
insert into t1 select 22, pk, a from t1;
|
insert into t1 select 22, pk, a from t1;
|
||||||
select
|
select
|
||||||
|
@ -275,9 +275,9 @@ public:
|
|||||||
{
|
{
|
||||||
int rc= read_record->table->file->ha_rnd_pos(read_record->record, p);
|
int rc= read_record->table->file->ha_rnd_pos(read_record->record, p);
|
||||||
if (!rc)
|
if (!rc)
|
||||||
return true;
|
return true; // restored ok
|
||||||
}
|
}
|
||||||
return false;
|
return false; // didn't restore
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: should move_to() also read row here?
|
// todo: should move_to() also read row here?
|
||||||
@ -409,6 +409,11 @@ class Frame_range_n_top : public Frame_cursor
|
|||||||
Item *item_add;
|
Item *item_add;
|
||||||
|
|
||||||
const bool is_preceding;
|
const bool is_preceding;
|
||||||
|
/*
|
||||||
|
1 when order_list uses ASC ordering
|
||||||
|
-1 when order_list uses DESC ordering
|
||||||
|
*/
|
||||||
|
int order_direction;
|
||||||
public:
|
public:
|
||||||
Frame_range_n_top(bool is_preceding_arg, Item *n_val_arg) :
|
Frame_range_n_top(bool is_preceding_arg, Item *n_val_arg) :
|
||||||
n_val(n_val_arg), item_add(NULL), is_preceding(is_preceding_arg)
|
n_val(n_val_arg), item_add(NULL), is_preceding(is_preceding_arg)
|
||||||
@ -422,9 +427,18 @@ public:
|
|||||||
|
|
||||||
DBUG_ASSERT(order_list->elements == 1);
|
DBUG_ASSERT(order_list->elements == 1);
|
||||||
Item *src_expr= order_list->first->item[0];
|
Item *src_expr= order_list->first->item[0];
|
||||||
|
if (order_list->first->direction == ORDER::ORDER_ASC)
|
||||||
|
order_direction= 1;
|
||||||
|
else
|
||||||
|
order_direction= -1;
|
||||||
|
|
||||||
range_expr= (Cached_item_item*) new_Cached_item(thd, src_expr, FALSE);
|
range_expr= (Cached_item_item*) new_Cached_item(thd, src_expr, FALSE);
|
||||||
if (is_preceding)
|
|
||||||
|
bool use_minus= is_preceding;
|
||||||
|
if (order_direction == -1)
|
||||||
|
use_minus= !use_minus;
|
||||||
|
|
||||||
|
if (use_minus)
|
||||||
item_add= new (thd->mem_root) Item_func_minus(thd, src_expr, n_val);
|
item_add= new (thd->mem_root) Item_func_minus(thd, src_expr, n_val);
|
||||||
else
|
else
|
||||||
item_add= new (thd->mem_root) Item_func_plus(thd, src_expr, n_val);
|
item_add= new (thd->mem_root) Item_func_plus(thd, src_expr, n_val);
|
||||||
@ -458,7 +472,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
if (cursor.restore_last_row())
|
if (cursor.restore_last_row())
|
||||||
{
|
{
|
||||||
if (range_expr->cmp_read_only() <= 0)
|
if (order_direction * range_expr->cmp_read_only() <= 0)
|
||||||
return;
|
return;
|
||||||
item->remove();
|
item->remove();
|
||||||
}
|
}
|
||||||
@ -470,7 +484,7 @@ private:
|
|||||||
{
|
{
|
||||||
while (!cursor.get_next())
|
while (!cursor.get_next())
|
||||||
{
|
{
|
||||||
if (range_expr->cmp_read_only() <= 0)
|
if (order_direction * range_expr->cmp_read_only() <= 0)
|
||||||
break;
|
break;
|
||||||
item->remove();
|
item->remove();
|
||||||
}
|
}
|
||||||
@ -487,6 +501,8 @@ private:
|
|||||||
|
|
||||||
Bottom end moves first so it needs to check for partition end
|
Bottom end moves first so it needs to check for partition end
|
||||||
(todo: unless it's PRECEDING and in that case it doesnt)
|
(todo: unless it's PRECEDING and in that case it doesnt)
|
||||||
|
(todo: factor out common parts with Frame_range_n_top into
|
||||||
|
a common ancestor)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class Frame_range_n_bottom: public Frame_cursor
|
class Frame_range_n_bottom: public Frame_cursor
|
||||||
@ -502,6 +518,12 @@ class Frame_range_n_bottom: public Frame_cursor
|
|||||||
|
|
||||||
Group_bound_tracker bound_tracker;
|
Group_bound_tracker bound_tracker;
|
||||||
bool end_of_partition;
|
bool end_of_partition;
|
||||||
|
|
||||||
|
/*
|
||||||
|
1 when order_list uses ASC ordering
|
||||||
|
-1 when order_list uses DESC ordering
|
||||||
|
*/
|
||||||
|
int order_direction;
|
||||||
public:
|
public:
|
||||||
Frame_range_n_bottom(bool is_preceding_arg, Item *n_val_arg) :
|
Frame_range_n_bottom(bool is_preceding_arg, Item *n_val_arg) :
|
||||||
n_val(n_val_arg), item_add(NULL), is_preceding(is_preceding_arg)
|
n_val(n_val_arg), item_add(NULL), is_preceding(is_preceding_arg)
|
||||||
@ -516,8 +538,18 @@ public:
|
|||||||
DBUG_ASSERT(order_list->elements == 1);
|
DBUG_ASSERT(order_list->elements == 1);
|
||||||
Item *src_expr= order_list->first->item[0];
|
Item *src_expr= order_list->first->item[0];
|
||||||
|
|
||||||
|
if (order_list->first->direction == ORDER::ORDER_ASC)
|
||||||
|
order_direction= 1;
|
||||||
|
else
|
||||||
|
order_direction= -1;
|
||||||
|
|
||||||
range_expr= (Cached_item_item*) new_Cached_item(thd, src_expr, FALSE);
|
range_expr= (Cached_item_item*) new_Cached_item(thd, src_expr, FALSE);
|
||||||
if (is_preceding)
|
|
||||||
|
bool use_minus= is_preceding;
|
||||||
|
if (order_direction == -1)
|
||||||
|
use_minus= !use_minus;
|
||||||
|
|
||||||
|
if (use_minus)
|
||||||
item_add= new (thd->mem_root) Item_func_minus(thd, src_expr, n_val);
|
item_add= new (thd->mem_root) Item_func_minus(thd, src_expr, n_val);
|
||||||
else
|
else
|
||||||
item_add= new (thd->mem_root) Item_func_plus(thd, src_expr, n_val);
|
item_add= new (thd->mem_root) Item_func_plus(thd, src_expr, n_val);
|
||||||
@ -560,7 +592,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
if (cursor.restore_last_row())
|
if (cursor.restore_last_row())
|
||||||
{
|
{
|
||||||
if (range_expr->cmp_read_only() < 0)
|
if (order_direction * range_expr->cmp_read_only() < 0)
|
||||||
return;
|
return;
|
||||||
item->add();
|
item->add();
|
||||||
}
|
}
|
||||||
@ -578,7 +610,7 @@ private:
|
|||||||
end_of_partition= true;
|
end_of_partition= true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (range_expr->cmp_read_only() < 0)
|
if (order_direction * range_expr->cmp_read_only() < 0)
|
||||||
break;
|
break;
|
||||||
item->add();
|
item->add();
|
||||||
}
|
}
|
||||||
@ -695,7 +727,6 @@ class Frame_range_current_row_top : public Frame_cursor
|
|||||||
Group_bound_tracker peer_tracker;
|
Group_bound_tracker peer_tracker;
|
||||||
|
|
||||||
bool move;
|
bool move;
|
||||||
bool at_partition_start;
|
|
||||||
public:
|
public:
|
||||||
void init(THD *thd, READ_RECORD *info,
|
void init(THD *thd, READ_RECORD *info,
|
||||||
SQL_I_List<ORDER> *partition_list,
|
SQL_I_List<ORDER> *partition_list,
|
||||||
@ -709,31 +740,33 @@ public:
|
|||||||
|
|
||||||
void pre_next_partition(longlong rownum, Item_sum* item)
|
void pre_next_partition(longlong rownum, Item_sum* item)
|
||||||
{
|
{
|
||||||
// fetch the value from the first row
|
// Fetch the value from the first row
|
||||||
peer_tracker.check_if_next_group();
|
peer_tracker.check_if_next_group();
|
||||||
}
|
|
||||||
|
|
||||||
void next_partition(longlong rownum, Item_sum* item)
|
|
||||||
{
|
|
||||||
at_partition_start= true;
|
|
||||||
cursor.move_to(rownum+1);
|
cursor.move_to(rownum+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void next_partition(longlong rownum, Item_sum* item) {}
|
||||||
|
|
||||||
void pre_next_row(Item_sum* item)
|
void pre_next_row(Item_sum* item)
|
||||||
{
|
{
|
||||||
// Check if our current row is pointing to a peer of the current row.
|
// Check if the new current_row is a peer of the row that our cursor is
|
||||||
// If not, move forward until that becomes true.
|
// pointing to.
|
||||||
move= peer_tracker.check_if_next_group();
|
move= peer_tracker.check_if_next_group();
|
||||||
}
|
}
|
||||||
|
|
||||||
void next_row(Item_sum* item)
|
void next_row(Item_sum* item)
|
||||||
{
|
{
|
||||||
bool was_at_partition_start= at_partition_start;
|
|
||||||
at_partition_start= false;
|
|
||||||
if (move)
|
if (move)
|
||||||
{
|
{
|
||||||
if (!was_at_partition_start && cursor.restore_last_row())
|
/*
|
||||||
|
Our cursor is pointing at the first row that was a peer of the previous
|
||||||
|
current row. Or, it was the first row in the partition.
|
||||||
|
*/
|
||||||
|
if (cursor.restore_last_row())
|
||||||
{
|
{
|
||||||
|
// todo: need the following check ?
|
||||||
|
if (!peer_tracker.compare_with_cache())
|
||||||
|
return;
|
||||||
item->remove();
|
item->remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user