所有SQL数据库都支持标准聚合函数:COUNT()
, SUM()
, AVG()
, MIN()
, MAX()
.
一些数据库支持其他聚合功能,如:
EVERY()
STDDEV_POP()
STDDEV_SAMP()
VAR_POP()
VAR_SAMP()
ARRAY_AGG()
STRING_AGG()
但如果你想自己玩呢?
Java 8流收集器
当使用Java 8流时,我们可以轻松地提交自己的聚合函数(即a)。让我们假设我们希望在流中找到第二个最高值。可以获得这样的最高值:
System.out.println(
Stream.of(1, 2, 3, 4)
.collect(Collectors.maxBy(Integer::compareTo))
) ;
屈服:
Optional[4]
那么,第二高的价值呢?我们可以编写以下收集器:
System.out.println(
Stream.of(1, 6, 2, 3, 4, 4, 5).parallel()
.collect(Collector.of(
() -> new int[] {
Integer.MIN_VALUE,
Integer.MIN_VALUE
},
(a, i) -> {
if (a[0] < i) {
a[1] = a[0];
a[0] = i;
}
else if (a[1] < i)
a[1] = i;
},
(a1, a2) -> {
if (a2[0] > a1[0]) {
a1[1] = a1[0];
a1[0] = a2[0];
if (a2[1] > a1[1])
a1[1] = a2[1];
}
else if (a2[0] > a1[1])
a1[1] = a2[0];
return a1;
},
a -> a[1]
))
) ;
不会有什么花哨的。它具有以下四项职能:
- 供应商<int[]>*a供货商它提供了一个中介
int[]
长度为2,初始化为Integer.MIN_VALUE
,每个。此数组将记住MAX()
值位于0位置的流中,而SECOND_MAX()
值在位置1处的流中。 - BiConsumer<int[],integer>*a累加器它将新值从流积累到中间数据结构中。
- BinaryOperator<int[]>*a组合器它结合了两个中间数据结构。这仅用于并行流。
- 函数<int[],整数>*修整机函数提取
SECOND_MAX()
函数从中间数组中的第二个位置开始。
现在的产出是:
5
如何使用SQL做同样的事情?
Many SQL databases offer a very similar way of calculating custom aggregate functions. Here's how to do the exact same thing with...
甲骨文
With the usual syntactic ceremony...
CREATE TYPE u_second_max AS OBJECT (
-- Intermediary data structure
MAX NUMBER,
SECMAX NUMBER,
-- Corresponds to the Collector.supplier() function
STATIC FUNCTION ODCIAggregateInitialize(sctx IN OUT u_second_max) RETURN NUMBER,
-- Corresponds to the Collector.accumulate() function
MEMBER FUNCTION ODCIAggregateIterate(self IN OUT u_second_max, value IN NUMBER) RETURN NUMBER,
-- Corresponds to the Collector.combineer() function
MEMBER FUNCTION ODCIAggregateMerge(self IN OUT u_second_max, ctx2 IN u_second_max) RETURN NUMBER,
-- Correspodns to the Collector.finisher() function
MEMBER FUNCTION ODCIAggregateTerminate(self IN u_second_max, returnValue OUT NUMBER, flags IN NUMBER) RETURN NUMBER
)
/
-- This is our "colletor" implementation
CREATE OR REPLACE TYPE BODY u_second_max IS
STATIC FUNCTION ODCIAggregateInitialize(sctx IN OUT u_second_max)
RETURN NUMBER IS
BEGIN
SCTX := U_SECOND_MAX(0, 0);
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateIterate(self IN OUT u_second_max, value IN NUMBER) RETURN NUMBER IS
BEGIN
IF VALUE > SELF.MAX THEN
SELF.SECMAX := SELF.MAX;
SELF.MAX := VALUE;
ELSIF VALUE > SELF.SECMAX THEN
SELF.SECMAX := VALUE;
END IF;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateTerminate(self IN u_second_max, returnValue OUT NUMBER, flags IN NUMBER) RETURN NUMBER IS
BEGIN
RETURNVALUE := SELF.SECMAX;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateMerge(self IN OUT u_second_max, ctx2 IN u_second_max) RETURN NUMBER IS
BEGIN
IF CTX2.MAX > SELF.MAX THEN
SELF.SECMAX := SELF.MAX;
SELF.MAX := CTX2.MAX;
IF CTX2.SECMAX > SELF.SECMAX THEN
SELF.SECMAX := CTX2.SECMAX;
END IF;
ELSIF CTX2.MAX > SELF.SECMAX THEN
SELF.SECMAX := CTX2.MAX;
END IF;
RETURN ODCIConst.Success;
END;
END;
/
-- Finally, we have to give this aggregate function a name
CREATE FUNCTION SECOND_MAX (input NUMBER) RETURN NUMBER
PARALLEL_ENABLE AGGREGATE USING u_second_max;
/
我们现在可以在Sakila数据库:
SELECT
max(film_id),
second_max(film_id)
FROM film;
得到:
MAX SECOND_MAX
------------------
1000 999
更好的是,我们可以免费使用聚合函数作为窗口函数!
SELECT
film_id,
length,
max(film_id) OVER (PARTITION BY length),
second_max(film_id) OVER (PARTITION BY length)
FROM film
ORDER BY length, film_id;
上述收益率:
FILM_ID LENGTH MAX SECOND_MAX
---------------------------------
15 46 730 505
469 46 730 505
504 46 730 505
505 46 730 505
730 46 730 505
237 47 869 784
247 47 869 784
393 47 869 784
398 47 869 784
407 47 869 784
784 47 869 784
869 47 869 784
2 48 931 866
410 48 931 866
575 48 931 866
630 48 931 866
634 48 931 866
657 48 931 866
670 48 931 866
753 48 931 866
845 48 931 866
866 48 931 866
931 48 931 866
很漂亮对吧?
PostgreSQL
类中,PostgreSQL支持稍微简洁的语法。CREATE AGGREGATE
声明。如果我们不允许并行性,我们可以编写这个最小的实现:
CREATE FUNCTION second_max_sfunc (
state INTEGER[], data INTEGER
) RETURNS INTEGER[] AS
$
BEGIN
IF state IS NULL THEN
RETURN ARRAY[data, NULL];
ELSE
RETURN CASE
WHEN state[1] > data
THEN CASE
WHEN state[2] > data
THEN state
ELSE ARRAY[state[1], data]
END
ELSE ARRAY[data, state[1]]
END;
END IF;
END;
$ LANGUAGE plpgsql;
/
CREATE FUNCTION second_max_ffunc (
state INTEGER[]
) RETURNS INTEGER AS
$
BEGIN
RETURN state[2];
END;
$ LANGUAGE plpgsql;
CREATE AGGREGATE second_max (INTEGER) (
SFUNC = second_max_sfunc,
STYPE = INTEGER[],
FINALFUNC = second_max_ffunc
);
在这里,我们使用STYPE
( Collector.supplier()
),SFUNC
( Collector.accumulator()
),以及FINALFUNC
( Collector.finisher()
)规格。
其他数据库
许多其他数据库允许指定用户定义的聚合函数。查找您的数据库手册的详细信息,以了解更多。它们总是以与Java 8相同的方式工作Collector
.