รวมผลลัพธ์หลายรายการในแบบสอบถามย่อยเป็นค่าเดียวที่คั่นด้วยจุลภาค


84

ฉันมีสองโต๊ะ:

TableA
------
ID,
Name

TableB
------
ID,
SomeColumn,
TableA_ID (FK for TableA)

ความสัมพันธ์เป็นหนึ่งแถวTableA- หลายแถวTableBหลาย

ตอนนี้ฉันต้องการเห็นผลลัพธ์ดังนี้:

ID     Name      SomeColumn

1.     ABC       X, Y, Z (these are three different rows)
2.     MNO       R, S

สิ่งนี้จะไม่ทำงาน (ผลลัพธ์หลายรายการในแบบสอบถามย่อย):

SELECT ID,
       Name, 
       (SELECT SomeColumn FROM TableB WHERE F_ID=TableA.ID)
FROM TableA

นี่เป็นปัญหาเล็กน้อยหากฉันดำเนินการกับฝั่งไคลเอ็นต์ แต่นี้จะหมายความว่าฉันจะมีการเรียกใช้คำสั่ง X ในทุกหน้าที่ X TableAคือจำนวนของผลของการ

โปรดทราบว่าฉันไม่สามารถทำ GROUP BY หรือสิ่งที่คล้ายกันได้เนื่องจากจะส่งคืนผลลัพธ์หลายรายการสำหรับแถวของTableA.

ฉันไม่แน่ใจว่า UDF ที่ใช้ COALESCE หรือสิ่งที่คล้ายกันอาจได้ผลหรือไม่?

คำตอบ:


135

แม้สิ่งนี้จะตอบสนองวัตถุประสงค์

ข้อมูลตัวอย่าง

declare @t table(id int, name varchar(20),somecolumn varchar(MAX))
insert into @t
    select 1,'ABC','X' union all
    select 1,'ABC','Y' union all
    select 1,'ABC','Z' union all
    select 2,'MNO','R' union all
    select 2,'MNO','S'

คำถาม:

SELECT ID,Name,
    STUFF((SELECT ',' + CAST(T2.SomeColumn AS VARCHAR(MAX))
     FROM @T T2 WHERE T1.id = T2.id AND T1.name = T2.name
     FOR XML PATH('')),1,1,'') SOMECOLUMN
FROM @T T1
GROUP BY id,Name

เอาท์พุต:

ID  Name    SomeColumn
1   ABC     X,Y,Z
2   MNO     R,S

13
ไม่แน่ใจว่าเหตุใดจึงไม่ถูกหยิบขึ้นมาเนื่องจากสามารถแก้ปัญหาได้โดยไม่ต้องใช้ฟังก์ชันผู้ใช้ คุณสามารถดูแนวคิดเดียวกันนี้ได้ที่codecorner.galanter.net/2009/06/25/…ซึ่งมาก่อนคำตอบนี้และอาจเป็น "ต้นฉบับ"
Paul D'Ambra

1
เหมือนกันที่นี่ไม่แน่ใจว่าทำไมถึงไม่ได้รับการจัดอันดับสูงกว่านี้
Marcel

1
สวัสดีคุณ Priyanka คุณช่วยบอกฉันได้ไหมว่าทำไมจึงจำเป็นต้องใช้ประโยค "และ t1.name = t2.name" ที่นี่
Koen

2
ยอดเยี่ยมมาก ฉันต้องการเพิ่มประสิทธิภาพฟังก์ชัน UDF ตามที่ระบุไว้ในคำตอบที่ยอมรับซึ่งกำลังฆ่าเซิร์ฟเวอร์ของฉัน ฉันเปลี่ยนจากการค้นหา 102 วินาทีลงมาเหลือน้อยกว่า 1 การเปรียบเทียบแผนการดำเนินการคือ 78% -22% แต่นั่นไม่เกี่ยวกับเวลาดำเนินการ ...
toxaq

เพียงเตือนว่าคุณต้องมีเครื่องหมาย "" ที่นำหน้าไม่เช่นนั้นคุณจะต้องใช้วงเล็บเหลี่ยมในผลลัพธ์
Tim Scarborough

45

1. สร้าง UDF:

CREATE FUNCTION CombineValues
(
    @FK_ID INT -- The foreign key from TableA which is used 
               -- to fetch corresponding records
)
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE @SomeColumnList VARCHAR(8000);

SELECT @SomeColumnList =
    COALESCE(@SomeColumnList + ', ', '') + CAST(SomeColumn AS varchar(20)) 
FROM TableB C
WHERE C.FK_ID = @FK_ID;

