mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-27 01:51:59 +00:00
Merge pull request #62619 from kitaisreal/analyzer-support-qualify-clause
Analyzer support QUALIFY clause
This commit is contained in:
commit
d731838246
@ -21,6 +21,8 @@ SELECT [DISTINCT [ON (column1, column2, ...)]] expr_list
|
||||
[WHERE expr]
|
||||
[GROUP BY expr_list] [WITH ROLLUP|WITH CUBE] [WITH TOTALS]
|
||||
[HAVING expr]
|
||||
[WINDOW window_expr_list]
|
||||
[QUALIFY expr]
|
||||
[ORDER BY expr_list] [WITH FILL] [FROM expr] [TO expr] [STEP expr] [INTERPOLATE [(expr_list)]]
|
||||
[LIMIT [offset_value, ]n BY columns]
|
||||
[LIMIT [n, ]m] [WITH TIES]
|
||||
@ -45,6 +47,7 @@ Specifics of each optional clause are covered in separate sections, which are li
|
||||
- [GROUP BY clause](../../../sql-reference/statements/select/group-by.md)
|
||||
- [LIMIT BY clause](../../../sql-reference/statements/select/limit-by.md)
|
||||
- [HAVING clause](../../../sql-reference/statements/select/having.md)
|
||||
- [QUALIFY clause](../../../sql-reference/statements/select/qualify.md)
|
||||
- [LIMIT clause](../../../sql-reference/statements/select/limit.md)
|
||||
- [OFFSET clause](../../../sql-reference/statements/select/offset.md)
|
||||
- [UNION clause](../../../sql-reference/statements/select/union.md)
|
||||
|
34
docs/en/sql-reference/statements/select/qualify.md
Normal file
34
docs/en/sql-reference/statements/select/qualify.md
Normal file
@ -0,0 +1,34 @@
|
||||
---
|
||||
slug: /en/sql-reference/statements/select/qualify
|
||||
sidebar_label: QUALIFY
|
||||
---
|
||||
|
||||
# QUALIFY Clause
|
||||
|
||||
Allows filtering window functions results. It is similar to the [WHERE](../../../sql-reference/statements/select/where.md) clause, but the difference is that `WHERE` is performed before window functions evaluation, while `QUALIFY` is performed after it.
|
||||
|
||||
It is possible to reference window functions results from `SELECT` clause in `QUALIFY` clause by their alias. Alternatively, `QUALIFY` clause can filter on results of additional window functions that are not returned in query results.
|
||||
|
||||
## Limitations
|
||||
|
||||
`QUALIFY` can’t be used if there are no window functions to evaluate. Use `WHERE` instead.
|
||||
|
||||
## Examples
|
||||
|
||||
Example:
|
||||
|
||||
``` sql
|
||||
SELECT number, COUNT() OVER (PARTITION BY number % 3) AS partition_count
|
||||
FROM numbers(10)
|
||||
QUALIFY partition_count = 4
|
||||
ORDER BY number;
|
||||
```
|
||||
|
||||
``` text
|
||||
┌─number─┬─partition_count─┐
|
||||
│ 0 │ 4 │
|
||||
│ 3 │ 4 │
|
||||
│ 6 │ 4 │
|
||||
│ 9 │ 4 │
|
||||
└────────┴─────────────────┘
|
||||
```
|
@ -7895,6 +7895,9 @@ void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, Identifier
|
||||
if (query_node_typed.hasHaving() && query_node_typed.isGroupByWithTotals() && is_rollup_or_cube)
|
||||
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "WITH TOTALS and WITH ROLLUP or CUBE are not supported together in presence of HAVING");
|
||||
|
||||
if (query_node_typed.hasQualify() && query_node_typed.isGroupByWithTotals() && is_rollup_or_cube)
|
||||
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "WITH TOTALS and WITH ROLLUP or CUBE are not supported together in presence of QUALIFY");
|
||||
|
||||
/// Initialize aliases in query node scope
|
||||
QueryExpressionsAliasVisitor visitor(scope);
|
||||
|
||||
@ -7919,6 +7922,9 @@ void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, Identifier
|
||||
if (query_node_typed.hasWindow())
|
||||
visitor.visit(query_node_typed.getWindowNode());
|
||||
|
||||
if (query_node_typed.hasQualify())
|
||||
visitor.visit(query_node_typed.getQualify());
|
||||
|
||||
if (query_node_typed.hasOrderBy())
|
||||
visitor.visit(query_node_typed.getOrderByNode());
|
||||
|
||||
@ -8067,6 +8073,9 @@ void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, Identifier
|
||||
if (query_node_typed.hasWindow())
|
||||
resolveWindowNodeList(query_node_typed.getWindowNode(), scope);
|
||||
|
||||
if (query_node_typed.hasQualify())
|
||||
resolveExpressionNode(query_node_typed.getQualify(), scope, false /*allow_lambda_expression*/, false /*allow_table_expression*/);
|
||||
|
||||
if (query_node_typed.hasOrderBy())
|
||||
{
|
||||
replaceNodesWithPositionalArguments(query_node_typed.getOrderByNode(), query_node_typed.getProjection().getNodes(), scope);
|
||||
|
@ -197,6 +197,12 @@ void QueryNode::dumpTreeImpl(WriteBuffer & buffer, FormatState & format_state, s
|
||||
getWindow().dumpTreeImpl(buffer, format_state, indent + 4);
|
||||
}
|
||||
|
||||
if (hasQualify())
|
||||
{
|
||||
buffer << '\n' << std::string(indent + 2, ' ') << "QUALIFY\n";
|
||||
getQualify()->dumpTreeImpl(buffer, format_state, indent + 4);
|
||||
}
|
||||
|
||||
if (hasOrderBy())
|
||||
{
|
||||
buffer << '\n' << std::string(indent + 2, ' ') << "ORDER BY\n";
|
||||
@ -381,6 +387,9 @@ ASTPtr QueryNode::toASTImpl(const ConvertToASTOptions & options) const
|
||||
if (hasWindow())
|
||||
select_query->setExpression(ASTSelectQuery::Expression::WINDOW, getWindow().toAST(options));
|
||||
|
||||
if (hasQualify())
|
||||
select_query->setExpression(ASTSelectQuery::Expression::QUALIFY, getQualify()->toAST(options));
|
||||
|
||||
if (hasOrderBy())
|
||||
select_query->setExpression(ASTSelectQuery::Expression::ORDER_BY, getOrderBy().toAST(options));
|
||||
|
||||
|
@ -416,6 +416,24 @@ public:
|
||||
return children[window_child_index];
|
||||
}
|
||||
|
||||
/// Returns true if query node QUALIFY section is not empty, false otherwise
|
||||
bool hasQualify() const
|
||||
{
|
||||
return getQualify() != nullptr;
|
||||
}
|
||||
|
||||
/// Get QUALIFY section node
|
||||
const QueryTreeNodePtr & getQualify() const
|
||||
{
|
||||
return children[qualify_child_index];
|
||||
}
|
||||
|
||||
/// Get QUALIFY section node
|
||||
QueryTreeNodePtr & getQualify()
|
||||
{
|
||||
return children[qualify_child_index];
|
||||
}
|
||||
|
||||
/// Returns true if query node ORDER BY section is not empty, false otherwise
|
||||
bool hasOrderBy() const
|
||||
{
|
||||
@ -622,13 +640,14 @@ private:
|
||||
static constexpr size_t group_by_child_index = 5;
|
||||
static constexpr size_t having_child_index = 6;
|
||||
static constexpr size_t window_child_index = 7;
|
||||
static constexpr size_t order_by_child_index = 8;
|
||||
static constexpr size_t interpolate_child_index = 9;
|
||||
static constexpr size_t limit_by_limit_child_index = 10;
|
||||
static constexpr size_t limit_by_offset_child_index = 11;
|
||||
static constexpr size_t limit_by_child_index = 12;
|
||||
static constexpr size_t limit_child_index = 13;
|
||||
static constexpr size_t offset_child_index = 14;
|
||||
static constexpr size_t qualify_child_index = 8;
|
||||
static constexpr size_t order_by_child_index = 9;
|
||||
static constexpr size_t interpolate_child_index = 10;
|
||||
static constexpr size_t limit_by_limit_child_index = 11;
|
||||
static constexpr size_t limit_by_offset_child_index = 12;
|
||||
static constexpr size_t limit_by_child_index = 13;
|
||||
static constexpr size_t limit_child_index = 14;
|
||||
static constexpr size_t offset_child_index = 15;
|
||||
static constexpr size_t children_size = offset_child_index + 1;
|
||||
};
|
||||
|
||||
|
@ -330,6 +330,10 @@ QueryTreeNodePtr QueryTreeBuilder::buildSelectExpression(const ASTPtr & select_q
|
||||
if (window_list)
|
||||
current_query_tree->getWindowNode() = buildWindowList(window_list, current_context);
|
||||
|
||||
auto qualify_expression = select_query_typed.qualify();
|
||||
if (qualify_expression)
|
||||
current_query_tree->getQualify() = buildExpression(qualify_expression, current_context);
|
||||
|
||||
auto select_order_by_list = select_query_typed.orderBy();
|
||||
if (select_order_by_list)
|
||||
current_query_tree->getOrderByNode() = buildSortList(select_order_by_list, current_context);
|
||||
|
@ -56,6 +56,9 @@ void validateFilters(const QueryTreeNodePtr & query_node)
|
||||
|
||||
if (query_node_typed.hasHaving())
|
||||
validateFilter(query_node_typed.getHaving(), "HAVING", query_node);
|
||||
|
||||
if (query_node_typed.hasQualify())
|
||||
validateFilter(query_node_typed.getQualify(), "QUALIFY", query_node);
|
||||
}
|
||||
|
||||
namespace
|
||||
|
@ -47,7 +47,7 @@ Suggest::Suggest()
|
||||
"GRANT", "REVOKE", "OPTION", "ADMIN", "EXCEPT", "REPLACE", "IDENTIFIED", "HOST",
|
||||
"NAME", "READONLY", "WRITABLE", "PERMISSIVE", "FOR", "RESTRICTIVE", "RANDOMIZED", "INTERVAL",
|
||||
"LIMITS", "ONLY", "TRACKING", "IP", "REGEXP", "ILIKE", "CLEANUP", "APPEND",
|
||||
"IGNORE NULLS", "RESPECT NULLS", "OVER", "PASTE"});
|
||||
"IGNORE NULLS", "RESPECT NULLS", "OVER", "PASTE", "WINDOW", "QUALIFY"});
|
||||
}
|
||||
|
||||
static String getLoadSuggestionQuery(Int32 suggestion_limit, bool basic_suggestion)
|
||||
|
@ -144,6 +144,12 @@ void ASTSelectQuery::formatImpl(const FormatSettings & s, FormatState & state, F
|
||||
window()->as<ASTExpressionList &>().formatImplMultiline(s, state, frame);
|
||||
}
|
||||
|
||||
if (qualify())
|
||||
{
|
||||
s.ostr << (s.hilite ? hilite_keyword : "") << s.nl_or_ws << indent_str << "QUALIFY " << (s.hilite ? hilite_none : "");
|
||||
qualify()->formatImpl(s, state, frame);
|
||||
}
|
||||
|
||||
if (!order_by_all && orderBy())
|
||||
{
|
||||
s.ostr << (s.hilite ? hilite_keyword : "") << s.nl_or_ws << indent_str << "ORDER BY" << (s.hilite ? hilite_none : "");
|
||||
|
@ -25,6 +25,7 @@ public:
|
||||
GROUP_BY,
|
||||
HAVING,
|
||||
WINDOW,
|
||||
QUALIFY,
|
||||
ORDER_BY,
|
||||
LIMIT_BY_OFFSET,
|
||||
LIMIT_BY_LENGTH,
|
||||
@ -55,6 +56,8 @@ public:
|
||||
return "HAVING";
|
||||
case Expression::WINDOW:
|
||||
return "WINDOW";
|
||||
case Expression::QUALIFY:
|
||||
return "QUALIFY";
|
||||
case Expression::ORDER_BY:
|
||||
return "ORDER BY";
|
||||
case Expression::LIMIT_BY_OFFSET:
|
||||
@ -95,6 +98,7 @@ public:
|
||||
ASTPtr & refPrewhere() { return getExpression(Expression::PREWHERE); }
|
||||
ASTPtr & refWhere() { return getExpression(Expression::WHERE); }
|
||||
ASTPtr & refHaving() { return getExpression(Expression::HAVING); }
|
||||
ASTPtr & refQualify() { return getExpression(Expression::QUALIFY); }
|
||||
|
||||
ASTPtr with() const { return getExpression(Expression::WITH); }
|
||||
ASTPtr select() const { return getExpression(Expression::SELECT); }
|
||||
@ -104,6 +108,7 @@ public:
|
||||
ASTPtr groupBy() const { return getExpression(Expression::GROUP_BY); }
|
||||
ASTPtr having() const { return getExpression(Expression::HAVING); }
|
||||
ASTPtr window() const { return getExpression(Expression::WINDOW); }
|
||||
ASTPtr qualify() const { return getExpression(Expression::QUALIFY); }
|
||||
ASTPtr orderBy() const { return getExpression(Expression::ORDER_BY); }
|
||||
ASTPtr limitByOffset() const { return getExpression(Expression::LIMIT_BY_OFFSET); }
|
||||
ASTPtr limitByLength() const { return getExpression(Expression::LIMIT_BY_LENGTH); }
|
||||
@ -113,7 +118,7 @@ public:
|
||||
ASTPtr settings() const { return getExpression(Expression::SETTINGS); }
|
||||
ASTPtr interpolate() const { return getExpression(Expression::INTERPOLATE); }
|
||||
|
||||
bool hasFiltration() const { return where() || prewhere() || having(); }
|
||||
bool hasFiltration() const { return where() || prewhere() || having() || qualify(); }
|
||||
|
||||
/// Set/Reset/Remove expression.
|
||||
void setExpression(Expression expr, ASTPtr && ast);
|
||||
|
@ -507,6 +507,7 @@ namespace DB
|
||||
MR_MACROS(WHEN, "WHEN") \
|
||||
MR_MACROS(WHERE, "WHERE") \
|
||||
MR_MACROS(WINDOW, "WINDOW") \
|
||||
MR_MACROS(QUALIFY, "QUALIFY") \
|
||||
MR_MACROS(WITH_ADMIN_OPTION, "WITH ADMIN OPTION") \
|
||||
MR_MACROS(WITH_CHECK, "WITH CHECK") \
|
||||
MR_MACROS(WITH_FILL, "WITH FILL") \
|
||||
|
@ -1481,6 +1481,7 @@ const char * ParserAlias::restricted_keywords[] =
|
||||
"USING",
|
||||
"WHERE",
|
||||
"WINDOW",
|
||||
"QUALIFY",
|
||||
"WITH",
|
||||
"INTERSECT",
|
||||
"EXCEPT",
|
||||
|
@ -49,6 +49,7 @@ bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
ParserKeyword s_totals(Keyword::TOTALS);
|
||||
ParserKeyword s_having(Keyword::HAVING);
|
||||
ParserKeyword s_window(Keyword::WINDOW);
|
||||
ParserKeyword s_qualify(Keyword::QUALIFY);
|
||||
ParserKeyword s_order_by(Keyword::ORDER_BY);
|
||||
ParserKeyword s_limit(Keyword::LIMIT);
|
||||
ParserKeyword s_settings(Keyword::SETTINGS);
|
||||
@ -86,6 +87,7 @@ bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
ASTPtr group_expression_list;
|
||||
ASTPtr having_expression;
|
||||
ASTPtr window_list;
|
||||
ASTPtr qualify_expression;
|
||||
ASTPtr order_expression_list;
|
||||
ASTPtr interpolate_expression_list;
|
||||
ASTPtr limit_by_length;
|
||||
@ -266,6 +268,13 @@ bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
}
|
||||
}
|
||||
|
||||
/// QUALIFY expr
|
||||
if (s_qualify.ignore(pos, expected))
|
||||
{
|
||||
if (!exp_elem.parse(pos, qualify_expression, expected))
|
||||
return false;
|
||||
}
|
||||
|
||||
/// ORDER BY expr ASC|DESC COLLATE 'locale' list
|
||||
if (s_order_by.ignore(pos, expected))
|
||||
{
|
||||
@ -489,6 +498,7 @@ bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
select_query->setExpression(ASTSelectQuery::Expression::GROUP_BY, std::move(group_expression_list));
|
||||
select_query->setExpression(ASTSelectQuery::Expression::HAVING, std::move(having_expression));
|
||||
select_query->setExpression(ASTSelectQuery::Expression::WINDOW, std::move(window_list));
|
||||
select_query->setExpression(ASTSelectQuery::Expression::QUALIFY, std::move(qualify_expression));
|
||||
select_query->setExpression(ASTSelectQuery::Expression::ORDER_BY, std::move(order_expression_list));
|
||||
select_query->setExpression(ASTSelectQuery::Expression::LIMIT_BY_OFFSET, std::move(limit_by_offset));
|
||||
select_query->setExpression(ASTSelectQuery::Expression::LIMIT_BY_LENGTH, std::move(limit_by_length));
|
||||
|
@ -1367,6 +1367,16 @@ void Planner::buildPlanForQueryNode()
|
||||
select_query_info.has_aggregates = hasAggregateFunctionNodes(query_tree);
|
||||
select_query_info.need_aggregate = query_node.hasGroupBy() || select_query_info.has_aggregates;
|
||||
|
||||
if (!select_query_info.has_window && query_node.hasQualify())
|
||||
{
|
||||
if (query_node.hasHaving())
|
||||
query_node.getHaving() = mergeConditionNodes({query_node.getHaving(), query_node.getQualify()}, query_context);
|
||||
else
|
||||
query_node.getHaving() = query_node.getQualify();
|
||||
|
||||
query_node.getQualify() = {};
|
||||
}
|
||||
|
||||
if (!select_query_info.need_aggregate && query_node.hasHaving())
|
||||
{
|
||||
if (query_node.hasWhere())
|
||||
@ -1636,6 +1646,9 @@ void Planner::buildPlanForQueryNode()
|
||||
addWindowSteps(query_plan, planner_context, window_analysis_result);
|
||||
}
|
||||
|
||||
if (expression_analysis_result.hasQualify())
|
||||
addFilterStep(query_plan, expression_analysis_result.getQualify(), "QUALIFY", result_actions_to_execute);
|
||||
|
||||
const auto & projection_analysis_result = expression_analysis_result.getProjection();
|
||||
addExpressionStep(query_plan, projection_analysis_result.projection_actions, "Projection", result_actions_to_execute);
|
||||
|
||||
|
@ -513,6 +513,16 @@ PlannerExpressionsAnalysisResult buildExpressionAnalysisResult(const QueryTreeNo
|
||||
if (window_analysis_result_optional)
|
||||
current_output_columns = actions_chain.getLastStepAvailableOutputColumns();
|
||||
|
||||
std::optional<FilterAnalysisResult> qualify_analysis_result_optional;
|
||||
std::optional<size_t> qualify_action_step_index_optional;
|
||||
|
||||
if (query_node.hasQualify())
|
||||
{
|
||||
qualify_analysis_result_optional = analyzeFilter(query_node.getQualify(), current_output_columns, planner_context, actions_chain);
|
||||
qualify_action_step_index_optional = actions_chain.getLastStepIndex();
|
||||
current_output_columns = actions_chain.getLastStepAvailableOutputColumns();
|
||||
}
|
||||
|
||||
auto projection_analysis_result = analyzeProjection(query_node, current_output_columns, planner_context, actions_chain);
|
||||
current_output_columns = actions_chain.getLastStepAvailableOutputColumns();
|
||||
|
||||
@ -604,7 +614,7 @@ PlannerExpressionsAnalysisResult buildExpressionAnalysisResult(const QueryTreeNo
|
||||
|
||||
PlannerExpressionsAnalysisResult expressions_analysis_result(std::move(projection_analysis_result));
|
||||
|
||||
if (where_action_step_index_optional && where_analysis_result_optional)
|
||||
if (where_analysis_result_optional && where_action_step_index_optional)
|
||||
{
|
||||
auto & where_analysis_result = *where_analysis_result_optional;
|
||||
auto & where_actions_chain_node = actions_chain.at(*where_action_step_index_optional);
|
||||
@ -615,7 +625,7 @@ PlannerExpressionsAnalysisResult buildExpressionAnalysisResult(const QueryTreeNo
|
||||
if (aggregation_analysis_result_optional)
|
||||
expressions_analysis_result.addAggregation(std::move(*aggregation_analysis_result_optional));
|
||||
|
||||
if (having_action_step_index_optional && having_analysis_result_optional)
|
||||
if (having_analysis_result_optional && having_action_step_index_optional)
|
||||
{
|
||||
auto & having_analysis_result = *having_analysis_result_optional;
|
||||
auto & having_actions_chain_node = actions_chain.at(*having_action_step_index_optional);
|
||||
@ -626,6 +636,14 @@ PlannerExpressionsAnalysisResult buildExpressionAnalysisResult(const QueryTreeNo
|
||||
if (window_analysis_result_optional)
|
||||
expressions_analysis_result.addWindow(std::move(*window_analysis_result_optional));
|
||||
|
||||
if (qualify_analysis_result_optional && qualify_action_step_index_optional)
|
||||
{
|
||||
auto & qualify_analysis_result = *qualify_analysis_result_optional;
|
||||
auto & qualify_actions_chain_node = actions_chain.at(*qualify_action_step_index_optional);
|
||||
qualify_analysis_result.remove_filter_column = !qualify_actions_chain_node->getChildRequiredOutputColumnsNames().contains(qualify_analysis_result.filter_column_name);
|
||||
expressions_analysis_result.addQualify(std::move(qualify_analysis_result));
|
||||
}
|
||||
|
||||
if (sort_analysis_result_optional)
|
||||
expressions_analysis_result.addSort(std::move(*sort_analysis_result_optional));
|
||||
|
||||
|
@ -129,6 +129,21 @@ public:
|
||||
window_analysis_result = std::move(window_analysis_result_);
|
||||
}
|
||||
|
||||
bool hasQualify() const
|
||||
{
|
||||
return qualify_analysis_result.filter_actions != nullptr;
|
||||
}
|
||||
|
||||
const FilterAnalysisResult & getQualify() const
|
||||
{
|
||||
return qualify_analysis_result;
|
||||
}
|
||||
|
||||
void addQualify(FilterAnalysisResult qualify_analysis_result_)
|
||||
{
|
||||
qualify_analysis_result = std::move(qualify_analysis_result_);
|
||||
}
|
||||
|
||||
bool hasSort() const
|
||||
{
|
||||
return sort_analysis_result.before_order_by_actions != nullptr;
|
||||
@ -165,6 +180,7 @@ private:
|
||||
AggregationAnalysisResult aggregation_analysis_result;
|
||||
FilterAnalysisResult having_analysis_result;
|
||||
WindowAnalysisResult window_analysis_result;
|
||||
FilterAnalysisResult qualify_analysis_result;
|
||||
SortAnalysisResult sort_analysis_result;
|
||||
LimitByAnalysisResult limit_by_analysis_result;
|
||||
};
|
||||
|
@ -1,5 +1,4 @@
|
||||
aaa
|
||||
(1,1) (1,1)
|
||||
1
|
||||
a1 1
|
||||
1
|
||||
|
@ -16,8 +16,6 @@ SELECT __getScalar(); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH }
|
||||
SELECT __getScalar(1); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT }
|
||||
SELECT __getScalar(materialize('1')); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT }
|
||||
|
||||
WITH ( SELECT (1,1) ) as a SELECT materialize(a), __getScalar('17789833925953107877_7493841889429261611') SETTINGS allow_experimental_analyzer = 1;
|
||||
|
||||
SELECT __scalarSubqueryResult('1');
|
||||
SELECT 'a' || __scalarSubqueryResult(a), materialize('1') as a;
|
||||
SELECT __scalarSubqueryResult(a, a), materialize('1') as a; -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH }
|
||||
|
@ -0,0 +1,74 @@
|
||||
0 4
|
||||
3 4
|
||||
6 4
|
||||
9 4
|
||||
--
|
||||
0
|
||||
3
|
||||
6
|
||||
9
|
||||
--
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
--
|
||||
0 5
|
||||
--
|
||||
0 5
|
||||
--
|
||||
0 4
|
||||
3 4
|
||||
6 4
|
||||
9 4
|
||||
--
|
||||
Expression (Project names)
|
||||
Header: number UInt64
|
||||
partition_count UInt64
|
||||
Actions: INPUT : 0 -> __table1.number UInt64 : 0
|
||||
INPUT : 1 -> count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) UInt64 : 1
|
||||
ALIAS __table1.number :: 0 -> number UInt64 : 2
|
||||
ALIAS count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) :: 1 -> partition_count UInt64 : 0
|
||||
Positions: 2 0
|
||||
Sorting (Sorting for ORDER BY)
|
||||
Header: __table1.number UInt64
|
||||
count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) UInt64
|
||||
Sort description: __table1.number ASC
|
||||
Expression ((Before ORDER BY + Projection))
|
||||
Header: __table1.number UInt64
|
||||
count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) UInt64
|
||||
Actions: INPUT :: 0 -> __table1.number UInt64 : 0
|
||||
INPUT :: 1 -> count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) UInt64 : 1
|
||||
Positions: 0 1
|
||||
Filter (QUALIFY)
|
||||
Header: __table1.number UInt64
|
||||
count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) UInt64
|
||||
Filter column: equals(count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)), 4_UInt8) (removed)
|
||||
Actions: INPUT :: 0 -> __table1.number UInt64 : 0
|
||||
INPUT :: 1 -> count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) UInt64 : 1
|
||||
INPUT : 2 -> count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) UInt64 : 2
|
||||
COLUMN Const(UInt8) -> 4_UInt8 UInt8 : 3
|
||||
FUNCTION equals(count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) :: 2, 4_UInt8 :: 3) -> equals(count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)), 4_UInt8) UInt8 : 4
|
||||
Positions: 4 0 1
|
||||
Window (Window step for window \'PARTITION BY modulo(__table1.number, 3_UInt8)\')
|
||||
Header: modulo(__table1.number, 3_UInt8) UInt8
|
||||
__table1.number UInt64
|
||||
count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) UInt64
|
||||
count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8)) UInt64
|
||||
Window: (PARTITION BY modulo(__table1.number, 3_UInt8))
|
||||
Functions: count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8))
|
||||
count() OVER (PARTITION BY modulo(__table1.number, 3_UInt8))
|
||||
Sorting (Sorting for window \'PARTITION BY modulo(__table1.number, 3_UInt8)\')
|
||||
Header: modulo(__table1.number, 3_UInt8) UInt8
|
||||
__table1.number UInt64
|
||||
Sort description: modulo(__table1.number, 3_UInt8) ASC
|
||||
Expression ((Before WINDOW + Change column names to column identifiers))
|
||||
Header: modulo(__table1.number, 3_UInt8) UInt8
|
||||
__table1.number UInt64
|
||||
Actions: INPUT : 0 -> number UInt64 : 0
|
||||
COLUMN Const(UInt8) -> 3_UInt8 UInt8 : 1
|
||||
ALIAS number :: 0 -> __table1.number UInt64 : 2
|
||||
FUNCTION modulo(__table1.number : 2, 3_UInt8 :: 1) -> modulo(__table1.number, 3_UInt8) UInt8 : 0
|
||||
Positions: 0 2
|
||||
ReadFromSystemNumbers
|
||||
Header: number UInt64
|
36
tests/queries/0_stateless/03095_window_functions_qualify.sql
Normal file
36
tests/queries/0_stateless/03095_window_functions_qualify.sql
Normal file
@ -0,0 +1,36 @@
|
||||
SET allow_experimental_analyzer = 1;
|
||||
|
||||
SELECT number, COUNT() OVER (PARTITION BY number % 3) AS partition_count FROM numbers(10) QUALIFY partition_count = 4 ORDER BY number;
|
||||
|
||||
SELECT '--';
|
||||
|
||||
SELECT number FROM numbers(10) QUALIFY (COUNT() OVER (PARTITION BY number % 3) AS partition_count) = 4 ORDER BY number;
|
||||
|
||||
SELECT '--';
|
||||
|
||||
SELECT number FROM numbers(10) QUALIFY number > 5 ORDER BY number;
|
||||
|
||||
SELECT '--';
|
||||
|
||||
SELECT (number % 2) AS key, count() FROM numbers(10) GROUP BY key HAVING key = 0 QUALIFY key == 0;
|
||||
|
||||
SELECT '--';
|
||||
|
||||
SELECT (number % 2) AS key, count() FROM numbers(10) GROUP BY key QUALIFY key == 0;
|
||||
|
||||
SELECT '--';
|
||||
|
||||
SELECT number, COUNT() OVER (PARTITION BY number % 3) AS partition_count FROM numbers(10) QUALIFY COUNT() OVER (PARTITION BY number % 3) = 4 ORDER BY number;
|
||||
|
||||
SELECT '--';
|
||||
|
||||
EXPLAIN header = 1, actions = 1
|
||||
SELECT number, COUNT() OVER (PARTITION BY number % 3) AS partition_count FROM numbers(10) QUALIFY COUNT() OVER (PARTITION BY number % 3) = 4 ORDER BY number;
|
||||
|
||||
SELECT number % toUInt256(2) AS key, count() FROM numbers(10) GROUP BY key WITH CUBE WITH TOTALS QUALIFY key = toNullable(toNullable(0)); -- { serverError 48 }
|
||||
|
||||
SELECT number % 2 AS key, count(materialize(5)) IGNORE NULLS FROM numbers(10) WHERE toLowCardinality(toLowCardinality(materialize(2))) GROUP BY key WITH CUBE WITH TOTALS QUALIFY key = 0; -- { serverError 48 }
|
||||
|
||||
SELECT 4, count(4) IGNORE NULLS, number % 2 AS key FROM numbers(10) GROUP BY key WITH ROLLUP WITH TOTALS QUALIFY key = materialize(0); -- { serverError 48 }
|
||||
|
||||
SELECT 3, number % toLowCardinality(2) AS key, count() IGNORE NULLS FROM numbers(10) GROUP BY key WITH ROLLUP WITH TOTALS QUALIFY key = 0; -- { serverError 48 }
|
Loading…
Reference in New Issue
Block a user