Skip to content

Commit 3450c2d

Browse files
committed
Added sorting window function to minimize the number of scans
of the temporary table needed to compute them.
1 parent de35787 commit 3450c2d

File tree

2 files changed

+264
-0
lines changed

2 files changed

+264
-0
lines changed

sql/sql_window.cc

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
#include "sql_select.h"
2+
#include "sql_list.h"
23
#include "item_windowfunc.h"
34
#include "filesort.h"
45
#include "sql_base.h"
56
#include "sql_window.h"
67

78

9+
#define SORTORDER_CHANGE_FLAG 1
10+
#define PARTITION_CHANGE_FLAG 2
11+
#define FRAME_CHANGE_FLAG 4
12+
13+
814
bool
915
Window_spec::check_window_names(List_iterator_fast<Window_spec> &it)
1016
{
@@ -218,6 +224,251 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables,
218224
DBUG_RETURN(0);
219225
}
220226

227+
/////////////////////////////////////////////////////////////////////////////
228+
// Sorting window functions to minimize the number of table scans
229+
// performed during the computation of these functions
230+
/////////////////////////////////////////////////////////////////////////////
231+
232+
static
233+
int compare_order_elements(ORDER *ord1, ORDER *ord2)
234+
{
235+
Item *item1= (*ord1->item)->real_item();
236+
Item *item2= (*ord2->item)->real_item();
237+
if (item1->used_tables() == item2->used_tables())
238+
{
239+
int cmp= strcmp(item1->name, item2->name);
240+
if (cmp == 0)
241+
{
242+
if (ord1->direction == ord2->direction)
243+
return 0;
244+
return ord1->direction > ord2->direction ? -2 : 2;
245+
}
246+
else
247+
return cmp < 0 ? 2 : -2;
248+
}
249+
return item1->used_tables() > item2->used_tables() ? -2 : 2;
250+
}
251+
252+
static
253+
int compare_order_lists(SQL_I_List<ORDER> *part_list1,
254+
SQL_I_List<ORDER> *part_list2)
255+
{
256+
if (part_list1 == part_list2)
257+
return 0;
258+
ORDER *elem1= part_list1->first;
259+
ORDER *elem2= part_list2->first;
260+
for ( ; elem1 && elem2; elem1= elem1->next, elem2= elem2->next)
261+
{
262+
int cmp;
263+
if ((cmp= compare_order_elements(elem1, elem2)))
264+
return cmp;
265+
}
266+
if (elem1)
267+
return -1;
268+
if (elem2)
269+
return 1;
270+
return 0;
271+
}
272+
273+
274+
static
275+
int compare_window_frame_bounds(Window_frame_bound *win_frame_bound1,
276+
Window_frame_bound *win_frame_bound2,
277+
bool is_bottom_bound)
278+
{
279+
int res;
280+
if (win_frame_bound1->precedence_type != win_frame_bound2->precedence_type)
281+
{
282+
res= win_frame_bound1->precedence_type > win_frame_bound2->precedence_type ?
283+
-2 : 2;
284+
if (is_bottom_bound)
285+
res= -res;
286+
return res;
287+
}
288+
289+
if (win_frame_bound1->is_unbounded() && win_frame_bound2->is_unbounded())
290+
return 0;
291+
292+
if (!win_frame_bound1->is_unbounded() && !win_frame_bound2->is_unbounded())
293+
{
294+
if (win_frame_bound1->offset->eq(win_frame_bound2->offset, true))
295+
return 0;
296+
else
297+
{
298+
res= strcasecmp(win_frame_bound1->offset->name,
299+
win_frame_bound2->offset->name);
300+
res= res < 0 ? 2 : -2;
301+
if (is_bottom_bound)
302+
res= -res;
303+
return res;
304+
}
305+
}
306+
307+
return (is_bottom_bound != win_frame_bound1->is_unbounded()) ? 2 : -2;
308+
309+
return 0;
310+
}
311+
312+
313+
static
314+
int compare_window_frames(Window_frame *win_frame1,
315+
Window_frame *win_frame2)
316+
{
317+
int cmp;
318+
319+
if (win_frame1 == win_frame2)
320+
return 0;
321+
322+
if (win_frame1->units != win_frame2->units)
323+
return win_frame1->units > win_frame2->units ? -2 : 2;
324+
325+
cmp= compare_window_frame_bounds(win_frame1->top_bound,
326+
win_frame2->top_bound,
327+
false);
328+
if (cmp)
329+
return cmp;
330+
331+
cmp= compare_window_frame_bounds(win_frame1->bottom_bound,
332+
win_frame2->bottom_bound,
333+
true);
334+
if (cmp)
335+
return cmp;
336+
337+
if (win_frame1->exclusion != win_frame2->exclusion)
338+
return win_frame1->exclusion > win_frame2->exclusion ? -1 : 1;
339+
340+
return 0;
341+
}
342+
343+
static
344+
int compare_window_spec_joined_lists(Window_spec *win_spec1,
345+
Window_spec *win_spec2)
346+
{
347+
win_spec1->join_partition_and_order_lists();
348+
win_spec2->join_partition_and_order_lists();
349+
int cmp= compare_order_lists(win_spec1->partition_list,
350+
win_spec2->partition_list);
351+
win_spec1->disjoin_partition_and_order_lists();
352+
win_spec2->disjoin_partition_and_order_lists();
353+
return cmp;
354+
}
355+
356+
357+
static
358+
int compare_window_funcs_by_window_specs(Item_window_func *win_func1,
359+
Item_window_func *win_func2,
360+
void *arg)
361+
{
362+
int cmp;
363+
Window_spec *win_spec1= win_func1->window_spec;
364+
Window_spec *win_spec2= win_func2->window_spec;
365+
if (win_spec1 == win_spec2)
366+
return 0;
367+
cmp= compare_order_lists(win_spec1->partition_list,
368+
win_spec2->partition_list);
369+
if (cmp == 0)
370+
{
371+
/*
372+
Partition lists contain the same elements.
373+
Let's use only one of the lists.
374+
*/
375+
if (!win_spec1->name() && win_spec2->name())
376+
win_spec1->partition_list= win_spec2->partition_list;
377+
else
378+
win_spec2->partition_list= win_spec1->partition_list;
379+
380+
cmp= compare_order_lists(win_spec1->order_list,
381+
win_spec2->order_list);
382+
383+
if (cmp != 0)
384+
return cmp;
385+
386+
/*
387+
Order lists contain the same elements.
388+
Let's use only one of the lists.
389+
*/
390+
if (!win_spec1->name() && win_spec2->name())
391+
win_spec1->order_list= win_spec2->order_list;
392+
else
393+
win_spec2->order_list= win_spec1->order_list;
394+
395+
cmp= compare_window_frames(win_spec1->window_frame,
396+
win_spec2->window_frame);
397+
398+
if (cmp != 0)
399+
return cmp;
400+
401+
/* Window frames are equal. Let's use only one of them. */
402+
if (!win_spec1->name() && win_spec2->name())
403+
win_spec1->window_frame= win_spec2->window_frame;
404+
else
405+
win_spec2->window_frame= win_spec1->window_frame;
406+
407+
return 0;
408+
}
409+
410+
if (cmp == 2 || cmp == -2)
411+
return cmp;
412+
413+
/* one of the partitions lists is the proper beginning of the another */
414+
cmp= compare_window_spec_joined_lists(win_spec1, win_spec2);
415+
416+
if (-1 <= cmp && cmp <= 1)
417+
cmp= win_spec1->partition_list->elements <
418+
win_spec2->partition_list->elements ? -1 : 1;
419+
420+
return cmp;
421+
}
422+
423+
typedef int (*Item_window_func_cmp)(Item_window_func *f1,
424+
Item_window_func *f2,
425+
void *arg);
426+
427+
428+
static
429+
void order_window_funcs_by_window_specs(List<Item_window_func> *win_func_list)
430+
{
431+
if (win_func_list->elements == 0)
432+
return;
433+
434+
bubble_sort<Item_window_func>(win_func_list,
435+
compare_window_funcs_by_window_specs,
436+
NULL);
437+
438+
List_iterator_fast<Item_window_func> it(*win_func_list);
439+
Item_window_func *prev= it++;
440+
prev->marker= SORTORDER_CHANGE_FLAG |
441+
PARTITION_CHANGE_FLAG |
442+
FRAME_CHANGE_FLAG;
443+
Item_window_func *curr;
444+
while ((curr= it++))
445+
{
446+
Window_spec *win_spec_prev= prev->window_spec;
447+
Window_spec *win_spec_curr= curr->window_spec;
448+
curr->marker= 0;
449+
if (!(win_spec_prev->partition_list == win_spec_curr->partition_list &&
450+
win_spec_prev->order_list == win_spec_curr->order_list))
451+
{
452+
int cmp= compare_window_spec_joined_lists(win_spec_prev, win_spec_curr);
453+
if (!(-1 <= cmp && cmp <= 1))
454+
{
455+
curr->marker= SORTORDER_CHANGE_FLAG |
456+
PARTITION_CHANGE_FLAG |
457+
FRAME_CHANGE_FLAG;
458+
}
459+
else if (win_spec_prev->partition_list != win_spec_curr->partition_list)
460+
curr->marker|= PARTITION_CHANGE_FLAG | FRAME_CHANGE_FLAG;
461+
}
462+
else if (win_spec_prev->window_frame != win_spec_curr->window_frame)
463+
curr->marker|= FRAME_CHANGE_FLAG;
464+
465+
prev= curr;
466+
}
467+
}
468+
469+
470+
/////////////////////////////////////////////////////////////////////////////
471+
221472

222473
/*
223474
Do a pass over sorted table and compute window function values.
@@ -1540,6 +1791,8 @@ bool Window_funcs_computation::setup(THD *thd,
15401791
Item_window_func *item_win;
15411792
Window_func_runner *runner;
15421793

1794+
order_window_funcs_by_window_specs(window_funcs);
1795+
15431796
SQL_SELECT *sel= NULL;
15441797
if (tab->filesort && tab->filesort->select)
15451798
{
@@ -1714,3 +1967,4 @@ void Window_funcs_computation::cleanup()
17141967
else
17151968
#endif
17161969

1970+

sql/sql_window.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,16 @@ class Window_spec : public Sql_alloc
112112
bool check_window_names(List_iterator_fast<Window_spec> &it);
113113

114114
char *window_reference() { return window_ref ? window_ref->str : NULL; }
115+
116+
void join_partition_and_order_lists()
117+
{
118+
*(partition_list->next)= order_list->first;
119+
}
120+
121+
void disjoin_partition_and_order_lists()
122+
{
123+
*(partition_list->next)= NULL;
124+
}
115125
};
116126

117127
class Window_def : public Window_spec

0 commit comments

Comments
 (0)