简体   繁体   English

SQL查询以连接Oracle中多个行的列值

[英]SQL Query to concatenate column values from multiple rows in Oracle

Would it be possible to construct SQL to concatenate column values from multiple rows? 可以构造SQL来连接多行中的列值吗?

The following is an example: 以下是一个示例:

Table A 表A

PID
A
B
C

Table B 表B

PID   SEQ    Desc

A     1      Have
A     2      a nice
A     3      day.
B     1      Nice Work.
C     1      Yes
C     2      we can 
C     3      do 
C     4      this work!

Output of the SQL should be - SQL的输出应为-

PID   Desc
A     Have a nice day.
B     Nice Work.
C     Yes we can do this work!

So basically the Desc column for out put table is a concatenation of the SEQ values from Table B? 因此,基本上,输出表的Desc列是表B中SEQ值的串联?

Any help with the SQL? 对SQL有帮助吗?

There are a few ways depending on what version you have - see the oracle documentation on string aggregation techniques . 有几种方法取决于您使用的版本-请参阅有关字符串聚合技术oracle文档 A very common one is to use LISTAGG : 一种非常常见的方法是使用LISTAGG

SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description
FROM B GROUP BY pid;

Then join to A to pick out the pids you want. 然后加入A以选择您想要的pids

Note: Out of the box, LISTAGG only works correctly with VARCHAR2 columns. 注意:开箱即用, LISTAGG仅可与VARCHAR2列一起正常使用。

There's also an XMLAGG function, which works on versions prior to 11.2. 还有一个XMLAGG函数,可用于11.2之前的版本。 Because WM_CONCAT is undocumented and unsupported by Oracle , it's recommended not to use it in production system. 由于WM_CONCATOracle未记录和不支持的 ,因此建议不要在生产系统中使用它。

With XMLAGG you can do the following: 使用XMLAGG您可以执行以下操作:

SELECT XMLAGG(XMLELEMENT(E,ename||',')).EXTRACT('//text()') "Result" 
FROM employee_names

What this does is 这是什么

  • put the values of the ename column (concatenated with a comma) from the employee_names table in an xml element (with tag E) employee_names表中ename列的值(用逗号连接)放在xml元素中(带有标签E)
  • extract the text of this 提取此文本
  • aggregate the xml (concatenate it) 聚合xml(将其连接)
  • call the resulting column "Result" 将结果列称为“结果”

With SQL model clause: 使用SQL模型子句:

SQL> select pid
  2       , ltrim(sentence) sentence
  3    from ( select pid
  4                , seq
  5                , sentence
  6             from b
  7            model
  8                  partition by (pid)
  9                  dimension by (seq)
 10                  measures (descr,cast(null as varchar2(100)) as sentence)
 11                  ( sentence[any] order by seq desc
 12                    = descr[cv()] || ' ' || sentence[cv()+1]
 13                  )
 14         )
 15   where seq = 1
 16  /

P SENTENCE
- ---------------------------------------------------------------------------
A Have a nice day
B Nice Work.
C Yes we can do this work!

3 rows selected.

I wrote about this here . 在这里写过这个。 And if you follow the link to the OTN-thread you will find some more, including a performance comparison. 而且,如果您单击指向OTN线程的链接,则会发现更多内容,包括性能比较。

The LISTAGG analytic function was introduced in Oracle 11g Release 2 , making it very easy to aggregate strings. LISTAGG分析功能是在Oracle 11g第2版中引入的,使聚集字符串非常容易。 If you are using 11g Release 2 you should use this function for string aggregation. 如果您使用的是11g第2版,则应使用此函数进行字符串聚合。 Please refer below url for more information about string concatenation. 请参考以下网址,以获取有关字符串连接的更多信息。

http://www.oracle-base.com/articles/misc/StringAggregationTechniques.php http://www.oracle-base.com/articles/misc/StringAggregationTechniques.php

String Concatenation 字符串串联

