SQL Query เพื่อต่อค่าคอลัมน์จากหลายแถวใน Oracle


169

เป็นไปได้ไหมที่จะสร้าง SQL เพื่อต่อค่าคอลัมน์จากหลายแถวเข้าด้วยกัน?

ต่อไปนี้เป็นตัวอย่าง:

ตารางที่

PID

B
ค

ตาราง B

PID SEQ เรียงลำดับ

1 มี
2 ดี
3 วัน
B 1 การทำงานที่ดี
C 1 ใช่
C 2 เราทำได้ 
C 3 ทำได้ 
C 4 งานนี้!

ผลลัพธ์ของ SQL ควรเป็น -

PID
A ขอให้เป็นวันที่ดี
B Nice Work
C ใช่เราสามารถทำงานนี้ได้!

ดังนั้นโดยทั่วไปคอลัมน์ Desc สำหรับการวางตารางจึงเป็นการรวมกันของค่า SEQ จากตาราง B

มีความช่วยเหลือเกี่ยวกับ SQL หรือไม่?


ดูตัวอย่าง: halisway.blogspot.com/2006/08/…
Andomar

โปรดดูวิธีนี้ มันจะเป็นประโยชน์กับคุณ
Jineesh Uvantavida

คำตอบ:


237

มีไม่กี่วิธีขึ้นอยู่กับสิ่งที่รุ่นที่คุณมีอยู่ - ดูเอกสารของ Oracle เกี่ยวกับเทคนิคการรวมสตริง สิ่งที่ธรรมดามากคือการใช้LISTAGG:

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

จากนั้นเข้าร่วมเพื่อAเลือกสิ่งที่pidsคุณต้องการ

หมายเหตุ:ใช้LISTAGGงานได้เฉพาะกับVARCHAR2คอลัมน์เท่านั้น


2
การใช้ wm_concat () สำหรับ Oracle 10g ต่อข้อความในลำดับที่มากขึ้นของหมายเลขลำดับที่คั่นด้วยเครื่องหมายจุลภาคเราสามารถทำการคั่นด้วย descending โดยอย่างอื่นได้หรือไม่
jagamot

19

นอกจากนี้ยังมี XMLAGGฟังก์ชั่นซึ่งทำงานกับรุ่นก่อนหน้า 11.2 เพราะWM_CONCATเป็นที่ไม่มีเอกสารและได้รับการสนับสนุนโดยออราเคิลก็ไม่แนะนำให้ใช้ในระบบการผลิต

กับ XMLAGGคุณสามารถทำสิ่งต่อไปนี้:

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

สิ่งนี้ทำคืออะไร

  • ใส่ค่าของ enameคอลัมน์ (ตัดแบ่งด้วยเครื่องหมายจุลภาค) จากemployee_namesตารางในองค์ประกอบ xml (พร้อมแท็ก E)
  • แยกข้อความของสิ่งนี้
  • รวม xml (เชื่อมต่อกัน)
  • เรียกคอลัมน์ผลลัพธ์ "ผลลัพธ์"

XMLAGG ทำงานบน Oracle 12.2 นอกจากนี้ XLMAGG ยังอนุญาตให้เชื่อมโยงสตริงที่ยาวมากซึ่ง LISTAGG อาจไม่ใช่เพราะความยาวสุดท้าย
Marco

13

ด้วยข้อโมเดล 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.

ผมเขียนเกี่ยวกับเรื่องนี้ที่นี่ และหากคุณไปที่ลิงก์ไปยัง OTN-thread คุณจะพบข้อมูลเพิ่มเติมรวมถึงการเปรียบเทียบประสิทธิภาพ


10

LISTAGGฟังก์ชั่นการวิเคราะห์เป็นที่รู้จักในOracle 11g Release 2ทำให้มันง่ายมากที่จะสตริงรวม หากคุณใช้ 11g Release 2 คุณควรใช้ฟังก์ชันนี้เพื่อรวมสตริง โปรดอ้างอิง URL ด้านล่างสำหรับข้อมูลเพิ่มเติมเกี่ยวกับการต่อสตริง

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

การต่อสตริง


8

ตามคำตอบส่วนใหญ่แนะนำLISTAGGเป็นตัวเลือกที่ชัดเจน อย่างไรก็ตามสิ่งที่น่ารำคาญอย่างหนึ่งLISTAGGคือถ้าความยาวรวมของสตริงที่ต่อกันเกิน 4,000 ตัวอักษร (จำกัด สำหรับVARCHAR2ใน SQL) ข้อผิดพลาดด้านล่างจะเกิดขึ้นซึ่งทำให้จัดการได้ยากใน Oracle เวอร์ชันไม่เกิน 12.1

ORA-01489: ผลลัพธ์ของการต่อสตริงยาวเกินไป

คุณลักษณะใหม่ที่เพิ่มเข้ามาใน 12cR2 เป็นประโยคON OVERFLOW LISTAGGแบบสอบถามรวมถึงข้อนี้จะมีลักษณะ:

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

ด้านบนจะ จำกัด การส่งออกถึง 4000 ตัวอักษร แต่จะไม่โยน ORA-01489ข้อผิดพลาด

