summaryrefslogtreecommitdiff
path: root/sql/sql_statistics.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_statistics.cc')
-rw-r--r--sql/sql_statistics.cc203
1 files changed, 135 insertions, 68 deletions
diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc
index 5b8c1b63b6f..5c8d85c2cc1 100644
--- a/sql/sql_statistics.cc
+++ b/sql/sql_statistics.cc
@@ -29,7 +29,6 @@
#include "sql_statistics.h"
#include "opt_range.h"
#include "uniques.h"
-#include "my_atomic.h"
#include "sql_show.h"
#include "sql_partition.h"
@@ -325,8 +324,8 @@ private:
public:
inline void init(THD *thd, Field * table_field);
- inline bool add(ha_rows rowno);
- inline void finish(ha_rows rows);
+ inline bool add();
+ inline void finish(ha_rows rows, double sample_fraction);
inline void cleanup();
};
@@ -1545,6 +1544,8 @@ class Histogram_builder
uint curr_bucket; /* number of the current bucket to be built */
ulonglong count; /* number of values retrieved */
ulonglong count_distinct; /* number of distinct values retrieved */
+ /* number of distinct values that occured only once */
+ ulonglong count_distinct_single_occurence;
public:
Histogram_builder(Field *col, uint col_len, ha_rows rows)
@@ -1558,14 +1559,21 @@ public:
bucket_capacity= (double) records / (hist_width + 1);
curr_bucket= 0;
count= 0;
- count_distinct= 0;
+ count_distinct= 0;
+ count_distinct_single_occurence= 0;
}
- ulonglong get_count_distinct() { return count_distinct; }
+ ulonglong get_count_distinct() const { return count_distinct; }
+ ulonglong get_count_single_occurence() const
+ {
+ return count_distinct_single_occurence;
+ }
int next(void *elem, element_count elem_cnt)
{
count_distinct++;
+ if (elem_cnt == 1)
+ count_distinct_single_occurence++;
count+= elem_cnt;
if (curr_bucket == hist_width)
return 0;
@@ -1579,7 +1587,7 @@ public:
count > bucket_capacity * (curr_bucket + 1))
{
histogram->set_prev_value(curr_bucket);
- curr_bucket++;
+ curr_bucket++;
}
}
return 0;
@@ -1595,9 +1603,18 @@ int histogram_build_walk(void *elem, element_count elem_cnt, void *arg)
return hist_builder->next(elem, elem_cnt);
}
-C_MODE_END
+static int count_distinct_single_occurence_walk(void *elem,
+ element_count count, void *arg)
+{
+ ((ulonglong*)arg)[0]+= 1;
+ if (count == 1)
+ ((ulonglong*)arg)[1]+= 1;
+ return 0;
+}
+
+C_MODE_END
/*
The class Count_distinct_field is a helper class used to calculate
the number of distinct values for a column. The class employs the
@@ -1616,6 +1633,9 @@ protected:
Unique *tree; /* The helper object to contain distinct values */
uint tree_key_length; /* The length of the keys for the elements of 'tree */
+ ulonglong distincts;
+ ulonglong distincts_single_occurence;
+
public:
Count_distinct_field() {}
@@ -1667,30 +1687,40 @@ public:
{
return tree->unique_add(table_field->ptr);
}
-
+
/*
@brief
Calculate the number of elements accumulated in the container of 'tree'
*/
- ulonglong get_value()
- {
- ulonglong count;
- if (tree->elements == 0)
- return (ulonglong) tree->elements_in_tree();
- count= 0;
- tree->walk(table_field->table, count_distinct_walk, (void*) &count);
- return count;
+ void walk_tree()
+ {
+ ulonglong counts[2] = {0, 0};
+ tree->walk(table_field->table,
+ count_distinct_single_occurence_walk, counts);
+ distincts= counts[0];
+ distincts_single_occurence= counts[1];
}
/*
@brief
- Build the histogram for the elements accumulated in the container of 'tree'
+ Calculate a histogram of the tree
*/
- ulonglong get_value_with_histogram(ha_rows rows)
+ void walk_tree_with_histogram(ha_rows rows)
{
Histogram_builder hist_builder(table_field, tree_key_length, rows);
tree->walk(table_field->table, histogram_build_walk, (void *) &hist_builder);
- return hist_builder.get_count_distinct();
+ distincts= hist_builder.get_count_distinct();
+ distincts_single_occurence= hist_builder.get_count_single_occurence();
+ }
+
+ ulonglong get_count_distinct()
+ {
+ return distincts;
+ }
+
+ ulonglong get_count_distinct_single_occurence()
+ {
+ return distincts_single_occurence;
}
/*
@@ -2494,7 +2524,7 @@ void Column_statistics_collected::init(THD *thd, Field *table_field)
*/
inline
-bool Column_statistics_collected::add(ha_rows rowno)
+bool Column_statistics_collected::add()
{
bool err= 0;
@@ -2503,9 +2533,11 @@ bool Column_statistics_collected::add(ha_rows rowno)
else
{
column_total_length+= column->value_length();
- if (min_value && column->update_min(min_value, rowno == nulls))
+ if (min_value && column->update_min(min_value,
+ is_null(COLUMN_STAT_MIN_VALUE)))
set_not_null(COLUMN_STAT_MIN_VALUE);
- if (max_value && column->update_max(max_value, rowno == nulls))
+ if (max_value && column->update_max(max_value,
+ is_null(COLUMN_STAT_MAX_VALUE)))
set_not_null(COLUMN_STAT_MAX_VALUE);
if (count_distinct)
err= count_distinct->add();
@@ -2523,7 +2555,7 @@ bool Column_statistics_collected::add(ha_rows rowno)
*/
inline
-void Column_statistics_collected::finish(ha_rows rows)
+void Column_statistics_collected::finish(ha_rows rows, double sample_fraction)
{
double val;
@@ -2541,16 +2573,44 @@ void Column_statistics_collected::finish(ha_rows rows)
}
if (count_distinct)
{
- ulonglong distincts;
uint hist_size= count_distinct->get_hist_size();
+
+ /* Compute cardinality statistics and optionally histogram. */
if (hist_size == 0)
- distincts= count_distinct->get_value();
+ count_distinct->walk_tree();
else
- distincts= count_distinct->get_value_with_histogram(rows - nulls);
+ count_distinct->walk_tree_with_histogram(rows - nulls);
+
+ ulonglong distincts= count_distinct->get_count_distinct();
+ ulonglong distincts_single_occurence=
+ count_distinct->get_count_distinct_single_occurence();
+
if (distincts)
{
- val= (double) (rows - nulls) / distincts;
- set_avg_frequency(val);
+ /*
+ We use the unsmoothed first-order jackknife estimator" to estimate
+ the number of distinct values.
+ With a sufficient large percentage of rows sampled (80%), we revert back
+ to computing the avg_frequency off of the raw data.
+ */
+ if (sample_fraction > 0.8)
+ val= (double) (rows - nulls) / distincts;
+ else
+ {
+ if (nulls == 1)
+ distincts_single_occurence+= 1;
+ if (nulls)
+ distincts+= 1;
+ double fraction_single_occurence=
+ static_cast<double>(distincts_single_occurence) / rows;
+ double total_number_of_rows= rows / sample_fraction;
+ double estimate_total_distincts= total_number_of_rows /
+ (distincts /
+ (1.0 - (1.0 - sample_fraction) * fraction_single_occurence));
+ val = std::fmax(estimate_total_distincts * (rows - nulls) / rows, 1.0);
+ }
+
+ set_avg_frequency(val);
set_not_null(COLUMN_STAT_AVG_FREQUENCY);
}
else
@@ -2738,12 +2798,28 @@ int collect_statistics_for_table(THD *thd, TABLE *table)
Field *table_field;
ha_rows rows= 0;
handler *file=table->file;
+ double sample_fraction= thd->variables.sample_percentage / 100;
+ const ha_rows MIN_THRESHOLD_FOR_SAMPLING= 50000;
DBUG_ENTER("collect_statistics_for_table");
table->collected_stats->cardinality_is_null= TRUE;
table->collected_stats->cardinality= 0;
+ if (thd->variables.sample_percentage == 0)
+ {
+ if (file->records() < MIN_THRESHOLD_FOR_SAMPLING)
+ {
+ sample_fraction= 1;
+ }
+ else
+ {
+ sample_fraction= std::fmin(
+ (MIN_THRESHOLD_FOR_SAMPLING + 4096 *
+ log(200 * file->records())) / file->records(), 1);
+ }
+ }
+
for (field_ptr= table->field; *field_ptr; field_ptr++)
{
table_field= *field_ptr;
@@ -2756,7 +2832,7 @@ int collect_statistics_for_table(THD *thd, TABLE *table)
/* Perform a full table scan to collect statistics on 'table's columns */
if (!(rc= file->ha_rnd_init(TRUE)))
- {
+ {
DEBUG_SYNC(table->in_use, "statistics_collection_start");
while ((rc= file->ha_rnd_next(table->record[0])) != HA_ERR_END_OF_FILE)
@@ -2767,17 +2843,20 @@ int collect_statistics_for_table(THD *thd, TABLE *table)
if (rc)
break;
- for (field_ptr= table->field; *field_ptr; field_ptr++)
+ if (thd_rnd(thd) <= sample_fraction)
{
- table_field= *field_ptr;
- if (!bitmap_is_set(table->read_set, table_field->field_index))
- continue;
- if ((rc= table_field->collected_stats->add(rows)))
+ for (field_ptr= table->field; *field_ptr; field_ptr++)
+ {
+ table_field= *field_ptr;
+ if (!bitmap_is_set(table->read_set, table_field->field_index))
+ continue;
+ if ((rc= table_field->collected_stats->add()))
+ break;
+ }
+ if (rc)
break;
+ rows++;
}
- if (rc)
- break;
- rows++;
}
file->ha_rnd_end();
}
@@ -2791,7 +2870,8 @@ int collect_statistics_for_table(THD *thd, TABLE *table)
if (!rc)
{
table->collected_stats->cardinality_is_null= FALSE;
- table->collected_stats->cardinality= rows;
+ table->collected_stats->cardinality=
+ static_cast<ha_rows>(rows / sample_fraction);
}
bitmap_clear_all(table->write_set);
@@ -2802,7 +2882,7 @@ int collect_statistics_for_table(THD *thd, TABLE *table)
continue;
bitmap_set_bit(table->write_set, table_field->field_index);
if (!rc)
- table_field->collected_stats->finish(rows);
+ table_field->collected_stats->finish(rows, sample_fraction);
else
table_field->collected_stats->cleanup();
}
@@ -3264,7 +3344,6 @@ int read_statistics_for_tables_if_needed(THD *thd, TABLE_LIST *tables)
{
TABLE_LIST stat_tables[STATISTICS_TABLES];
Open_tables_backup open_tables_backup;
-
DBUG_ENTER("read_statistics_for_tables_if_needed");
DEBUG_SYNC(thd, "statistics_read_start");
@@ -3273,10 +3352,7 @@ int read_statistics_for_tables_if_needed(THD *thd, TABLE_LIST *tables)
DBUG_RETURN(0);
if (open_stat_tables(thd, stat_tables, &open_tables_backup, FALSE))
- {
- thd->clear_error();
DBUG_RETURN(1);
- }
for (TABLE_LIST *tl= tables; tl; tl= tl->next_global)
{
@@ -3329,7 +3405,7 @@ int read_statistics_for_tables_if_needed(THD *thd, TABLE_LIST *tables)
'db' from all statistical tables: table_stats, column_stats, index_stats.
@retval
- 0 If all deletions are successful
+ 0 If all deletions are successful or we couldn't open statistics table
@retval
1 Otherwise
@@ -3337,7 +3413,8 @@ int read_statistics_for_tables_if_needed(THD *thd, TABLE_LIST *tables)
The function is called when executing the statement DROP TABLE 'tab'.
*/
-int delete_statistics_for_table(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *tab)
+int delete_statistics_for_table(THD *thd, const LEX_CSTRING *db,
+ const LEX_CSTRING *tab)
{
int err;
enum_binlog_format save_binlog_format;
@@ -3345,11 +3422,10 @@ int delete_statistics_for_table(THD *thd, const LEX_CSTRING *db, const LEX_CSTRI
TABLE_LIST tables[STATISTICS_TABLES];
Open_tables_backup open_tables_backup;
int rc= 0;
-
DBUG_ENTER("delete_statistics_for_table");
if (open_stat_tables(thd, tables, &open_tables_backup, TRUE))
- DBUG_RETURN(rc);
+ DBUG_RETURN(0);
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
@@ -3414,7 +3490,7 @@ int delete_statistics_for_table(THD *thd, const LEX_CSTRING *db, const LEX_CSTRI
'tab' from the statistical table column_stats.
@retval
- 0 If the deletion is successful
+ 0 If all deletions are successful or we couldn't open statistics table
@retval
1 Otherwise
@@ -3431,15 +3507,11 @@ int delete_statistics_for_column(THD *thd, TABLE *tab, Field *col)
TABLE_LIST tables;
Open_tables_backup open_tables_backup;
int rc= 0;
-
DBUG_ENTER("delete_statistics_for_column");
if (open_single_stat_table(thd, &tables, &stat_table_name[1],
&open_tables_backup, TRUE))
- {
- thd->clear_error();
- DBUG_RETURN(rc);
- }
+ DBUG_RETURN(0);
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
@@ -3480,7 +3552,7 @@ int delete_statistics_for_column(THD *thd, TABLE *tab, Field *col)
defined on the table 'tab' from the statistical table index_stats.
@retval
- 0 If the deletion is successful
+ 0 If all deletions are successful or we couldn't open statistics table
@retval
1 Otherwise
@@ -3498,15 +3570,11 @@ int delete_statistics_for_index(THD *thd, TABLE *tab, KEY *key_info,
TABLE_LIST tables;
Open_tables_backup open_tables_backup;
int rc= 0;
-
DBUG_ENTER("delete_statistics_for_index");
if (open_single_stat_table(thd, &tables, &stat_table_name[2],
&open_tables_backup, TRUE))
- {
- thd->clear_error();
- DBUG_RETURN(rc);
- }
+ DBUG_RETURN(0);
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
@@ -3575,8 +3643,10 @@ int delete_statistics_for_index(THD *thd, TABLE *tab, KEY *key_info,
The function is called when executing any statement that renames a table
*/
-int rename_table_in_stat_tables(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *tab,
- const LEX_CSTRING *new_db, const LEX_CSTRING *new_tab)
+int rename_table_in_stat_tables(THD *thd, const LEX_CSTRING *db,
+ const LEX_CSTRING *tab,
+ const LEX_CSTRING *new_db,
+ const LEX_CSTRING *new_tab)
{
int err;
enum_binlog_format save_binlog_format;
@@ -3587,7 +3657,9 @@ int rename_table_in_stat_tables(THD *thd, const LEX_CSTRING *db, const LEX_CSTRI
DBUG_ENTER("rename_table_in_stat_tables");
if (open_stat_tables(thd, tables, &open_tables_backup, TRUE))
+ {
DBUG_RETURN(0); // not an error
+ }
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
@@ -3679,7 +3751,6 @@ int rename_column_in_stat_tables(THD *thd, TABLE *tab, Field *col,
TABLE_LIST tables;
Open_tables_backup open_tables_backup;
int rc= 0;
-
DBUG_ENTER("rename_column_in_stat_tables");
if (tab->s->tmp_table != NO_TMP_TABLE)
@@ -3687,10 +3758,7 @@ int rename_column_in_stat_tables(THD *thd, TABLE *tab, Field *col,
if (open_single_stat_table(thd, &tables, &stat_table_name[1],
&open_tables_backup, TRUE))
- {
- thd->clear_error();
DBUG_RETURN(rc);
- }
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
@@ -3732,9 +3800,8 @@ void set_statistics_for_table(THD *thd, TABLE *table)
{
TABLE_STATISTICS_CB *stats_cb= &table->s->stats_cb;
Table_statistics *read_stats= stats_cb->table_stats;
- Use_stat_tables_mode use_stat_table_mode= get_use_stat_tables_mode(thd);
table->used_stat_records=
- (use_stat_table_mode <= COMPLEMENTARY ||
+ (!check_eits_preferred(thd) ||
!table->stats_is_read || read_stats->cardinality_is_null) ?
table->file->stats.records : read_stats->cardinality;
@@ -3758,7 +3825,7 @@ void set_statistics_for_table(THD *thd, TABLE *table)
key_info < key_info_end; key_info++)
{
key_info->is_statistics_from_stat_tables=
- (use_stat_table_mode > COMPLEMENTARY &&
+ (check_eits_preferred(thd) &&
table->stats_is_read &&
key_info->read_stats->avg_frequency_is_inited() &&
key_info->read_stats->get_avg_frequency(0) > 0.5);