未验证 提交 d4e5d5fd 编写于 作者: A Alexander Kuzmenkov 提交者: GitHub

Merge pull request #18455 from ClickHouse/aku/window-prototype

[wip] some window function fixes
......@@ -405,8 +405,8 @@ void QueryFuzzer::fuzz(ASTPtr & ast)
if (fn->is_window_function)
{
fuzzColumnLikeExpressionList(fn->window_partition_by);
fuzzOrderByList(fn->window_order_by);
fuzzColumnLikeExpressionList(fn->window_partition_by.get());
fuzzOrderByList(fn->window_order_by.get());
}
fuzz(fn->children);
......
......@@ -738,15 +738,26 @@ void ActionsMatcher::visit(const ASTFunction & node, const ASTPtr & ast, Data &
if (node.is_window_function)
{
// Also add columns from PARTITION BY and ORDER BY of window functions.
// Requiring a constant reference to a shared pointer to non-const AST
// doesn't really look sane, but the visitor does indeed require it.
if (node.window_partition_by)
{
visit(node.window_partition_by->clone(), data);
visit(node.window_partition_by, data);
}
if (node.window_order_by)
{
visit(node.window_order_by->clone(), data);
visit(node.window_order_by, data);
}
// Also manually add columns for arguments of the window function itself.
// ActionVisitor is written in such a way that this method must itself
// descend into all needed function children. Window functions can't have
// any special functions as argument, so the code below that handles
// special arguments is not needed. This is analogous to the
// appendWindowFunctionsArguments() in SelectQueryExpressionAnalyzer and
// partially duplicates its code. Probably we can remove most of the
// logic from that function, but I don't yet have it all figured out...
for (const auto & arg : node.arguments->children)
{
visit(arg, data);
}
// Don't need to do anything more for window functions here -- the
......
......@@ -970,7 +970,9 @@ void SelectQueryExpressionAnalyzer::appendWindowFunctionsArguments(
ExpressionActionsChain::Step & step = chain.lastStep(aggregated_columns);
// 1) Add actions for window functions and their arguments;
// 2) Mark the columns that are really required.
// 2) Mark the columns that are really required. We have to mark them as
// required because we finish the expression chain before processing the
// window functions.
for (const auto & [_, w] : window_descriptions)
{
for (const auto & f : w.window_functions)
......@@ -981,41 +983,14 @@ void SelectQueryExpressionAnalyzer::appendWindowFunctionsArguments(
getRootActionsNoMakeSet(f.function_node->clone(),
true /* no_subqueries */, step.actions());
// 1.2) result of window function: an empty INPUT.
// It is an aggregate function, so it won't be added by getRootActions.
// This is something of a hack. Other options:
// a] do it like aggregate function -- break the chain of actions
// and manually add window functions to the starting list of
// input columns. Logically this is similar to what we're doing
// now, but would require to split the window function processing
// into a full-fledged step after plain functions. This would be
// somewhat cumbersome. With INPUT hack we can avoid a separate
// step and pretend that window functions are almost "normal"
// select functions. The limitation of both these ways is that
// we can't reference window functions in other SELECT
// expressions.
// b] add a WINDOW action type, then sort, then split the chain on
// each WINDOW action and insert the Window pipeline between the
// Expression pipelines. This is a "proper" way that would allow
// us to depend on window functions in other functions. But it's
// complicated so I avoid doing it for now.
ColumnWithTypeAndName col;
col.type = f.aggregate_function->getReturnType();
col.column = col.type->createColumn();
col.name = f.column_name;
step.actions()->addInput(col);
// 2.1) function arguments;
for (const auto & a : f.function_node->arguments->children)
{
// 2.1) function arguments;
step.required_output.push_back(a->getColumnName());
}
// 2.2) function result;
step.required_output.push_back(f.column_name);
}
// 2.3) PARTITION BY and ORDER BY columns.
// 2.1) PARTITION BY and ORDER BY columns.
for (const auto & c : w.full_sort_description)
{
step.required_output.push_back(c.column_name);
......@@ -1048,6 +1023,15 @@ void SelectQueryExpressionAnalyzer::appendSelect(ExpressionActionsChain & chain,
for (const auto & child : select_query->select()->children)
{
if (const auto * function = typeid_cast<const ASTFunction *>(child.get());
function
&& function->is_window_function)
{
// Skip window function columns here -- they are calculated after
// other SELECT expressions by a special step.
continue;
}
step.required_output.push_back(child->getColumnName());
}
}
......@@ -1421,11 +1405,54 @@ ExpressionAnalysisResult::ExpressionAnalysisResult(
/// If there is aggregation, we execute expressions in SELECT and ORDER BY on the initiating server, otherwise on the source servers.
query_analyzer.appendSelect(chain, only_types || (need_aggregate ? !second_stage : !first_stage));
query_analyzer.appendWindowFunctionsArguments(chain, only_types || !first_stage);
// Window functions are processed in a separate expression chain after
// the main SELECT, similar to what we do for aggregate functions.
if (has_window)
{
query_analyzer.appendWindowFunctionsArguments(chain, only_types || !first_stage);
// Build a list of output columns of the window step.
// 1) We need the columns that are the output of ExpressionActions.
for (const auto & x : chain.getLastActions()->getNamesAndTypesList())
{
query_analyzer.columns_after_window.push_back(x);
}
// 2) We also have to manually add the output of the window function
// to the list of the output columns of the window step, because the
// window functions are not in the ExpressionActions.
for (const auto & [_, w] : query_analyzer.window_descriptions)
{
for (const auto & f : w.window_functions)
{
query_analyzer.columns_after_window.push_back(
{f.column_name, f.aggregate_function->getReturnType()});
}
}
before_window = chain.getLastActions();
finalize_chain(chain);
auto & step = chain.lastStep(query_analyzer.columns_after_window);
// The output of this expression chain is the result of
// SELECT (before "final projection" i.e. renaming the columns), so
// we have to mark the expressions that are required in the output,
// again. We did it for the previous expression chain ("select w/o
// window functions") earlier, in appendSelect(). But that chain also
// produced the expressions required to calculate window functions.
// They are not needed in the final SELECT result. Knowing the correct
// list of columns is important when we apply SELECT DISTINCT later.
const auto * select_query = query_analyzer.getSelectQuery();
for (const auto & child : select_query->select()->children)
{
step.required_output.push_back(child->getColumnName());
}
}
selected_columns = chain.getLastStep().required_output;
has_order_by = query.orderBy() != nullptr;
before_order_and_select = query_analyzer.appendOrderBy(
before_order_by = query_analyzer.appendOrderBy(
chain,
only_types || (need_aggregate ? !second_stage : !first_stage),
optimize_read_in_order,
......@@ -1572,9 +1599,9 @@ std::string ExpressionAnalysisResult::dump() const
ss << "before_window " << before_window->dumpDAG() << "\n";
}
if (before_order_and_select)
if (before_order_by)
{
ss << "before_order_and_select " << before_order_and_select->dumpDAG() << "\n";
ss << "before_order_by " << before_order_by->dumpDAG() << "\n";
}
if (before_limit_by)
......@@ -1587,6 +1614,20 @@ std::string ExpressionAnalysisResult::dump() const
ss << "final_projection " << final_projection->dumpDAG() << "\n";
}
if (!selected_columns.empty())
{
ss << "selected_columns ";
for (size_t i = 0; i < selected_columns.size(); i++)
{
if (i > 0)
{
ss << ", ";
}
ss << backQuote(selected_columns[i]);
}
ss << "\n";
}
return ss.str();
}
......
......@@ -55,6 +55,8 @@ struct ExpressionAnalyzerData
NamesAndTypesList columns_after_join;
/// Columns after ARRAY JOIN, JOIN, and/or aggregation.
NamesAndTypesList aggregated_columns;
/// Columns after window functions.
NamesAndTypesList columns_after_window;
bool has_aggregation = false;
NamesAndTypesList aggregation_keys;
......@@ -202,11 +204,12 @@ struct ExpressionAnalysisResult
ActionsDAGPtr before_aggregation;
ActionsDAGPtr before_having;
ActionsDAGPtr before_window;
ActionsDAGPtr before_order_and_select;
ActionsDAGPtr before_order_by;
ActionsDAGPtr before_limit_by;
ActionsDAGPtr final_projection;
/// Columns from the SELECT list, before renaming them to aliases.
/// Columns from the SELECT list, before renaming them to aliases. Used to
/// perform SELECT DISTINCT.
Names selected_columns;
/// Columns will be removed after prewhere actions execution.
......
......@@ -22,15 +22,22 @@ void ExpressionInfoMatcher::visit(const ASTFunction & ast_function, const ASTPtr
{
data.is_array_join = true;
}
// "is_aggregate_function" doesn't mean much by itself. Apparently here it is
// used to move filters from HAVING to WHERE, and probably for this purpose
// an aggregate function calculated as a window function is not relevant.
// "is_aggregate_function" is used to determine whether we can move a filter
// (1) from HAVING to WHERE or (2) from WHERE of a parent query to HAVING of
// a subquery.
// For aggregate functions we can't do (1) but can do (2).
// For window functions both don't make sense -- they are not allowed in
// WHERE or HAVING.
else if (!ast_function.is_window_function
&& AggregateFunctionFactory::instance().isAggregateFunctionName(
ast_function.name))
{
data.is_aggregate_function = true;
}
else if (ast_function.is_window_function)
{
data.is_window_function = true;
}
else
{
const auto & function = FunctionFactory::instance().tryGet(ast_function.name, data.context);
......@@ -75,15 +82,26 @@ bool ExpressionInfoMatcher::needChildVisit(const ASTPtr & node, const ASTPtr &)
return !node->as<ASTSubquery>();
}
bool hasStatefulFunction(const ASTPtr & node, const Context & context)
bool hasNonRewritableFunction(const ASTPtr & node, const Context & context)
{
for (const auto & select_expression : node->children)
{
ExpressionInfoVisitor::Data expression_info{.context = context, .tables = {}};
ExpressionInfoVisitor(expression_info).visit(select_expression);
if (expression_info.is_stateful_function)
if (expression_info.is_stateful_function
|| expression_info.is_window_function)
{
// If an outer query has a WHERE on window function, we can't move
// it into the subquery, because window functions are not allowed in
// WHERE and HAVING. Example:
// select * from (
// select number,
// count(*) over (partition by intDiv(number, 3)) c
// from numbers(3)
// ) where c > 1;
return true;
}
}
return false;
......
......@@ -21,6 +21,7 @@ struct ExpressionInfoMatcher
bool is_array_join = false;
bool is_stateful_function = false;
bool is_aggregate_function = false;
bool is_window_function = false;
bool is_deterministic_function = true;
std::unordered_set<size_t> unique_reference_tables_pos = {};
};
......@@ -36,6 +37,6 @@ struct ExpressionInfoMatcher
using ExpressionInfoVisitor = ConstInDepthNodeVisitor<ExpressionInfoMatcher, true>;
bool hasStatefulFunction(const ASTPtr & node, const Context & context);
bool hasNonRewritableFunction(const ASTPtr & node, const Context & context);
}
......@@ -33,11 +33,14 @@ public:
return false;
if (auto * func = node->as<ASTFunction>())
{
if (isAggregateFunction(*func)
|| func->is_window_function)
if (isAggregateFunction(*func))
{
return false;
}
// Window functions can contain aggregation results as arguments
// to the window functions, or columns of PARTITION BY or ORDER BY
// of the window.
}
return true;
}
......
......@@ -538,7 +538,10 @@ Block InterpreterSelectQuery::getSampleBlockImpl()
if (options.to_stage == QueryProcessingStage::Enum::WithMergeableState)
{
if (!analysis_result.need_aggregate)
return analysis_result.before_order_and_select->getResultColumns();
{
// What's the difference with selected_columns?
return analysis_result.before_order_by->getResultColumns();
}
Block header = analysis_result.before_aggregation->getResultColumns();
......@@ -564,7 +567,8 @@ Block InterpreterSelectQuery::getSampleBlockImpl()
if (options.to_stage == QueryProcessingStage::Enum::WithMergeableStateAfterAggregation)
{
return analysis_result.before_order_and_select->getResultColumns();
// What's the difference with selected_columns?
return analysis_result.before_order_by->getResultColumns();
}
return analysis_result.final_projection->getResultColumns();
......@@ -958,8 +962,9 @@ void InterpreterSelectQuery::executeImpl(QueryPlan & query_plan, const BlockInpu
}
else
{
executeExpression(query_plan, expressions.before_order_and_select, "Before ORDER BY and SELECT");
executeExpression(query_plan, expressions.before_window, "Before window functions");
executeWindow(query_plan);
executeExpression(query_plan, expressions.before_order_by, "Before ORDER BY");
executeDistinct(query_plan, true, expressions.selected_columns, true);
}
......@@ -1005,8 +1010,10 @@ void InterpreterSelectQuery::executeImpl(QueryPlan & query_plan, const BlockInpu
else if (expressions.hasHaving())
executeHaving(query_plan, expressions.before_having);
executeExpression(query_plan, expressions.before_order_and_select, "Before ORDER BY and SELECT");
executeExpression(query_plan, expressions.before_window,
"Before window functions");
executeWindow(query_plan);
executeExpression(query_plan, expressions.before_order_by, "Before ORDER BY");
executeDistinct(query_plan, true, expressions.selected_columns, true);
}
......@@ -1758,6 +1765,11 @@ void InterpreterSelectQuery::executeRollupOrCube(QueryPlan & query_plan, Modific
void InterpreterSelectQuery::executeExpression(QueryPlan & query_plan, const ActionsDAGPtr & expression, const std::string & description)
{
if (!expression)
{
return;
}
auto expression_step = std::make_unique<ExpressionStep>(query_plan.getCurrentDataStream(), expression);
expression_step->setStepDescription(description);
......
......@@ -90,8 +90,12 @@ std::vector<ASTs> PredicateExpressionsOptimizer::extractTablesPredicates(const A
ExpressionInfoVisitor::Data expression_info{.context = context, .tables = tables_with_columns};
ExpressionInfoVisitor(expression_info).visit(predicate_expression);
if (expression_info.is_stateful_function || !expression_info.is_deterministic_function)
return {}; /// Not optimized when predicate contains stateful function or indeterministic function
if (expression_info.is_stateful_function
|| !expression_info.is_deterministic_function
|| expression_info.is_window_function)
{
return {}; /// Not optimized when predicate contains stateful function or indeterministic function or window functions
}
if (!expression_info.is_array_join)
{
......@@ -190,6 +194,12 @@ bool PredicateExpressionsOptimizer::tryMovePredicatesFromHavingToWhere(ASTSelect
if (expression_info.is_stateful_function)
return false;
if (expression_info.is_window_function)
{
// Window functions are not allowed in either HAVING or WHERE.
return false;
}
if (expression_info.is_aggregate_function)
having_predicates.emplace_back(moving_predicate);
else
......
......@@ -88,7 +88,7 @@ bool PredicateRewriteVisitorData::rewriteSubquery(ASTSelectQuery & subquery, con
|| (!optimize_with && subquery.with())
|| subquery.withFill()
|| subquery.limitBy() || subquery.limitLength()
|| hasStatefulFunction(subquery.select(), context))
|| hasNonRewritableFunction(subquery.select(), context))
return false;
for (const auto & predicate : predicates)
......
......@@ -148,9 +148,9 @@ void QueryNormalizer::visit(ASTSelectQuery & select, const ASTPtr &, Data & data
/// Don't go into select query. It processes children itself.
/// Do not go to the left argument of lambda expressions, so as not to replace the formal parameters
/// on aliases in expressions of the form 123 AS x, arrayMap(x -> 1, [2]).
void QueryNormalizer::visitChildren(const ASTPtr & node, Data & data)
void QueryNormalizer::visitChildren(IAST * node, Data & data)
{
if (const auto * func_node = node->as<ASTFunction>())
if (auto * func_node = node->as<ASTFunction>())
{
if (func_node->tryGetQueryArgument())
{
......@@ -176,6 +176,16 @@ void QueryNormalizer::visitChildren(const ASTPtr & node, Data & data)
visit(child, data);
}
}
if (func_node->window_partition_by)
{
visitChildren(func_node->window_partition_by.get(), data);
}
if (func_node->window_order_by)
{
visitChildren(func_node->window_order_by.get(), data);
}
}
else if (!node->as<ASTSelectQuery>())
{
......@@ -221,7 +231,7 @@ void QueryNormalizer::visit(ASTPtr & ast, Data & data)
if (ast.get() != initial_ast.get())
visit(ast, data);
else
visitChildren(ast, data);
visitChildren(ast.get(), data);
current_asts.erase(initial_ast.get());
current_asts.erase(ast.get());
......
......@@ -69,7 +69,7 @@ private:
static void visit(ASTTablesInSelectQueryElement &, const ASTPtr &, Data &);
static void visit(ASTSelectQuery &, const ASTPtr &, Data &);
static void visitChildren(const ASTPtr &, Data & data);
static void visitChildren(IAST * node, Data & data);
};
}
......@@ -29,6 +29,7 @@
#include <DataTypes/DataTypeNullable.h>
#include <IO/WriteHelpers.h>
#include <IO/WriteBufferFromOStream.h>
#include <Storages/IStorage.h>
#include <AggregateFunctions/AggregateFunctionFactory.h>
......@@ -445,6 +446,8 @@ std::vector<const ASTFunction *> getAggregates(ASTPtr & query, const ASTSelectQu
for (auto & arg : node->arguments->children)
{
assertNoAggregates(arg, "inside another aggregate function");
// We also can't have window functions inside aggregate functions,
// because the window functions are calculated later.
assertNoWindows(arg, "inside an aggregate function");
}
}
......@@ -454,7 +457,9 @@ std::vector<const ASTFunction *> getAggregates(ASTPtr & query, const ASTSelectQu
std::vector<const ASTFunction *> getWindowFunctions(ASTPtr & query, const ASTSelectQuery & select_query)
{
/// There can not be window functions inside the WHERE and PREWHERE.
/// There can not be window functions inside the WHERE, PREWHERE and HAVING
if (select_query.having())
assertNoWindows(select_query.having(), "in HAVING");
if (select_query.where())
assertNoWindows(select_query.where(), "in WHERE");
if (select_query.prewhere())
......@@ -463,17 +468,34 @@ std::vector<const ASTFunction *> getWindowFunctions(ASTPtr & query, const ASTSel
GetAggregatesVisitor::Data data;
GetAggregatesVisitor(data).visit(query);
/// There can not be other window functions within the aggregate functions.
/// Window functions cannot be inside aggregates or other window functions.
/// Aggregate functions can be inside window functions because they are
/// calculated earlier.
for (const ASTFunction * node : data.window_functions)
{
if (node->arguments)
{
for (auto & arg : node->arguments->children)
{
assertNoAggregates(arg, "inside a window function");
assertNoWindows(arg, "inside another window function");
}
}
if (node->window_partition_by)
{
for (auto & arg : node->window_partition_by->children)
{
assertNoWindows(arg, "inside PARTITION BY of a window");
}
}
if (node->window_order_by)
{
for (auto & arg : node->window_order_by->children)
{
assertNoWindows(arg, "inside ORDER BY of a window");
}
}
}
return data.window_functions;
......
......@@ -39,6 +39,16 @@ void ASTFunction::appendColumnNameImpl(WriteBuffer & ostr) const
(*it)->appendColumnName(ostr);
}
writeChar(')', ostr);
if (is_window_function)
{
writeCString(" OVER (", ostr);
FormatSettings settings{ostr, true /* one_line */};
FormatState state;
FormatStateStacked frame;
appendWindowDescription(settings, state, frame);
writeCString(")", ostr);
}
}
/** Get the text that identifies this element. */
......@@ -57,17 +67,20 @@ ASTPtr ASTFunction::clone() const
if (window_name)
{
res->set(res->window_name, window_name->clone());
res->window_name = window_name->clone();
res->children.push_back(res->window_name);
}
if (window_partition_by)
{
res->set(res->window_partition_by, window_partition_by->clone());
res->window_partition_by = window_partition_by->clone();
res->children.push_back(res->window_partition_by);
}
if (window_order_by)
{
res->set(res->window_order_by, window_order_by->clone());
res->window_order_by = window_order_by->clone();
res->children.push_back(res->window_order_by);
}
return res;
......
......@@ -21,9 +21,25 @@ public:
ASTPtr parameters;
bool is_window_function = false;
ASTIdentifier * window_name;
ASTExpressionList * window_partition_by;
ASTExpressionList * window_order_by;
// We have to make these fields ASTPtr because this is what the visitors
// expect. Some of them take const ASTPtr & (makes no sense), and some
// take ASTPtr & and modify it. I don't understand how the latter is
// compatible with also having an owning `children` array -- apparently it
// leads to some dangling children that are not referenced by the fields of
// the AST class itself. Some older code hints at the idea of having
// ownership in `children` only, and making the class fields to be raw
// pointers of proper type (see e.g. IAST::set), but this is not compatible
// with the visitor interface.
// ASTIdentifier
ASTPtr window_name;
// ASTExpressionList
ASTPtr window_partition_by;
// ASTExpressionList of
ASTPtr window_order_by;
/// do not print empty parentheses if there are no args - compatibility with new AST for data types and engine names.
bool no_empty_args = false;
......
......@@ -419,7 +419,8 @@ bool ParserWindowDefinition::parseImpl(Pos & pos, ASTPtr & node, Expected & expe
ParserIdentifier window_name_parser;
if (window_name_parser.parse(pos, window_name_ast, expected))
{
function->set(function->window_name, window_name_ast);
function->children.push_back(window_name_ast);
function->window_name = window_name_ast;
return true;
}
else
......@@ -442,7 +443,8 @@ bool ParserWindowDefinition::parseImpl(Pos & pos, ASTPtr & node, Expected & expe
ASTPtr partition_by_ast;
if (columns_partition_by.parse(pos, partition_by_ast, expected))
{
function->set(function->window_partition_by, partition_by_ast);
function->children.push_back(partition_by_ast);
function->window_partition_by = partition_by_ast;
}
else
{
......@@ -455,7 +457,8 @@ bool ParserWindowDefinition::parseImpl(Pos & pos, ASTPtr & node, Expected & expe
ASTPtr order_by_ast;
if (columns_order_by.parse(pos, order_by_ast, expected))
{
function->set(function->window_order_by, order_by_ast);
function->children.push_back(order_by_ast);
function->window_order_by = order_by_ast;
}
else
{
......
......@@ -46,6 +46,8 @@ static void doDescribeHeader(const Block & header, size_t count, IQueryPlanStep:
first = false;
elem.dumpNameAndType(settings.out);
settings.out << ": ";
elem.dumpStructure(settings.out);
settings.out << '\n';
}
}
......
......@@ -247,6 +247,15 @@ static void explainStep(
step.describeActions(settings);
}
std::string debugExplainStep(const IQueryPlanStep & step)
{
WriteBufferFromOwnString out;
IQueryPlanStep::FormatSettings settings{.out = out};
QueryPlan::ExplainPlanOptions options{.actions = true};
explainStep(step, settings, options);
return out.str();
}
void QueryPlan::explainPlan(WriteBuffer & buffer, const ExplainPlanOptions & options)
{
checkInitialized();
......@@ -488,6 +497,7 @@ static bool tryMergeExpressions(QueryPlan::Node * parent_node, QueryPlan::Node *
{
auto & parent = parent_node->step;
auto & child = child_node->step;
/// TODO: FilterStep
auto * parent_expr = typeid_cast<ExpressionStep *>(parent.get());
auto * child_expr = typeid_cast<ExpressionStep *>(child.get());
......
......@@ -97,4 +97,6 @@ private:
std::vector<std::shared_ptr<Context>> interpreter_context;
};
std::string debugExplainStep(const IQueryPlanStep & step);
}
......@@ -77,6 +77,11 @@ void WindowTransform::transform(Chunk & chunk)
ws.argument_columns.clear();
for (const auto column_index : ws.argument_column_indices)
{
// Aggregate functions can't work with constant columns, so we have to
// materialize them like the Aggregator does.
columns[column_index]
= std::move(columns[column_index])->convertToFullColumnIfConst();
ws.argument_columns.push_back(columns[column_index].get());
}
......
<test>
<preconditions>
<table_exists>hits_100m_single</table_exists>
</preconditions>
<settings>
<allow_experimental_window_functions>1</allow_experimental_window_functions>
</settings>
<!--
For some counters, find top 10 users by the numer of records.
First with LIMIT BY, next with window functions.
-->
<query><![CDATA[
select CounterID, UserID, count(*) user_hits
from hits_100m_single
where CounterID < 10000
group by CounterID, UserID
order by user_hits desc
limit 10 by CounterID
format Null
]]></query>
<query><![CDATA[
select *
from (
select CounterID, UserID, count(*) user_hits,
count() over (partition by CounterID order by user_hits desc)
user_rank
from hits_100m_single
where CounterID < 10000
group by CounterID, UserID
)
where user_rank <= 10
format Null
]]></query>
</test>
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
Header: x UInt8
SettingQuotaAndLimits (Set limits and quota after reading from storage)
Header: dummy UInt8
......
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Distinct
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Distinct
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Distinct
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Distinct
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Distinct
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Distinct
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Distinct
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Distinct
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Distinct
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Distinct
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Distinct
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Distinct
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Distinct
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Union
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
Expression (Projection + Before ORDER BY and SELECT)
Expression (Projection + Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (SystemOne)
......@@ -9,7 +9,7 @@ Expression (Projection)
MergingSorted (Merge sorted streams for ORDER BY)
MergeSorting (Merge sorted blocks for ORDER BY)
PartialSorting (Sort each block for ORDER BY)
Expression (Before ORDER BY and SELECT)
Expression (Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (MergeTree)
SELECT
......@@ -21,7 +21,7 @@ LIMIT 10
Expression (Projection)
Limit (preliminary LIMIT)
FinishSorting
Expression (Before ORDER BY and SELECT)
Expression (Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (MergeTree with order)
SELECT
......@@ -35,7 +35,7 @@ LIMIT 10
Expression (Projection)
Limit (preliminary LIMIT)
FinishSorting
Expression (Before ORDER BY and SELECT)
Expression (Before ORDER BY)
SettingQuotaAndLimits (Set limits and quota after reading from storage)
ReadFromStorage (MergeTree with order)
SELECT
......
......@@ -79,17 +79,18 @@ select number, quantileExact(number) over (partition by intDiv(number, 3)) q fro
9 9
select q * 10, quantileExact(number) over (partition by intDiv(number, 3)) q from numbers(10); -- { serverError 47 }
-- should work in ORDER BY though
-- must work in WHERE if you wrap it in a subquery
select number, max(number) over (partition by intDiv(number, 3) order by number desc) m from numbers(10) order by m desc, number;
select * from (select count(*) over () c from numbers(3)) where c > 0;
-- this one doesn't work yet -- looks like the column names clash, and the
-- window count() is overwritten with aggregate count()
-- select number, count(), count() over (partition by intDiv(number, 3)) from numbers(10) group by number order by count() desc;
-- should work in ORDER BY
-- different windows
-- an explain test would also be helpful, but it's too immature now and I don't
-- want to change reference all the time
1
2
3
select number, max(number) over (partition by intDiv(number, 3) order by number desc) m from numbers(10) order by m desc, number;
-- also works in ORDER BY if you wrap it in a subquery
9 9
6 8
......@@ -101,6 +102,37 @@ select number, max(number) over (partition by intDiv(number, 3) order by number
0 2
1 2
2 2
select * from (select count(*) over () c from numbers(3)) order by c;
-- Example with window function only in ORDER BY. Here we make a rank of all
-- numbers sorted descending, and then sort by this rank descending, and must get
-- the ascending order.
1
2
3
select * from (select * from numbers(5) order by rand()) order by count() over (order by number desc) desc;
-- Aggregate functions as window function arguments. This query is semantically
-- the same as the above one, only we replace `number` with
-- `any(number) group by number` and so on.
0
1
2
3
4
select * from (select * from numbers(5) order by rand()) group by number order by sum(any(number + 1)) over (order by min(number) desc) desc;
-- different windows
-- an explain test would also be helpful, but it's too immature now and I don't
-- want to change reference all the time
0
1
2
3
4
select number, max(number) over (partition by intDiv(number, 3) order by number desc), count(number) over (partition by intDiv(number, 5) order by number) as m from numbers(31) order by number settings max_block_size = 2;
-- two functions over the same window
......@@ -140,6 +172,8 @@ select number, max(number) over (partition by intDiv(number, 3) order by number
30 30 1
select number, max(number) over (partition by intDiv(number, 3) order by number desc), count(number) over (partition by intDiv(number, 3) order by number desc) as m from numbers(7) order by number settings max_block_size = 2;
-- check that we can work with constant columns
0 2 3
1 2 2
2 2 1
......@@ -147,3 +181,39 @@ select number, max(number) over (partition by intDiv(number, 3) order by number
4 5 2
5 5 1
6 6 1
select median(x) over (partition by x) from (select 1 x);
-- an empty window definition is valid as well
1
select groupArray(number) over () from numbers(3);
-- This one tests we properly process the window function arguments.
-- Seen errors like 'column `1` not found' from count(1).
[0]
[0,1]
[0,1,2]
select count(1) over (), max(number + 1) over () from numbers(3);
-- Should work in DISTINCT
1 3
select distinct sum(0) over () from numbers(2);
0
select distinct any(number) over () from numbers(2);
-- Various kinds of aliases are properly substituted into various parts of window
-- function definition.
0
with number + 1 as x select intDiv(number, 3) as y, sum(x + y) over (partition by y order by x) from numbers(7);
0 1
0 3
0 6
1 5
1 11
1 18
2 9
......@@ -24,12 +24,24 @@ select number, quantileExact(number) over (partition by intDiv(number, 3)) q fro
-- last stage of select, after all other functions.
select q * 10, quantileExact(number) over (partition by intDiv(number, 3)) q from numbers(10); -- { serverError 47 }
-- should work in ORDER BY though
-- must work in WHERE if you wrap it in a subquery
select * from (select count(*) over () c from numbers(3)) where c > 0;
-- should work in ORDER BY
select number, max(number) over (partition by intDiv(number, 3) order by number desc) m from numbers(10) order by m desc, number;
-- this one doesn't work yet -- looks like the column names clash, and the
-- window count() is overwritten with aggregate count()
-- select number, count(), count() over (partition by intDiv(number, 3)) from numbers(10) group by number order by count() desc;
-- also works in ORDER BY if you wrap it in a subquery
select * from (select count(*) over () c from numbers(3)) order by c;
-- Example with window function only in ORDER BY. Here we make a rank of all
-- numbers sorted descending, and then sort by this rank descending, and must get
-- the ascending order.
select * from (select * from numbers(5) order by rand()) order by count() over (order by number desc) desc;
-- Aggregate functions as window function arguments. This query is semantically
-- the same as the above one, only we replace `number` with
-- `any(number) group by number` and so on.
select * from (select * from numbers(5) order by rand()) group by number order by sum(any(number + 1)) over (order by min(number) desc) desc;
-- different windows
-- an explain test would also be helpful, but it's too immature now and I don't
......@@ -40,3 +52,21 @@ select number, max(number) over (partition by intDiv(number, 3) order by number
-- an explain test would also be helpful, but it's too immature now and I don't
-- want to change reference all the time
select number, max(number) over (partition by intDiv(number, 3) order by number desc), count(number) over (partition by intDiv(number, 3) order by number desc) as m from numbers(7) order by number settings max_block_size = 2;
-- check that we can work with constant columns
select median(x) over (partition by x) from (select 1 x);
-- an empty window definition is valid as well
select groupArray(number) over () from numbers(3);
-- This one tests we properly process the window function arguments.
-- Seen errors like 'column `1` not found' from count(1).
select count(1) over (), max(number + 1) over () from numbers(3);
-- Should work in DISTINCT
select distinct sum(0) over () from numbers(2);
select distinct any(number) over () from numbers(2);
-- Various kinds of aliases are properly substituted into various parts of window
-- function definition.
with number + 1 as x select intDiv(number, 3) as y, sum(x + y) over (partition by y order by x) from numbers(7);
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册