diff --git a/mysql-test/main/analyze_format_json.result b/mysql-test/main/analyze_format_json.result index fc643d1a8af..abe3fab4643 100644 --- a/mysql-test/main/analyze_format_json.result +++ b/mysql-test/main/analyze_format_json.result @@ -151,7 +151,9 @@ ANALYZE "buffer_type": "flat", "buffer_size": "1Kb", "join_type": "BNL", - "r_filtered": 100 + "r_loops": 20, + "r_filtered": 100, + "r_effective_rows": 60 } } } @@ -192,7 +194,9 @@ ANALYZE "buffer_size": "1Kb", "join_type": "BNL", "attached_condition": "tbl1.c > tbl2.c", - "r_filtered": 15.83333333 + "r_loops": 20, + "r_filtered": 15.83333333, + "r_effective_rows": 60 } } } @@ -652,7 +656,9 @@ ANALYZE "buffer_size": "65", "join_type": "BNL", "attached_condition": "(t2.b,t2.b in (subquery#2))", - "r_filtered": null + "r_loops": 2, + "r_filtered": null, + "r_effective_rows": 0 }, "subqueries": [ { @@ -742,7 +748,9 @@ ANALYZE "buffer_type": "flat", "buffer_size": "1", "join_type": "BNL", - "r_filtered": null + "r_loops": 2, + "r_filtered": null, + "r_effective_rows": 0 }, "subqueries": [ { @@ -774,7 +782,9 @@ ANALYZE "buffer_size": "65", "join_type": "BNL", "attached_condition": "t2.f2 = t3.f3", - "r_filtered": null + "r_loops": 0, + "r_filtered": null, + "r_effective_rows": null } } } @@ -849,3 +859,123 @@ ANALYZE } } drop table t0,t1,t2; +# +# MDEV-30806: ANALYZE FORMAT=JSON: better support for BNL and BNL-H joins +# +create table t10 ( +a int, +b int +); +insert into t10 select seq, seq/3 from seq_0_to_999; +create table t11 ( +a int, +b int +); +insert into t11 select seq, seq/5 from seq_0_to_999; +analyze table t10,t11 persistent for all; +Table Op Msg_type Msg_text +test.t10 analyze status Engine-independent statistics collected +test.t10 analyze status OK +test.t11 analyze status Engine-independent statistics collected +test.t11 analyze status OK +analyze format=json +select * from t10, t11 +where +t10.a < 700 and +t11.a < 100 +and t10.b=t11.b; +ANALYZE +{ + "query_block": { + "select_id": 1, + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "table": { + "table_name": "t11", + "access_type": "ALL", + "r_loops": 1, + "rows": 1000, + "r_rows": 1000, + "r_table_time_ms": "REPLACED", + "r_other_time_ms": "REPLACED", + "filtered": 10.15625, + "r_filtered": 10, + "attached_condition": "t11.a < 100" + }, + "block-nl-join": { + "table": { + "table_name": "t10", + "access_type": "ALL", + "r_loops": 1, + "rows": 1000, + "r_rows": 1000, + "r_table_time_ms": "REPLACED", + "r_other_time_ms": "REPLACED", + "filtered": 70.3125, + "r_filtered": 70, + "attached_condition": "t10.a < 700" + }, + "buffer_type": "flat", + "buffer_size": "1Kb", + "join_type": "BNL", + "attached_condition": "t10.b = t11.b", + "r_loops": 100, + "r_filtered": 0.424285714, + "r_effective_rows": 700 + } + } +} +set @tmp=@@join_cache_level, join_cache_level=6; +analyze format=json +select * from t10, t11 +where +t10.a < 700 and +t11.a < 100 +and t10.b=t11.b; +ANALYZE +{ + "query_block": { + "select_id": 1, + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "table": { + "table_name": "t11", + "access_type": "ALL", + "r_loops": 1, + "rows": 1000, + "r_rows": 1000, + "r_table_time_ms": "REPLACED", + "r_other_time_ms": "REPLACED", + "filtered": 10.15625, + "r_filtered": 10, + "attached_condition": "t11.a < 100 and t11.b is not null" + }, + "block-nl-join": { + "table": { + "table_name": "t10", + "access_type": "hash_ALL", + "key": "#hash#$hj", + "key_length": "5", + "used_key_parts": ["b"], + "ref": ["test.t11.b"], + "r_loops": 1, + "rows": 1000, + "r_rows": 1000, + "r_table_time_ms": "REPLACED", + "r_other_time_ms": "REPLACED", + "filtered": 70.3125, + "r_filtered": 70, + "attached_condition": "t10.a < 700" + }, + "buffer_type": "flat", + "buffer_size": "3Kb", + "join_type": "BNLH", + "attached_condition": "t10.b = t11.b", + "r_loops": 100, + "r_filtered": 100, + "r_effective_rows": 2.97 + } + } +} +set join_cache_level=@tmp; +drop table t10, t11; diff --git a/mysql-test/main/analyze_format_json.test b/mysql-test/main/analyze_format_json.test index 3f3324e9eec..84f44869afe 100644 --- a/mysql-test/main/analyze_format_json.test +++ b/mysql-test/main/analyze_format_json.test @@ -226,3 +226,45 @@ create table t2 as select * from t1; --source include/analyze-format.inc analyze format=json select a, (select t2.b from t2 where t2.atimed)) \ { (tracker)->stop_tracking(thd); } + +/* + Just a counter to increment one value. Wrapped in a class to be uniform + with other counters used by ANALYZE. +*/ + +class Counter_tracker +{ +public: + Counter_tracker() : r_scans(0) {} + ha_rows r_scans; + + inline void on_scan_init() { r_scans++; } + + bool has_scans() const { return (r_scans != 0); } + ha_rows get_loops() const { return r_scans; } +}; + + /* A class for collecting read statistics. @@ -168,20 +187,16 @@ public: It can be used to track reading from files, buffers, etc). */ -class Table_access_tracker +class Table_access_tracker { public: - Table_access_tracker() : - r_scans(0), r_rows(0), /*r_rows_after_table_cond(0),*/ - r_rows_after_where(0) + Table_access_tracker() : r_scans(0), r_rows(0), r_rows_after_where(0) {} - ha_rows r_scans; /* How many scans were ran on this join_tab */ + ha_rows r_scans; /* how many scans were ran on this join_tab */ ha_rows r_rows; /* How many rows we've got after that */ ha_rows r_rows_after_where; /* Rows after applying attached part of WHERE */ - bool has_scans() const { return (r_scans != 0); } - ha_rows get_loops() const { return r_scans; } double get_avg_rows() const { return r_scans @@ -200,6 +215,9 @@ public: inline void on_scan_init() { r_scans++; } inline void on_record_read() { r_rows++; } inline void on_record_after_where() { r_rows_after_where++; } + + bool has_scans() const { return (r_scans != 0); } + ha_rows get_loops() const { return r_scans; } }; diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index 480a1259ba8..caec818d130 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -1891,12 +1891,29 @@ void Explain_table_access::print_explain_json(Explain_query *query, if (is_analyze) { - //writer->add_member("r_loops").add_ll(jbuf_tracker.get_loops()); + writer->add_member("r_loops").add_ll(jbuf_loops_tracker.get_loops()); + writer->add_member("r_filtered"); if (jbuf_tracker.has_scans()) writer->add_double(jbuf_tracker.get_filtered_after_where()*100.0); else writer->add_null(); + /* + effective_rows is average number of matches we got for an incoming + row. The row is stored in the join buffer and then is read + from there, possibly multiple times. We can't count this number + directly. Infer it as: + total_number_of_row_combinations_considered / r_loops. + */ + writer->add_member("r_effective_rows"); + if (jbuf_loops_tracker.has_scans()) + { + double loops= (double)jbuf_loops_tracker.get_loops(); + double row_combinations= (double)jbuf_tracker.r_rows; + writer->add_double(row_combinations / loops); + } + else + writer->add_null(); } } diff --git a/sql/sql_explain.h b/sql/sql_explain.h index 31143ca9f77..746e1db1abd 100644 --- a/sql/sql_explain.h +++ b/sql/sql_explain.h @@ -842,8 +842,12 @@ public: Exec_time_tracker op_tracker; Gap_time_tracker extra_time_tracker; + /* When using join buffer: Track the reads from join buffer */ Table_access_tracker jbuf_tracker; + /* When using join buffer: Track the number of incoming record combinations */ + Counter_tracker jbuf_loops_tracker; + Explain_rowid_filter *rowid_filter; int print_explain(select_result_sink *output, uint8 explain_flags, diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 14766302298..2889d757e20 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -21231,6 +21231,8 @@ sub_select_cache(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) /* The user has aborted the execution of the query */ DBUG_RETURN(NESTED_LOOP_KILLED); } + join_tab->jbuf_loops_tracker->on_scan_init(); + if (!test_if_use_dynamic_range_scan(join_tab)) { if (!cache->put_record()) @@ -27393,6 +27395,7 @@ bool JOIN_TAB::save_explain_data(Explain_table_access *eta, // psergey-todo: data for filtering! tracker= &eta->tracker; jbuf_tracker= &eta->jbuf_tracker; + jbuf_loops_tracker= &eta->jbuf_loops_tracker; /* Enable the table access time tracker only for "ANALYZE stmt" */ if (thd->lex->analyze_stmt) diff --git a/sql/sql_select.h b/sql/sql_select.h index 4ff95b31f3f..178573413af 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -309,6 +309,7 @@ typedef struct st_join_table { Table_access_tracker *tracker; Table_access_tracker *jbuf_tracker; + Counter_tracker *jbuf_loops_tracker; /* Bitmap of TAB_INFO_* bits that encodes special line for EXPLAIN 'Extra' column, or 0 if there is no info.