mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-24 16:42:05 +00:00
dbms: added least and greatest functions [#METR-17233].
This commit is contained in:
parent
1f4f0f591b
commit
2af61829fe
@ -262,6 +262,30 @@ struct BitShiftRightImpl
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename A, typename B>
|
||||||
|
struct LeastImpl
|
||||||
|
{
|
||||||
|
typedef typename NumberTraits::ResultOfIf<A, B>::Type ResultType;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
return static_cast<Result>(a) < static_cast<Result>(b) ? static_cast<Result>(a) : static_cast<Result>(b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename A, typename B>
|
||||||
|
struct GreatestImpl
|
||||||
|
{
|
||||||
|
typedef typename NumberTraits::ResultOfIf<A, B>::Type ResultType;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
return static_cast<Result>(a) > static_cast<Result>(b) ? static_cast<Result>(a) : static_cast<Result>(b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template<typename A>
|
template<typename A>
|
||||||
struct NegateImpl
|
struct NegateImpl
|
||||||
{
|
{
|
||||||
@ -319,6 +343,12 @@ template <typename T> using Else = T;
|
|||||||
/// Used to indicate undefined operation
|
/// Used to indicate undefined operation
|
||||||
struct InvalidType;
|
struct InvalidType;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct DataTypeFromFieldType<NumberTraits::Error>
|
||||||
|
{
|
||||||
|
using Type = InvalidType;
|
||||||
|
};
|
||||||
|
|
||||||
template <typename DataType> struct IsIntegral { static constexpr auto value = false; };
|
template <typename DataType> struct IsIntegral { static constexpr auto value = false; };
|
||||||
template <> struct IsIntegral<DataTypeUInt8> { static constexpr auto value = true; };
|
template <> struct IsIntegral<DataTypeUInt8> { static constexpr auto value = true; };
|
||||||
template <> struct IsIntegral<DataTypeUInt16> { static constexpr auto value = true; };
|
template <> struct IsIntegral<DataTypeUInt16> { static constexpr auto value = true; };
|
||||||
@ -342,11 +372,13 @@ template <typename DataType> struct IsDateOrDateTime { static constexpr auto val
|
|||||||
template <> struct IsDateOrDateTime<DataTypeDate> { static constexpr auto value = true; };
|
template <> struct IsDateOrDateTime<DataTypeDate> { static constexpr auto value = true; };
|
||||||
template <> struct IsDateOrDateTime<DataTypeDateTime> { static constexpr auto value = true; };
|
template <> struct IsDateOrDateTime<DataTypeDateTime> { static constexpr auto value = true; };
|
||||||
|
|
||||||
/** Returns appropriate result type for binary operator on dates:
|
/** Returns appropriate result type for binary operator on dates (or datetimes):
|
||||||
* Date + Integral -> Date
|
* Date + Integral -> Date
|
||||||
* Integral + Date -> Date
|
* Integral + Date -> Date
|
||||||
* Date - Date -> Int32
|
* Date - Date -> Int32
|
||||||
* Date - Integral -> Date
|
* Date - Integral -> Date
|
||||||
|
* least(Date, Date) -> Date
|
||||||
|
* greatest(Date, Date) -> Date
|
||||||
* All other operations are not defined and return InvalidType, operations on
|
* All other operations are not defined and return InvalidType, operations on
|
||||||
* distinct date types are also undefined (e.g. DataTypeDate - DataTypeDateTime) */
|
* distinct date types are also undefined (e.g. DataTypeDate - DataTypeDateTime) */
|
||||||
template <template <typename, typename> class Operation, typename LeftDataType, typename RightDataType>
|
template <template <typename, typename> class Operation, typename LeftDataType, typename RightDataType>
|
||||||
@ -387,7 +419,13 @@ struct DateBinaryOperationTraits
|
|||||||
Else<InvalidType>
|
Else<InvalidType>
|
||||||
>
|
>
|
||||||
>,
|
>,
|
||||||
Else<InvalidType>
|
Else<
|
||||||
|
If<std::is_same<T0, T1>::value
|
||||||
|
&& (std::is_same<Op, LeastImpl<T0, T1>>::value || std::is_same<Op, GreatestImpl<T0, T1>>::value),
|
||||||
|
Then<LeftDataType>,
|
||||||
|
Else<InvalidType>
|
||||||
|
>
|
||||||
|
>
|
||||||
>
|
>
|
||||||
>
|
>
|
||||||
>;
|
>;
|
||||||
@ -479,35 +517,27 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Overload for date operations
|
/// Overload for date operations
|
||||||
template <typename LeftDataType, typename RightDataType, typename ColumnType,
|
template <typename LeftDataType, typename RightDataType, typename ColumnType>
|
||||||
typename std::enable_if<IsDateOrDateTime<LeftDataType>::value || IsDateOrDateTime<RightDataType>::value>::type * = nullptr>
|
|
||||||
bool executeRightType(Block & block, const ColumnNumbers & arguments, const size_t result, const ColumnType * col_left)
|
bool executeRightType(Block & block, const ColumnNumbers & arguments, const size_t result, const ColumnType * col_left)
|
||||||
{
|
{
|
||||||
if (!typeid_cast<const RightDataType *>(block.getByPosition(arguments[1]).type.get()))
|
if (!typeid_cast<const RightDataType *>(block.getByPosition(arguments[1]).type.get()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
using ResultDataType = typename DateBinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType;
|
using ResultDataType = typename BinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType;
|
||||||
|
|
||||||
return executeRightTypeDispatch<LeftDataType, RightDataType, ResultDataType>(
|
return executeRightTypeDispatch<LeftDataType, RightDataType, ResultDataType>(
|
||||||
block, arguments, result, col_left);
|
block, arguments, result, col_left);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Overload for numeric operations
|
|
||||||
template <typename LeftDataType, typename RightDataType, typename ColumnType,
|
|
||||||
typename T0 = typename LeftDataType::FieldType, typename T1 = typename RightDataType::FieldType,
|
|
||||||
typename std::enable_if<IsNumeric<LeftDataType>::value && IsNumeric<RightDataType>::value>::type * = nullptr>
|
|
||||||
bool executeRightType(Block & block, const ColumnNumbers & arguments, const size_t result, const ColumnType * col_left)
|
|
||||||
{
|
|
||||||
return executeRightTypeImpl<T0, T1>(block, arguments, result, col_left);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Overload for InvalidType
|
/// Overload for InvalidType
|
||||||
template <typename LeftDataType, typename RightDataType, typename ResultDataType, typename ColumnType,
|
template <typename LeftDataType, typename RightDataType, typename ResultDataType, typename ColumnType,
|
||||||
typename std::enable_if<std::is_same<ResultDataType, InvalidType>::value>::type * = nullptr>
|
typename std::enable_if<std::is_same<ResultDataType, InvalidType>::value>::type * = nullptr>
|
||||||
bool executeRightTypeDispatch(Block & block, const ColumnNumbers & arguments, const size_t result,
|
bool executeRightTypeDispatch(Block & block, const ColumnNumbers & arguments, const size_t result,
|
||||||
const ColumnType * col_left)
|
const ColumnType * col_left)
|
||||||
{
|
{
|
||||||
return false;
|
throw Exception("Types " + TypeName<typename LeftDataType::FieldType>::get()
|
||||||
|
+ " and " + TypeName<typename LeftDataType::FieldType>::get()
|
||||||
|
+ " are incompatible for function " + getName() + " or not upscaleable to common type", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Overload for well-defined operations
|
/// Overload for well-defined operations
|
||||||
@ -527,7 +557,7 @@ private:
|
|||||||
template <typename T0, typename T1, typename ResultType = typename Op<T0, T1>::ResultType>
|
template <typename T0, typename T1, typename ResultType = typename Op<T0, T1>::ResultType>
|
||||||
bool executeRightTypeImpl(Block & block, const ColumnNumbers & arguments, size_t result, const ColumnVector<T0> * col_left)
|
bool executeRightTypeImpl(Block & block, const ColumnNumbers & arguments, size_t result, const ColumnVector<T0> * col_left)
|
||||||
{
|
{
|
||||||
if (auto col_right = typeid_cast<ColumnVector<T1> *>(block.getByPosition(arguments[1]).column.get()))
|
if (auto col_right = typeid_cast<const ColumnVector<T1> *>(block.getByPosition(arguments[1]).column.get()))
|
||||||
{
|
{
|
||||||
auto col_res = new ColumnVector<ResultType>;
|
auto col_res = new ColumnVector<ResultType>;
|
||||||
block.getByPosition(result).column = col_res;
|
block.getByPosition(result).column = col_res;
|
||||||
@ -538,7 +568,7 @@ private:
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (auto col_right = typeid_cast<ColumnConst<T1> *>(block.getByPosition(arguments[1]).column.get()))
|
else if (auto col_right = typeid_cast<const ColumnConst<T1> *>(block.getByPosition(arguments[1]).column.get()))
|
||||||
{
|
{
|
||||||
auto col_res = new ColumnVector<ResultType>;
|
auto col_res = new ColumnVector<ResultType>;
|
||||||
block.getByPosition(result).column = col_res;
|
block.getByPosition(result).column = col_res;
|
||||||
@ -550,14 +580,14 @@ private:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
throw Exception("Logical error: unexpected type of column", ErrorCodes::LOGICAL_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ColumnConst overload
|
/// ColumnConst overload
|
||||||
template <typename T0, typename T1, typename ResultType = typename Op<T0, T1>::ResultType>
|
template <typename T0, typename T1, typename ResultType = typename Op<T0, T1>::ResultType>
|
||||||
bool executeRightTypeImpl(Block & block, const ColumnNumbers & arguments, size_t result, const ColumnConst<T0> * col_left)
|
bool executeRightTypeImpl(Block & block, const ColumnNumbers & arguments, size_t result, const ColumnConst<T0> * col_left)
|
||||||
{
|
{
|
||||||
if (auto col_right = typeid_cast<ColumnVector<T1> *>(block.getByPosition(arguments[1]).column.get()))
|
if (auto col_right = typeid_cast<const ColumnVector<T1> *>(block.getByPosition(arguments[1]).column.get()))
|
||||||
{
|
{
|
||||||
auto col_res = new ColumnVector<ResultType>;
|
auto col_res = new ColumnVector<ResultType>;
|
||||||
block.getByPosition(result).column = col_res;
|
block.getByPosition(result).column = col_res;
|
||||||
@ -568,7 +598,7 @@ private:
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (auto col_right = typeid_cast<ColumnConst<T1> *>(block.getByPosition(arguments[1]).column.get()))
|
else if (auto col_right = typeid_cast<const ColumnConst<T1> *>(block.getByPosition(arguments[1]).column.get()))
|
||||||
{
|
{
|
||||||
ResultType res = 0;
|
ResultType res = 0;
|
||||||
BinaryOperationImpl<T0, T1, Op<T0, T1>, ResultType>::constant_constant(col_left->getData(), col_right->getData(), res);
|
BinaryOperationImpl<T0, T1, Op<T0, T1>, ResultType>::constant_constant(col_left->getData(), col_right->getData(), res);
|
||||||
@ -582,26 +612,12 @@ private:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename LeftDataType,
|
template <typename LeftDataType>
|
||||||
typename std::enable_if<IsDateOrDateTime<LeftDataType>::value>::type * = nullptr>
|
|
||||||
bool executeLeftType(Block & block, const ColumnNumbers & arguments, const size_t result)
|
bool executeLeftType(Block & block, const ColumnNumbers & arguments, const size_t result)
|
||||||
{
|
{
|
||||||
if (!typeid_cast<const LeftDataType *>(block.getByPosition(arguments[0]).type.get()))
|
if (!typeid_cast<const LeftDataType *>(block.getByPosition(arguments[0]).type.get()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return executeLeftTypeDispatch<LeftDataType>(block, arguments, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename LeftDataType,
|
|
||||||
typename std::enable_if<IsNumeric<LeftDataType>::value>::type * = nullptr>
|
|
||||||
bool executeLeftType(Block & block, const ColumnNumbers & arguments, const size_t result)
|
|
||||||
{
|
|
||||||
return executeLeftTypeDispatch<LeftDataType>(block, arguments, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename LeftDataType>
|
|
||||||
bool executeLeftTypeDispatch(Block & block, const ColumnNumbers & arguments, const size_t result)
|
|
||||||
{
|
|
||||||
using T0 = typename LeftDataType::FieldType;
|
using T0 = typename LeftDataType::FieldType;
|
||||||
|
|
||||||
if ( executeLeftTypeImpl<LeftDataType, ColumnVector<T0>>(block, arguments, result)
|
if ( executeLeftTypeImpl<LeftDataType, ColumnVector<T0>>(block, arguments, result)
|
||||||
@ -614,7 +630,7 @@ private:
|
|||||||
template <typename LeftDataType, typename ColumnType>
|
template <typename LeftDataType, typename ColumnType>
|
||||||
bool executeLeftTypeImpl(Block & block, const ColumnNumbers & arguments, const size_t result)
|
bool executeLeftTypeImpl(Block & block, const ColumnNumbers & arguments, const size_t result)
|
||||||
{
|
{
|
||||||
if (auto col_left = typeid_cast<ColumnType *>(block.getByPosition(arguments[0]).column.get()))
|
if (auto col_left = typeid_cast<const ColumnType *>(block.getByPosition(arguments[0]).column.get()))
|
||||||
{
|
{
|
||||||
if ( executeRightType<LeftDataType, DataTypeDate>(block, arguments, result, col_left)
|
if ( executeRightType<LeftDataType, DataTypeDate>(block, arguments, result, col_left)
|
||||||
|| executeRightType<LeftDataType, DataTypeDateTime>(block, arguments, result, col_left)
|
|| executeRightType<LeftDataType, DataTypeDateTime>(block, arguments, result, col_left)
|
||||||
@ -718,7 +734,7 @@ private:
|
|||||||
template <typename T0>
|
template <typename T0>
|
||||||
bool executeType(Block & block, const ColumnNumbers & arguments, size_t result)
|
bool executeType(Block & block, const ColumnNumbers & arguments, size_t result)
|
||||||
{
|
{
|
||||||
if (ColumnVector<T0> * col = typeid_cast<ColumnVector<T0> *>(&*block.getByPosition(arguments[0]).column))
|
if (const ColumnVector<T0> * col = typeid_cast<const ColumnVector<T0> *>(&*block.getByPosition(arguments[0]).column))
|
||||||
{
|
{
|
||||||
typedef typename Op<T0>::ResultType ResultType;
|
typedef typename Op<T0>::ResultType ResultType;
|
||||||
|
|
||||||
@ -731,7 +747,7 @@ private:
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (ColumnConst<T0> * col = typeid_cast<ColumnConst<T0> *>(&*block.getByPosition(arguments[0]).column))
|
else if (const ColumnConst<T0> * col = typeid_cast<const ColumnConst<T0> *>(&*block.getByPosition(arguments[0]).column))
|
||||||
{
|
{
|
||||||
typedef typename Op<T0>::ResultType ResultType;
|
typedef typename Op<T0>::ResultType ResultType;
|
||||||
|
|
||||||
@ -815,6 +831,8 @@ struct NameBitXor { static constexpr auto name = "bitXor"; };
|
|||||||
struct NameBitNot { static constexpr auto name = "bitNot"; };
|
struct NameBitNot { static constexpr auto name = "bitNot"; };
|
||||||
struct NameBitShiftLeft { static constexpr auto name = "bitShiftLeft"; };
|
struct NameBitShiftLeft { static constexpr auto name = "bitShiftLeft"; };
|
||||||
struct NameBitShiftRight { static constexpr auto name = "bitShiftRight"; };
|
struct NameBitShiftRight { static constexpr auto name = "bitShiftRight"; };
|
||||||
|
struct NameLeast { static constexpr auto name = "least"; };
|
||||||
|
struct NameGreatest { static constexpr auto name = "greatest"; };
|
||||||
|
|
||||||
typedef FunctionBinaryArithmetic<PlusImpl, NamePlus> FunctionPlus;
|
typedef FunctionBinaryArithmetic<PlusImpl, NamePlus> FunctionPlus;
|
||||||
typedef FunctionBinaryArithmetic<MinusImpl, NameMinus> FunctionMinus;
|
typedef FunctionBinaryArithmetic<MinusImpl, NameMinus> FunctionMinus;
|
||||||
@ -831,7 +849,8 @@ typedef FunctionBinaryArithmetic<BitXorImpl, NameBitXor> FunctionBitXor;
|
|||||||
typedef FunctionUnaryArithmetic<BitNotImpl, NameBitNot> FunctionBitNot;
|
typedef FunctionUnaryArithmetic<BitNotImpl, NameBitNot> FunctionBitNot;
|
||||||
typedef FunctionBinaryArithmetic<BitShiftLeftImpl, NameBitShiftLeft> FunctionBitShiftLeft;
|
typedef FunctionBinaryArithmetic<BitShiftLeftImpl, NameBitShiftLeft> FunctionBitShiftLeft;
|
||||||
typedef FunctionBinaryArithmetic<BitShiftRightImpl, NameBitShiftRight> FunctionBitShiftRight;
|
typedef FunctionBinaryArithmetic<BitShiftRightImpl, NameBitShiftRight> FunctionBitShiftRight;
|
||||||
|
typedef FunctionBinaryArithmetic<LeastImpl, NameLeast> FunctionLeast;
|
||||||
|
typedef FunctionBinaryArithmetic<GreatestImpl, NameGreatest> FunctionGreatest;
|
||||||
|
|
||||||
|
|
||||||
/// Оптимизации для целочисленного деления на константу.
|
/// Оптимизации для целочисленного деления на константу.
|
||||||
|
@ -725,7 +725,11 @@ public:
|
|||||||
column_first_array = column_array;
|
column_first_array = column_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
temp_block.insert(ColumnWithNameAndType(column_array->getDataPtr(), argument_type, argument_name));
|
temp_block.insert(ColumnWithNameAndType(
|
||||||
|
column_array->getDataPtr(),
|
||||||
|
argument_type,
|
||||||
|
argument_name));
|
||||||
|
|
||||||
argument_names.insert(argument_name);
|
argument_names.insert(argument_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -745,6 +749,7 @@ public:
|
|||||||
|
|
||||||
replicated_column.name = name;
|
replicated_column.name = name;
|
||||||
replicated_column.column = typeid_cast<ColumnArray &>(*replicated_column.column).getDataPtr();
|
replicated_column.column = typeid_cast<ColumnArray &>(*replicated_column.column).getDataPtr();
|
||||||
|
replicated_column.type = typeid_cast<const DataTypeArray &>(*replicated_column.type).getNestedType(),
|
||||||
temp_block.insert(replicated_column);
|
temp_block.insert(replicated_column);
|
||||||
|
|
||||||
++prerequisite_index;
|
++prerequisite_index;
|
||||||
|
@ -21,6 +21,8 @@ void registerFunctionsArithmetic(FunctionFactory & factory)
|
|||||||
factory.registerFunction<FunctionBitNot>();
|
factory.registerFunction<FunctionBitNot>();
|
||||||
factory.registerFunction<FunctionBitShiftLeft>();
|
factory.registerFunction<FunctionBitShiftLeft>();
|
||||||
factory.registerFunction<FunctionBitShiftRight>();
|
factory.registerFunction<FunctionBitShiftRight>();
|
||||||
|
factory.registerFunction<FunctionLeast>();
|
||||||
|
factory.registerFunction<FunctionGreatest>();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
1 2 1 2 1 2 2 UInt8
|
||||||
|
1.1 2 1.1 2 1.1 2 2 Float64
|
||||||
|
-1 2 -1 2 -1 2 2 Int16
|
||||||
|
1 2 1 2 1 2 2 Float64
|
||||||
|
1 2000 1 2000 1 2000 2000 UInt16
|
||||||
|
1 200000 1 200000 1 200000 200000 UInt32
|
||||||
|
1 20000000000 1 20000000000 1 20000000000 20000000000 UInt64
|
||||||
|
123 123 123 123 123 123 123 UInt8
|
||||||
|
2010-01-02 2011-02-03 2010-01-02 2011-02-03 2010-01-02 2011-02-03 2011-02-03 Date
|
||||||
|
2010-01-02 03:04:05 2011-02-03 04:05:06 2010-01-02 03:04:05 2011-02-03 04:05:06 2010-01-02 03:04:05 2011-02-03 04:05:06 2011-02-03 04:05:06 DateTime
|
||||||
|
10
|
||||||
|
9
|
12
dbms/tests/queries/0_stateless/00192_least_greatest.sql
Normal file
12
dbms/tests/queries/0_stateless/00192_least_greatest.sql
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
SELECT 1 AS x, 2 AS y, least(x, y), greatest(x, y), least(x, materialize(y)), greatest(materialize(x), y), greatest(materialize(x), materialize(y)), toTypeName(least(x, y));
|
||||||
|
SELECT 1.1 AS x, 2 AS y, least(x, y), greatest(x, y), least(x, materialize(y)), greatest(materialize(x), y), greatest(materialize(x), materialize(y)), toTypeName(least(x, y));
|
||||||
|
SELECT -1 AS x, 2 AS y, least(x, y), greatest(x, y), least(x, materialize(y)), greatest(materialize(x), y), greatest(materialize(x), materialize(y)), toTypeName(least(x, y));
|
||||||
|
SELECT 1.0 AS x, 2.0 AS y, least(x, y), greatest(x, y), least(x, materialize(y)), greatest(materialize(x), y), greatest(materialize(x), materialize(y)), toTypeName(least(x, y));
|
||||||
|
SELECT 1 AS x, 2000 AS y, least(x, y), greatest(x, y), least(x, materialize(y)), greatest(materialize(x), y), greatest(materialize(x), materialize(y)), toTypeName(least(x, y));
|
||||||
|
SELECT 1 AS x, 200000 AS y, least(x, y), greatest(x, y), least(x, materialize(y)), greatest(materialize(x), y), greatest(materialize(x), materialize(y)), toTypeName(least(x, y));
|
||||||
|
SELECT 1 AS x, 20000000000 AS y, least(x, y), greatest(x, y), least(x, materialize(y)), greatest(materialize(x), y), greatest(materialize(x), materialize(y)), toTypeName(least(x, y));
|
||||||
|
SELECT 123 AS x, 123 AS y, least(x, y), greatest(x, y), least(x, materialize(y)), greatest(materialize(x), y), greatest(materialize(x), materialize(y)), toTypeName(least(x, y));
|
||||||
|
SELECT toDate('2010-01-02') AS x, toDate('2011-02-03') AS y, least(x, y), greatest(x, y), least(x, materialize(y)), greatest(materialize(x), y), greatest(materialize(x), materialize(y)), toTypeName(least(x, y));
|
||||||
|
SELECT toDateTime('2010-01-02 03:04:05') AS x, toDateTime('2011-02-03 04:05:06') AS y, least(x, y), greatest(x, y), least(x, materialize(y)), greatest(materialize(x), y), greatest(materialize(x), materialize(y)), toTypeName(least(x, y));
|
||||||
|
SELECT greatest(now(), now() + 10) - now();
|
||||||
|
SELECT greatest(today(), yesterday() + 10) - today();
|
Loading…
Reference in New Issue
Block a user