RETURN 
(
    SELECT @SomeColumnList
)
END

2. ใช้ในแบบสอบถามย่อย:

SELECT ID, Name, dbo.CombineValues(FK_ID) FROM TableA

3. หากคุณใช้ขั้นตอนการจัดเก็บคุณสามารถทำได้ดังนี้:

CREATE PROCEDURE GetCombinedValues
 @FK_ID int
As
BEGIN
DECLARE @SomeColumnList VARCHAR(800)
SELECT @SomeColumnList =
    COALESCE(@SomeColumnList + ', ', '') + CAST(SomeColumn AS varchar(20)) 
FROM TableB
WHERE FK_ID = @FK_ID 

Select *, @SomeColumnList as SelectedIds
    FROM 
        TableA
    WHERE 
        FK_ID = @FK_ID 
END

1
นี่ยังรู้สึกเหมือนแฮ็ค ฉันยังคงใช้เคียวรีย่อยดังนั้นยังมีการประมวลผลเพิ่มเติมอีกมากมาย ฉันแน่ใจว่ามีทางออกที่ดีกว่าอยู่แล้ว (การปรับโครงสร้างตารางหรือวิธีอื่นในการดูปัญหา)
Donnie Thomas

1
ฉันจะไม่เรียกสิ่งนี้ว่าแฮ็ค มีประสิทธิภาพมากกว่าเคอร์เซอร์และไม่มีค่าใช้จ่ายที่จำเป็นในการสร้างตารางชั่วคราวที่มีโครงสร้างข้อมูลตามที่คุณต้องการ
Scott Lawrence

1
อัปยศคอลัมน์ต้องไม่เป็นพารามิเตอร์ ในฐานะที่เป็นคุณจะต้องสร้างฟังก์ชันสำหรับความสัมพันธ์ของเด็กทุกคน!
จอห์นพอลโจนส์

1
ไม่เป็นไร - ฉันต้องการรวมเฉพาะคอลัมน์เหล่านี้เท่านั้น ส่วนที่เหลือคือการรวมแบบ 'ดั้งเดิม'
Donnie Thomas

ฉันจำวิธีที่ดีที่สุดในการทำเช่นนี้ไม่ได้หากไม่มีวิธีนี้
เอฟ.

11

ฉันคิดว่าคุณมาถูกทางแล้วกับ COALESCE ดูตัวอย่างการสร้างสตริงคั่นด้วยจุลภาคได้ที่นี่:

http://www.sqlteam.com/article/using-coalesce-to-build-comma-delimited-string


2
สุดยอด! ฉันเคยเห็นลิงค์ที่พูดถึง COALESCE แต่พวกเขาเกี่ยวข้องกับการสร้าง UDF ด้วยทริกเกอร์ ลิงก์ที่คุณส่งมีคีย์พร้อมด้วยคำสั่ง SELECT เดียว ฉันกำลังเพิ่มคำตอบพร้อมวิธีแก้ปัญหาที่ถูกต้องเพื่อให้คนอื่นหาได้ง่ายขึ้น ขอบคุณ!
Donnie Thomas

1
สวัสดีเบ็นฉันคิดว่าคำตอบต้องการรายละเอียดอีกเล็กน้อยคือวิธีสร้าง UDF เป็นต้นเมื่อฉันเข้าใจแล้วฉันจะเพิ่มโซลูชันเป็นคำตอบที่แก้ไขได้โดยชุมชน โปรดอย่าลังเลที่จะแก้ไขหลังจากนั้นฉันจะยอมรับว่าเป็นคำตอบ ขอโทษสำหรับความสับสน.
Donnie Thomas

11

ใน MySQL มีฟังก์ชันgroup_concatที่จะส่งคืนสิ่งที่คุณต้องการ

SELECT TableA.ID, TableA.Name, group_concat(TableB.SomeColumn) 
as SomColumnGroup FROM TableA LEFT JOIN TableB ON 
TableB.TableA_ID = TableA.ID

1
สิ่งนี้จะสมบูรณ์แบบถ้ามีฟังก์ชันที่คล้ายกันใน SQL Server ฉันกำลังใช้วิธีแก้ปัญหาของเบ็นเพื่อตอกสิ่งที่ฉันต้องการเข้าด้วยกัน
Donnie Thomas

0

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

เนื่องจากชุดข้อมูลของคุณดูเหมือนแคบคุณอาจพิจารณาใช้เพียงแถวต่อผลลัพธ์และดำเนินการหลังการประมวลผลที่ไคลเอ็นต์

