From 47a0f4e16280b996d0315ec55b9546564e2806cf Mon Sep 17 00:00:00 2001 From: Ildus Kurbangaliev Date: Tue, 24 Nov 2020 16:25:45 +0500 Subject: [PATCH] Add tuple argument support for argMin and argMax --- .../aggregate-functions/reference/argmax.md | 24 ++- .../aggregate-functions/reference/argmin.md | 12 +- .../data-types/simpleaggregatefunction.md | 2 + .../AggregateFunctionArgMinMax.h | 64 ++++-- src/AggregateFunctions/Helpers.h | 6 + src/AggregateFunctions/HelpersMinMaxAny.h | 80 ++++---- src/AggregateFunctions/IAggregateFunction.h | 194 ++++++++++++++---- .../DataTypeCustomSimpleAggregateFunction.cpp | 3 +- .../0_stateless/00027_argMinMax.reference | 5 + tests/queries/0_stateless/00027_argMinMax.sql | 8 + .../00027_simple_argMinArray.reference | 1 - .../0_stateless/00027_simple_argMinArray.sql | 1 - .../00915_simple_aggregate_function.reference | 6 +- .../00915_simple_aggregate_function.sql | 18 +- 14 files changed, 310 insertions(+), 114 deletions(-) create mode 100644 tests/queries/0_stateless/00027_argMinMax.reference create mode 100644 tests/queries/0_stateless/00027_argMinMax.sql delete mode 100644 tests/queries/0_stateless/00027_simple_argMinArray.reference delete mode 100644 tests/queries/0_stateless/00027_simple_argMinArray.sql diff --git a/docs/en/sql-reference/aggregate-functions/reference/argmax.md b/docs/en/sql-reference/aggregate-functions/reference/argmax.md index 3093a4f67ef..35e87d49e60 100644 --- a/docs/en/sql-reference/aggregate-functions/reference/argmax.md +++ b/docs/en/sql-reference/aggregate-functions/reference/argmax.md @@ -4,6 +4,28 @@ toc_priority: 106 # argMax {#agg-function-argmax} -Syntax: `argMax(arg, val)` +Syntax: `argMax(arg, val)` or `argMax(tuple(arg, val))` Calculates the `arg` value for a maximum `val` value. If there are several different values of `arg` for maximum values of `val`, the first of these values encountered is output. + +Tuple version of this function will return the tuple with the maximum `val` value. It is convinient for use with `SimpleAggregateFunction`. + +**Example:** + +``` text +┌─user─────┬─salary─┐ +│ director │ 5000 │ +│ manager │ 3000 │ +│ worker │ 1000 │ +└──────────┴────────┘ +``` + +``` sql +SELECT argMax(user, salary), argMax(tuple(user, salary)) FROM salary +``` + +``` text +┌─argMax(user, salary)─┬─argMax(tuple(user, salary))─┐ +│ director │ ('director',5000) │ +└──────────────────────┴─────────────────────────────┘ +``` diff --git a/docs/en/sql-reference/aggregate-functions/reference/argmin.md b/docs/en/sql-reference/aggregate-functions/reference/argmin.md index 315c7b6c29a..72c9bce6817 100644 --- a/docs/en/sql-reference/aggregate-functions/reference/argmin.md +++ b/docs/en/sql-reference/aggregate-functions/reference/argmin.md @@ -4,10 +4,12 @@ toc_priority: 105 # argMin {#agg-function-argmin} -Syntax: `argMin(arg, val)` +Syntax: `argMin(arg, val)` or `argMin(tuple(arg, val))` Calculates the `arg` value for a minimal `val` value. If there are several different values of `arg` for minimal values of `val`, the first of these values encountered is output. +Tuple version of this function will return the tuple with the minimal `val` value. It is convinient for use with `SimpleAggregateFunction`. + **Example:** ``` text @@ -19,11 +21,11 @@ Calculates the `arg` value for a minimal `val` value. If there are several diffe ``` ``` sql -SELECT argMin(user, salary) FROM salary +SELECT argMin(user, salary), argMin(tuple(user, salary)) FROM salary ``` ``` text -┌─argMin(user, salary)─┐ -│ worker │ -└──────────────────────┘ +┌─argMin(user, salary)─┬─argMin(tuple(user, salary))─┐ +│ worker │ ('worker',1000) │ +└──────────────────────┴─────────────────────────────┘ ``` diff --git a/docs/en/sql-reference/data-types/simpleaggregatefunction.md b/docs/en/sql-reference/data-types/simpleaggregatefunction.md index b23ab5a6717..2d2746f85d3 100644 --- a/docs/en/sql-reference/data-types/simpleaggregatefunction.md +++ b/docs/en/sql-reference/data-types/simpleaggregatefunction.md @@ -18,6 +18,8 @@ The following aggregate functions are supported: - [`sumMap`](../../sql-reference/aggregate-functions/reference/summap.md#agg_functions-summap) - [`minMap`](../../sql-reference/aggregate-functions/reference/minmap.md#agg_functions-minmap) - [`maxMap`](../../sql-reference/aggregate-functions/reference/maxmap.md#agg_functions-maxmap) +- [`argMin`](../../sql-reference/aggregate-functions/reference/argmin.md) +- [`argMax`](../../sql-reference/aggregate-functions/reference/argmax.md) Values of the `SimpleAggregateFunction(func, Type)` look and stored the same way as `Type`, so you do not need to apply functions with `-Merge`/`-State` suffixes. `SimpleAggregateFunction` has better performance than `AggregateFunction` with same aggregation function. diff --git a/src/AggregateFunctions/AggregateFunctionArgMinMax.h b/src/AggregateFunctions/AggregateFunctionArgMinMax.h index 9470b1b8692..67f21db0240 100644 --- a/src/AggregateFunctions/AggregateFunctionArgMinMax.h +++ b/src/AggregateFunctions/AggregateFunctionArgMinMax.h @@ -1,14 +1,16 @@ #pragma once -#include -#include -#include #include // SingleValueDataString used in embedded compiler +#include +#include +#include +#include +#include +#include "Columns/IColumn.h" namespace DB { - namespace ErrorCodes { extern const int ILLEGAL_TYPE_OF_ARGUMENT; @@ -22,37 +24,49 @@ struct AggregateFunctionArgMinMaxData using ResultData_t = ResultData; using ValueData_t = ValueData; - ResultData result; // the argument at which the minimum/maximum value is reached. - ValueData value; // value for which the minimum/maximum is calculated. + ResultData result; // the argument at which the minimum/maximum value is reached. + ValueData value; // value for which the minimum/maximum is calculated. - static bool allocatesMemoryInArena() - { - return ResultData::allocatesMemoryInArena() || ValueData::allocatesMemoryInArena(); - } + static bool allocatesMemoryInArena() { return ResultData::allocatesMemoryInArena() || ValueData::allocatesMemoryInArena(); } + + static String name() { return StringRef(ValueData_t::name()) == StringRef("min") ? "argMin" : "argMax"; } }; /// Returns the first arg value found for the minimum/maximum value. Example: argMax(arg, value). template -class AggregateFunctionArgMinMax final : public IAggregateFunctionDataHelper> +class AggregateFunctionArgMinMax final : public IAggregateFunctionTupleArgHelper, 2> { private: const DataTypePtr & type_res; const DataTypePtr & type_val; + bool tuple_argument; + + using Base = IAggregateFunctionTupleArgHelper, 2>; public: - AggregateFunctionArgMinMax(const DataTypePtr & type_res_, const DataTypePtr & type_val_) - : IAggregateFunctionDataHelper>({type_res_, type_val_}, {}), - type_res(this->argument_types[0]), type_val(this->argument_types[1]) + AggregateFunctionArgMinMax(const DataTypePtr & type_res_, const DataTypePtr & type_val_, const bool tuple_argument_) + : Base({type_res_, type_val_}, {}, tuple_argument_) + , type_res(this->argument_types[0]) + , type_val(this->argument_types[1]) { if (!type_val->isComparable()) - throw Exception("Illegal type " + type_val->getName() + " of second argument of aggregate function " + getName() - + " because the values of that data type are not comparable", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + throw Exception( + "Illegal type " + type_val->getName() + " of second argument of aggregate function " + getName() + + " because the values of that data type are not comparable", + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + + this->tuple_argument = tuple_argument_; } - String getName() const override { return StringRef(Data::ValueData_t::name()) == StringRef("min") ? "argMin" : "argMax"; } + String getName() const override { return Data::name(); } DataTypePtr getReturnType() const override { + if (tuple_argument) + { + return std::make_shared(DataTypes{this->type_res, this->type_val}); + } + return type_res; } @@ -80,15 +94,21 @@ public: this->data(place).value.read(buf, *type_val, arena); } - bool allocatesMemoryInArena() const override - { - return Data::allocatesMemoryInArena(); - } + bool allocatesMemoryInArena() const override { return Data::allocatesMemoryInArena(); } void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { - this->data(place).result.insertResultInto(to); + if (tuple_argument) + { + auto & tup = assert_cast(to); + + this->data(place).result.insertResultInto(tup.getColumn(0)); + this->data(place).value.insertResultInto(tup.getColumn(1)); + } + else + this->data(place).result.insertResultInto(to); } }; + } diff --git a/src/AggregateFunctions/Helpers.h b/src/AggregateFunctions/Helpers.h index fb727bf98b0..2b21b745a0e 100644 --- a/src/AggregateFunctions/Helpers.h +++ b/src/AggregateFunctions/Helpers.h @@ -31,6 +31,12 @@ M(Float32) \ M(Float64) +#define FOR_DECIMAL_TYPES(M) \ + M(Decimal32) \ + M(Decimal64) \ + M(Decimal128) + + namespace DB { diff --git a/src/AggregateFunctions/HelpersMinMaxAny.h b/src/AggregateFunctions/HelpersMinMaxAny.h index dc165f50d8e..e995f52f498 100644 --- a/src/AggregateFunctions/HelpersMinMaxAny.h +++ b/src/AggregateFunctions/HelpersMinMaxAny.h @@ -8,10 +8,14 @@ #include #include #include - +#include namespace DB { +namespace ErrorCodes +{ + extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; +} /// min, max, any, anyLast, anyHeavy, etc... template