นี่เป็นตัวเลือกเพิ่มเติมของON OVERFLOWclause:

  • ON OVERFLOW TRUNCATE 'Contd..' : สิ่งนี้จะแสดง'Contd..'ที่ท้ายสตริง (ค่าเริ่มต้นคือ...)
  • ON OVERFLOW TRUNCATE '' : นี่จะแสดงตัวละคร 4000 ตัวโดยไม่มีสตริงใด ๆ สิ้นสุด
  • ON OVERFLOW TRUNCATE WITH COUNT: นี่จะแสดงจำนวนตัวอักษรทั้งหมดในตอนท้ายหลังจากตัวอักษรสิ้นสุด เช่น: - ' ...(5512)'
  • ON OVERFLOW ERROR: ถ้าคุณคาดว่าLISTAGGจะล้มเหลวด้วย ORA-01489ข้อผิดพลาด (ซึ่งเป็นค่าเริ่มต้นอยู่แล้ว)

6

สำหรับผู้ที่ต้องแก้ปัญหานี้โดยใช้ Oracle 9i (หรือก่อนหน้า) คุณอาจต้องใช้ SYS_CONNECT_BY_PATH เนื่องจาก LISTAGG ไม่พร้อมใช้งาน

ในการตอบ OP ข้อความค้นหาต่อไปนี้จะแสดง PID จากตาราง A และเชื่อมคอลัมน์ DESC ทั้งหมดจากตาราง 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 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;

อาจมีอินสแตนซ์ที่คีย์และค่าทั้งหมดอยู่ในหนึ่งตาราง สามารถใช้แบบสอบถามต่อไปนี้เมื่อไม่มีตาราง 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;

ค่าทั้งหมดสามารถจัดลำดับใหม่ได้ตามต้องการ สามารถเรียงลำดับคำอธิบายแบบต่อส่วนบุคคลใน PARTITION ตามข้อและรายการของ PID สามารถจัดเรียงใหม่ใน ORDER สุดท้ายโดยส่วนคำสั่ง


อีกทางเลือกหนึ่ง:อาจมีบางครั้งที่คุณต้องการเชื่อมค่าทั้งหมดจากตารางทั้งหมดลงในแถวเดียว

แนวคิดหลักที่นี่คือการใช้ค่าเทียมสำหรับกลุ่มของคำอธิบายที่จะต่อกัน

ในแบบสอบถามต่อไปนี้จะใช้สตริงคงที่ '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;

สามารถเรียงลำดับคำอธิบายแบบต่อส่วนบุคคลใน PARTITION โดยข้อ

คำตอบอื่น ๆ อีกมากมายในหน้านี้ได้กล่าวถึงการอ้างอิงที่มีประโยชน์อย่างยิ่งนี้: https://oracle-base.com/articles/misc/string-aggregation-techniques


3
  1. LISTAGG มอบประสิทธิภาพที่ดีที่สุดหากการคัดแยกเป็นสิ่งที่จำเป็น (00: 00: 05.85)

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

  2. COLLECT ให้ประสิทธิภาพที่ดีที่สุดหากไม่ต้องการเรียงลำดับ (00: 00: 02.90):

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

  3. สะสมด้วยการสั่งซื้อช้าลงเล็กน้อย (00: 00: 07.08):

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

เทคนิคอื่น ๆ ทั้งหมดช้าลง


1
มันจะมีประโยชน์ในการอธิบายรายละเอียดเกี่ยวกับคำตอบของคุณ
Jon Surrell

John, l ไม่ต้องการที่จะทำซ้ำจากบทความ แต่ในระยะสั้นเหล่านี้คือผลลัพธ์: 1. LISTAGG มอบประสิทธิภาพที่ดีที่สุดหากการเรียงลำดับคือต้อง (00: 00: 05.85) 2. COLLECT ให้ประสิทธิภาพที่ดีที่สุดหากการเรียงลำดับไม่ ต้องการ (00: 00: 02.90): เลือก pid, TO_STRING (นักแสดง (รวบรวม (มาก) AS varchar2_ntt)) เป็นค่าเริ่มต้นจากกลุ่ม B โดย pid; 3. การรวบรวมที่มีการสั่งซื้อช้าลงเล็กน้อย (00: 00: 07.08): เลือก pid, TO_STRING (นักแสดง (คอลเลกชัน (เรียงตามลำดับโดยมากที่สุด) AS varchar2_ntt)) เป็น Vals จากกลุ่ม B โดย pid; เทคนิคอื่น ๆ ทั้งหมดช้าลง
Misho

1
คุณสามารถแก้ไขคำตอบของคุณเพื่อรวมข้อมูลที่เกี่ยวข้อง
Jon Surrell

ฉันสายเกินไปในการแก้ไขและนั่นคือเหตุผลที่ฉันเพิ่มอีกครั้ง ขอโทษฉันใหม่ที่นี่และเพิ่งเริ่มที่จะได้รับมัน
Misho

1

ก่อนที่คุณจะเรียกใช้คิวรีที่เลือกให้รันสิ่งนี้:

SET SERVEROUT ON SIZE 6000

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


-3

ในการเลือกตำแหน่งที่คุณต้องการการต่อข้อมูลของคุณเรียกใช้ฟังก์ชัน SQL

ตัวอย่างเช่น:

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

จากนั้นสำหรับฟังก์ชัน 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

ไวยากรณ์ของส่วนหัวของฟังก์ชันอาจผิด แต่หลักการทำงาน


สิ่งนี้ไม่ถูกต้องสำหรับ Oracle
a_horse_with_no_name
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.