```sql

CREATE MATERIALIZED VIEW player_champ_counts_mv
ENGINE = AggregatingMergeTree() ORDER BY (patch_num, my_account_id, champ_id)
as select patch_num, my_account_id, champ_id, countState() as c_state
from full_info
group by patch_num, my_account_id, champ_id;

create view player_champ_counts as
select patch_num, my_account_id, champ_id, countMerge(c_state) as c
from player_champ_counts_mv
group by patch_num, my_account_id, champ_id;
```

2.4s:
`select * from player_champ_counts`

900ms:
```
select patch_num, my_account_id, champ_id, countMerge(c_state) as c
from player_champ_counts_mv
group by patch_num, my_account_id, champ_id;
```

In other words, just inlining the definition of the view is doing something different than querying the view itself.
while (!stack.empty())
    {
        auto & frame = stack.top();
        if (last_pipeline)
        {
            frame.pipelines.emplace_back(std::move(last_pipeline));
            last_pipeline = nullptr;
        }
        size_t next_child = frame.pipelines.size();
        if (next_child == frame.node->children.size())
        {
            bool limit_max_threads = frame.pipelines.empty();
            last_pipeline = frame.node->step->updatePipeline(std::move(frame.pipelines));
            if (limit_max_threads)
                last_pipeline->setMaxThreads(max_threads);
            stack.pop();
        }
        else
            stack.push(Frame{.node = frame.node->children[next_child]});
    }
    for (auto & context : interpreter_context)
        last_pipeline->addInterpreterContext(std::move(context));
    return last_pipeline;
}
void QueryPlan::addInterpreterContext(std::shared_ptr<Context> context)
{
    interpreter_context.emplace_back(std::move(context));
}
}