As most of the answers suggest, LISTAGG is the obvious option. 正如大多数答案所暗示的那样, LISTAGG是显而易见的选择。 However, one annoying aspect with LISTAGG is that if the total length of concatenated string exceeds 4000 characters( limit for VARCHAR2 in SQL ), the below error is thrown, which is difficult to manage in Oracle versions upto 12.1 但是, LISTAGG一个令人讨厌的方面是,如果串联字符串的总长度超过4000个字符(SQL中的VARCHAR2限制),则会引发以下错误,这在Oracle 12.1版之前的版本中很难管理。

ORA-01489: result of string concatenation is too long ORA-01489:字符串连接的结果太长

A new feature added in 12cR2 is the ON OVERFLOW clause of LISTAGG . LISTAGGON OVERFLOW子句是12cR2中添加的新功能。 The query including this clause would look like: 包含此子句的查询如下所示:

SELECT pid, LISTAGG(Desc, ' ' on overflow truncate) WITHIN GROUP (ORDER BY seq) AS desc
FROM B GROUP BY pid;

The above will restrict the output to 4000 characters but will not throw the ORA-01489 error. 以上将输出限制为4000个字符,但不会引发ORA-01489错误。

These are some of the additional options of ON OVERFLOW clause: 这些是ON OVERFLOW子句的一些附加选项:

  • ON OVERFLOW TRUNCATE 'Contd..' : This will display 'Contd..' at the end of string (Default is ... ) ON OVERFLOW TRUNCATE 'Contd..' :这将在字符串的末尾显示'Contd..' (默认为...
  • ON OVERFLOW TRUNCATE '' : This will display the 4000 characters without any terminating string. ON OVERFLOW TRUNCATE '' :将显示4000个字符而没有任何终止字符串。
  • ON OVERFLOW TRUNCATE WITH COUNT : This will display the total number of characters at the end after the terminating characters. ON OVERFLOW TRUNCATE WITH COUNT :将在结束字符之后的末尾显示字符总数。 Eg:- ' ...(5512) ' 例如: ...(5512) '
  • ON OVERFLOW ERROR : If you expect the LISTAGG to fail with the ORA-01489 error ( Which is default anyway ). ON OVERFLOW ERROR :如果您期望LISTAGG失败并出现ORA-01489错误(无论如何都是默认值)。

For those who must solve this problem using Oracle 9i (or earlier), you will probably need to use SYS_CONNECT_BY_PATH, since LISTAGG is not available. 对于必须使用Oracle 9i(或更早版本)解决此问题的用户,您可能需要使用SYS_CONNECT_BY_PATH,因为LISTAGG不可用。

To answer the OP, the following query will display the PID from Table A and concatenate all the DESC columns from Table B: 为了回答OP,以下查询将显示表A中的PID并连接表B中的所有DESC列:

SELECT pid, SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions
FROM (
       SELECT ROW_NUMBER () OVER (PARTITION BY pid ORDER BY pid, seq) rnum, pid, description
       FROM (
              SELECT a.pid, seq, description
              FROM table_a a, table_b b
              WHERE a.pid = b.pid(+)
             )
      )
START WITH rnum = 1
CONNECT BY PRIOR rnum = rnum - 1 AND PRIOR pid = pid
GROUP BY pid
ORDER BY pid;

There may also be instances where keys and values are all contained in one table. 在某些情况下,键和值都包含在一个表中。 The following query can be used where there is no Table A, and only Table B exists: 在没有表A且仅存在表B的情况下,可以使用以下查询:

SELECT pid, SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions
FROM (
       SELECT ROW_NUMBER () OVER (PARTITION BY pid ORDER BY pid, seq) rnum, pid, description
       FROM (
              SELECT pid, seq, description
              FROM table_b
             )
      )
START WITH rnum = 1
CONNECT BY PRIOR rnum = rnum - 1 AND PRIOR pid = pid
GROUP BY pid
ORDER BY pid;

All values can be reordered as desired. 所有值都可以根据需要重新排序。 Individual concatenated descriptions can be reordered in the PARTITION BY clause, and the list of PIDs can be reordered in the final ORDER BY clause. 各个串联的描述可以在PARTITION BY子句中重新排序,而PID列表可以在最终的ORDER BY子句中重新排序。


Alternately: there may be times when you want to concatenate all the values from an entire table into one row. 或者:有时您可能希望将整个表中的所有值连接成一行。

The key idea here is using an artificial value for the group of descriptions to be concatenated. 这里的关键思想是为要连接的一组描述使用人工值。

In the following query, the constant string '1' is used, but any value will work: 在以下查询中,使用常量字符串“ 1”,但是任何值都可以使用:

SELECT SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions
FROM (
       SELECT ROW_NUMBER () OVER (PARTITION BY unique_id ORDER BY pid, seq) rnum, description
       FROM (
              SELECT '1' unique_id, b.pid, b.seq, b.description
              FROM table_b b
             )
      )
START WITH rnum = 1
CONNECT BY PRIOR rnum = rnum - 1;

Individual concatenated descriptions can be reordered in the PARTITION BY clause. 各个串联的描述可以在PARTITION BY子句中重新排序。

Several other answers on this page have also mentioned this extremely helpful reference: https://oracle-base.com/articles/misc/string-aggregation-techniques 此页面上的其他几个答案也提到了此非常有用的参考: https : //oracle-base.com/articles/misc/string-aggregation-techniques

  1. LISTAGG delivers the best performance if sorting is a must(00:00:05.85) 如果必须进行排序,LISTAGG可提供最佳性能(00:00:05.85)

    SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description FROM B GROUP BY pid;

  2. COLLECT delivers the best performance if sorting is not needed(00:00:02.90): 如果不需要排序,COLLECT可以提供最佳性能(00:00:02.90):

    SELECT pid, TO_STRING(CAST(COLLECT(Desc) AS varchar2_ntt)) AS Vals FROM B GROUP BY pid;

  3. COLLECT with ordering is bit slower(00:00:07.08): 按顺序进行收藏会比较慢(00:00:07.08):

    SELECT pid, TO_STRING(CAST(COLLECT(Desc ORDER BY Desc) AS varchar2_ntt)) AS Vals FROM B GROUP BY pid;

All other techniques were slower. 所有其他技术都比较慢。

Before you run a select query, run this: 在运行选择查询之前,请运行以下命令:

SET SERVEROUT ON SIZE 6000

SELECT XMLAGG(XMLELEMENT(E,SUPLR_SUPLR_ID||',')).EXTRACT('//text()') "SUPPLIER" 
FROM SUPPLIERS;

I using the LISTAGG but return this string for persian string ! 我使用LISTAGG,但是将此字符串返回波斯字符串!

my query: 我的查询:

SELECT
 listagg(DESCRIPTION,' , ') within group (order by DESCRIPTION) 
FROM
B_CEREMONY

result: 结果:

'A7'1 , ,4F

Please help me. 请帮我。

wow this solution is worked: 哇,这个解决方案是可行的:

SELECT listagg(convert(DESCRIPTION, 'UTF8', 'AL16UTF16'),' , ') within group 
(order by DESCRIPTION) 
FROM  B_CEREMONY;

Try this code: 试试这个代码:

 SELECT XMLAGG(XMLELEMENT(E,fieldname||',')).EXTRACT('//text()') "FieldNames"
    FROM FIELD_MASTER
    WHERE FIELD_ID > 10 AND FIELD_AREA != 'NEBRASKA';

In the select where you want your concatenation, call a SQL function. 在选择要串联的位置,调用SQL函数。

For example: 例如:

select PID, dbo.MyConcat(PID)
   from TableA;

Then for the SQL function: 然后对于SQL函数:

Function MyConcat(@PID varchar(10))
returns varchar(1000)
as
begin

declare @x varchar(1000);

select @x = isnull(@x +',', @x, @x +',') + Desc
  from TableB
    where PID = @PID;

return @x;

end

The Function Header syntax might be wrong, but the principle does work. 函数头语法可能是错误的,但是该原理确实有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM