在处理数据库中的数据聚合任务时,我们经常需要将多行数据合并成一行字符串或列表。Oracle 提供了两种函数来实现这一目标:LISTAGG 和 WM_CONCAT。这两种函数各有特点,适用于不同的场景。
LISTAGG 是一个聚合函数,用于将一组值连接成一个字符串。它支持指定分隔符,并且可以设置最大长度以避免结果超出限制。
LISTAGG(column, separator) WITHIN GROUP (ORDER BY column) 假设我们有一个表 orders,其中包含以下数据:
| order_id | product |
|---|---|
| 1 | Apples |
| 1 | Bananas |
| 2 | Oranges |
| 2 | Apples |
| 2 | Oranges |
创建表 orders
CREATE TABLE orders ( order_id NUMBER(5) NOT NULL, product VARCHAR2(20) NOT NULL ); 插入数据
INSERT INTO orders (order_id, product) VALUES (1, 'Apples'); INSERT INTO orders (order_id, product) VALUES (1, 'Bananas'); INSERT INTO orders (order_id, product) VALUES (2, 'Oranges'); INSERT INTO orders (order_id, product) VALUES (2, 'Apples'); INSERT INTO orders (order_id, product) VALUES (2, 'Oranges'); 使用上面的 orders 表,我们可以使用 LISTAGG 函数来获取每个订单的所有产品:
SELECT order_id, LISTAGG(product, ', ') WITHIN GROUP (ORDER BY product) AS products FROM orders GROUP BY order_id; 输出:
| order_id | products |
|---|---|
| 1 | Apples, Bananas |
| 2 | Apples, Oranges, Oranges |
由于 LISTAGG 不支持 DISTINCT,我们可以通过子查询或者窗口函数来达到去除重复值的目的:
SELECT order_id, LISTAGG(product, ', ') WITHIN GROUP (ORDER BY product) AS products FROM ( SELECT order_id, product FROM ( SELECT order_id, product, ROW_NUMBER() OVER (PARTITION BY order_id, product ORDER BY (NULL)) AS rn FROM orders ) WHERE rn = 1 ) GROUP BY order_id; 输出:
| order_id | products |
|---|---|
| 1 | Apples, Bananas |
| 2 | Apples, Oranges |
WM_CONCAT 是另一个连接函数,它将多个值连接成一个字符串。这个函数主要用于连接少量的数据,因为它没有内置的排序功能,并且可能会遇到性能问题。
WM_CONCAT(column) 使用上面的 orders 表,我们可以使用 WM_CONCAT 函数来获取每个订单的所有产品:
SELECT order_id, WM_CONCAT(product) AS products FROM orders GROUP BY order_id; 输出:
| order_id | products |
|---|---|
| 1 | Apples, Bananas |
| 2 | Oranges,Oranges,Apples |
备注:使用 WM_CONCAT 函数没有内置的排序功能,结果可能无顺序。
在 WM_CONCAT 中去除重复值的方式非常直观,可以直接使用 DISTINCT 关键字:
SELECT order_id, WM_CONCAT(DISTINCT product) AS products FROM orders GROUP BY order_id; 输出:
| order_id | products |
|---|---|
| 1 | Apples, Bananas |
| 2 | Apples ,Oranges |
DISTINCT 关键字去除重复值。LISTAGG 和 WM_CONCAT 都可以用来连接多个值,但 LISTAGG 更加灵活并且性能更优。对于大多数情况来说,推荐使用 LISTAGG 函数,特别是在需要对结果进行排序或需要自定义分隔符的情况下。在仅需要去除重复值时 WM_CONCAT 可能更方便,WM_CONCAT 可以直接使用 DISTINCT 关键字,而 LISTAGG 则需要通过子查询或窗口函数的方法来实现。