mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-30 11:32:03 +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]
|
[WHERE expr]
|
||||||
[GROUP BY expr_list] [WITH ROLLUP|WITH CUBE] [WITH TOTALS]
|
[GROUP BY expr_list] [WITH ROLLUP|WITH CUBE] [WITH TOTALS]
|
||||||
[HAVING expr]
|
[HAVING expr]
|
||||||
|
[WINDOW window_expr_list]
|
||||||
|
[QUALIFY expr]
|
||||||
[ORDER BY expr_list] [WITH FILL] [FROM expr] [TO expr] [STEP expr] [INTERPOLATE [(expr_list)]]
|
[ORDER BY expr_list] [WITH FILL] [FROM expr] [TO expr] [STEP expr] [INTERPOLATE [(expr_list)]]
|
||||||
[LIMIT [offset_value, ]n BY columns]
|
[LIMIT [offset_value, ]n BY columns]
|
||||||
[LIMIT [n, ]m] [WITH TIES]
|
[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)
|
- [GROUP BY clause](../../../sql-reference/statements/select/group-by.md)
|
||||||
- [LIMIT BY clause](../../../sql-reference/statements/select/limit-by.md)
|
- [LIMIT BY clause](../../../sql-reference/statements/select/limit-by.md)
|
||||||
- [HAVING clause](../../../sql-reference/statements/select/having.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)
|
- [LIMIT clause](../../../sql-reference/statements/select/limit.md)
|
||||||
- [OFFSET clause](../../../sql-reference/statements/select/offset.md)
|
- [OFFSET clause](../../../sql-reference/statements/select/offset.md)
|
||||||
- [UNION clause](../../../sql-reference/statements/select/union.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)
|
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");
|
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
|
/// Initialize aliases in query node scope
|
||||||
QueryExpressionsAliasVisitor visitor(scope);
|
QueryExpressionsAliasVisitor visitor(scope);
|
||||||
|
|
||||||
@ -7919,6 +7922,9 @@ void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, Identifier
|
|||||||
if (query_node_typed.hasWindow())
|
if (query_node_typed.hasWindow())
|
||||||
visitor.visit(query_node_typed.getWindowNode());
|
visitor.visit(query_node_typed.getWindowNode());
|
||||||
|
|
||||||
|
if (query_node_typed.hasQualify())
|
||||||
|
visitor.visit(query_node_typed.getQualify());
|
||||||
|
|
||||||
if (query_node_typed.hasOrderBy())
|
if (query_node_typed.hasOrderBy())
|
||||||
visitor.visit(query_node_typed.getOrderByNode());
|
visitor.visit(query_node_typed.getOrderByNode());
|
||||||
|
|
||||||
@ -8067,6 +8073,9 @@ void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, Identifier
|
|||||||
if (query_node_typed.hasWindow())
|
if (query_node_typed.hasWindow())
|
||||||
resolveWindowNodeList(query_node_typed.getWindowNode(), scope);
|
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())
|
if (query_node_typed.hasOrderBy())
|
||||||
{
|
{
|
||||||
replaceNodesWithPositionalArguments(query_node_typed.getOrderByNode(), query_node_typed.getProjection().getNodes(), scope);
|
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);
|
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())
|
if (hasOrderBy())
|
||||||
{
|
{
|
||||||
buffer << '\n' << std::string(indent + 2, ' ') << "ORDER BY\n";
|
buffer << '\n' << std::string(indent + 2, ' ') << "ORDER BY\n";
|
||||||
@ -381,6 +387,9 @@ ASTPtr QueryNode::toASTImpl(const ConvertToASTOptions & options) const
|
|||||||
if (hasWindow())
|
if (hasWindow())
|
||||||
select_query->setExpression(ASTSelectQuery::Expression::WINDOW, getWindow().toAST(options));
|
select_query->setExpression(ASTSelectQuery::Expression::WINDOW, getWindow().toAST(options));
|
||||||
|
|
||||||
|
if (hasQualify())
|
||||||
|
select_query->setExpression(ASTSelectQuery::Expression::QUALIFY, getQualify()->toAST(options));
|
||||||
|
|
||||||
if (hasOrderBy())
|
if (hasOrderBy())
|
||||||
select_query->setExpression(ASTSelectQuery::Expression::ORDER_BY, getOrderBy().toAST(options));
|
select_query->setExpression(ASTSelectQuery::Expression::ORDER_BY, getOrderBy().toAST(options));
|
||||||
|
|
||||||
|
@ -416,6 +416,24 @@ public:
|
|||||||
return children[window_child_index];
|
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
|
/// Returns true if query node ORDER BY section is not empty, false otherwise
|
||||||
bool hasOrderBy() const
|
bool hasOrderBy() const
|
||||||
{
|
{
|
||||||
@ -622,13 +640,14 @@ private:
|
|||||||
static constexpr size_t group_by_child_index = 5;
|
static constexpr size_t group_by_child_index = 5;
|
||||||
static constexpr size_t having_child_index = 6;
|
static constexpr size_t having_child_index = 6;
|
||||||
static constexpr size_t window_child_index = 7;
|
static constexpr size_t window_child_index = 7;
|
||||||
static constexpr size_t order_by_child_index = 8;
|
static constexpr size_t qualify_child_index = 8;
|
||||||
static constexpr size_t interpolate_child_index = 9;
|
static constexpr size_t order_by_child_index = 9;
|
||||||
static constexpr size_t limit_by_limit_child_index = 10;
|
static constexpr size_t interpolate_child_index = 10;
|
||||||
static constexpr size_t limit_by_offset_child_index = 11;
|
static constexpr size_t limit_by_limit_child_index = 11;
|
||||||
static constexpr size_t limit_by_child_index = 12;
|
static constexpr size_t limit_by_offset_child_index = 12;
|
||||||
static constexpr size_t limit_child_index = 13;
|
static constexpr size_t limit_by_child_index = 13;
|
||||||
static constexpr size_t offset_child_index = 14;
|
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;
|
static constexpr size_t children_size = offset_child_index + 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -330,6 +330,10 @@ QueryTreeNodePtr QueryTreeBuilder::buildSelectExpression(const ASTPtr & select_q
|
|||||||
if (window_list)
|
if (window_list)
|
||||||
current_query_tree->getWindowNode() = buildWindowList(window_list, current_context);
|
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();
|
auto select_order_by_list = select_query_typed.orderBy();
|
||||||
if (select_order_by_list)
|
if (select_order_by_list)
|
||||||
current_query_tree->getOrderByNode() = buildSortList(select_order_by_list, current_context);
|
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())
|
if (query_node_typed.hasHaving())
|
||||||
validateFilter(query_node_typed.getHaving(), "HAVING", query_node);
|
validateFilter(query_node_typed.getHaving(), "HAVING", query_node);
|
||||||
|
|
||||||
|
if (query_node_typed.hasQualify())
|
||||||
|
validateFilter(query_node_typed.getQualify(), "QUALIFY", query_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
@ -47,7 +47,7 @@ Suggest::Suggest()
|
|||||||
"GRANT", "REVOKE", "OPTION", "ADMIN", "EXCEPT", "REPLACE", "IDENTIFIED", "HOST",
|
"GRANT", "REVOKE", "OPTION", "ADMIN", "EXCEPT", "REPLACE", "IDENTIFIED", "HOST",
|
||||||
"NAME", "READONLY", "WRITABLE", "PERMISSIVE", "FOR", "RESTRICTIVE", "RANDOMIZED", "INTERVAL",
|
"NAME", "READONLY", "WRITABLE", "PERMISSIVE", "FOR", "RESTRICTIVE", "RANDOMIZED", "INTERVAL",
|
||||||
"LIMITS", "ONLY", "TRACKING", "IP", "REGEXP", "ILIKE", "CLEANUP", "APPEND",
|
"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)
|
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);
|
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())
|
if (!order_by_all && orderBy())
|
||||||
{
|
{
|
||||||
s.ostr << (s.hilite ? hilite_keyword : "") << s.nl_or_ws << indent_str << "ORDER BY" << (s.hilite ? hilite_none : "");
|
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,
|
GROUP_BY,
|
||||||
HAVING,
|
HAVING,
|
||||||
WINDOW,
|
WINDOW,
|
||||||
|
QUALIFY,
|
||||||
ORDER_BY,
|
ORDER_BY,
|
||||||
LIMIT_BY_OFFSET,
|
LIMIT_BY_OFFSET,
|
||||||
LIMIT_BY_LENGTH,
|
LIMIT_BY_LENGTH,
|
||||||
@ -55,6 +56,8 @@ public:
|
|||||||
return "HAVING";
|
return "HAVING";
|
||||||
case Expression::WINDOW:
|
case Expression::WINDOW:
|
||||||
return "WINDOW";
|
return "WINDOW";
|
||||||
|
case Expression::QUALIFY:
|
||||||
|
return "QUALIFY";
|
||||||
case Expression::ORDER_BY:
|
case Expression::ORDER_BY:
|
||||||
return "ORDER BY";
|
return "ORDER BY";
|
||||||
case Expression::LIMIT_BY_OFFSET:
|
case Expression::LIMIT_BY_OFFSET:
|
||||||
@ -95,6 +98,7 @@ public:
|
|||||||
ASTPtr & refPrewhere() { return getExpression(Expression::PREWHERE); }
|
ASTPtr & refPrewhere() { return getExpression(Expression::PREWHERE); }
|
||||||
ASTPtr & refWhere() { return getExpression(Expression::WHERE); }
|
ASTPtr & refWhere() { return getExpression(Expression::WHERE); }
|
||||||
ASTPtr & refHaving() { return getExpression(Expression::HAVING); }
|
ASTPtr & refHaving() { return getExpression(Expression::HAVING); }
|
||||||
|
ASTPtr & refQualify() { return getExpression(Expression::QUALIFY); }
|
||||||
|
|
||||||
ASTPtr with() const { return getExpression(Expression::WITH); }
|
ASTPtr with() const { return getExpression(Expression::WITH); }
|
||||||
ASTPtr select() const { return getExpression(Expression::SELECT); }
|
ASTPtr select() const { return getExpression(Expression::SELECT); }
|
||||||
@ -104,6 +108,7 @@ public:
|
|||||||
ASTPtr groupBy() const { return getExpression(Expression::GROUP_BY); }
|
ASTPtr groupBy() const { return getExpression(Expression::GROUP_BY); }
|
||||||
ASTPtr having() const { return getExpression(Expression::HAVING); }
|
ASTPtr having() const { return getExpression(Expression::HAVING); }
|
||||||
ASTPtr window() const { return getExpression(Expression::WINDOW); }
|
ASTPtr window() const { return getExpression(Expression::WINDOW); }
|
||||||
|
ASTPtr qualify() const { return getExpression(Expression::QUALIFY); }
|
||||||
ASTPtr orderBy() const { return getExpression(Expression::ORDER_BY); }
|
ASTPtr orderBy() const { return getExpression(Expression::ORDER_BY); }
|
||||||
ASTPtr limitByOffset() const { return getExpression(Expression::LIMIT_BY_OFFSET); }
|
ASTPtr limitByOffset() const { return getExpression(Expression::LIMIT_BY_OFFSET); }
|
||||||
ASTPtr limitByLength() const { return getExpression(Expression::LIMIT_BY_LENGTH); }
|
ASTPtr limitByLength() const { return getExpression(Expression::LIMIT_BY_LENGTH); }
|
||||||
@ -113,7 +118,7 @@ public:
|
|||||||
ASTPtr settings() const { return getExpression(Expression::SETTINGS); }
|
ASTPtr settings() const { return getExpression(Expression::SETTINGS); }
|
||||||
ASTPtr interpolate() const { return getExpression(Expression::INTERPOLATE); }
|
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.
|
/// Set/Reset/Remove expression.
|
||||||
void setExpression(Expression expr, ASTPtr && ast);
|
void setExpression(Expression expr, ASTPtr && ast);
|
||||||
|
@ -507,6 +507,7 @@ namespace DB
|
|||||||
MR_MACROS(WHEN, "WHEN") \
|
MR_MACROS(WHEN, "WHEN") \
|
||||||
MR_MACROS(WHERE, "WHERE") \
|
MR_MACROS(WHERE, "WHERE") \
|
||||||
MR_MACROS(WINDOW, "WINDOW") \
|
MR_MACROS(WINDOW, "WINDOW") \
|
||||||
|
MR_MACROS(QUALIFY, "QUALIFY") \
|
||||||
MR_MACROS(WITH_ADMIN_OPTION, "WITH ADMIN OPTION") \
|
MR_MACROS(WITH_ADMIN_OPTION, "WITH ADMIN OPTION") \
|
||||||
MR_MACROS(WITH_CHECK, "WITH CHECK") \
|
MR_MACROS(WITH_CHECK, "WITH CHECK") \
|
||||||
MR_MACROS(WITH_FILL, "WITH FILL") \
|
MR_MACROS(WITH_FILL, "WITH FILL") \
|
||||||
|
@ -1481,6 +1481,7 @@ const char * ParserAlias::restricted_keywords[] =
|
|||||||
"USING",
|
"USING",
|
||||||
"WHERE",
|
"WHERE",
|
||||||
"WINDOW",
|
"WINDOW",
|
||||||
|
"QUALIFY",
|
||||||
"WITH",
|
"WITH",
|
||||||
"INTERSECT",
|
"INTERSECT",
|
||||||
"EXCEPT",
|
"EXCEPT",
|
||||||
|
@ -49,6 +49,7 @@ bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
|||||||
ParserKeyword s_totals(Keyword::TOTALS);
|
ParserKeyword s_totals(Keyword::TOTALS);
|
||||||
ParserKeyword s_having(Keyword::HAVING);
|
ParserKeyword s_having(Keyword::HAVING);
|
||||||
ParserKeyword s_window(Keyword::WINDOW);
|
ParserKeyword s_window(Keyword::WINDOW);
|
||||||
|
ParserKeyword s_qualify(Keyword::QUALIFY);
|
||||||
ParserKeyword s_order_by(Keyword::ORDER_BY);
|
ParserKeyword s_order_by(Keyword::ORDER_BY);
|
||||||
ParserKeyword s_limit(Keyword::LIMIT);
|
ParserKeyword s_limit(Keyword::LIMIT);
|
||||||
ParserKeyword s_settings(Keyword::SETTINGS);
|
ParserKeyword s_settings(Keyword::SETTINGS);
|
||||||
@ -86,6 +87,7 @@ bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
|||||||
ASTPtr group_expression_list;
|
ASTPtr group_expression_list;
|
||||||
ASTPtr having_expression;
|
ASTPtr having_expression;
|
||||||
ASTPtr window_list;
|
ASTPtr window_list;
|
||||||
|
ASTPtr qualify_expression;
|
||||||
ASTPtr order_expression_list;
|
ASTPtr order_expression_list;
|
||||||
ASTPtr interpolate_expression_list;
|
ASTPtr interpolate_expression_list;
|
||||||
ASTPtr limit_by_length;
|
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
|
/// ORDER BY expr ASC|DESC COLLATE 'locale' list
|
||||||
if (s_order_by.ignore(pos, expected))
|
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::GROUP_BY, std::move(group_expression_list));
|
||||||
select_query->setExpression(ASTSelectQuery::Expression::HAVING, std::move(having_expression));
|
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::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::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_OFFSET, std::move(limit_by_offset));
|
||||||
select_query->setExpression(ASTSelectQuery::Expression::LIMIT_BY_LENGTH, std::move(limit_by_length));
|
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.has_aggregates = hasAggregateFunctionNodes(query_tree);
|
||||||
select_query_info.need_aggregate = query_node.hasGroupBy() || select_query_info.has_aggregates;
|
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 (!select_query_info.need_aggregate && query_node.hasHaving())
|
||||||
{
|
{
|
||||||
if (query_node.hasWhere())
|
if (query_node.hasWhere())
|
||||||
@ -1636,6 +1646,9 @@ void Planner::buildPlanForQueryNode()
|
|||||||
addWindowSteps(query_plan, planner_context, window_analysis_result);
|
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();
|
const auto & projection_analysis_result = expression_analysis_result.getProjection();
|
||||||
addExpressionStep(query_plan, projection_analysis_result.projection_actions, "Projection", result_actions_to_execute);
|
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)
|
if (window_analysis_result_optional)
|
||||||
current_output_columns = actions_chain.getLastStepAvailableOutputColumns();
|
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);
|
auto projection_analysis_result = analyzeProjection(query_node, current_output_columns, planner_context, actions_chain);
|
||||||
current_output_columns = actions_chain.getLastStepAvailableOutputColumns();
|
current_output_columns = actions_chain.getLastStepAvailableOutputColumns();
|
||||||
|
|
||||||
@ -604,7 +614,7 @@ PlannerExpressionsAnalysisResult buildExpressionAnalysisResult(const QueryTreeNo
|
|||||||
|
|
||||||
PlannerExpressionsAnalysisResult expressions_analysis_result(std::move(projection_analysis_result));
|
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_analysis_result = *where_analysis_result_optional;
|
||||||
auto & where_actions_chain_node = actions_chain.at(*where_action_step_index_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)
|
if (aggregation_analysis_result_optional)
|
||||||
expressions_analysis_result.addAggregation(std::move(*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_analysis_result = *having_analysis_result_optional;
|
||||||
auto & having_actions_chain_node = actions_chain.at(*having_action_step_index_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)
|
if (window_analysis_result_optional)
|
||||||
expressions_analysis_result.addWindow(std::move(*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)
|
if (sort_analysis_result_optional)
|
||||||
expressions_analysis_result.addSort(std::move(*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_);
|
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
|
bool hasSort() const
|
||||||
{
|
{
|
||||||
return sort_analysis_result.before_order_by_actions != nullptr;
|
return sort_analysis_result.before_order_by_actions != nullptr;
|
||||||
@ -165,6 +180,7 @@ private:
|
|||||||
AggregationAnalysisResult aggregation_analysis_result;
|
AggregationAnalysisResult aggregation_analysis_result;
|
||||||
FilterAnalysisResult having_analysis_result;
|
FilterAnalysisResult having_analysis_result;
|
||||||
WindowAnalysisResult window_analysis_result;
|
WindowAnalysisResult window_analysis_result;
|
||||||
|
FilterAnalysisResult qualify_analysis_result;
|
||||||
SortAnalysisResult sort_analysis_result;
|
SortAnalysisResult sort_analysis_result;
|
||||||
LimitByAnalysisResult limit_by_analysis_result;
|
LimitByAnalysisResult limit_by_analysis_result;
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
aaa
|
aaa
|
||||||
(1,1) (1,1)
|
|
||||||
1
|
1
|
||||||
a1 1
|
a1 1
|
||||||
1
|
1
|
||||||
|
@ -16,8 +16,6 @@ SELECT __getScalar(); -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH }
|
|||||||
SELECT __getScalar(1); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT }
|
SELECT __getScalar(1); -- { serverError ILLEGAL_TYPE_OF_ARGUMENT }
|
||||||
SELECT __getScalar(materialize('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 __scalarSubqueryResult('1');
|
||||||
SELECT 'a' || __scalarSubqueryResult(a), materialize('1') as a;
|
SELECT 'a' || __scalarSubqueryResult(a), materialize('1') as a;
|
||||||
SELECT __scalarSubqueryResult(a, a), materialize('1') as a; -- { serverError NUMBER_OF_ARGUMENTS_DOESNT_MATCH }
|
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