提交 c84b7de5 编写于 作者: Z zs0 提交者: LINGuanRen

enhance plan expiration, optimize index prune for small row count

上级 f2f2aed9
......@@ -7526,6 +7526,9 @@ int ObCodeGeneratorImpl::set_optimization_info(ObLogTableScan& log_ts, ObTableSc
if (OB_FAIL(phy_ts->set_available_index_name(
log_ts.get_table_opt_info()->available_index_name_, phy_plan_->get_allocator()))) {
LOG_WARN("failed to set available index name", K(ret));
} else if (OB_FAIL(phy_ts->set_unstable_index_name(
log_ts.get_table_opt_info()->unstable_index_name_, phy_plan_->get_allocator()))) {
LOG_WARN("failedd to set unstable index name", K(ret));
} else if (OB_FAIL(phy_ts->set_pruned_index_name(
log_ts.get_table_opt_info()->pruned_index_name_, phy_plan_->get_allocator()))) {
LOG_WARN("failedd to set prunned index name", K(ret));
......
......@@ -4856,6 +4856,7 @@ int ObStaticEngineCG::set_optimization_info(ObLogTableScan& op, ObTableScanSpec&
spec.optimization_method_ = op.get_table_opt_info()->optimization_method_;
spec.available_index_count_ = op.get_table_opt_info()->available_index_id_.count();
OZ(spec.set_available_index_name(op.get_table_opt_info()->available_index_name_, phy_plan_->get_allocator()));
OZ(spec.set_unstable_index_name(op.get_table_opt_info()->unstable_index_name_, phy_plan_->get_allocator()));
OZ(spec.set_pruned_index_name(op.get_table_opt_info()->pruned_index_name_, phy_plan_->get_allocator()));
}
return ret;
......
......@@ -589,62 +589,84 @@ void ObPhysicalPlan::update_plan_stat(const ObAuditRecordData& record, const boo
record.get_elapsed_time() - record.exec_record_.wait_time_end_ -
(record.exec_timestamp_.run_ts_ - record.exec_timestamp_.receive_ts_));
}
if (stat_.is_bind_sensitive_ || stat_.enable_plan_expiration_) {
if (stat_.is_bind_sensitive_ && execute_count > 0) {
int64_t pos = execute_count % ObPlanStat::MAX_SCAN_STAT_SIZE;
ATOMIC_STORE(&(stat_.table_scan_stat_[pos].query_range_row_count_), record.table_scan_stat_.query_range_row_count_);
ATOMIC_STORE(&(stat_.table_scan_stat_[pos].indexback_row_count_), record.table_scan_stat_.indexback_row_count_);
ATOMIC_STORE(&(stat_.table_scan_stat_[pos].output_row_count_), record.table_scan_stat_.output_row_count_);
if (is_first) {
ATOMIC_STORE(&(stat_.first_exec_row_count_), record.table_scan_stat_.query_range_row_count_);
}
}
if (!is_expired() && stat_.enable_plan_expiration_) {
// if the first request is timeout, the execute_count is zero, the avg cpu time should be the cpu time
if (record.is_timeout() || record.status_ == OB_SESSION_KILLED) {
set_is_expired(true);
LOG_INFO("query plan is expired due to execution timeout", K(stat_));
} else if (execute_count == 0 || (execute_count % SLOW_QUERY_SAMPLE_SIZE) != 0) {
// do nothing when query execution samples are not enough
} else if (stat_.cpu_time_ <= SLOW_QUERY_TIME_FOR_PLAN_EXPIRE * stat_.execute_times_) {
// do nothing for fast query
} else if (is_plan_unstable()) {
set_is_expired(true);
} else if (is_first) {
ATOMIC_STORE(&(stat_.sample_times_), 0);
ATOMIC_STORE(&(stat_.first_exec_row_count_),
record.exec_record_.get_memstore_read_row_count() + record.exec_record_.get_ssstore_read_row_count());
ATOMIC_STORE(&(stat_.first_elapsed_time_), record.get_elapsed_time());
} else if (0 == stat_.sample_times_) { // first sample query
ATOMIC_INC(&(stat_.sample_times_));
ATOMIC_STORE(&(stat_.sample_exec_row_count_),
record.exec_record_.get_memstore_read_row_count() + record.exec_record_.get_ssstore_read_row_count());
ATOMIC_STORE(&(stat_.sample_exec_usec_),
record.get_elapsed_time() - record.exec_record_.wait_time_end_ -
(record.exec_timestamp_.run_ts_ - record.exec_timestamp_.receive_ts_));
} else {
int64_t sample_count = ATOMIC_AAF(&(stat_.sample_times_), 1);
int64_t sample_exec_row_count = ATOMIC_AAF(&(stat_.sample_exec_row_count_),
record.exec_record_.get_memstore_read_row_count() + record.exec_record_.get_ssstore_read_row_count());
int64_t sample_exec_usec = ATOMIC_AAF(&(stat_.sample_exec_usec_),
record.get_elapsed_time() - record.exec_record_.wait_time_end_ -
(record.exec_timestamp_.run_ts_ - record.exec_timestamp_.receive_ts_));
if (sample_count < SLOW_QUERY_SAMPLE_SIZE) {
// do nothing when query execution samples are not enough
} else {
if (stat_.cpu_time_ <= SLOW_QUERY_TIME_FOR_PLAN_EXPIRE * stat_.execute_times_) {
// do nothing for fast query
} else if (is_plan_unstable(sample_count, sample_exec_row_count, sample_exec_usec)) {
set_is_expired(true);
}
ATOMIC_STORE(&(stat_.sample_times_), 0);
}
}
}
}
bool ObPhysicalPlan::is_plan_unstable()
bool ObPhysicalPlan::is_plan_unstable(
const int64_t sample_count, const int64_t sample_exec_row_count, const int64_t sample_exec_usec)
{
bool bret = false;
int64_t exec_count = 0;
int64_t total_index_back_rows = 0;
int64_t total_query_range_rows = 0;
int64_t first_query_range_rows = ATOMIC_LOAD(&stat_.first_exec_row_count_);
if (first_query_range_rows != -1) {
for (int64_t i = 0; i < SLOW_QUERY_SAMPLE_SIZE; ++i) {
int64_t query_range_rows = ATOMIC_LOAD(&(stat_.table_scan_stat_[i].query_range_row_count_));
int64_t index_back_rows = ATOMIC_LOAD(&(stat_.table_scan_stat_[i].indexback_row_count_));
if (query_range_rows != -1) {
exec_count++;
total_query_range_rows += query_range_rows;
total_index_back_rows += index_back_rows;
}
}
int64_t total_access_cost = (total_query_range_rows + 10 * total_index_back_rows);
if (total_access_cost <= SLOW_QUERY_ROW_COUNT_THRESOLD * exec_count) {
// the query plan does not accesses too many rows in the average
} else if (total_query_range_rows / exec_count > first_query_range_rows * 10) {
// the average query range row count increases great
if (sample_exec_usec <= SLOW_QUERY_TIME_FOR_PLAN_EXPIRE * sample_count) {
// sample query is fast query in the average
} else if (OB_PHY_PLAN_LOCAL == plan_type_) {
int64_t first_query_range_rows = ATOMIC_LOAD(&stat_.first_exec_row_count_);
if (sample_exec_row_count <= SLOW_QUERY_ROW_COUNT_THRESOLD * sample_count) {
// the sample query does not accesses too many rows in the average
} else if (sample_exec_row_count / sample_count > first_query_range_rows * 10) {
// the average sample query range row count increases great
bret = true;
LOG_INFO("query plan is expired due to unstable performance",
LOG_INFO("local query plan is expired due to unstable performance",
K(bret),
K(stat_.execute_times_),
K(exec_count),
K(first_query_range_rows),
K(total_query_range_rows),
K(total_index_back_rows));
K(sample_exec_row_count),
K(sample_count));
}
} else if (OB_PHY_PLAN_DISTRIBUTED == plan_type_) {
int64_t first_elapsed_time = ATOMIC_LOAD(&stat_.first_elapsed_time_);
if (sample_exec_usec / sample_count > first_elapsed_time * 2) {
// the average sample query execute time increases great
bret = true;
LOG_INFO("distribute query plan is expired due to unstable performance",
K(bret),
K(stat_.execute_times_),
K(first_elapsed_time),
K(sample_exec_usec),
K(sample_count));
}
} else {
// do nothing
}
return bret;
}
......
......@@ -149,7 +149,8 @@ public:
}
inline bool check_if_is_expired(const int64_t first_exec_row_count, const int64_t current_row_count) const;
bool is_plan_unstable();
bool is_plan_unstable(
const int64_t sample_count, const int64_t sample_exec_row_count, const int64_t sample_exec_usec);
bool is_expired() const
{
return stat_.is_expired_;
......
......@@ -331,6 +331,7 @@ ObTableScan::ObTableScan(ObIAllocator& allocator)
est_records_(allocator),
available_index_name_(allocator),
pruned_index_name_(allocator),
unstable_index_name_(allocator),
gi_above_(false),
is_vt_mapping_(false),
use_real_tenant_id_(false),
......@@ -2046,6 +2047,23 @@ int ObTableScan::set_available_index_name(const common::ObIArray<common::ObStrin
return ret;
}
int ObTableScan::set_unstable_index_name(const common::ObIArray<common::ObString>& idx_name, ObIAllocator& phy_alloc)
{
int ret = OB_SUCCESS;
if (OB_FAIL(init_array_size<>(unstable_index_name_, idx_name.count()))) {
LOG_WARN("init unstable_index_name failed", K(ret));
}
for (int64_t i = 0; OB_SUCC(ret) && i < idx_name.count(); ++i) {
ObString name;
if (OB_FAIL(ob_write_string(phy_alloc, idx_name.at(i), name))) {
LOG_WARN("copy unstable index name failed", K(ret));
} else if (OB_FAIL(unstable_index_name_.push_back(name))) {
LOG_WARN("push unstable index name failed", K(ret));
}
}
return ret;
}
int ObTableScan::set_pruned_index_name(const common::ObIArray<common::ObString>& idx_name, ObIAllocator& phy_alloc)
{
int ret = OB_SUCCESS;
......@@ -2129,6 +2147,30 @@ int ObTableScan::explain_index_selection_info(char* buf, int64_t buf_len, int64_
}
}
if (OB_SUCC(ret) && unstable_index_name_.count() > 0) {
if (OB_FAIL(BUF_PRINTF(", unstable_index_name["))) {
LOG_WARN("BUF_PRINTF fails", K(ret));
}
for (int64_t i = 0; OB_SUCC(ret) && i < unstable_index_name_.count(); ++i) {
if (OB_FAIL(BUF_PRINTF("%.*s", unstable_index_name_.at(i).length(), unstable_index_name_.at(i).ptr()))) {
LOG_WARN("BUF_PRINTF fails", K(ret));
} else if (i != unstable_index_name_.count() - 1) {
if (OB_FAIL(BUF_PRINTF(","))) {
LOG_WARN("BUF_PRINTF fails", K(ret));
} else { /* do nothing*/
}
} else { /* do nothing*/
}
}
if (OB_SUCC(ret)) {
if (OB_FAIL(BUF_PRINTF("]"))) {
LOG_WARN("BUF_PRINTF fails", K(ret));
} else { /* Do nothing */
}
} else { /* Do nothing */
}
}
if (OB_SUCC(ret) && est_records_.count() > 0) {
// print est row count infos
if (OB_FAIL(BUF_PRINTF(", estimation info[table_id:%ld,", est_records_.at(0).table_id_))) {
......
......@@ -329,6 +329,7 @@ public:
return estimate_method_;
}
int set_pruned_index_name(const common::ObIArray<common::ObString>& pruned_index_name, ObIAllocator& phy_alloc);
int set_unstable_index_name(const common::ObIArray<common::ObString>& unstable_index_name, ObIAllocator& phy_alloc);
int set_available_index_name(const common::ObIArray<common::ObString>& available_index_name, ObIAllocator& phy_alloc);
int set_est_row_count_record(const common::ObIArray<common::ObEstRowCountRecord>& est_records);
inline const common::ObIArray<common::ObEstRowCountRecord>& get_est_row_count_record() const
......@@ -673,6 +674,7 @@ protected:
common::ObFixedArray<common::ObEstRowCountRecord, common::ObIAllocator> est_records_;
common::ObFixedArray<common::ObString, common::ObIAllocator> available_index_name_;
common::ObFixedArray<common::ObString, common::ObIAllocator> pruned_index_name_;
common::ObFixedArray<common::ObString, common::ObIAllocator> unstable_index_name_;
//***********************************
bool gi_above_;
......
......@@ -219,6 +219,7 @@ ObTableScanSpec::ObTableScanSpec(ObIAllocator& alloc, const ObPhyOperatorType ty
est_records_(alloc),
available_index_name_(alloc),
pruned_index_name_(alloc),
unstable_index_name_(alloc),
gi_above_(false),
expected_part_id_(NULL),
need_scn_(false),
......@@ -294,7 +295,20 @@ int ObTableScanSpec::set_available_index_name(const ObIArray<ObString>& idx_name
return ret;
}
int ObTableScanSpec::set_pruned_index_name(const ObIArray<ObString>& idx_name, ObIAllocator& phy_alloc)
int ObTableScanSpec::set_unstable_index_name(const ObIArray<ObString> &idx_name, ObIAllocator &phy_alloc)
{
int ret = OB_SUCCESS;
OZ(unstable_index_name_.init(idx_name.count()));
FOREACH_CNT_X(n, idx_name, OB_SUCC(ret))
{
ObString name;
OZ(ob_write_string(phy_alloc, *n, name));
OZ(unstable_index_name_.push_back(name));
}
return ret;
}
int ObTableScanSpec::set_pruned_index_name(const ObIArray<ObString> &idx_name, ObIAllocator &phy_alloc)
{
int ret = OB_SUCCESS;
OZ(pruned_index_name_.init(idx_name.count()));
......@@ -373,6 +387,30 @@ int ObTableScanSpec::explain_index_selection_info(char* buf, int64_t buf_len, in
}
}
if (OB_SUCC(ret) && unstable_index_name_.count() > 0) {
if (OB_FAIL(BUF_PRINTF(", unstable_index_name["))) {
LOG_WARN("BUF_PRINTF fails", K(ret));
}
for (int64_t i = 0; OB_SUCC(ret) && i < unstable_index_name_.count(); ++i) {
if (OB_FAIL(BUF_PRINTF("%.*s", unstable_index_name_.at(i).length(), unstable_index_name_.at(i).ptr()))) {
LOG_WARN("BUF_PRINTF fails", K(ret));
} else if (i != unstable_index_name_.count() - 1) {
if (OB_FAIL(BUF_PRINTF(","))) {
LOG_WARN("BUF_PRINTF fails", K(ret));
} else { /* do nothing*/
}
} else { /* do nothing*/
}
}
if (OB_SUCC(ret)) {
if (OB_FAIL(BUF_PRINTF("]"))) {
LOG_WARN("BUF_PRINTF fails", K(ret));
} else { /* Do nothing */
}
} else { /* Do nothing */
}
}
if (OB_SUCC(ret) && est_records_.count() > 0) {
// print est row count infos
if (OB_FAIL(BUF_PRINTF(", estimation info[table_id:%ld,", est_records_.at(0).table_id_))) {
......
......@@ -94,6 +94,8 @@ public:
int set_pruned_index_name(
const common::ObIArray<common::ObString>& pruned_index_name, common::ObIAllocator& phy_alloc);
int set_unstable_index_name(
const common::ObIArray<common::ObString>& unstable_index_name, common::ObIAllocator& phy_alloc);
int set_available_index_name(
const common::ObIArray<common::ObString>& available_index_name, common::ObIAllocator& phy_alloc);
int set_est_row_count_record(const common::ObIArray<common::ObEstRowCountRecord>& est_records);
......@@ -225,6 +227,7 @@ public:
common::ObFixedArray<common::ObEstRowCountRecord, common::ObIAllocator> est_records_;
common::ObFixedArray<common::ObString, common::ObIAllocator> available_index_name_;
common::ObFixedArray<common::ObString, common::ObIAllocator> pruned_index_name_;
common::ObFixedArray<common::ObString, common::ObIAllocator> unstable_index_name_;
bool gi_above_;
ObExpr* expected_part_id_;
......
......@@ -1418,9 +1418,9 @@ int ObJoinOrder::cal_dimension_info(const uint64_t table_id, // alias table id
return ret;
}
int ObJoinOrder::prunning_index(const uint64_t table_id, const uint64_t base_table_id, const ObDMLStmt* stmt,
int ObJoinOrder::skyline_prunning_index(const uint64_t table_id, const uint64_t base_table_id, const ObDMLStmt* stmt,
const bool do_prunning, const ObIndexInfoCache& index_info_cache, const ObIArray<uint64_t>& valid_index_ids,
ObIArray<uint64_t>& skyline_index_ids, ObIArray<ObRawExpr*>& restrict_infos)
ObIArray<uint64_t>& skyline_index_ids, ObIArray<ObRawExpr *>& restrict_infos)
{
int ret = OB_SUCCESS;
if (!do_prunning) {
......@@ -1610,7 +1610,7 @@ int ObJoinOrder::add_table(
LOG_WARN("failed to add table by heuristics", K(ret));
} else if (heuristics_used) {
LOG_TRACE("table added using heuristics", K(table_id));
} else if (OB_FAIL(prunning_index(table_id,
} else if (OB_FAIL(skyline_prunning_index(table_id,
ref_table_id,
stmt,
true,
......@@ -1619,8 +1619,6 @@ int ObJoinOrder::add_table(
skyline_index_ids,
helper.filters_))) {
LOG_WARN("failed to pruning_index", K(table_id), K(ref_table_id), K(ret));
} else if (OB_FAIL(compute_pruned_index(table_id, ref_table_id, skyline_index_ids, helper))) {
LOG_WARN("failed to compute pruned index", K(table_id), K(ref_table_id), K(skyline_index_ids), K(ret));
} else {
LOG_TRACE("table added not using heuristics", K(table_id), K(skyline_index_ids));
helper.table_opt_info_->optimization_method_ = OptimizationMethod::COST_BASED;
......@@ -2478,73 +2476,68 @@ int ObJoinOrder::revise_output_rows_after_creating_path(PathHelper& helper, ObIA
return ret;
}
int ObJoinOrder::compute_pruned_index(
const uint64_t table_id, const uint64_t base_table_id, ObIArray<uint64_t>& available_index_id, PathHelper& helper)
int ObJoinOrder::fill_opt_info_index_name(const uint64_t base_table_id, ObIArray<uint64_t> &available_index_id,
ObIArray<uint64_t> &unstable_index_id, BaseTableOptInfo *table_opt_info)
{
int ret = OB_SUCCESS;
const ObTableSchema* table_schema = NULL;
uint64_t index_ids[OB_MAX_INDEX_PER_TABLE + 1];
int64_t index_count = OB_MAX_INDEX_PER_TABLE + 1;
ObSqlSchemaGuard* schema_guard = NULL;
if (OB_ISNULL(get_plan()) || OB_ISNULL(schema_guard = OPT_CTX.get_sql_schema_guard()) ||
OB_ISNULL(helper.table_opt_info_)) {
const ObTableSchema *table_schema = NULL;
uint64_t index_ids[OB_MAX_INDEX_PER_TABLE + 3];
int64_t index_count = OB_MAX_INDEX_PER_TABLE + 3;
ObSqlSchemaGuard *schema_guard = NULL;
if (OB_ISNULL(table_opt_info) || OB_ISNULL(get_plan()) || OB_ISNULL(schema_guard = OPT_CTX.get_sql_schema_guard())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(get_plan()), K(schema_guard), K(helper.table_opt_info_), K(ret));
} else if (OB_UNLIKELY(OB_INVALID_ID == table_id) || OB_UNLIKELY(OB_INVALID_ID == base_table_id)) {
LOG_WARN("get unexpected null", K(get_plan()), K(schema_guard), K(table_opt_info), K(ret));
} else if (OB_UNLIKELY(OB_INVALID_ID == base_table_id)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("Invalid table id", K(table_id), K(base_table_id), K(ret));
LOG_WARN("Invalid table id", K(base_table_id), K(ret));
} else if (OB_FAIL(schema_guard->get_can_read_index_array(
base_table_id, index_ids, index_count, false, true /*global index*/, false /*domain index*/))) {
LOG_WARN("failed to get can read index", K(base_table_id), K(ret));
} else if (index_count > OB_MAX_INDEX_PER_TABLE + 1) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("Invalid index count", K(base_table_id), K(index_count), K(ret));
} else if (OB_FAIL(helper.table_opt_info_->available_index_id_.assign(available_index_id))) {
} else if (OB_FAIL(table_opt_info->available_index_id_.assign(available_index_id))) {
LOG_WARN("failed to assign available index id", K(ret));
} else {
const uint64_t rowid_index_id = ObSQLMockSchemaUtils::get_rowid_index_table_id(base_table_id);
index_ids[index_count++] = base_table_id;
index_ids[index_count++] = rowid_index_id;
// i == -1 represents primary key, other value of i represent index
for (int64_t i = -1; OB_SUCC(ret) && i < index_count; i++) {
for (int64_t i = 0; OB_SUCC(ret) && i < index_count; ++i) {
ObString name;
bool is_find = false;
uint64_t index_id = (i == -1) ? base_table_id : index_ids[i];
if (OB_FAIL(schema_guard->get_table_schema(index_id, table_schema))) {
uint64_t index_id = index_ids[i];
if (rowid_index_id == index_id) {
name = ObString::make_string(ObSQLMockSchemaUtils::get_rowid_index_name());
} else if (OB_FAIL(schema_guard->get_table_schema(index_id, table_schema))) {
LOG_WARN("fail to get table schema", K(index_id), K(ret));
} else if (OB_ISNULL(table_schema)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("index schema should not be null", K(ret), K(index_id));
} else if (i == -1) {
} else if (base_table_id == index_id) {
name = table_schema->get_table_name_str();
} else if (OB_FAIL(table_schema->get_index_name(name))) {
LOG_WARN("failed to get index name", K(ret));
} else { /*do nothing*/
}
for (int64_t j = 0; OB_SUCC(ret) && (!is_find) && j < available_index_id.count(); j++) {
if (index_id == available_index_id.at(j)) {
is_find = true;
if (OB_FAIL(ret)) {
} else if (ObOptimizerUtil::find_item(available_index_id, index_id)) {
if (OB_FAIL(table_opt_info->available_index_name_.push_back(name))) {
LOG_WARN("failed to push back index name", K(name), K(ret));
} else { /* do nothing */
}
}
if (OB_SUCC(ret)) {
if (is_find) {
if (OB_FAIL(helper.table_opt_info_->available_index_name_.push_back(name))) {
LOG_WARN("failed to push back index name", K(name), K(ret));
} else { /* do nothing */
}
} else {
if (OB_FAIL(helper.table_opt_info_->pruned_index_name_.push_back(name))) {
LOG_WARN("failed to push back index name", K(name), K(ret));
} else { /* do nothing */
}
} else if (ObOptimizerUtil::find_item(unstable_index_id, index_id)) {
if (OB_FAIL(table_opt_info->unstable_index_name_.push_back(name))) {
LOG_WARN("failed to push back index name", K(name), K(ret));
} else { /* do nothing */
}
} else if (rowid_index_id == index_id) {
/* do nothing */
} else if (OB_FAIL(table_opt_info->pruned_index_name_.push_back(name))) {
LOG_WARN("failed to push back index name", K(name), K(ret));
} else { /* do nothing */
}
}
if (OB_FAIL(ret)) {
// do nothing
} else if (ObSQLMockSchemaUtils::contain_mock_index(base_table_id) &&
OB_FAIL(helper.table_opt_info_->available_index_name_.push_back(
ObString::make_string(ObSQLMockSchemaUtils::get_rowid_index_name())))) {
LOG_WARN("failed to push back rowid index name", K(ret));
}
}
return ret;
}
......@@ -3005,6 +2998,83 @@ int ObJoinOrder::estimate_size_and_width_for_access(PathHelper& helper, ObIArray
return ret;
}
int ObJoinOrder::pruning_unstable_access_path(BaseTableOptInfo *table_opt_info, ObIArray<AccessPath *> &access_paths)
{
int ret = OB_SUCCESS;
ObSQLSessionInfo *session_info = NULL;
bool use_acs = false;
ObSEArray<uint64_t, 4> unstable_index_id;
if (OB_UNLIKELY(access_paths.empty()) || OB_ISNULL(get_plan()) ||
OB_ISNULL(session_info = get_plan()->get_optimizer_context().get_session_info())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(access_paths.count()), K(get_plan()), K(session_info));
} else if (OB_FAIL(session_info->get_adaptive_cursor_sharing(use_acs))) {
LOG_WARN("failed to check is acs enabled", K(ret));
} else if (use_acs || access_paths.count() <= 1 || OB_DEFAULT_STAT_EST == table_meta_info_.cost_est_type_) {
/* do not pruning access path */
} else if (OB_FAIL(try_pruning_base_table_access_path(access_paths, unstable_index_id))) {
LOG_WARN("failed to pruning base table access path", K(ret));
}
if (OB_SUCC(ret)) {
ObSEArray<uint64_t, 4> available_index_id;
uint64_t base_table_id = OB_INVALID_ID;
AccessPath *ap = NULL;
for (int64_t i = 0; OB_SUCC(ret) && i < access_paths.count(); ++i) {
if (OB_ISNULL(ap = access_paths.at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret));
} else if (OB_FAIL(available_index_id.push_back(ap->index_id_))) {
LOG_WARN("failed to push back index id", K(ret));
} else if (0 == i) {
base_table_id = ap->ref_table_id_;
}
}
if (OB_SUCC(ret) &&
OB_FAIL(fill_opt_info_index_name(base_table_id, available_index_id, unstable_index_id, table_opt_info))) {
LOG_WARN(
"failed to fill opt info index name", K(ret), K(base_table_id), K(available_index_id), K(unstable_index_id));
}
}
return ret;
}
int ObJoinOrder::try_pruning_base_table_access_path(
ObIArray<AccessPath *> &access_paths, ObIArray<uint64_t> &unstable_index_id)
{
int ret = OB_SUCCESS;
bool need_prune = false;
int64_t base_path_pos = OB_INVALID_INDEX;
AccessPath *ap = NULL;
for (int64_t i = 0; OB_SUCC(ret) && i < access_paths.count(); ++i) {
if (OB_ISNULL(ap = access_paths.at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret));
} else if (ap->ref_table_id_ == ap->index_id_) {
base_path_pos = i;
} else {
need_prune |= ap->range_prefix_count_ > 0 && ap->query_range_row_count_ < PRUNING_ROW_COUNT_THRESHOLD;
}
}
if (OB_SUCC(ret) && need_prune && OB_INVALID_INDEX != base_path_pos) {
if (OB_UNLIKELY(base_path_pos < 0 || base_path_pos > access_paths.count()) ||
OB_ISNULL(ap = access_paths.at(base_path_pos))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected pos or access path", K(ret), K(base_path_pos), K(access_paths.count()), K(ap));
} else if (ap->range_prefix_count_ > 0) {
/* do nothing */
} else if (OB_FAIL(access_paths.remove(base_path_pos))) {
LOG_WARN("failed to remove access path", K(ret), K(base_path_pos));
} else if (OB_FAIL(unstable_index_id.push_back(ap->index_id_))) {
LOG_WARN("failed to push back index id", K(ret));
} else {
LOG_TRACE("pruned base table access paths", K(*ap));
}
}
return ret;
}
int ObJoinOrder::estimate_join_width(
const ObJoinOrder* left_tree, const ObJoinOrder* right_tree, const ObJoinType join_type)
{
......@@ -3493,6 +3563,25 @@ int ObJoinOrder::compute_path_relationship(const sql::Path* first_path, const sq
}
}
// relation is EQUAL now, check index column count when both not index back
// remove this if adjusted estimate cost for table scan
if (OB_SUCC(ret) && PathType::ACCESS == first_path->path_type_ && PathType::ACCESS == second_path->path_type_ &&
left_dominated_count == 0 && right_dominated_count == 0 && uncompareable_count == 0) {
const ObIndexMetaInfo &first_index_info =
static_cast<const AccessPath *>(first_path)->get_cost_table_scan_info().index_meta_info_;
const ObIndexMetaInfo &second_index_info =
static_cast<const AccessPath *>(second_path)->get_cost_table_scan_info().index_meta_info_;
if (first_index_info.is_index_back_ || second_index_info.is_index_back_) {
// do nothing for this, will return EQUAL final
} else if (first_index_info.index_column_count_ < second_index_info.index_column_count_) {
++left_dominated_count;
} else if (first_index_info.index_column_count_ > second_index_info.index_column_count_) {
++right_dominated_count;
} else {
// do nothing
}
}
// compute final result
if (OB_SUCC(ret)) {
if (left_dominated_count > 0 && right_dominated_count == 0 && uncompareable_count == 0) {
......@@ -3996,6 +4085,9 @@ int ObJoinOrder::generate_access_paths(PathHelper& helper)
LOG_WARN("failed to calc table location", K(ret));
} else if (OB_FAIL(estimate_size_and_width_for_access(helper, access_paths))) {
LOG_WARN("failed to estimate_size", K(ret));
} else if (!access_paths.empty() && // when generate inner path, access_paths may be empty
OB_FAIL(pruning_unstable_access_path(helper.table_opt_info_, access_paths))) {
LOG_WARN("failed to pruning unstable access path", K(ret));
} else if (!helper.is_inner_path_) {
if (OB_FAIL(compute_const_exprs(NULL, NULL, type_, UNKNOWN_JOIN))) {
LOG_WARN("failed to compute const exprs", K(ret));
......
......@@ -120,7 +120,8 @@ struct BaseTableOptInfo {
heuristic_rule_(HeuristicRule::MAX_RULE),
available_index_id_(),
available_index_name_(),
pruned_index_name_()
pruned_index_name_(),
unstable_index_name_()
{}
// this following variables are tracked to remember how base table access path are generated
......@@ -129,6 +130,7 @@ struct BaseTableOptInfo {
common::ObSEArray<uint64_t, 4, common::ModulePageAllocator, true> available_index_id_;
common::ObSEArray<common::ObString, 4, common::ModulePageAllocator, true> available_index_name_;
common::ObSEArray<common::ObString, 4, common::ModulePageAllocator, true> pruned_index_name_;
common::ObSEArray<common::ObString, 4, common::ModulePageAllocator, true> unstable_index_name_;
};
class Path {
......@@ -611,6 +613,7 @@ public:
static const int8_t NEED_BNL = 0x1 << 3;
// used for heuristic index selection
static const int64_t TABLE_HEURISTIC_UNIQUE_KEY_RANGE_THRESHOLD = 10000;
static const int64_t PRUNING_ROW_COUNT_THRESHOLD = 1000;
ObJoinOrder(common::ObIAllocator* allocator, ObLogPlan* plan, PathType type)
: allocator_(allocator),
......@@ -643,11 +646,14 @@ public:
{}
virtual ~ObJoinOrder();
int prunning_index(const uint64_t table_id, const uint64_t base_table_id, const ObDMLStmt* stmt,
int skyline_prunning_index(const uint64_t table_id, const uint64_t base_table_id, const ObDMLStmt* stmt,
const bool do_prunning, const ObIndexInfoCache& index_info_cache,
const common::ObIArray<uint64_t>& valid_index_ids, common::ObIArray<uint64_t>& skyline_index_ids,
ObIArray<ObRawExpr*>& restrict_infos);
int pruning_unstable_access_path(BaseTableOptInfo *table_opt_info, ObIArray<AccessPath *> &access_paths);
int try_pruning_base_table_access_path(ObIArray<AccessPath *> &access_paths, ObIArray<uint64_t> &unstable_index_id);
int cal_dimension_info(const uint64_t table_id, const uint64_t data_table_id, const uint64_t index_table_id,
const ObDMLStmt* stmt, ObIndexSkylineDim& index_dim, const ObIndexInfoCache& index_info_cache,
ObIArray<ObRawExpr*>& restrict_infos);
......@@ -657,8 +663,8 @@ public:
int fill_index_info_cache(const uint64_t table_id, const uint64_t base_table_id,
const common::ObIArray<uint64_t>& valid_index_ids, ObIndexInfoCache& index_info_cache, PathHelper& helper);
int compute_pruned_index(const uint64_t table_id, const uint64_t base_table_id,
common::ObIArray<uint64_t>& available_index, PathHelper& helper);
int fill_opt_info_index_name(const uint64_t base_table_id, ObIArray<uint64_t>& available_index_id,
ObIArray<uint64_t>& unstable_index_id, BaseTableOptInfo* table_opt_info);
int extract_used_column_ids(const uint64_t table_id, const uint64_t ref_table_id, ObEstSelInfo& est_sel_info,
ObIArray<uint64_t>& column_ids, const bool eliminate_rowid_col = false);
......
......@@ -7250,8 +7250,6 @@ int ObLogPlan::check_enable_plan_expiration(bool& enable) const
LOG_WARN("stmt is null", K(ret));
} else if (!get_stmt()->is_select_stmt()) {
// do nothing
} else if (get_phy_plan_type() != OB_PHY_PLAN_LOCAL && get_phy_plan_type() != OB_PHY_PLAN_REMOTE) {
// do nothing
} else if (OB_FAIL(session->get_adaptive_cursor_sharing(use_acs))) {
LOG_WARN("failed to check is acs enabled", K(ret));
} else if (use_acs) {
......@@ -7260,21 +7258,10 @@ int ObLogPlan::check_enable_plan_expiration(bool& enable) const
LOG_WARN("failed to check is spm enabled", K(ret));
} else if (use_spm) {
// do nothing
} else if (get_phy_plan_type() != OB_PHY_PLAN_LOCAL && get_phy_plan_type() != OB_PHY_PLAN_DISTRIBUTED) {
// do nothing
} else {
const ObLogicalOperator* node = root_;
while (OB_SUCC(ret)) {
if (OB_ISNULL(node)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("node is null", K(ret));
} else if (node->get_num_of_child() == 1) {
node = node->get_child(ObLogicalOperator::first_child);
} else {
break;
}
}
if (OB_SUCC(ret) && node->is_table_scan()) {
enable = (static_cast<const ObLogTableScan*>(node)->get_diverse_path_count() >= 2);
}
enable = true;
}
return ret;
}
......
......@@ -1227,6 +1227,36 @@ int ObLogTableScan::explain_index_selection_info(char* buf, int64_t& buf_len, in
} else { /* Do nothing */
}
}
// print unstable index name
if (OB_SUCC(ret) && table_opt_info_->unstable_index_name_.count() > 0) {
if (OB_FAIL(BUF_PRINTF(", "))) {
LOG_WARN("BUF_PRINTF fails", K(ret));
} else if (OB_FAIL(BUF_PRINTF("unstable_index_name["))) {
LOG_WARN("BUF_PRINTF fails", K(ret));
} else {
for (int64_t i = 0; OB_SUCC(ret) && i < table_opt_info_->unstable_index_name_.count(); ++i) {
if (OB_FAIL(BUF_PRINTF("%.*s",
table_opt_info_->unstable_index_name_.at(i).length(),
table_opt_info_->unstable_index_name_.at(i).ptr()))) {
LOG_WARN("BUF_PRINTF fails", K(ret));
} else if (i != table_opt_info_->unstable_index_name_.count() - 1) {
if (OB_FAIL(BUF_PRINTF(","))) {
LOG_WARN("BUF_PRINTF fails", K(ret));
} else { /* do nothing*/
}
} else { /* do nothing*/
}
}
}
if (OB_SUCC(ret)) {
if (OB_FAIL(BUF_PRINTF("]"))) {
LOG_WARN("BUF_PRINTF fails", K(ret));
} else { /* Do nothing */
}
} else { /* Do nothing */
}
}
// print est row count infos
if (OB_SUCC(ret) && est_records_.count() > 0) {
if (OB_FAIL(BUF_PRINTF(", estimation info[table_id:%ld,", est_records_.at(0).table_id_))) {
......
......@@ -729,6 +729,10 @@ struct ObPlanStat {
// check whether plan has stable performance
bool enable_plan_expiration_;
int64_t first_exec_row_count_;
int64_t first_elapsed_time_;
int64_t sample_times_;
int64_t sample_exec_row_count_;
int64_t sample_exec_usec_;
uint64_t sessid_;
char plan_tmp_tbl_name_str_[STMT_MAX_LEN];
......
......@@ -178,8 +178,8 @@ SQL: select /*+leading(z1,z2),use_nl(z2)*/ * from z1,z2 where z1.b = z2.b and z2
=============================================
|ID|OPERATOR |NAME |EST. ROWS|COST|
---------------------------------------------
|0 |NESTED-LOOP JOIN| |93 |9008|
|1 | TABLE SCAN |z1 |100 |111 |
|0 |NESTED-LOOP JOIN| |93 |9518|
|1 | TABLE SCAN |z1(z1_b)|100 |621 |
|2 | TABLE SCAN |z2(z2_b)|1 |89 |
=============================================
......@@ -187,10 +187,11 @@ Outputs & filters:
-------------------------------------
0 - output([z1.a], [z1.b], [z1.c], [z1.d], [z2.a], [z2.b], [z2.c], [z2.d]), filter(nil),
conds(nil), nl_params_([z1.b]), batch_join=true
1 - output([z1.b], [z1.a], [z1.c], [z1.d]), filter([z1.b > ?]),
1 - output([z1.b], [z1.a], [z1.c], [z1.d]), filter(nil),
access([z1.b], [z1.a], [z1.c], [z1.d]), partitions(p0),
is_index_back=false, filter_before_indexback[false],
range_key([z1.a]), range(MIN ; MAX)always true
is_index_back=true,
range_key([z1.b], [z1.a]), range(1,MAX ; MAX,MAX),
range_cond([z1.b > ?])
2 - output([z2.b], [z2.a], [z2.c], [z2.d]), filter([z2.c > ?]),
access([z2.b], [z2.a], [z2.c], [z2.d]), partitions(p0),
is_index_back=true, filter_before_indexback[false],
......
......@@ -13147,26 +13147,27 @@ Outputs & filters:
SQL: select * from t4 join t7 on t4.c1 = t7.c1 where t4.c2 = 1;
====================================
|ID|OPERATOR |NAME|EST. ROWS|COST|
------------------------------------
|0 |MERGE JOIN | |100 |286 |
|1 | TABLE SCAN|t4 |100 |109 |
|2 | TABLE SCAN|t7 |100 |90 |
====================================
================================================
|ID|OPERATOR |NAME |EST. ROWS|COST|
------------------------------------------------
|0 |HASH JOIN | |100 |402 |
|1 | TABLE SCAN|t7 |100 |90 |
|2 | TABLE SCAN|t4(idx_t4_c2_c3)|100 |92 |
================================================
Outputs & filters:
-------------------------------------
0 - output([t4.c1], [t4.c2], [t4.c3], [t7.c1], [t7.c2]), filter(nil),
equal_conds([t4.c1 = t7.c1]), other_conds(nil)
1 - output([t4.c1], [t4.c2], [t4.c3]), filter([t4.c2 = ?]),
access([t4.c1], [t4.c2], [t4.c3]), partitions(p0),
is_index_back=false, filter_before_indexback[false],
range_key([t4.c1], [t4.c2]), range(MIN,MIN ; MAX,MAX)always true
2 - output([t7.c1], [t7.c2]), filter(nil),
1 - output([t7.c1], [t7.c2]), filter(nil),
access([t7.c1], [t7.c2]), partitions(p0),
is_index_back=false,
range_key([t7.c1]), range(MIN ; MAX)always true
2 - output([t4.c1], [t4.c2], [t4.c3]), filter(nil),
access([t4.c1], [t4.c2], [t4.c3]), partitions(p0),
is_index_back=false,
range_key([t4.c2], [t4.c3], [t4.c1]), range(1,MIN,MIN ; 1,MAX,MAX),
range_cond([t4.c2 = ?])
*************** Case 374(end) **************
......@@ -13260,26 +13261,27 @@ Outputs & filters:
SQL: select * from t4 left join t7 on t4.c1 = t7.c1 where t4.c2 = 5;
=========================================
|ID|OPERATOR |NAME|EST. ROWS|COST|
-----------------------------------------
|0 |MERGE OUTER JOIN| |100 |286 |
|1 | TABLE SCAN |t4 |100 |109 |
|2 | TABLE SCAN |t7 |100 |90 |
=========================================
==========================================================
|ID|OPERATOR |NAME |EST. ROWS|COST|
----------------------------------------------------------
|0 |HASH RIGHT OUTER JOIN| |100 |402 |
|1 | TABLE SCAN |t7 |100 |90 |
|2 | TABLE SCAN |t4(idx_t4_c2_c3)|100 |92 |
==========================================================
Outputs & filters:
-------------------------------------
0 - output([t4.c1], [t4.c2], [t4.c3], [t7.c1], [t7.c2]), filter(nil),
equal_conds([t4.c1 = t7.c1]), other_conds(nil)
1 - output([t4.c1], [t4.c2], [t4.c3]), filter([t4.c2 = ?]),
access([t4.c1], [t4.c2], [t4.c3]), partitions(p0),
is_index_back=false, filter_before_indexback[false],
range_key([t4.c1], [t4.c2]), range(MIN,MIN ; MAX,MAX)always true
2 - output([t7.c1], [t7.c2]), filter(nil),
1 - output([t7.c1], [t7.c2]), filter(nil),
access([t7.c1], [t7.c2]), partitions(p0),
is_index_back=false,
range_key([t7.c1]), range(MIN ; MAX)always true
2 - output([t4.c1], [t4.c2], [t4.c3]), filter(nil),
access([t4.c1], [t4.c2], [t4.c3]), partitions(p0),
is_index_back=false,
range_key([t4.c2], [t4.c3], [t4.c1]), range(5,MIN,MIN ; 5,MAX,MAX),
range_cond([t4.c2 = ?])
*************** Case 378(end) **************
......@@ -23460,18 +23462,18 @@ Outputs & filters:
SQL: select c1 from t4 where c1 <=> null and c2 <=> null;
==================================
|ID|OPERATOR |NAME|EST. ROWS|COST|
----------------------------------
|0 |TABLE GET|t4 |1 |52 |
==================================
===========================================
|ID|OPERATOR |NAME |EST. ROWS|COST|
-------------------------------------------
|0 |TABLE GET|t4(idx_t4_c2)|1 |52 |
===========================================
Outputs & filters:
-------------------------------------
0 - output([t4.c1]), filter(nil),
access([t4.c1]), partitions(p0),
is_index_back=false,
range_key([t4.c1], [t4.c2]), range[NULL,NULL ; NULL,NULL],
range_key([t4.c2], [t4.c1]), range[NULL,NULL ; NULL,NULL],
range_cond([t4.c1 <=> ?], [t4.c2 <=> ?])
*************** Case 714(end) **************
......@@ -26418,23 +26420,22 @@ Outputs & filters:
SQL: select * from t1 join t5 on t1.c1 = t5.c3 where t5.c3 > 0 and t5.c3 < 100 order by t5.c3 limit 100;
=============================================================
|ID|OPERATOR |NAME |EST. ROWS|COST|
-------------------------------------------------------------
|0 |LIMIT | |100 |1610|
|1 | PX COORDINATOR MERGE SORT | |100 |1596|
|2 | EXCHANGE OUT DISTR |:EX10001|100 |1573|
|3 | LIMIT | |100 |1573|
|4 | MERGE JOIN | |100 |1559|
|5 | SORT | |294 |608 |
|6 | PX PARTITION ITERATOR | |294 |197 |
|7 | TABLE SCAN |t1 |294 |197 |
|8 | SORT | |177 |827 |
|9 | EXCHANGE IN DISTR | |300 |348 |
|10| EXCHANGE OUT DISTR (PKEY)|:EX10000|300 |305 |
|11| PX PARTITION ITERATOR | |300 |305 |
|12| TABLE SCAN |t5 |300 |305 |
=============================================================
===================================================================
|ID|OPERATOR |NAME |EST. ROWS|COST|
-------------------------------------------------------------------
|0 |LIMIT | |100 |1910|
|1 | PX COORDINATOR MERGE SORT | |100 |1896|
|2 | EXCHANGE OUT DISTR |:EX10001 |100 |1872|
|3 | LIMIT | |100 |1872|
|4 | MERGE JOIN | |100 |1859|
|5 | EXCHANGE IN MERGE SORT DISTR| |177 |1127|
|6 | EXCHANGE OUT DISTR (PKEY) |:EX10000 |177 |1102|
|7 | PX PARTITION ITERATOR | |177 |1102|
|8 | TABLE SCAN |t5(idx_t5_c3)|177 |1102|
|9 | SORT | |294 |608 |
|10| PX PARTITION ITERATOR | |294 |197 |
|11| TABLE SCAN |t1 |294 |197 |
===================================================================
Outputs & filters:
-------------------------------------
......@@ -26444,23 +26445,23 @@ Outputs & filters:
3 - output([t1.c1], [t1.c2], [t5.c1], [t5.c2], [t5.c3]), filter(nil), limit(100), offset(nil)
4 - output([t1.c1], [t1.c2], [t5.c1], [t5.c2], [t5.c3]), filter(nil),
equal_conds([t1.c1 = t5.c3]), other_conds(nil)
5 - output([t1.c1], [t1.c2]), filter(nil), sort_keys([t1.c1, ASC]), local merge sort
6 - output([t1.c1], [t1.c2]), filter(nil),
5 - output([t5.c2], [t5.c3], [t5.c1]), filter(nil), sort_keys([t5.c3, ASC]), Local Order
6 - (#keys=1, [t5.c3]), output([t5.c2], [t5.c3], [t5.c1]), filter(nil), dop=1
7 - output([t5.c2], [t5.c3], [t5.c1]), filter(nil),
force partition granule, asc.
8 - output([t5.c2], [t5.c3], [t5.c1]), filter(nil),
access([t5.c2], [t5.c3], [t5.c1]), partitions(p[0-2]),
is_index_back=true,
range_key([t5.c3], [t5.c2]), range(0,MAX ; 100,MIN),
range_cond([t5.c3 > ?], [t5.c3 < ?])
9 - output([t1.c1], [t1.c2]), filter(nil), sort_keys([t1.c1, ASC]), local merge sort
10 - output([t1.c1], [t1.c2]), filter(nil),
affinitize, force partition granule, asc.
7 - output([t1.c1], [t1.c2]), filter(nil),
11 - output([t1.c1], [t1.c2]), filter(nil),
access([t1.c1], [t1.c2]), partitions(p[0-4]),
is_index_back=false,
range_key([t1.c1]), range(0 ; 100),
range_cond([t1.c1 < ?], [t1.c1 > ?])
8 - output([t5.c1], [t5.c2], [t5.c3]), filter(nil), sort_keys([t5.c3, ASC])
9 - output([t5.c2], [t5.c3], [t5.c1]), filter(nil)
10 - (#keys=1, [t5.c3]), output([t5.c2], [t5.c3], [t5.c1]), filter(nil), dop=1
11 - output([t5.c2], [t5.c3], [t5.c1]), filter(nil),
force partition granule, asc.
12 - output([t5.c2], [t5.c3], [t5.c1]), filter([t5.c3 > ?], [t5.c3 < ?]),
access([t5.c2], [t5.c3], [t5.c1]), partitions(p[0-2]),
is_index_back=false, filter_before_indexback[false,false],
range_key([t5.c2], [t5.c3]), range(MIN,MIN ; MAX,MAX)always true
*************** Case 810(end) **************
......@@ -26468,23 +26469,22 @@ Outputs & filters:
SQL: select * from t1 join t5 on t1.c1 = t5.c3 where t5.c3 > 0 and t5.c3 < 100 order by t5.c3 limit 10000;
=============================================================
|ID|OPERATOR |NAME |EST. ROWS|COST|
-------------------------------------------------------------
|0 |LIMIT | |290 |2332|
|1 | PX COORDINATOR MERGE SORT | |290 |2292|
|2 | EXCHANGE OUT DISTR |:EX10001|290 |2223|
|3 | LIMIT | |290 |2223|
|4 | MERGE JOIN | |290 |2183|
|5 | SORT | |500 |1074|
|6 | PX PARTITION ITERATOR | |500 |342 |
|7 | TABLE SCAN |t1 |500 |342 |
|8 | SORT | |300 |827 |
|9 | EXCHANGE IN DISTR | |300 |348 |
|10| EXCHANGE OUT DISTR (PKEY)|:EX10000|300 |305 |
|11| PX PARTITION ITERATOR | |300 |305 |
|12| TABLE SCAN |t5 |300 |305 |
=============================================================
===================================================================
|ID|OPERATOR |NAME |EST. ROWS|COST|
-------------------------------------------------------------------
|0 |LIMIT | |290 |3390|
|1 | PX COORDINATOR MERGE SORT | |290 |3350|
|2 | EXCHANGE OUT DISTR |:EX10001 |290 |3281|
|3 | LIMIT | |290 |3281|
|4 | MERGE JOIN | |290 |3241|
|5 | EXCHANGE IN MERGE SORT DISTR| |300 |1885|
|6 | EXCHANGE OUT DISTR (PKEY) |:EX10000 |300 |1842|
|7 | PX PARTITION ITERATOR | |300 |1842|
|8 | TABLE SCAN |t5(idx_t5_c3)|300 |1842|
|9 | SORT | |500 |1074|
|10| PX PARTITION ITERATOR | |500 |342 |
|11| TABLE SCAN |t1 |500 |342 |
===================================================================
Outputs & filters:
-------------------------------------
......@@ -26494,23 +26494,23 @@ Outputs & filters:
3 - output([t1.c1], [t1.c2], [t5.c1], [t5.c2], [t5.c3]), filter(nil), limit(10000), offset(nil)
4 - output([t1.c1], [t1.c2], [t5.c1], [t5.c2], [t5.c3]), filter(nil),
equal_conds([t1.c1 = t5.c3]), other_conds(nil)
5 - output([t1.c1], [t1.c2]), filter(nil), sort_keys([t1.c1, ASC]), local merge sort
6 - output([t1.c1], [t1.c2]), filter(nil),
5 - output([t5.c2], [t5.c3], [t5.c1]), filter(nil), sort_keys([t5.c3, ASC]), Local Order
6 - (#keys=1, [t5.c3]), output([t5.c2], [t5.c3], [t5.c1]), filter(nil), dop=1
7 - output([t5.c2], [t5.c3], [t5.c1]), filter(nil),
force partition granule, asc.
8 - output([t5.c2], [t5.c3], [t5.c1]), filter(nil),
access([t5.c2], [t5.c3], [t5.c1]), partitions(p[0-2]),
is_index_back=true,
range_key([t5.c3], [t5.c2]), range(0,MAX ; 100,MIN),
range_cond([t5.c3 > ?], [t5.c3 < ?])
9 - output([t1.c1], [t1.c2]), filter(nil), sort_keys([t1.c1, ASC]), local merge sort
10 - output([t1.c1], [t1.c2]), filter(nil),
affinitize, force partition granule, asc.
7 - output([t1.c1], [t1.c2]), filter(nil),
11 - output([t1.c1], [t1.c2]), filter(nil),
access([t1.c1], [t1.c2]), partitions(p[0-4]),
is_index_back=false,
range_key([t1.c1]), range(0 ; 100),
range_cond([t1.c1 < ?], [t1.c1 > ?])
8 - output([t5.c1], [t5.c2], [t5.c3]), filter(nil), sort_keys([t5.c3, ASC])
9 - output([t5.c2], [t5.c3], [t5.c1]), filter(nil)
10 - (#keys=1, [t5.c3]), output([t5.c2], [t5.c3], [t5.c1]), filter(nil), dop=1
11 - output([t5.c2], [t5.c3], [t5.c1]), filter(nil),
force partition granule, asc.
12 - output([t5.c2], [t5.c3], [t5.c1]), filter([t5.c3 > ?], [t5.c3 < ?]),
access([t5.c2], [t5.c3], [t5.c1]), partitions(p[0-2]),
is_index_back=false, filter_before_indexback[false,false],
range_key([t5.c2], [t5.c3]), range(MIN,MIN ; MAX,MAX)always true
*************** Case 811(end) **************
......@@ -2041,15 +2041,13 @@ Outputs & filters:
*************** Case 85 ***************
SQL: select max(c3) from t9 where c2 = 1 and c1 = 1;
=========================================
|ID|OPERATOR |NAME |EST. ROWS|COST|
-----------------------------------------
|0 |SCALAR GROUP BY| |1 |151 |
|1 | SUBPLAN SCAN |VIEW1|1 |150 |
|2 | LIMIT | |1 |150 |
|3 | TOP-N SORT | |1 |150 |
|4 | TABLE SCAN |t9 |1 |149 |
=========================================
======================================================
|ID|OPERATOR |NAME |EST. ROWS|COST|
------------------------------------------------------
|0 |SCALAR GROUP BY| |1 |678 |
|1 | SUBPLAN SCAN |VIEW1 |1 |678 |
|2 | TABLE SCAN |t9(idx_t9,Reverse)|1 |677 |
======================================================
Outputs & filters:
-------------------------------------
......@@ -2057,10 +2055,9 @@ Outputs & filters:
group(nil), agg_func([T_FUN_MAX(VIEW1.c3)])
1 - output([VIEW1.c3]), filter(nil),
access([VIEW1.c3])
2 - output([t9.c3]), filter(nil), limit(1), offset(nil)
3 - output([t9.c3]), filter(nil), sort_keys([t9.c3, DESC]), topn(1)
4 - output([t9.c3]), filter([t9.c2 = ?], [t9.c1 = ?], [(T_OP_IS_NOT, t9.c3, NULL, 0)]),
access([t9.c2], [t9.c1], [t9.c3]), partitions(p0)
2 - output([t9.c3]), filter([t9.c1 = ?], [(T_OP_IS_NOT, t9.c3, NULL, 0)]),
access([t9.c1], [t9.c3]), partitions(p0),
limit(1), offset(nil)
*************** Case 86 ***************
SQL: select max(c3) from t9 where c2 != 1;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册