diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d5c06c..8beca8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,6 +64,9 @@ target_link_libraries(sql_optimizer_cost_model PRIVATE sql_engine) add_executable(sql_bench benchmarks/sql_bench.cpp) target_link_libraries(sql_bench PRIVATE sql_engine) +add_executable(yard_export benchmarks/yard_export.cpp) +target_link_libraries(yard_export PRIVATE sql_engine) + if(SQL_ENGINE_BUILD_FUZZER) add_executable(sql_parser_planner_fuzz fuzz/parser_planner_fuzz.cpp) target_link_libraries(sql_parser_planner_fuzz PRIVATE sql_engine) diff --git a/README.md b/README.md index e344601..212061c 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ cmake -S . -B build && cmake --build build && ctest --test-dir build --output-on Linux CI runs the full test suite with address and undefined-behavior sanitizers. Continuous parser and planner fuzzing replays known queries before a timed fuzzing run. +`yard_export` writes `docs/yard/yard-routes.json`: every memo alternative for four workings, priced by the cost model and timed through vectorized execution. See the "Yard Routes" section of `docs/benchmarks.md`. + ## Phase map 1. Parser, binder, logical algebra, and golden-query oracle. diff --git a/benchmarks/bench_common.hpp b/benchmarks/bench_common.hpp new file mode 100644 index 0000000..27cb641 --- /dev/null +++ b/benchmarks/bench_common.hpp @@ -0,0 +1,376 @@ +#pragma once + +#include "execution/interpreter.hpp" +#include "execution/vectorized.hpp" +#include "optimizer/memo.hpp" +#include "optimizer/rewrite.hpp" +#include "sql/ast.hpp" +#include "sql/binder.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace bench { + +constexpr std::size_t kFactRows = 200'000; +constexpr std::size_t kJoinLeftRows = 100'000; +constexpr std::size_t kJoinRightRows = 256; +constexpr std::size_t kEndToEndLeftRows = 120'000; +constexpr std::size_t kEndToEndRightRows = 256; +constexpr std::size_t kClassRows = 128; +constexpr std::size_t kStringRows = 120'000; +constexpr std::size_t kRepetitions = 5; + +inline std::uint64_t benchmark_sink = 0; + +struct SplitMix64 { + std::uint64_t state; + + explicit SplitMix64(std::uint64_t seed) : state(seed) {} + + std::uint64_t next() { + std::uint64_t z = (state += 0x9e3779b97f4a7c15ULL); + z = (z ^ (z >> 30U)) * 0xbf58476d1ce4e5b9ULL; + z = (z ^ (z >> 27U)) * 0x94d049bb133111ebULL; + return z ^ (z >> 31U); + } +}; + +struct ResultSignature { + std::size_t row_count{0}; + std::size_t column_count{0}; + std::uint64_t checksum{0}; +}; + +struct Timings { + double min_ms{0.0}; + double median_ms{0.0}; +}; + +inline void mix_byte(std::uint64_t& hash, std::uint8_t byte) { + hash ^= byte; + hash *= 1099511628211ULL; +} + +inline void mix_u64(std::uint64_t& hash, std::uint64_t value) { + for (std::size_t i = 0; i < 8; ++i) { + mix_byte(hash, static_cast((value >> (i * 8U)) & 0xffU)); + } +} + +inline void mix_string(std::uint64_t& hash, const std::string& value) { + mix_u64(hash, value.size()); + for (unsigned char ch : value) { + mix_byte(hash, ch); + } +} + +inline ResultSignature signature_for(const storage::ColumnarBatch& batch) { + std::uint64_t hash = 1469598103934665603ULL; + mix_u64(hash, batch.row_count()); + mix_u64(hash, batch.column_names().size()); + for (const auto& name : batch.column_names()) { + mix_string(hash, name); + } + for (std::size_t row = 0; row < batch.row_count(); ++row) { + for (const auto& name : batch.column_names()) { + if (batch.column_type(name) == catalog::ColumnType::Int64) { + const auto& column = batch.column(name); + mix_byte(hash, column.is_null(row) ? 0 : 1); + if (!column.is_null(row)) { + mix_u64(hash, static_cast(column.at(row))); + } + } else { + const auto& column = batch.string_column(name); + mix_byte(hash, column.is_null(row) ? 0 : 1); + if (!column.is_null(row)) { + mix_string(hash, column.at(row)); + } + } + } + } + return ResultSignature{batch.row_count(), batch.column_names().size(), hash}; +} + +inline bool operator==(const ResultSignature& left, const ResultSignature& right) { + return left.row_count == right.row_count && left.column_count == right.column_count && + left.checksum == right.checksum; +} + +inline std::string hex_checksum(std::uint64_t value) { + std::ostringstream out; + out << "0x" << std::hex << std::setw(16) << std::setfill('0') << value; + return out.str(); +} + +inline void add_column(storage::ColumnarBatch& batch, + std::string name, + std::vector values) { + storage::Int64Column column; + for (auto value : values) { + column.append(value); + } + batch.add_column(std::move(name), std::move(column)); +} + +inline void add_string_column(storage::ColumnarBatch& batch, + std::string name, + std::vector values) { + storage::StringColumn column; + for (auto& value : values) { + column.append(std::move(value)); + } + batch.add_column(std::move(name), std::move(column)); +} + +inline storage::ColumnarBatch make_fact_table() { + SplitMix64 rng(0x5eed000000000001ULL); + std::vector a; + std::vector bucket; + std::vector decile; + std::vector half; + std::vector group_few; + std::vector group_many; + std::vector sort_key; + std::vector value; + a.reserve(kFactRows); + bucket.reserve(kFactRows); + decile.reserve(kFactRows); + half.reserve(kFactRows); + group_few.reserve(kFactRows); + group_many.reserve(kFactRows); + sort_key.reserve(kFactRows); + value.reserve(kFactRows); + + for (std::size_t row = 0; row < kFactRows; ++row) { + a.push_back(static_cast(row)); + bucket.push_back(static_cast(row % 100)); + decile.push_back(static_cast(row % 10)); + half.push_back(static_cast(row % 2)); + group_few.push_back(static_cast(row % 8)); + group_many.push_back(static_cast(row % 50'000)); + sort_key.push_back(static_cast(rng.next() % kFactRows)); + value.push_back(static_cast(rng.next() % 1'000)); + } + + storage::ColumnarBatch batch; + add_column(batch, "a", std::move(a)); + add_column(batch, "bucket", std::move(bucket)); + add_column(batch, "decile", std::move(decile)); + add_column(batch, "half", std::move(half)); + add_column(batch, "group_few", std::move(group_few)); + add_column(batch, "group_many", std::move(group_many)); + add_column(batch, "sort_key", std::move(sort_key)); + add_column(batch, "value", std::move(value)); + return batch; +} + +inline storage::ColumnarBatch make_join_left_table() { + std::vector k1; + std::vector k2; + std::vector payload; + k1.reserve(kJoinLeftRows); + k2.reserve(kJoinLeftRows); + payload.reserve(kJoinLeftRows); + for (std::size_t row = 0; row < kJoinLeftRows; ++row) { + k1.push_back(static_cast(row % 16)); + k2.push_back(static_cast((row / 16) % 16)); + payload.push_back(static_cast((row * 17) % 1'000'003)); + } + + storage::ColumnarBatch batch; + add_column(batch, "k1", std::move(k1)); + add_column(batch, "k2", std::move(k2)); + add_column(batch, "payload", std::move(payload)); + return batch; +} + +inline storage::ColumnarBatch make_join_right_table() { + std::vector k1; + std::vector k2; + std::vector payload; + k1.reserve(kJoinRightRows); + k2.reserve(kJoinRightRows); + payload.reserve(kJoinRightRows); + for (std::size_t row = 0; row < kJoinRightRows; ++row) { + k1.push_back(static_cast(row % 16)); + k2.push_back(static_cast(row / 16)); + payload.push_back(static_cast(10'000 + row)); + } + + storage::ColumnarBatch batch; + add_column(batch, "k1", std::move(k1)); + add_column(batch, "k2", std::move(k2)); + add_column(batch, "payload", std::move(payload)); + return batch; +} + +inline storage::ColumnarBatch make_e2e_left_table() { + std::vector k; + std::vector group_id; + std::vector filter_key; + k.reserve(kEndToEndLeftRows); + group_id.reserve(kEndToEndLeftRows); + filter_key.reserve(kEndToEndLeftRows); + for (std::size_t row = 0; row < kEndToEndLeftRows; ++row) { + k.push_back(static_cast(row % kEndToEndRightRows)); + group_id.push_back(static_cast((row / 3) % 128)); + filter_key.push_back(static_cast(row % 100)); + } + + storage::ColumnarBatch batch; + add_column(batch, "k", std::move(k)); + add_column(batch, "group_id", std::move(group_id)); + add_column(batch, "filter_key", std::move(filter_key)); + return batch; +} + +inline storage::ColumnarBatch make_e2e_right_table() { + std::vector k; + std::vector measure; + k.reserve(kEndToEndRightRows); + measure.reserve(kEndToEndRightRows); + for (std::size_t row = 0; row < kEndToEndRightRows; ++row) { + k.push_back(static_cast(row)); + measure.push_back(static_cast(1 + (row % 97))); + } + + storage::ColumnarBatch batch; + add_column(batch, "k", std::move(k)); + add_column(batch, "measure", std::move(measure)); + return batch; +} + +inline storage::ColumnarBatch make_e2e_class_table() { + std::vector group_id; + std::vector tier; + group_id.reserve(kClassRows); + tier.reserve(kClassRows); + for (std::size_t row = 0; row < kClassRows; ++row) { + group_id.push_back(static_cast(row)); + tier.push_back(static_cast(row % 4)); + } + storage::ColumnarBatch batch; + add_column(batch, "group_id", std::move(group_id)); + add_column(batch, "tier", std::move(tier)); + return batch; +} + +inline storage::ColumnarBatch make_string_fact_table() { + static const std::vector key_pool{ + "", + "alpha", + "beta", + "gamma", + "delta", + "key000", + "key001", + "key002", + "key003", + "key004", + "key005", + "key006", + "key007", + "key008", + "key009", + "omega", + }; + + std::vector k; + std::vector label; + k.reserve(kStringRows); + label.reserve(kStringRows); + for (std::size_t row = 0; row < kStringRows; ++row) { + k.push_back(key_pool[row % key_pool.size()]); + label.push_back("label" + std::to_string((row * 17) % 4096)); + } + + storage::ColumnarBatch batch; + add_string_column(batch, "k", std::move(k)); + add_string_column(batch, "label", std::move(label)); + return batch; +} + +inline execution::Catalog make_catalog() { + execution::Catalog catalog; + catalog.add_table("fact", make_fact_table()); + catalog.add_table("join_left", make_join_left_table()); + catalog.add_table("join_right", make_join_right_table()); + catalog.add_table("e2e_left", make_e2e_left_table()); + catalog.add_table("e2e_right", make_e2e_right_table()); + catalog.add_table("e2e_class", make_e2e_class_table()); + catalog.add_table("string_fact", make_string_fact_table()); + return catalog; +} + +inline plan::LogicalPlan bind_query(const execution::Catalog& catalog, const std::string& sql) { + return sql::bind_select(sql::parse_select(sql), catalog); +} + +inline plan::LogicalPlan bind_decorrelated_semi_query(const execution::Catalog& catalog, + const std::string& sql) { + const auto logical = bind_query(catalog, sql); + optimizer::Memo memo; + const auto root = memo.insert(logical); + const auto explored = optimizer::explore_memo_to_fixpoint(memo, optimizer::default_memo_rules()); + if (!explored.reached_fixpoint) { + throw std::logic_error("benchmark semi-join memo exploration did not reach fixpoint"); + } + const auto alternatives = + memo.extract_alternatives(root, optimizer::AlternativeExtractionOptions{128, 1024}); + for (const auto& alternative : alternatives.plans) { + if (plan::to_string(alternative).find("SemiJoin[") != std::string::npos) { + return alternative; + } + } + throw std::logic_error("benchmark IN query did not produce a SemiJoin alternative"); +} + +inline plan::LogicalPlan bind_best_null_aware_anti_query(const execution::Catalog& catalog, + const std::string& sql) { + const auto logical = bind_query(catalog, sql); + optimizer::Memo memo; + const auto root = memo.insert(logical); + const auto explored = optimizer::explore_memo_to_fixpoint(memo, optimizer::default_memo_rules()); + if (!explored.reached_fixpoint) { + throw std::logic_error("benchmark NULL-aware anti memo exploration did not reach fixpoint"); + } + const auto best = memo.extract_best(root, catalog); + if (plan::to_string(best).find("NullAwareAntiJoin[") == std::string::npos) { + throw std::logic_error("benchmark NOT IN query did not choose its NullAwareAnti alternative"); + } + return best; +} + +using ExecuteFn = storage::ColumnarBatch (*)(const plan::LogicalPlan&, const execution::Catalog&); + +inline Timings measure(const plan::LogicalPlan& plan, + const execution::Catalog& catalog, + const std::string& engine_name, + ExecuteFn execute) { + std::vector durations; + durations.reserve(kRepetitions); + for (std::size_t repetition = 0; repetition < kRepetitions; ++repetition) { + const auto start = std::chrono::steady_clock::now(); + auto result = execute(plan, catalog); + const auto end = std::chrono::steady_clock::now(); + const auto signature = signature_for(result); + benchmark_sink ^= signature.checksum + signature.row_count + signature.column_count + repetition; + durations.push_back(std::chrono::duration(end - start).count()); + } + + if (durations.size() != kRepetitions) { + throw std::logic_error(engine_name + " timing loop did not run all repetitions"); + } + std::sort(durations.begin(), durations.end()); + return Timings{durations.front(), durations.at(durations.size() / 2)}; +} + +} // namespace bench diff --git a/benchmarks/sql_bench.cpp b/benchmarks/sql_bench.cpp index cd05fda..ec7550c 100644 --- a/benchmarks/sql_bench.cpp +++ b/benchmarks/sql_bench.cpp @@ -1,55 +1,10 @@ -#include "execution/interpreter.hpp" -#include "execution/vectorized.hpp" -#include "optimizer/memo.hpp" -#include "optimizer/rewrite.hpp" -#include "sql/binder.hpp" +#include "bench_common.hpp" -#include -#include -#include -#include #include -#include -#include -#include -#include -#include namespace { -constexpr std::size_t kFactRows = 200'000; -constexpr std::size_t kJoinLeftRows = 100'000; -constexpr std::size_t kJoinRightRows = 256; -constexpr std::size_t kEndToEndLeftRows = 120'000; -constexpr std::size_t kEndToEndRightRows = 256; -constexpr std::size_t kStringRows = 120'000; -constexpr std::size_t kRepetitions = 5; - -std::uint64_t benchmark_sink = 0; - -struct SplitMix64 { - std::uint64_t state; - - explicit SplitMix64(std::uint64_t seed) : state(seed) {} - - std::uint64_t next() { - std::uint64_t z = (state += 0x9e3779b97f4a7c15ULL); - z = (z ^ (z >> 30U)) * 0xbf58476d1ce4e5b9ULL; - z = (z ^ (z >> 27U)) * 0x94d049bb133111ebULL; - return z ^ (z >> 31U); - } -}; - -struct ResultSignature { - std::size_t row_count{0}; - std::size_t column_count{0}; - std::uint64_t checksum{0}; -}; - -struct Timings { - double min_ms{0.0}; - double median_ms{0.0}; -}; +using namespace bench; struct Workload { std::string name; @@ -66,280 +21,6 @@ struct BenchmarkResult { Timings vectorized; }; -void mix_byte(std::uint64_t& hash, std::uint8_t byte) { - hash ^= byte; - hash *= 1099511628211ULL; -} - -void mix_u64(std::uint64_t& hash, std::uint64_t value) { - for (std::size_t i = 0; i < 8; ++i) { - mix_byte(hash, static_cast((value >> (i * 8U)) & 0xffU)); - } -} - -void mix_string(std::uint64_t& hash, const std::string& value) { - mix_u64(hash, value.size()); - for (unsigned char ch : value) { - mix_byte(hash, ch); - } -} - -ResultSignature signature_for(const storage::ColumnarBatch& batch) { - std::uint64_t hash = 1469598103934665603ULL; - mix_u64(hash, batch.row_count()); - mix_u64(hash, batch.column_names().size()); - for (const auto& name : batch.column_names()) { - mix_string(hash, name); - } - for (std::size_t row = 0; row < batch.row_count(); ++row) { - for (const auto& name : batch.column_names()) { - if (batch.column_type(name) == catalog::ColumnType::Int64) { - const auto& column = batch.column(name); - mix_byte(hash, column.is_null(row) ? 0 : 1); - if (!column.is_null(row)) { - mix_u64(hash, static_cast(column.at(row))); - } - } else { - const auto& column = batch.string_column(name); - mix_byte(hash, column.is_null(row) ? 0 : 1); - if (!column.is_null(row)) { - mix_string(hash, column.at(row)); - } - } - } - } - return ResultSignature{batch.row_count(), batch.column_names().size(), hash}; -} - -bool operator==(const ResultSignature& left, const ResultSignature& right) { - return left.row_count == right.row_count && left.column_count == right.column_count && - left.checksum == right.checksum; -} - -std::string hex_checksum(std::uint64_t value) { - std::ostringstream out; - out << "0x" << std::hex << std::setw(16) << std::setfill('0') << value; - return out.str(); -} - -void add_column(storage::ColumnarBatch& batch, std::string name, std::vector values) { - storage::Int64Column column; - for (auto value : values) { - column.append(value); - } - batch.add_column(std::move(name), std::move(column)); -} - -void add_string_column(storage::ColumnarBatch& batch, std::string name, std::vector values) { - storage::StringColumn column; - for (auto& value : values) { - column.append(std::move(value)); - } - batch.add_column(std::move(name), std::move(column)); -} - -storage::ColumnarBatch make_fact_table() { - SplitMix64 rng(0x5eed000000000001ULL); - std::vector a; - std::vector bucket; - std::vector decile; - std::vector half; - std::vector group_few; - std::vector group_many; - std::vector sort_key; - std::vector value; - a.reserve(kFactRows); - bucket.reserve(kFactRows); - decile.reserve(kFactRows); - half.reserve(kFactRows); - group_few.reserve(kFactRows); - group_many.reserve(kFactRows); - sort_key.reserve(kFactRows); - value.reserve(kFactRows); - - for (std::size_t row = 0; row < kFactRows; ++row) { - a.push_back(static_cast(row)); - bucket.push_back(static_cast(row % 100)); - decile.push_back(static_cast(row % 10)); - half.push_back(static_cast(row % 2)); - group_few.push_back(static_cast(row % 8)); - group_many.push_back(static_cast(row % 50'000)); - sort_key.push_back(static_cast(rng.next() % kFactRows)); - value.push_back(static_cast(rng.next() % 1'000)); - } - - storage::ColumnarBatch batch; - add_column(batch, "a", std::move(a)); - add_column(batch, "bucket", std::move(bucket)); - add_column(batch, "decile", std::move(decile)); - add_column(batch, "half", std::move(half)); - add_column(batch, "group_few", std::move(group_few)); - add_column(batch, "group_many", std::move(group_many)); - add_column(batch, "sort_key", std::move(sort_key)); - add_column(batch, "value", std::move(value)); - return batch; -} - -storage::ColumnarBatch make_join_left_table() { - std::vector k1; - std::vector k2; - std::vector payload; - k1.reserve(kJoinLeftRows); - k2.reserve(kJoinLeftRows); - payload.reserve(kJoinLeftRows); - for (std::size_t row = 0; row < kJoinLeftRows; ++row) { - k1.push_back(static_cast(row % 16)); - k2.push_back(static_cast((row / 16) % 16)); - payload.push_back(static_cast((row * 17) % 1'000'003)); - } - - storage::ColumnarBatch batch; - add_column(batch, "k1", std::move(k1)); - add_column(batch, "k2", std::move(k2)); - add_column(batch, "payload", std::move(payload)); - return batch; -} - -storage::ColumnarBatch make_join_right_table() { - std::vector k1; - std::vector k2; - std::vector payload; - k1.reserve(kJoinRightRows); - k2.reserve(kJoinRightRows); - payload.reserve(kJoinRightRows); - for (std::size_t row = 0; row < kJoinRightRows; ++row) { - k1.push_back(static_cast(row % 16)); - k2.push_back(static_cast(row / 16)); - payload.push_back(static_cast(10'000 + row)); - } - - storage::ColumnarBatch batch; - add_column(batch, "k1", std::move(k1)); - add_column(batch, "k2", std::move(k2)); - add_column(batch, "payload", std::move(payload)); - return batch; -} - -storage::ColumnarBatch make_e2e_left_table() { - std::vector k; - std::vector group_id; - std::vector filter_key; - k.reserve(kEndToEndLeftRows); - group_id.reserve(kEndToEndLeftRows); - filter_key.reserve(kEndToEndLeftRows); - for (std::size_t row = 0; row < kEndToEndLeftRows; ++row) { - k.push_back(static_cast(row % kEndToEndRightRows)); - group_id.push_back(static_cast((row / 3) % 128)); - filter_key.push_back(static_cast(row % 100)); - } - - storage::ColumnarBatch batch; - add_column(batch, "k", std::move(k)); - add_column(batch, "group_id", std::move(group_id)); - add_column(batch, "filter_key", std::move(filter_key)); - return batch; -} - -storage::ColumnarBatch make_e2e_right_table() { - std::vector k; - std::vector measure; - k.reserve(kEndToEndRightRows); - measure.reserve(kEndToEndRightRows); - for (std::size_t row = 0; row < kEndToEndRightRows; ++row) { - k.push_back(static_cast(row)); - measure.push_back(static_cast(1 + (row % 97))); - } - - storage::ColumnarBatch batch; - add_column(batch, "k", std::move(k)); - add_column(batch, "measure", std::move(measure)); - return batch; -} - -storage::ColumnarBatch make_string_fact_table() { - static const std::vector key_pool{ - "", - "alpha", - "beta", - "gamma", - "delta", - "key000", - "key001", - "key002", - "key003", - "key004", - "key005", - "key006", - "key007", - "key008", - "key009", - "omega", - }; - - std::vector k; - std::vector label; - k.reserve(kStringRows); - label.reserve(kStringRows); - for (std::size_t row = 0; row < kStringRows; ++row) { - k.push_back(key_pool[row % key_pool.size()]); - label.push_back("label" + std::to_string((row * 17) % 4096)); - } - - storage::ColumnarBatch batch; - add_string_column(batch, "k", std::move(k)); - add_string_column(batch, "label", std::move(label)); - return batch; -} - -execution::Catalog make_catalog() { - execution::Catalog catalog; - catalog.add_table("fact", make_fact_table()); - catalog.add_table("join_left", make_join_left_table()); - catalog.add_table("join_right", make_join_right_table()); - catalog.add_table("e2e_left", make_e2e_left_table()); - catalog.add_table("e2e_right", make_e2e_right_table()); - catalog.add_table("string_fact", make_string_fact_table()); - return catalog; -} - -plan::LogicalPlan bind_query(const execution::Catalog& catalog, const std::string& sql) { - return sql::bind_select(sql::parse_select(sql), catalog); -} - -plan::LogicalPlan bind_decorrelated_semi_query(const execution::Catalog& catalog, const std::string& sql) { - const auto logical = bind_query(catalog, sql); - optimizer::Memo memo; - const auto root = memo.insert(logical); - const auto explored = optimizer::explore_memo_to_fixpoint(memo, optimizer::default_memo_rules()); - if (!explored.reached_fixpoint) { - throw std::logic_error("benchmark semi-join memo exploration did not reach fixpoint"); - } - const auto alternatives = - memo.extract_alternatives(root, optimizer::AlternativeExtractionOptions{128, 1024}); - for (const auto& alternative : alternatives.plans) { - if (plan::to_string(alternative).find("SemiJoin[") != std::string::npos) { - return alternative; - } - } - throw std::logic_error("benchmark IN query did not produce a SemiJoin alternative"); -} - -plan::LogicalPlan bind_best_null_aware_anti_query(const execution::Catalog& catalog, - const std::string& sql) { - const auto logical = bind_query(catalog, sql); - optimizer::Memo memo; - const auto root = memo.insert(logical); - const auto explored = optimizer::explore_memo_to_fixpoint(memo, optimizer::default_memo_rules()); - if (!explored.reached_fixpoint) { - throw std::logic_error("benchmark NULL-aware anti memo exploration did not reach fixpoint"); - } - const auto best = memo.extract_best(root, catalog); - if (plan::to_string(best).find("NullAwareAntiJoin[") == std::string::npos) { - throw std::logic_error("benchmark NOT IN query did not choose its NullAwareAnti alternative"); - } - return best; -} - std::vector make_workloads(const execution::Catalog& catalog) { std::vector workloads; auto add = [&](std::string name, std::string rows, std::string sql) { @@ -407,6 +88,13 @@ std::vector make_workloads(const execution::Catalog& catalog) { "WHERE l.filter_key < 80 " "GROUP BY l.group_id HAVING SUM(r.measure) > 0 " "ORDER BY total DESC LIMIT 20"); + add("three_way_route", + "left=120000,right=256,class=128", + "SELECT l.group_id AS group_id, COUNT(*) AS n, MAX(r.measure) AS top " + "FROM e2e_left AS l JOIN e2e_right AS r ON l.k = r.k " + "JOIN e2e_class AS c ON l.group_id = c.group_id " + "WHERE c.tier = 1 " + "GROUP BY l.group_id ORDER BY n DESC, group_id ASC LIMIT 20"); add("string_group_by", "string_fact=120000,groups=16", "SELECT k, COUNT(*) AS n, MIN(label) AS first_label, MAX(label) AS last_label " @@ -415,28 +103,6 @@ std::vector make_workloads(const execution::Catalog& catalog) { return workloads; } -Timings measure(const plan::LogicalPlan& plan, - const execution::Catalog& catalog, - const std::string& engine_name, - storage::ColumnarBatch (*execute)(const plan::LogicalPlan&, const execution::Catalog&)) { - std::vector durations; - durations.reserve(kRepetitions); - for (std::size_t repetition = 0; repetition < kRepetitions; ++repetition) { - const auto start = std::chrono::steady_clock::now(); - auto result = execute(plan, catalog); - const auto end = std::chrono::steady_clock::now(); - const auto signature = signature_for(result); - benchmark_sink ^= signature.checksum + signature.row_count + signature.column_count + repetition; - durations.push_back(std::chrono::duration(end - start).count()); - } - - if (durations.size() != kRepetitions) { - throw std::logic_error(engine_name + " timing loop did not run all repetitions"); - } - std::sort(durations.begin(), durations.end()); - return Timings{durations.front(), durations.at(durations.size() / 2)}; -} - BenchmarkResult run_workload(const Workload& workload, const execution::Catalog& catalog) { const auto interpreted_result = execution::execute_interpreted(workload.plan, catalog); const auto vectorized_result = execution::execute_vectorized(workload.plan, catalog); diff --git a/benchmarks/yard_export.cpp b/benchmarks/yard_export.cpp new file mode 100644 index 0000000..3bc97eb --- /dev/null +++ b/benchmarks/yard_export.cpp @@ -0,0 +1,498 @@ +#include "bench_common.hpp" +#include "optimizer/explain.hpp" +#include "plan/logical_plan.hpp" + +#include +#include +#include +#include +#include +#include + +namespace { + +using namespace bench; + +struct Working { + std::string id; + std::string sql; + std::vector> tables; +}; + +std::vector make_workings() { + return { + {"coupling", + "SELECT l.payload AS left_payload, r.payload AS right_payload " + "FROM join_left AS l JOIN join_right AS r ON l.k1 = r.k1 AND l.k2 = r.k2", + {{"join_left", kJoinLeftRows}, {"join_right", kJoinRightRows}}}, + {"hump-and-bowl", + "SELECT l.group_id AS group_id, COUNT(*) AS n, MAX(r.measure) AS top " + "FROM e2e_left AS l JOIN e2e_right AS r ON l.k = r.k " + "WHERE l.filter_key < 80 " + "GROUP BY l.group_id ORDER BY n DESC, group_id ASC LIMIT 20", + {{"e2e_left", kEndToEndLeftRows}, {"e2e_right", kEndToEndRightRows}}}, + {"three-trains", + "SELECT l.group_id AS group_id, COUNT(*) AS n, MAX(r.measure) AS top " + "FROM e2e_left AS l JOIN e2e_right AS r ON l.k = r.k " + "JOIN e2e_class AS c ON l.group_id = c.group_id " + "WHERE c.tier = 1 " + "GROUP BY l.group_id ORDER BY n DESC, group_id ASC LIMIT 20", + {{"e2e_left", kEndToEndLeftRows}, {"e2e_right", kEndToEndRightRows}, {"e2e_class", kClassRows}}}, + {"special", + "SELECT l.payload AS payload FROM join_left AS l " + "WHERE l.k1 IN (SELECT r.k1 FROM join_right AS r WHERE r.payload = 10000)", + {{"join_left", kJoinLeftRows}, {"join_right", kJoinRightRows}}}, + }; +} + +struct HumpWorkload { + std::string id; + std::string predicate; + std::string sql; +}; + +std::vector make_hump_workloads() { + return { + {"scan_filter_1pct", "bucket = 7", "SELECT a FROM fact WHERE bucket = 7"}, + {"scan_filter_10pct", "decile = 3", "SELECT a FROM fact WHERE decile = 3"}, + {"scan_filter_50pct", "half = 1", "SELECT a FROM fact WHERE half = 1"}, + }; +} + +// ---- tiny JSON writer ------------------------------------------------------- + +std::string json_escape(const std::string& value) { + std::string out; + out.reserve(value.size() + 8); + for (const unsigned char ch : value) { + switch (ch) { + case '"': out += "\\\""; break; + case '\\': out += "\\\\"; break; + case '\n': out += "\\n"; break; + case '\t': out += "\\t"; break; + default: + if (ch < 0x20) { + char buf[8]; + std::snprintf(buf, sizeof buf, "\\u%04x", ch); + out += buf; + } else { + out += static_cast(ch); + } + } + } + return out; +} + +std::string fixed(double value, int decimals) { + std::ostringstream out; + out << std::fixed << std::setprecision(decimals) << value; + return out.str(); +} + +std::string quoted(const std::string& value) { return "\"" + json_escape(value) + "\""; } + +ResultSignature bag_signature_for(const storage::ColumnarBatch& batch) { + constexpr char kColumnSeparator = '\x1f'; + std::vector rows; + rows.reserve(batch.row_count()); + for (std::size_t row = 0; row < batch.row_count(); ++row) { + std::string canonical; + for (std::size_t column_index = 0; column_index < batch.column_names().size(); ++column_index) { + if (column_index != 0) canonical.push_back(kColumnSeparator); + const auto& name = batch.column_names()[column_index]; + if (batch.column_type(name) == catalog::ColumnType::Int64) { + const auto& column = batch.column(name); + if (column.is_null(row)) { + canonical += "N"; + } else { + canonical += "I" + std::to_string(column.at(row)); + } + } else { + const auto& column = batch.string_column(name); + if (column.is_null(row)) { + canonical += "N"; + } else { + const auto& value = column.at(row); + if (value.find(kColumnSeparator) != std::string::npos) { + throw std::logic_error("bag signature separator appeared in string data"); + } + canonical += "S" + value; + } + } + } + rows.push_back(std::move(canonical)); + } + std::sort(rows.begin(), rows.end()); + + std::uint64_t hash = 1469598103934665603ULL; + mix_u64(hash, batch.row_count()); + mix_u64(hash, batch.column_names().size()); + for (const auto& name : batch.column_names()) mix_string(hash, name); + for (const auto& row : rows) mix_string(hash, row); + return ResultSignature{batch.row_count(), batch.column_names().size(), hash}; +} + +// ---- plan tree -------------------------------------------------------------- + +std::string node_line(const plan::LogicalPlan& node) { + const auto text = plan::to_string(node); + const auto end = text.find('\n'); + auto line = end == std::string::npos ? text : text.substr(0, end); + const auto first = line.find_first_not_of(' '); + return first == std::string::npos ? line : line.substr(first); +} + +std::string node_op(const std::string& line) { + const auto bracket = line.find('['); + return bracket == std::string::npos ? line : line.substr(0, bracket); +} + +std::string node_detail(const std::string& line) { + const auto bracket = line.find('['); + if (bracket == std::string::npos || line.back() != ']') return ""; + return line.substr(bracket + 1, line.size() - bracket - 2); +} + +std::string tree_json(const plan::LogicalPlan& node, const execution::Catalog& catalog, int indent) { + const auto estimate = optimizer::estimate_cost(node, catalog); + const auto line = node_line(node); + const std::string pad(static_cast(indent), ' '); + std::string out = "{\n" + pad + " \"op\": " + quoted(node_op(line)) + + ",\n" + pad + " \"detail\": " + quoted(node_detail(line)) + + ",\n" + pad + " \"rows\": " + fixed(estimate.rows, 2) + + ",\n" + pad + " \"cost\": " + fixed(estimate.cost, 2) + + ",\n" + pad + " \"children\": ["; + std::vector children; + if (node.input) children.push_back(node.input.get()); + if (node.left) children.push_back(node.left.get()); + if (node.right) children.push_back(node.right.get()); + for (std::size_t i = 0; i < children.size(); ++i) { + out += (i == 0 ? "\n" : ",\n") + pad + " " + tree_json(*children[i], catalog, indent + 4); + } + out += children.empty() ? "]" : "\n" + pad + " ]"; + out += "\n" + pad + "}"; + return out; +} + +// ---- measurement ------------------------------------------------------------ + +struct Alternative { + std::size_t index{0}; + plan::LogicalPlan plan; + std::string text; + double total_cost{0.0}; + ResultSignature ordered_signature; + ResultSignature bag_signature; + Timings vectorized; +}; + +struct WorkingReport { + Working working; + std::vector explain_lines; + optimizer::MemoExploreResult explored; + std::size_t groups{0}; + std::vector alternatives; + std::size_t chosen{0}; + ResultSignature signature; + Timings interpreted; + bool hit_expression_bound{false}; + bool hit_plan_bound{false}; +}; + +std::vector explain_lines(const plan::LogicalPlan& logical, + const execution::Catalog& catalog) { + const auto batch = optimizer::explain(logical, catalog); + const auto& column = batch.string_column("plan"); + std::vector lines; + for (std::size_t row = 0; row < batch.row_count(); ++row) lines.push_back(column.at(row)); + return lines; +} + +WorkingReport run_working(const Working& working, const execution::Catalog& catalog) { + WorkingReport report; + report.working = working; + const auto logical = bind_query(catalog, working.sql); + report.explain_lines = explain_lines(logical, catalog); + + optimizer::Memo memo; + const auto root = memo.insert(logical); + report.explored = optimizer::explore_memo_to_fixpoint(memo, optimizer::default_memo_rules()); + if (!report.explored.reached_fixpoint) { + throw std::runtime_error(working.id + ": memo exploration did not reach fixpoint"); + } + report.groups = memo.group_count(); + + const auto extracted = memo.extract_alternatives(root, optimizer::AlternativeExtractionOptions{128, 1024}); + report.hit_expression_bound = extracted.hit_expression_bound; + report.hit_plan_bound = extracted.hit_plan_bound; + if (extracted.hit_expression_bound || extracted.hit_plan_bound) { + throw std::runtime_error(working.id + ": alternative extraction hit a cap"); + } + + const auto best = memo.extract_best(root, catalog); + const auto best_text = plan::to_string(best); + bool found = false; + for (std::size_t i = 0; i < extracted.plans.size(); ++i) { + Alternative alt; + alt.index = i; + alt.plan = extracted.plans[i]; + alt.text = plan::to_string(alt.plan); + alt.total_cost = optimizer::estimate_cost(alt.plan, catalog).cost; + const auto result = execution::execute_vectorized(alt.plan, catalog); + alt.ordered_signature = signature_for(result); + alt.bag_signature = bag_signature_for(result); + alt.vectorized = measure(alt.plan, catalog, "vectorized", execution::execute_vectorized); + if (!found && alt.text == best_text) { + report.chosen = i; + found = true; + } + report.alternatives.push_back(std::move(alt)); + } + if (!found) throw std::runtime_error(working.id + ": chosen plan is not among the extracted alternatives"); + + std::size_t min_index = 0; + for (std::size_t i = 1; i < report.alternatives.size(); ++i) { + if (report.alternatives[i].total_cost < report.alternatives[min_index].total_cost) min_index = i; + } + if (min_index != report.chosen) { + throw std::runtime_error(working.id + ": chosen plan is not the lowest-cost lowest-index alternative"); + } + report.signature = report.alternatives[report.chosen].ordered_signature; + for (const auto& alt : report.alternatives) { + if (!(alt.bag_signature == report.alternatives[report.chosen].bag_signature)) { + const auto& chosen = report.alternatives[report.chosen]; + std::cerr << working.id << ": chosen alternative " << chosen.index + << " rows=" << chosen.bag_signature.row_count + << " bag checksum=" << hex_checksum(chosen.bag_signature.checksum) << "\n" + << chosen.text << "\n" + << working.id << ": mismatching alternative " << alt.index + << " rows=" << alt.bag_signature.row_count + << " bag checksum=" << hex_checksum(alt.bag_signature.checksum) << "\n" + << alt.text << "\n"; + throw std::runtime_error(working.id + ": alternative " + std::to_string(alt.index) + " changed the result"); + } + } + const auto interpreted = execution::execute_interpreted(report.alternatives[report.chosen].plan, catalog); + if (!(signature_for(interpreted) == report.signature)) { + throw std::runtime_error(working.id + ": interpreted oracle disagrees with the chosen plan"); + } + report.interpreted = + measure(report.alternatives[report.chosen].plan, catalog, "interpreted", execution::execute_interpreted); + return report; +} + +void assert_three_trains_has_three_costs(const WorkingReport& report) { + std::set join_costs; + for (const auto& alt : report.alternatives) { + if (alt.text.find("Join[") != std::string::npos) join_costs.insert(fixed(alt.total_cost, 2)); + } + if (join_costs.size() < 3) { + for (const auto& alt : report.alternatives) { + std::cerr << "three-trains alternative " << alt.index << " cost=" << fixed(alt.total_cost, 2) + << "\n" << alt.text << "\n"; + } + throw std::runtime_error("three-trains: fewer than three distinct join-order costs (" + + std::to_string(join_costs.size()) + ")"); + } +} + +struct HumpReport { + HumpWorkload workload; + ResultSignature signature; + Timings interpreted; + Timings vectorized; +}; + +HumpReport run_hump(const HumpWorkload& workload, const execution::Catalog& catalog) { + const auto logical = bind_query(catalog, workload.sql); + const auto interpreted = signature_for(execution::execute_interpreted(logical, catalog)); + const auto vectorized = signature_for(execution::execute_vectorized(logical, catalog)); + if (!(interpreted == vectorized)) throw std::runtime_error(workload.id + ": engines disagree"); + return HumpReport{workload, vectorized, + measure(logical, catalog, "interpreted", execution::execute_interpreted), + measure(logical, catalog, "vectorized", execution::execute_vectorized)}; +} + +// ---- writers ---------------------------------------------------------------- + +std::string timings_json(const Timings& t) { + return "{ \"min\": " + fixed(t.min_ms, 3) + ", \"median\": " + fixed(t.median_ms, 3) + " }"; +} + +std::string string_array_json(const std::vector& values, const std::string& pad) { + if (values.empty()) return "[]"; + std::string out = "["; + for (std::size_t i = 0; i < values.size(); ++i) { + out += (i == 0 ? "\n" : ",\n") + pad + " " + quoted(values[i]); + } + return out + "\n" + pad + "]"; +} + +void write_json(const std::string& path, + const std::vector& workings, + const std::vector& humps, + const std::string& commit, + const std::string& date, + const std::string& machine, + const execution::Catalog& catalog) { + std::ofstream out(path); + if (!out) throw std::runtime_error("cannot write " + path); + out << "{\n \"engine\": {\n" + << " \"repo\": \"github.com/Aly700/vectorized-sql-engine\",\n" + << " \"commit\": " << quoted(commit) << ",\n" + << " \"exportedAt\": " << quoted(date) << ",\n" + << " \"machine\": " << quoted(machine) << ",\n" + << " \"toolchain\": " << quoted(std::string(__VERSION__)) << ",\n" + << " \"flags\": \"-O2 -DNDEBUG\",\n" + << " \"repetitions\": " << kRepetitions << "\n },\n" + << " \"workings\": ["; + for (std::size_t w = 0; w < workings.size(); ++w) { + const auto& r = workings[w]; + out << (w == 0 ? "\n" : ",\n") << " {\n" + << " \"id\": " << quoted(r.working.id) << ",\n" + << " \"sql\": " << quoted(r.working.sql) << ",\n" + << " \"tables\": ["; + for (std::size_t t = 0; t < r.working.tables.size(); ++t) { + out << (t == 0 ? " " : ", ") << "{ \"name\": " << quoted(r.working.tables[t].first) + << ", \"rows\": " << r.working.tables[t].second << " }"; + } + out << " ],\n" + << " \"explain\": " << string_array_json(r.explain_lines, " ") << ",\n" + << " \"memo\": { \"groups\": " << r.groups + << ", \"iterations\": " << r.explored.iterations + << ", \"reachedFixpoint\": " << (r.explored.reached_fixpoint ? "true" : "false") + << ", \"firedRules\": " << string_array_json(r.explored.fired_rules, " ") << " },\n" + << " \"alternatives\": ["; + for (std::size_t a = 0; a < r.alternatives.size(); ++a) { + const auto& alt = r.alternatives[a]; + out << (a == 0 ? "\n" : ",\n") << " {\n" + << " \"index\": " << alt.index << ",\n" + << " \"plan\": " << quoted(alt.text) << ",\n" + << " \"tree\": " << tree_json(alt.plan, catalog, 10) << ",\n" + << " \"totalCost\": " << fixed(alt.total_cost, 2) << ",\n" + << " \"rowCount\": " << alt.bag_signature.row_count << ",\n" + << " \"bagChecksum\": " << quoted(hex_checksum(alt.bag_signature.checksum)) << ",\n" + << " \"vectorizedMs\": " << timings_json(alt.vectorized) << "\n" + << " }"; + } + out << "\n ],\n" + << " \"chosen\": " << r.chosen << ",\n" + << " \"checksum\": " << quoted(hex_checksum(r.signature.checksum)) << ",\n" + << " \"interpretedMs\": " << timings_json(r.interpreted) << ",\n" + << " \"hitExpressionBound\": " << (r.hit_expression_bound ? "true" : "false") << ",\n" + << " \"hitPlanBound\": " << (r.hit_plan_bound ? "true" : "false") << "\n" + << " }"; + } + out << "\n ],\n \"hump\": ["; + for (std::size_t h = 0; h < humps.size(); ++h) { + const auto& r = humps[h]; + out << (h == 0 ? "\n" : ",\n") << " { \"id\": " << quoted(r.workload.id) + << ", \"sql\": " << quoted(r.workload.sql) + << ", \"predicate\": " << quoted(r.workload.predicate) + << ", \"kept\": " << r.signature.row_count + << ", \"of\": " << kFactRows + << ", \"checksum\": " << quoted(hex_checksum(r.signature.checksum)) + << ", \"interpretedMs\": " << timings_json(r.interpreted) + << ", \"vectorizedMs\": " << timings_json(r.vectorized) << " }"; + } + out << "\n ]\n}\n"; +} + +void write_markdown(const std::string& path, + const std::vector& workings, + const std::vector& humps, + const std::string& commit, + const std::string& date) { + std::ofstream out(path); + if (!out) throw std::runtime_error("cannot write " + path); + out << "## Yard Routes (" << date << ")\n\n" + << "`yard_export` (commit `" << commit << "`) enumerates every memo alternative for four\n" + << "workings, prices each with `estimate_cost`, and times each through vectorized\n" + << "execution with the same Release build, five-repetition min/median, deterministic\n" + << "data, and checksum-before-timing methodology as above. The chosen row is the\n" + << "`extract_best` winner; every alternative must reproduce its bag checksum. The\n" + << "interpreted timing is for the chosen plan only.\n\n"; + for (const auto& r : workings) { + out << "### " << r.working.id << "\n\n```sql\n" << r.working.sql << "\n```\n\n" + << "memo: " << r.groups << " groups, " << r.explored.iterations << " iterations, fired rules: "; + for (std::size_t i = 0; i < r.explored.fired_rules.size(); ++i) { + out << (i == 0 ? "" : ", ") << r.explored.fired_rules[i]; + } + if (r.explored.fired_rules.empty()) out << "none"; + out << "\n\n| alternative | plan | total cost | vectorized min ms | vectorized median ms |\n" + << "|---:|---|---:|---:|---:|\n"; + for (const auto& alt : r.alternatives) { + std::string one_line = alt.text; + for (auto& ch : one_line) if (ch == '\n') ch = ' '; + out << "| " << alt.index << (alt.index == r.chosen ? " (chosen)" : "") << " | `" << one_line + << "` | " << fixed(alt.total_cost, 2) << " | " << fixed(alt.vectorized.min_ms, 3) + << " | " << fixed(alt.vectorized.median_ms, 3) << " |\n"; + } + out << "\ninterpreted (chosen plan): " << fixed(r.interpreted.min_ms, 3) << " / " + << fixed(r.interpreted.median_ms, 3) << " ms; rows=" + << r.signature.row_count + << " checksum=" << hex_checksum(r.signature.checksum) + << " bag checksum=" << hex_checksum(r.alternatives[r.chosen].bag_signature.checksum) << "\n\n"; + } + out << "### hump workloads\n\n" + << "| workload | predicate | kept | interpreted min ms | interpreted median ms | vectorized min ms | vectorized median ms |\n" + << "|---|---|---:|---:|---:|---:|---:|\n"; + for (const auto& r : humps) { + out << "| " << r.workload.id << " | `" << r.workload.predicate << "` | " << r.signature.row_count + << " | " << fixed(r.interpreted.min_ms, 3) << " | " << fixed(r.interpreted.median_ms, 3) + << " | " << fixed(r.vectorized.min_ms, 3) << " | " << fixed(r.vectorized.median_ms, 3) << " |\n"; + } +} + +struct Args { + std::string json, markdown, commit, date, machine; +}; + +Args parse_args(int argc, char** argv) { + Args args; + for (int i = 1; i + 1 < argc; i += 2) { + const std::string key = argv[i]; + const std::string value = argv[i + 1]; + if (key == "--json") args.json = value; + else if (key == "--markdown") args.markdown = value; + else if (key == "--commit") args.commit = value; + else if (key == "--date") args.date = value; + else if (key == "--machine") args.machine = value; + else throw std::invalid_argument("unknown argument " + key); + } + if (args.json.empty() || args.markdown.empty() || args.commit.empty() || args.date.empty() || + args.machine.empty()) { + throw std::invalid_argument( + "usage: yard_export --json --markdown --commit --date --machine "); + } + return args; +} + +} // namespace + +int main(int argc, char** argv) { + try { + const auto args = parse_args(argc, argv); + const auto catalog = make_catalog(); + std::vector reports; + for (const auto& working : make_workings()) { + std::cerr << "working " << working.id << "\n"; + reports.push_back(run_working(working, catalog)); + std::cerr << " " << reports.back().alternatives.size() << " alternatives, chosen " + << reports.back().chosen << "\n"; + } + assert_three_trains_has_three_costs(reports[2]); + std::vector humps; + for (const auto& workload : make_hump_workloads()) humps.push_back(run_hump(workload, catalog)); + write_json(args.json, reports, humps, args.commit, args.date, args.machine, catalog); + write_markdown(args.markdown, reports, humps, args.commit, args.date); + if (benchmark_sink == 0x0123456789abcdefULL) std::cout << "sink=" << benchmark_sink << "\n"; + } catch (const std::invalid_argument& ex) { + std::cerr << ex.what() << "\n"; + return 2; + } catch (const std::exception& ex) { + std::cerr << "yard_export failed: " << ex.what() << "\n"; + return 1; + } + return 0; +} diff --git a/docs/benchmarks.md b/docs/benchmarks.md index f83d5eb..d62c83b 100644 --- a/docs/benchmarks.md +++ b/docs/benchmarks.md @@ -280,3 +280,114 @@ The checksum match is performed before timing and pins the left-only output and correlated candidate-set semantics. The vectorized grouped hash kernel is 16.350x faster on median than the interpreted left-row-major semantic oracle for this selective workload. + +## Yard Routes (2026-09-01) + +`yard_export` (commit `1058974ea747`) enumerates every memo alternative for four +workings, prices each with `estimate_cost`, and times each through vectorized +execution with the same Release build, five-repetition min/median, deterministic +data, and checksum-before-timing methodology as above. The chosen row is the +`extract_best` winner; every alternative must reproduce its bag checksum. The +interpreted timing is for the chosen plan only. + +### coupling + +```sql +SELECT l.payload AS left_payload, r.payload AS right_payload FROM join_left AS l JOIN join_right AS r ON l.k1 = r.k1 AND l.k2 = r.k2 +``` + +memo: 4 groups, 1 iterations, fired rules: JoinCommuteRule + +| alternative | plan | total cost | vectorized min ms | vectorized median ms | +|---:|---|---:|---:|---:| +| 0 (chosen) | `Project[left_payload=col(l.payload), right_payload=col(r.payload)] Join[col(l.k1) = col(r.k1) AND col(l.k2) = col(r.k2)] Scan[join_left AS l] Scan[join_right AS r]` | 200512.00 | 15.060 | 15.291 | +| 1 | `Project[left_payload=col(l.payload), right_payload=col(r.payload)] Join[col(l.k1) = col(r.k1) AND col(l.k2) = col(r.k2)] Scan[join_right AS r] Scan[join_left AS l]` | 200512.00 | 15.361 | 15.564 | + +interpreted (chosen plan): 5707.965 / 5737.780 ms; rows=100000 checksum=0xd6eff9218be6da07 bag checksum=0xe60844c222869ce8 + +### hump-and-bowl + +```sql +SELECT l.group_id AS group_id, COUNT(*) AS n, MAX(r.measure) AS top FROM e2e_left AS l JOIN e2e_right AS r ON l.k = r.k WHERE l.filter_key < 80 GROUP BY l.group_id ORDER BY n DESC, group_id ASC LIMIT 20 +``` + +memo: 11 groups, 2 iterations, fired rules: JoinCommuteRule, FilterIntoJoinRule, JoinCommuteRule + +| alternative | plan | total cost | vectorized min ms | vectorized median ms | +|---:|---|---:|---:|---:| +| 0 (chosen) | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Filter[col(l.filter_key) < lit(80)] Join[col(l.k) = col(r.k)] Scan[e2e_left AS l] Scan[e2e_right AS r]` | 241486.08 | 27.881 | 27.934 | +| 1 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Filter[col(l.filter_key) < lit(80)] Join[col(l.k) = col(r.k)] Scan[e2e_right AS r] Scan[e2e_left AS l]` | 241486.08 | 25.992 | 26.419 | +| 2 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Join[col(l.k) = col(r.k)] Filter[col(l.filter_key) < lit(80)] Scan[e2e_left AS l] Scan[e2e_right AS r]` | 283072.00 | 24.780 | 24.812 | +| 3 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Join[col(l.k) = col(r.k)] Scan[e2e_right AS r] Filter[col(l.filter_key) < lit(80)] Scan[e2e_left AS l]` | 283072.00 | 23.322 | 23.467 | + +interpreted (chosen plan): 4175.083 / 4200.672 ms; rows=20 checksum=0xae466c9b0f225c78 bag checksum=0x5ad6701e8dbf32d5 + +### three-trains + +```sql +SELECT l.group_id AS group_id, COUNT(*) AS n, MAX(r.measure) AS top FROM e2e_left AS l JOIN e2e_right AS r ON l.k = r.k JOIN e2e_class AS c ON l.group_id = c.group_id WHERE c.tier = 1 GROUP BY l.group_id ORDER BY n DESC, group_id ASC LIMIT 20 +``` + +memo: 22 groups, 3 iterations, fired rules: JoinCommuteRule, JoinCommuteRule, JoinAssociateRule, FilterIntoJoinRule, JoinAssociateRule, JoinCommuteRule, FilterIntoJoinRule, JoinCommuteRule, JoinCommuteRule, JoinAssociateRule, JoinAssociateRule, JoinCommuteRule, FilterIntoJoinRule, JoinCommuteRule, JoinCommuteRule, JoinCommuteRule + +| alternative | plan | total cost | vectorized min ms | vectorized median ms | +|---:|---|---:|---:|---:| +| 0 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Filter[col(c.tier) = lit(1)] Join[col(l.group_id) = col(c.group_id)] Join[col(l.k) = col(r.k)] Scan[e2e_left AS l] Scan[e2e_right AS r] Scan[e2e_class AS c]` | 241224.68 | 39.776 | 39.897 | +| 1 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Filter[col(c.tier) = lit(1)] Join[col(l.group_id) = col(c.group_id)] Join[col(l.k) = col(r.k)] Scan[e2e_right AS r] Scan[e2e_left AS l] Scan[e2e_class AS c]` | 241224.68 | 34.654 | 34.667 | +| 2 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Filter[col(c.tier) = lit(1)] Join[col(l.group_id) = col(c.group_id)] Scan[e2e_class AS c] Join[col(l.k) = col(r.k)] Scan[e2e_left AS l] Scan[e2e_right AS r]` | 241224.68 | 32.942 | 32.990 | +| 3 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Filter[col(c.tier) = lit(1)] Join[col(l.group_id) = col(c.group_id)] Scan[e2e_class AS c] Join[col(l.k) = col(r.k)] Scan[e2e_right AS r] Scan[e2e_left AS l]` | 241224.68 | 30.710 | 30.725 | +| 4 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Filter[col(c.tier) = lit(1)] Join[col(l.k) = col(r.k)] Scan[e2e_right AS r] Join[col(l.group_id) = col(c.group_id)] Scan[e2e_left AS l] Scan[e2e_class AS c]` | 241096.68 | 36.685 | 36.737 | +| 5 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Filter[col(c.tier) = lit(1)] Join[col(l.k) = col(r.k)] Scan[e2e_right AS r] Join[col(l.group_id) = col(c.group_id)] Scan[e2e_class AS c] Scan[e2e_left AS l]` | 241096.68 | 32.799 | 32.900 | +| 6 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Filter[col(c.tier) = lit(1)] Join[col(l.k) = col(r.k)] Join[col(l.group_id) = col(c.group_id)] Scan[e2e_left AS l] Scan[e2e_class AS c] Scan[e2e_right AS r]` | 241096.68 | 39.762 | 39.825 | +| 7 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Filter[col(c.tier) = lit(1)] Join[col(l.k) = col(r.k)] Join[col(l.group_id) = col(c.group_id)] Scan[e2e_class AS c] Scan[e2e_left AS l] Scan[e2e_right AS r]` | 241096.68 | 29.400 | 29.418 | +| 8 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Join[col(l.group_id) = col(c.group_id)] Join[col(l.k) = col(r.k)] Scan[e2e_left AS l] Scan[e2e_right AS r] Filter[col(c.tier) = lit(1)] Scan[e2e_class AS c]` | 241109.48 | 27.592 | 27.625 | +| 9 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Join[col(l.group_id) = col(c.group_id)] Join[col(l.k) = col(r.k)] Scan[e2e_right AS r] Scan[e2e_left AS l] Filter[col(c.tier) = lit(1)] Scan[e2e_class AS c]` | 241109.48 | 24.504 | 24.548 | +| 10 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Join[col(l.group_id) = col(c.group_id)] Filter[col(c.tier) = lit(1)] Scan[e2e_class AS c] Join[col(l.k) = col(r.k)] Scan[e2e_left AS l] Scan[e2e_right AS r]` | 241109.48 | 25.881 | 25.902 | +| 11 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Join[col(l.group_id) = col(c.group_id)] Filter[col(c.tier) = lit(1)] Scan[e2e_class AS c] Join[col(l.k) = col(r.k)] Scan[e2e_right AS r] Scan[e2e_left AS l]` | 241109.48 | 23.560 | 23.607 | +| 12 (chosen) | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Join[col(l.k) = col(r.k)] Scan[e2e_right AS r] Join[col(l.group_id) = col(c.group_id)] Scan[e2e_left AS l] Filter[col(c.tier) = lit(1)] Scan[e2e_class AS c]` | 240866.28 | 16.510 | 16.563 | +| 13 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Join[col(l.k) = col(r.k)] Scan[e2e_right AS r] Join[col(l.group_id) = col(c.group_id)] Filter[col(c.tier) = lit(1)] Scan[e2e_class AS c] Scan[e2e_left AS l]` | 240866.28 | 15.168 | 15.200 | +| 14 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Join[col(l.k) = col(r.k)] Join[col(l.group_id) = col(c.group_id)] Scan[e2e_left AS l] Filter[col(c.tier) = lit(1)] Scan[e2e_class AS c] Scan[e2e_right AS r]` | 240866.28 | 17.779 | 17.896 | +| 15 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Join[col(l.k) = col(r.k)] Join[col(l.group_id) = col(c.group_id)] Filter[col(c.tier) = lit(1)] Scan[e2e_class AS c] Scan[e2e_left AS l] Scan[e2e_right AS r]` | 240866.28 | 14.535 | 14.738 | +| 16 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Join[col(l.k) = col(r.k)] Scan[e2e_right AS r] Filter[col(c.tier) = lit(1)] Join[col(l.group_id) = col(c.group_id)] Scan[e2e_left AS l] Scan[e2e_class AS c]` | 240981.48 | 26.074 | 26.108 | +| 17 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Join[col(l.k) = col(r.k)] Scan[e2e_right AS r] Filter[col(c.tier) = lit(1)] Join[col(l.group_id) = col(c.group_id)] Scan[e2e_class AS c] Scan[e2e_left AS l]` | 240981.48 | 21.237 | 21.333 | +| 18 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Join[col(l.k) = col(r.k)] Scan[e2e_right AS r] Join[col(l.group_id) = col(c.group_id)] Scan[e2e_left AS l] Filter[col(c.tier) = lit(1)] Scan[e2e_class AS c]` | 240866.28 | 16.506 | 16.541 | +| 19 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Join[col(l.k) = col(r.k)] Scan[e2e_right AS r] Join[col(l.group_id) = col(c.group_id)] Filter[col(c.tier) = lit(1)] Scan[e2e_class AS c] Scan[e2e_left AS l]` | 240866.28 | 15.267 | 15.569 | +| 20 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Join[col(l.k) = col(r.k)] Filter[col(c.tier) = lit(1)] Join[col(l.group_id) = col(c.group_id)] Scan[e2e_left AS l] Scan[e2e_class AS c] Scan[e2e_right AS r]` | 240981.48 | 26.842 | 26.997 | +| 21 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Join[col(l.k) = col(r.k)] Filter[col(c.tier) = lit(1)] Join[col(l.group_id) = col(c.group_id)] Scan[e2e_class AS c] Scan[e2e_left AS l] Scan[e2e_right AS r]` | 240981.48 | 20.516 | 20.581 | +| 22 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Join[col(l.k) = col(r.k)] Join[col(l.group_id) = col(c.group_id)] Scan[e2e_left AS l] Filter[col(c.tier) = lit(1)] Scan[e2e_class AS c] Scan[e2e_right AS r]` | 240866.28 | 17.074 | 17.142 | +| 23 | `Limit[20] Sort[col(n) DESC, col(group_id) ASC] Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] Join[col(l.k) = col(r.k)] Join[col(l.group_id) = col(c.group_id)] Filter[col(c.tier) = lit(1)] Scan[e2e_class AS c] Scan[e2e_left AS l] Scan[e2e_right AS r]` | 240866.28 | 14.410 | 14.423 | + +interpreted (chosen plan): 1591.984 / 1601.294 ms; rows=20 checksum=0x7e53943623dc6b01 bag checksum=0x8f1c966523a83a50 + +### special + +```sql +SELECT l.payload AS payload FROM join_left AS l WHERE l.k1 IN (SELECT r.k1 FROM join_right AS r WHERE r.payload = 10000) +``` + +memo: 7 groups, 1 iterations, fired rules: InToSemiJoinRule + +| alternative | plan | total cost | vectorized min ms | vectorized median ms | +|---:|---|---:|---:|---:| +| 0 (chosen) | `Project[payload=col(l.payload)] Filter[col(l.k1) IN subquery(IN subquery at position 59)] Subquery[IN subquery at position 59] Project[r.k1=col(r.k1)] Filter[col(r.payload) = lit(10000)] Scan[join_right AS r] Scan[join_left AS l]` | 200512.00 | 3.063 | 3.151 | +| 1 | `Project[payload=col(l.payload)] SemiJoin[col(l.k1) = col(r.k1)] Scan[join_left AS l] Project[r.k1=col(r.k1)] Filter[col(r.payload) = lit(10000)] Scan[join_right AS r]` | 200537.60 | 5.250 | 5.272 | + +interpreted (chosen plan): 8.902 / 9.068 ms; rows=6250 checksum=0x72a0d9bc69515c7e bag checksum=0x5b144f1cb20c62ff + +### hump workloads + +| workload | predicate | kept | interpreted min ms | interpreted median ms | vectorized min ms | vectorized median ms | +|---|---|---:|---:|---:|---:|---:| +| scan_filter_1pct | `bucket = 7` | 2000 | 17.409 | 17.525 | 7.904 | 7.949 | +| scan_filter_10pct | `decile = 3` | 20000 | 18.751 | 18.816 | 9.067 | 9.089 | +| scan_filter_50pct | `half = 1` | 100000 | 25.338 | 25.427 | 14.235 | 14.249 | + +### three_way_route in sql_bench + +The three-trains working is also registered in `sql_bench` as `three_way_route`, timed on the +query as bound (the first alternative above, before the memo reorders it). Same Release build, +five repetitions, checksum before timing: + +| workload | rows | correctness | interpreted min ms | interpreted median ms | vectorized min ms | vectorized median ms | median speedup | +|---|---:|---|---:|---:|---:|---:|---:| +| three_way_route | left=120000,right=256,class=128 | match rows=20 checksum=0x7e53943623dc6b01 | 5829.809 | 5843.328 | 39.476 | 39.516 | 147.871x | diff --git a/docs/yard/yard-routes.json b/docs/yard/yard-routes.json new file mode 100644 index 0000000..3126fa6 --- /dev/null +++ b/docs/yard/yard-routes.json @@ -0,0 +1,2699 @@ +{ + "engine": { + "repo": "github.com/Aly700/vectorized-sql-engine", + "commit": "1058974ea747", + "exportedAt": "2026-09-01", + "machine": "MacBook Pro Mac14,7, Apple M2, 8 cores, 16 GB", + "toolchain": "Apple LLVM 17.0.0 (clang-1700.4.4.1)", + "flags": "-O2 -DNDEBUG", + "repetitions": 5 + }, + "workings": [ + { + "id": "coupling", + "sql": "SELECT l.payload AS left_payload, r.payload AS right_payload FROM join_left AS l JOIN join_right AS r ON l.k1 = r.k1 AND l.k2 = r.k2", + "tables": [ { "name": "join_left", "rows": 100000 }, { "name": "join_right", "rows": 256 } ], + "explain": [ + "EXPLAIN", + "bound logical plan:", + " Project[left_payload=col(l.payload), right_payload=col(r.payload)]", + " Join[col(l.k1) = col(r.k1) AND col(l.k2) = col(r.k2)]", + " Scan[join_left AS l]", + " Scan[join_right AS r]", + "memo exploration:", + " groups: 4", + " iterations: 1", + " reached_fixpoint: yes", + " fired rules:", + " 0: JoinCommuteRule", + "chosen plan:", + " Project[left_payload=col(l.payload), right_payload=col(r.payload)] rows=25.60 cost=200512.00", + " Join[col(l.k1) = col(r.k1) AND col(l.k2) = col(r.k2)] rows=25.60 cost=200512.00", + " Scan[join_left AS l] rows=100000.00 cost=100000.00", + " Scan[join_right AS r] rows=256.00 cost=256.00", + "total cost: 200512.00" + ], + "memo": { "groups": 4, "iterations": 1, "reachedFixpoint": true, "firedRules": [ + "JoinCommuteRule" + ] }, + "alternatives": [ + { + "index": 0, + "plan": "Project[left_payload=col(l.payload), right_payload=col(r.payload)]\n Join[col(l.k1) = col(r.k1) AND col(l.k2) = col(r.k2)]\n Scan[join_left AS l]\n Scan[join_right AS r]", + "tree": { + "op": "Project", + "detail": "left_payload=col(l.payload), right_payload=col(r.payload)", + "rows": 25.60, + "cost": 200512.00, + "children": [ + { + "op": "Join", + "detail": "col(l.k1) = col(r.k1) AND col(l.k2) = col(r.k2)", + "rows": 25.60, + "cost": 200512.00, + "children": [ + { + "op": "Scan", + "detail": "join_left AS l", + "rows": 100000.00, + "cost": 100000.00, + "children": [] + }, + { + "op": "Scan", + "detail": "join_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + } + ] + } + ] + }, + "totalCost": 200512.00, + "rowCount": 100000, + "bagChecksum": "0xe60844c222869ce8", + "vectorizedMs": { "min": 15.060, "median": 15.291 } + }, + { + "index": 1, + "plan": "Project[left_payload=col(l.payload), right_payload=col(r.payload)]\n Join[col(l.k1) = col(r.k1) AND col(l.k2) = col(r.k2)]\n Scan[join_right AS r]\n Scan[join_left AS l]", + "tree": { + "op": "Project", + "detail": "left_payload=col(l.payload), right_payload=col(r.payload)", + "rows": 25.60, + "cost": 200512.00, + "children": [ + { + "op": "Join", + "detail": "col(l.k1) = col(r.k1) AND col(l.k2) = col(r.k2)", + "rows": 25.60, + "cost": 200512.00, + "children": [ + { + "op": "Scan", + "detail": "join_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + }, + { + "op": "Scan", + "detail": "join_left AS l", + "rows": 100000.00, + "cost": 100000.00, + "children": [] + } + ] + } + ] + }, + "totalCost": 200512.00, + "rowCount": 100000, + "bagChecksum": "0xe60844c222869ce8", + "vectorizedMs": { "min": 15.361, "median": 15.564 } + } + ], + "chosen": 0, + "checksum": "0xd6eff9218be6da07", + "interpretedMs": { "min": 5707.965, "median": 5737.780 }, + "hitExpressionBound": false, + "hitPlanBound": false + }, + { + "id": "hump-and-bowl", + "sql": "SELECT l.group_id AS group_id, COUNT(*) AS n, MAX(r.measure) AS top FROM e2e_left AS l JOIN e2e_right AS r ON l.k = r.k WHERE l.filter_key < 80 GROUP BY l.group_id ORDER BY n DESC, group_id ASC LIMIT 20", + "tables": [ { "name": "e2e_left", "rows": 120000 }, { "name": "e2e_right", "rows": 256 } ], + "explain": [ + "EXPLAIN", + "bound logical plan:", + " Limit[20]", + " Sort[col(n) DESC, col(group_id) ASC]", + " Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]", + " Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]", + " Filter[col(l.filter_key) < lit(80)]", + " Join[col(l.k) = col(r.k)]", + " Scan[e2e_left AS l]", + " Scan[e2e_right AS r]", + "memo exploration:", + " groups: 11", + " iterations: 2", + " reached_fixpoint: yes", + " fired rules:", + " 0: JoinCommuteRule", + " 1: FilterIntoJoinRule", + " 2: JoinCommuteRule", + "chosen plan:", + " Limit[20] rows=20.00 cost=241486.08", + " Sort[col(n) DESC, col(group_id) ASC] rows=85.33 cost=241486.08", + " Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] rows=85.33 cost=240938.67", + " Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] rows=85.33 cost=240938.67", + " Filter[col(l.filter_key) < lit(80)] rows=85.33 cost=240768.00", + " Join[col(l.k) = col(r.k)] rows=256.00 cost=240512.00", + " Scan[e2e_left AS l] rows=120000.00 cost=120000.00", + " Scan[e2e_right AS r] rows=256.00 cost=256.00", + "total cost: 241486.08" + ], + "memo": { "groups": 11, "iterations": 2, "reachedFixpoint": true, "firedRules": [ + "JoinCommuteRule", + "FilterIntoJoinRule", + "JoinCommuteRule" + ] }, + "alternatives": [ + { + "index": 0, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Filter[col(l.filter_key) < lit(80)]\n Join[col(l.k) = col(r.k)]\n Scan[e2e_left AS l]\n Scan[e2e_right AS r]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 20.00, + "cost": 241486.08, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 85.33, + "cost": 241486.08, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 85.33, + "cost": 240938.67, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 85.33, + "cost": 240938.67, + "children": [ + { + "op": "Filter", + "detail": "col(l.filter_key) < lit(80)", + "rows": 85.33, + "cost": 240768.00, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 256.00, + "cost": 240512.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + }, + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 241486.08, + "rowCount": 20, + "bagChecksum": "0x5ad6701e8dbf32d5", + "vectorizedMs": { "min": 27.881, "median": 27.934 } + }, + { + "index": 1, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Filter[col(l.filter_key) < lit(80)]\n Join[col(l.k) = col(r.k)]\n Scan[e2e_right AS r]\n Scan[e2e_left AS l]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 20.00, + "cost": 241486.08, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 85.33, + "cost": 241486.08, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 85.33, + "cost": 240938.67, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 85.33, + "cost": 240938.67, + "children": [ + { + "op": "Filter", + "detail": "col(l.filter_key) < lit(80)", + "rows": 85.33, + "cost": 240768.00, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 256.00, + "cost": 240512.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + }, + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 241486.08, + "rowCount": 20, + "bagChecksum": "0x5ad6701e8dbf32d5", + "vectorizedMs": { "min": 25.992, "median": 26.419 } + }, + { + "index": 2, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Join[col(l.k) = col(r.k)]\n Filter[col(l.filter_key) < lit(80)]\n Scan[e2e_left AS l]\n Scan[e2e_right AS r]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 20.00, + "cost": 283072.00, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 256.00, + "cost": 283072.00, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 256.00, + "cost": 281024.00, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 256.00, + "cost": 281024.00, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 256.00, + "cost": 280512.00, + "children": [ + { + "op": "Filter", + "detail": "col(l.filter_key) < lit(80)", + "rows": 40000.00, + "cost": 240000.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + } + ] + }, + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 283072.00, + "rowCount": 20, + "bagChecksum": "0x5ad6701e8dbf32d5", + "vectorizedMs": { "min": 24.780, "median": 24.812 } + }, + { + "index": 3, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Join[col(l.k) = col(r.k)]\n Scan[e2e_right AS r]\n Filter[col(l.filter_key) < lit(80)]\n Scan[e2e_left AS l]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 20.00, + "cost": 283072.00, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 256.00, + "cost": 283072.00, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 256.00, + "cost": 281024.00, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 256.00, + "cost": 281024.00, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 256.00, + "cost": 280512.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + }, + { + "op": "Filter", + "detail": "col(l.filter_key) < lit(80)", + "rows": 40000.00, + "cost": 240000.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 283072.00, + "rowCount": 20, + "bagChecksum": "0x5ad6701e8dbf32d5", + "vectorizedMs": { "min": 23.322, "median": 23.467 } + } + ], + "chosen": 0, + "checksum": "0xae466c9b0f225c78", + "interpretedMs": { "min": 4175.083, "median": 4200.672 }, + "hitExpressionBound": false, + "hitPlanBound": false + }, + { + "id": "three-trains", + "sql": "SELECT l.group_id AS group_id, COUNT(*) AS n, MAX(r.measure) AS top FROM e2e_left AS l JOIN e2e_right AS r ON l.k = r.k JOIN e2e_class AS c ON l.group_id = c.group_id WHERE c.tier = 1 GROUP BY l.group_id ORDER BY n DESC, group_id ASC LIMIT 20", + "tables": [ { "name": "e2e_left", "rows": 120000 }, { "name": "e2e_right", "rows": 256 }, { "name": "e2e_class", "rows": 128 } ], + "explain": [ + "EXPLAIN", + "bound logical plan:", + " Limit[20]", + " Sort[col(n) DESC, col(group_id) ASC]", + " Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]", + " Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]", + " Filter[col(c.tier) = lit(1)]", + " Join[col(l.group_id) = col(c.group_id)]", + " Join[col(l.k) = col(r.k)]", + " Scan[e2e_left AS l]", + " Scan[e2e_right AS r]", + " Scan[e2e_class AS c]", + "memo exploration:", + " groups: 22", + " iterations: 3", + " reached_fixpoint: yes", + " fired rules:", + " 0: JoinCommuteRule", + " 1: JoinCommuteRule", + " 2: JoinAssociateRule", + " 3: FilterIntoJoinRule", + " 4: JoinAssociateRule", + " 5: JoinCommuteRule", + " 6: FilterIntoJoinRule", + " 7: JoinCommuteRule", + " 8: JoinCommuteRule", + " 9: JoinAssociateRule", + " 10: JoinAssociateRule", + " 11: JoinCommuteRule", + " 12: FilterIntoJoinRule", + " 13: JoinCommuteRule", + " 14: JoinCommuteRule", + " 15: JoinCommuteRule", + "chosen plan:", + " Limit[20] rows=12.80 cost=240866.28", + " Sort[col(n) DESC, col(group_id) ASC] rows=12.80 cost=240866.28", + " Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))] rows=12.80 cost=240819.20", + " Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]] rows=12.80 cost=240819.20", + " Join[col(l.k) = col(r.k)] rows=12.80 cost=240793.60", + " Scan[e2e_right AS r] rows=256.00 cost=256.00", + " Join[col(l.group_id) = col(c.group_id)] rows=12.80 cost=240268.80", + " Scan[e2e_left AS l] rows=120000.00 cost=120000.00", + " Filter[col(c.tier) = lit(1)] rows=12.80 cost=256.00", + " Scan[e2e_class AS c] rows=128.00 cost=128.00", + "total cost: 240866.28" + ], + "memo": { "groups": 22, "iterations": 3, "reachedFixpoint": true, "firedRules": [ + "JoinCommuteRule", + "JoinCommuteRule", + "JoinAssociateRule", + "FilterIntoJoinRule", + "JoinAssociateRule", + "JoinCommuteRule", + "FilterIntoJoinRule", + "JoinCommuteRule", + "JoinCommuteRule", + "JoinAssociateRule", + "JoinAssociateRule", + "JoinCommuteRule", + "FilterIntoJoinRule", + "JoinCommuteRule", + "JoinCommuteRule", + "JoinCommuteRule" + ] }, + "alternatives": [ + { + "index": 0, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Filter[col(c.tier) = lit(1)]\n Join[col(l.group_id) = col(c.group_id)]\n Join[col(l.k) = col(r.k)]\n Scan[e2e_left AS l]\n Scan[e2e_right AS r]\n Scan[e2e_class AS c]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 241224.68, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 241224.68, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 241177.60, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 241177.60, + "children": [ + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 241152.00, + "children": [ + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 128.00, + "cost": 241024.00, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 256.00, + "cost": 240512.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + }, + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + } + ] + }, + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 241224.68, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 39.776, "median": 39.897 } + }, + { + "index": 1, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Filter[col(c.tier) = lit(1)]\n Join[col(l.group_id) = col(c.group_id)]\n Join[col(l.k) = col(r.k)]\n Scan[e2e_right AS r]\n Scan[e2e_left AS l]\n Scan[e2e_class AS c]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 241224.68, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 241224.68, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 241177.60, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 241177.60, + "children": [ + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 241152.00, + "children": [ + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 128.00, + "cost": 241024.00, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 256.00, + "cost": 240512.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + }, + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + } + ] + }, + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 241224.68, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 34.654, "median": 34.667 } + }, + { + "index": 2, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Filter[col(c.tier) = lit(1)]\n Join[col(l.group_id) = col(c.group_id)]\n Scan[e2e_class AS c]\n Join[col(l.k) = col(r.k)]\n Scan[e2e_left AS l]\n Scan[e2e_right AS r]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 241224.68, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 241224.68, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 241177.60, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 241177.60, + "children": [ + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 241152.00, + "children": [ + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 128.00, + "cost": 241024.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + }, + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 256.00, + "cost": 240512.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + }, + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 241224.68, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 32.942, "median": 32.990 } + }, + { + "index": 3, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Filter[col(c.tier) = lit(1)]\n Join[col(l.group_id) = col(c.group_id)]\n Scan[e2e_class AS c]\n Join[col(l.k) = col(r.k)]\n Scan[e2e_right AS r]\n Scan[e2e_left AS l]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 241224.68, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 241224.68, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 241177.60, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 241177.60, + "children": [ + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 241152.00, + "children": [ + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 128.00, + "cost": 241024.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + }, + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 256.00, + "cost": 240512.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + }, + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 241224.68, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 30.710, "median": 30.725 } + }, + { + "index": 4, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Filter[col(c.tier) = lit(1)]\n Join[col(l.k) = col(r.k)]\n Scan[e2e_right AS r]\n Join[col(l.group_id) = col(c.group_id)]\n Scan[e2e_left AS l]\n Scan[e2e_class AS c]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 241096.68, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 241096.68, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 241049.60, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 241049.60, + "children": [ + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 241024.00, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 128.00, + "cost": 240896.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + }, + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 128.00, + "cost": 240256.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + }, + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 241096.68, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 36.685, "median": 36.737 } + }, + { + "index": 5, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Filter[col(c.tier) = lit(1)]\n Join[col(l.k) = col(r.k)]\n Scan[e2e_right AS r]\n Join[col(l.group_id) = col(c.group_id)]\n Scan[e2e_class AS c]\n Scan[e2e_left AS l]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 241096.68, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 241096.68, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 241049.60, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 241049.60, + "children": [ + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 241024.00, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 128.00, + "cost": 240896.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + }, + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 128.00, + "cost": 240256.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + }, + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 241096.68, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 32.799, "median": 32.900 } + }, + { + "index": 6, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Filter[col(c.tier) = lit(1)]\n Join[col(l.k) = col(r.k)]\n Join[col(l.group_id) = col(c.group_id)]\n Scan[e2e_left AS l]\n Scan[e2e_class AS c]\n Scan[e2e_right AS r]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 241096.68, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 241096.68, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 241049.60, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 241049.60, + "children": [ + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 241024.00, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 128.00, + "cost": 240896.00, + "children": [ + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 128.00, + "cost": 240256.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + }, + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + } + ] + }, + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 241096.68, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 39.762, "median": 39.825 } + }, + { + "index": 7, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Filter[col(c.tier) = lit(1)]\n Join[col(l.k) = col(r.k)]\n Join[col(l.group_id) = col(c.group_id)]\n Scan[e2e_class AS c]\n Scan[e2e_left AS l]\n Scan[e2e_right AS r]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 241096.68, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 241096.68, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 241049.60, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 241049.60, + "children": [ + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 241024.00, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 128.00, + "cost": 240896.00, + "children": [ + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 128.00, + "cost": 240256.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + }, + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + } + ] + }, + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 241096.68, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 29.400, "median": 29.418 } + }, + { + "index": 8, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Join[col(l.group_id) = col(c.group_id)]\n Join[col(l.k) = col(r.k)]\n Scan[e2e_left AS l]\n Scan[e2e_right AS r]\n Filter[col(c.tier) = lit(1)]\n Scan[e2e_class AS c]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 241109.48, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 241109.48, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 241062.40, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 241062.40, + "children": [ + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 12.80, + "cost": 241036.80, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 256.00, + "cost": 240512.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + }, + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + } + ] + }, + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 256.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 241109.48, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 27.592, "median": 27.625 } + }, + { + "index": 9, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Join[col(l.group_id) = col(c.group_id)]\n Join[col(l.k) = col(r.k)]\n Scan[e2e_right AS r]\n Scan[e2e_left AS l]\n Filter[col(c.tier) = lit(1)]\n Scan[e2e_class AS c]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 241109.48, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 241109.48, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 241062.40, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 241062.40, + "children": [ + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 12.80, + "cost": 241036.80, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 256.00, + "cost": 240512.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + }, + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + } + ] + }, + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 256.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 241109.48, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 24.504, "median": 24.548 } + }, + { + "index": 10, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Join[col(l.group_id) = col(c.group_id)]\n Filter[col(c.tier) = lit(1)]\n Scan[e2e_class AS c]\n Join[col(l.k) = col(r.k)]\n Scan[e2e_left AS l]\n Scan[e2e_right AS r]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 241109.48, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 241109.48, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 241062.40, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 241062.40, + "children": [ + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 12.80, + "cost": 241036.80, + "children": [ + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 256.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + } + ] + }, + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 256.00, + "cost": 240512.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + }, + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 241109.48, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 25.881, "median": 25.902 } + }, + { + "index": 11, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Join[col(l.group_id) = col(c.group_id)]\n Filter[col(c.tier) = lit(1)]\n Scan[e2e_class AS c]\n Join[col(l.k) = col(r.k)]\n Scan[e2e_right AS r]\n Scan[e2e_left AS l]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 241109.48, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 241109.48, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 241062.40, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 241062.40, + "children": [ + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 12.80, + "cost": 241036.80, + "children": [ + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 256.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + } + ] + }, + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 256.00, + "cost": 240512.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + }, + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 241109.48, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 23.560, "median": 23.607 } + }, + { + "index": 12, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Join[col(l.k) = col(r.k)]\n Scan[e2e_right AS r]\n Join[col(l.group_id) = col(c.group_id)]\n Scan[e2e_left AS l]\n Filter[col(c.tier) = lit(1)]\n Scan[e2e_class AS c]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 240866.28, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 240866.28, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 240819.20, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 240819.20, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 12.80, + "cost": 240793.60, + "children": [ + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + }, + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 12.80, + "cost": 240268.80, + "children": [ + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + }, + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 256.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 240866.28, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 16.510, "median": 16.563 } + }, + { + "index": 13, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Join[col(l.k) = col(r.k)]\n Scan[e2e_right AS r]\n Join[col(l.group_id) = col(c.group_id)]\n Filter[col(c.tier) = lit(1)]\n Scan[e2e_class AS c]\n Scan[e2e_left AS l]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 240866.28, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 240866.28, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 240819.20, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 240819.20, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 12.80, + "cost": 240793.60, + "children": [ + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + }, + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 12.80, + "cost": 240268.80, + "children": [ + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 256.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + } + ] + }, + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 240866.28, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 15.168, "median": 15.200 } + }, + { + "index": 14, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Join[col(l.k) = col(r.k)]\n Join[col(l.group_id) = col(c.group_id)]\n Scan[e2e_left AS l]\n Filter[col(c.tier) = lit(1)]\n Scan[e2e_class AS c]\n Scan[e2e_right AS r]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 240866.28, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 240866.28, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 240819.20, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 240819.20, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 12.80, + "cost": 240793.60, + "children": [ + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 12.80, + "cost": 240268.80, + "children": [ + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + }, + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 256.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + } + ] + } + ] + }, + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 240866.28, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 17.779, "median": 17.896 } + }, + { + "index": 15, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Join[col(l.k) = col(r.k)]\n Join[col(l.group_id) = col(c.group_id)]\n Filter[col(c.tier) = lit(1)]\n Scan[e2e_class AS c]\n Scan[e2e_left AS l]\n Scan[e2e_right AS r]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 240866.28, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 240866.28, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 240819.20, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 240819.20, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 12.80, + "cost": 240793.60, + "children": [ + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 12.80, + "cost": 240268.80, + "children": [ + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 256.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + } + ] + }, + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + } + ] + }, + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 240866.28, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 14.535, "median": 14.738 } + }, + { + "index": 16, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Join[col(l.k) = col(r.k)]\n Scan[e2e_right AS r]\n Filter[col(c.tier) = lit(1)]\n Join[col(l.group_id) = col(c.group_id)]\n Scan[e2e_left AS l]\n Scan[e2e_class AS c]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 240981.48, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 240981.48, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 240934.40, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 240934.40, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 12.80, + "cost": 240908.80, + "children": [ + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + }, + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 240384.00, + "children": [ + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 128.00, + "cost": 240256.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + }, + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 240981.48, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 26.074, "median": 26.108 } + }, + { + "index": 17, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Join[col(l.k) = col(r.k)]\n Scan[e2e_right AS r]\n Filter[col(c.tier) = lit(1)]\n Join[col(l.group_id) = col(c.group_id)]\n Scan[e2e_class AS c]\n Scan[e2e_left AS l]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 240981.48, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 240981.48, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 240934.40, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 240934.40, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 12.80, + "cost": 240908.80, + "children": [ + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + }, + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 240384.00, + "children": [ + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 128.00, + "cost": 240256.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + }, + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 240981.48, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 21.237, "median": 21.333 } + }, + { + "index": 18, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Join[col(l.k) = col(r.k)]\n Scan[e2e_right AS r]\n Join[col(l.group_id) = col(c.group_id)]\n Scan[e2e_left AS l]\n Filter[col(c.tier) = lit(1)]\n Scan[e2e_class AS c]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 240866.28, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 240866.28, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 240819.20, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 240819.20, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 12.80, + "cost": 240793.60, + "children": [ + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + }, + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 12.80, + "cost": 240268.80, + "children": [ + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + }, + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 256.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 240866.28, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 16.506, "median": 16.541 } + }, + { + "index": 19, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Join[col(l.k) = col(r.k)]\n Scan[e2e_right AS r]\n Join[col(l.group_id) = col(c.group_id)]\n Filter[col(c.tier) = lit(1)]\n Scan[e2e_class AS c]\n Scan[e2e_left AS l]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 240866.28, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 240866.28, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 240819.20, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 240819.20, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 12.80, + "cost": 240793.60, + "children": [ + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + }, + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 12.80, + "cost": 240268.80, + "children": [ + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 256.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + } + ] + }, + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 240866.28, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 15.267, "median": 15.569 } + }, + { + "index": 20, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Join[col(l.k) = col(r.k)]\n Filter[col(c.tier) = lit(1)]\n Join[col(l.group_id) = col(c.group_id)]\n Scan[e2e_left AS l]\n Scan[e2e_class AS c]\n Scan[e2e_right AS r]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 240981.48, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 240981.48, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 240934.40, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 240934.40, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 12.80, + "cost": 240908.80, + "children": [ + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 240384.00, + "children": [ + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 128.00, + "cost": 240256.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + }, + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + } + ] + } + ] + }, + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 240981.48, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 26.842, "median": 26.997 } + }, + { + "index": 21, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Join[col(l.k) = col(r.k)]\n Filter[col(c.tier) = lit(1)]\n Join[col(l.group_id) = col(c.group_id)]\n Scan[e2e_class AS c]\n Scan[e2e_left AS l]\n Scan[e2e_right AS r]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 240981.48, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 240981.48, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 240934.40, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 240934.40, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 12.80, + "cost": 240908.80, + "children": [ + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 240384.00, + "children": [ + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 128.00, + "cost": 240256.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + }, + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + } + ] + } + ] + }, + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 240981.48, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 20.516, "median": 20.581 } + }, + { + "index": 22, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Join[col(l.k) = col(r.k)]\n Join[col(l.group_id) = col(c.group_id)]\n Scan[e2e_left AS l]\n Filter[col(c.tier) = lit(1)]\n Scan[e2e_class AS c]\n Scan[e2e_right AS r]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 240866.28, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 240866.28, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 240819.20, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 240819.20, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 12.80, + "cost": 240793.60, + "children": [ + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 12.80, + "cost": 240268.80, + "children": [ + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + }, + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 256.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + } + ] + } + ] + }, + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 240866.28, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 17.074, "median": 17.142 } + }, + { + "index": 23, + "plan": "Limit[20]\n Sort[col(n) DESC, col(group_id) ASC]\n Project[group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))]\n Aggregate[group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]]\n Join[col(l.k) = col(r.k)]\n Join[col(l.group_id) = col(c.group_id)]\n Filter[col(c.tier) = lit(1)]\n Scan[e2e_class AS c]\n Scan[e2e_left AS l]\n Scan[e2e_right AS r]", + "tree": { + "op": "Limit", + "detail": "20", + "rows": 12.80, + "cost": 240866.28, + "children": [ + { + "op": "Sort", + "detail": "col(n) DESC, col(group_id) ASC", + "rows": 12.80, + "cost": 240866.28, + "children": [ + { + "op": "Project", + "detail": "group_id=col(l.group_id), n=col(COUNT(*)), top=col(MAX(r.measure))", + "rows": 12.80, + "cost": 240819.20, + "children": [ + { + "op": "Aggregate", + "detail": "group_keys=[col(l.group_id)], aggregates=[COUNT(*), MAX(r.measure)=col(r.measure)]", + "rows": 12.80, + "cost": 240819.20, + "children": [ + { + "op": "Join", + "detail": "col(l.k) = col(r.k)", + "rows": 12.80, + "cost": 240793.60, + "children": [ + { + "op": "Join", + "detail": "col(l.group_id) = col(c.group_id)", + "rows": 12.80, + "cost": 240268.80, + "children": [ + { + "op": "Filter", + "detail": "col(c.tier) = lit(1)", + "rows": 12.80, + "cost": 256.00, + "children": [ + { + "op": "Scan", + "detail": "e2e_class AS c", + "rows": 128.00, + "cost": 128.00, + "children": [] + } + ] + }, + { + "op": "Scan", + "detail": "e2e_left AS l", + "rows": 120000.00, + "cost": 120000.00, + "children": [] + } + ] + }, + { + "op": "Scan", + "detail": "e2e_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 240866.28, + "rowCount": 20, + "bagChecksum": "0x8f1c966523a83a50", + "vectorizedMs": { "min": 14.410, "median": 14.423 } + } + ], + "chosen": 12, + "checksum": "0x7e53943623dc6b01", + "interpretedMs": { "min": 1591.984, "median": 1601.294 }, + "hitExpressionBound": false, + "hitPlanBound": false + }, + { + "id": "special", + "sql": "SELECT l.payload AS payload FROM join_left AS l WHERE l.k1 IN (SELECT r.k1 FROM join_right AS r WHERE r.payload = 10000)", + "tables": [ { "name": "join_left", "rows": 100000 }, { "name": "join_right", "rows": 256 } ], + "explain": [ + "EXPLAIN", + "bound logical plan:", + " Project[payload=col(l.payload)]", + " Filter[col(l.k1) IN subquery(IN subquery at position 59)]", + " Subquery[IN subquery at position 59]", + " Project[r.k1=col(r.k1)]", + " Filter[col(r.payload) = lit(10000)]", + " Scan[join_right AS r]", + " Scan[join_left AS l]", + "memo exploration:", + " groups: 7", + " iterations: 1", + " reached_fixpoint: yes", + " fired rules:", + " 0: InToSemiJoinRule", + "chosen plan:", + " Project[payload=col(l.payload)] rows=10000.00 cost=200512.00", + " Filter[col(l.k1) IN subquery(IN subquery at position 59)] rows=10000.00 cost=200512.00", + " Subquery[IN subquery at position 59]", + " Project[r.k1=col(r.k1)] rows=25.60 cost=512.00", + " Filter[col(r.payload) = lit(10000)] rows=25.60 cost=512.00", + " Scan[join_right AS r] rows=256.00 cost=256.00", + " Scan[join_left AS l] rows=100000.00 cost=100000.00", + "total cost: 200512.00" + ], + "memo": { "groups": 7, "iterations": 1, "reachedFixpoint": true, "firedRules": [ + "InToSemiJoinRule" + ] }, + "alternatives": [ + { + "index": 0, + "plan": "Project[payload=col(l.payload)]\n Filter[col(l.k1) IN subquery(IN subquery at position 59)]\n Subquery[IN subquery at position 59]\n Project[r.k1=col(r.k1)]\n Filter[col(r.payload) = lit(10000)]\n Scan[join_right AS r]\n Scan[join_left AS l]", + "tree": { + "op": "Project", + "detail": "payload=col(l.payload)", + "rows": 10000.00, + "cost": 200512.00, + "children": [ + { + "op": "Filter", + "detail": "col(l.k1) IN subquery(IN subquery at position 59)", + "rows": 10000.00, + "cost": 200512.00, + "children": [ + { + "op": "Scan", + "detail": "join_left AS l", + "rows": 100000.00, + "cost": 100000.00, + "children": [] + } + ] + } + ] + }, + "totalCost": 200512.00, + "rowCount": 6250, + "bagChecksum": "0x5b144f1cb20c62ff", + "vectorizedMs": { "min": 3.063, "median": 3.151 } + }, + { + "index": 1, + "plan": "Project[payload=col(l.payload)]\n SemiJoin[col(l.k1) = col(r.k1)]\n Scan[join_left AS l]\n Project[r.k1=col(r.k1)]\n Filter[col(r.payload) = lit(10000)]\n Scan[join_right AS r]", + "tree": { + "op": "Project", + "detail": "payload=col(l.payload)", + "rows": 50000.00, + "cost": 200537.60, + "children": [ + { + "op": "SemiJoin", + "detail": "col(l.k1) = col(r.k1)", + "rows": 50000.00, + "cost": 200537.60, + "children": [ + { + "op": "Scan", + "detail": "join_left AS l", + "rows": 100000.00, + "cost": 100000.00, + "children": [] + }, + { + "op": "Project", + "detail": "r.k1=col(r.k1)", + "rows": 25.60, + "cost": 512.00, + "children": [ + { + "op": "Filter", + "detail": "col(r.payload) = lit(10000)", + "rows": 25.60, + "cost": 512.00, + "children": [ + { + "op": "Scan", + "detail": "join_right AS r", + "rows": 256.00, + "cost": 256.00, + "children": [] + } + ] + } + ] + } + ] + } + ] + }, + "totalCost": 200537.60, + "rowCount": 6250, + "bagChecksum": "0x5b144f1cb20c62ff", + "vectorizedMs": { "min": 5.250, "median": 5.272 } + } + ], + "chosen": 0, + "checksum": "0x72a0d9bc69515c7e", + "interpretedMs": { "min": 8.902, "median": 9.068 }, + "hitExpressionBound": false, + "hitPlanBound": false + } + ], + "hump": [ + { "id": "scan_filter_1pct", "sql": "SELECT a FROM fact WHERE bucket = 7", "predicate": "bucket = 7", "kept": 2000, "of": 200000, "checksum": "0x95ba2cbd0a720591", "interpretedMs": { "min": 17.409, "median": 17.525 }, "vectorizedMs": { "min": 7.904, "median": 7.949 } }, + { "id": "scan_filter_10pct", "sql": "SELECT a FROM fact WHERE decile = 3", "predicate": "decile = 3", "kept": 20000, "of": 200000, "checksum": "0x8031b34dc1b05c00", "interpretedMs": { "min": 18.751, "median": 18.816 }, "vectorizedMs": { "min": 9.067, "median": 9.089 } }, + { "id": "scan_filter_50pct", "sql": "SELECT a FROM fact WHERE half = 1", "predicate": "half = 1", "kept": 100000, "of": 200000, "checksum": "0xd4bb87b7e1354cb7", "interpretedMs": { "min": 25.338, "median": 25.427 }, "vectorizedMs": { "min": 14.235, "median": 14.249 } } + ] +}