ดังนั้นหากคุณต้องการให้เซิร์ฟเวอร์ทำงานจริง ๆ จะส่งคืนชุดผลลัพธ์เช่น

ID       Name       SomeColumn
1        ABC        X
1        ABC        Y
1        ABC        Z
2        MNO        R
2        MNO        S

ซึ่งแน่นอนว่าเป็น INNER JOIN บน ID ง่ายๆ

เมื่อคุณได้ผลลัพธ์กลับมาที่ไคลเอนต์แล้วให้รักษาตัวแปรที่เรียกว่า CurrentName และใช้เป็นทริกเกอร์เมื่อหยุดรวบรวม SomeColumn ในสิ่งที่มีประโยชน์ที่คุณต้องการให้ทำ


ฉันคิดถึงสิ่งนี้ แต่ไม่แน่ใจมากนักว่านี่เป็นโซลูชันที่ยอดเยี่ยมหรือไม่ - ฉันต้องการให้ SQL Server ส่งคืนผลลัพธ์ที่สร้างขึ้นอย่างถูกต้องไม่ใช่สิ่งที่จะต้องประมวลผลเพิ่มเติม คุณต้องการรายละเอียดเพิ่มเติมหรือไม่? ฉันได้ปรับโครงสร้างตารางให้เรียบง่ายขึ้นแล้ว แต่ฉันคิดว่าคุณเข้าใจแล้ว
Donnie Thomas

0

สมมติว่าคุณมีเฉพาะคำสั่ง WHERE บนตาราง A ให้สร้างกระบวนงานที่เก็บไว้ดังนี้:

SELECT Id, Name From tableA WHERE ...

SELECT tableA.Id AS ParentId, Somecolumn 
FROM tableA INNER JOIN tableB on TableA.Id = TableB.F_Id 
WHERE ...

จากนั้นเติม DataSet ds ลงไป แล้ว

ds.Relations.Add("foo", ds.Tables[0].Columns("Id"), ds.Tables[1].Columns("ParentId"));

ในที่สุดคุณสามารถเพิ่มตัวทำซ้ำในหน้าที่ใส่เครื่องหมายจุลภาคสำหรับทุกบรรทัด

 <asp:DataList ID="Subcategories" DataKeyField="ParentCatId" 
DataSource='<%# Container.DataItem.CreateChildView("foo") %>' RepeatColumns="1"
 RepeatDirection="Horizontal" ItemStyle-HorizontalAlign="left" ItemStyle-VerticalAlign="top" 
runat="server" >

ด้วยวิธีนี้คุณจะทำในฝั่งไคลเอ็นต์ แต่มีเพียงแบบสอบถามเดียวโดยส่งผ่านข้อมูลน้อยที่สุดระหว่างฐานข้อมูลและส่วนหน้า


0

ฉันลองใช้วิธีแก้ปัญหาที่ priyanka.sarkar กล่าวถึงและไม่ค่อยได้ผลตามที่ OP ขอ นี่คือวิธีแก้ปัญหาที่ฉันลงเอยด้วย:

SELECT ID, 
        SUBSTRING((
            SELECT ',' + T2.SomeColumn
            FROM  @T T2 
            WHERE WHERE T1.id = T2.id
            FOR XML PATH('')), 2, 1000000)
    FROM @T T1
GROUP BY ID

-1

วิธีแก้ไขด้านล่าง:

SELECT GROUP_CONCAT(field_attr_best_weekday_value)as RAVI
FROM content_field_attr_best_weekday LEFT JOIN content_type_attraction
    on content_field_attr_best_weekday.nid = content_type_attraction.nid
GROUP BY content_field_attr_best_weekday.nid

ใช้สิ่งนี้คุณยังสามารถเปลี่ยนการเข้าร่วม


-1
SELECT t.ID, 
       t.NAME, 
       (SELECT t1.SOMECOLUMN 
        FROM   TABLEB t1 
        WHERE  t1.F_ID = T.TABLEA.ID) 
FROM   TABLEA t; 

สิ่งนี้จะใช้ได้กับการเลือกจากตารางอื่นโดยใช้แบบสอบถามย่อย


-1

ฉันได้ตรวจสอบคำตอบทั้งหมดแล้ว ฉันคิดว่าในการแทรกฐานข้อมูลควรเป็นดังนี้:

ID     Name      SomeColumn
1.     ABC       ,X,Y Z (these are three different rows)
2.     MNO       ,R,S

ลูกน้ำควรอยู่ที่ส่วนท้ายก่อนหน้าและทำการค้นหาโดยชอบ %,X,%

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