Kết hợp nhiều kết quả trong một truy vấn con thành một giá trị được phân tách bằng dấu phẩy


84

Tôi có hai bảng:

TableA
------
ID,
Name

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

Mối quan hệ là một hàng của TableA- nhiều TableB.

Bây giờ, tôi muốn thấy một kết quả như thế này:

ID     Name      SomeColumn

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

Điều này sẽ không hoạt động (nhiều kết quả trong một truy vấn con):

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

Đây là một vấn đề nhỏ nếu tôi xử lý ở phía máy khách. Nhưng điều này có nghĩa là tôi sẽ phải chạy truy vấn X trên mọi trang, trong đó X là số kết quả TableA.

Lưu ý rằng tôi không thể đơn giản thực hiện GROUP BY hoặc thứ gì đó tương tự, vì nó sẽ trả về nhiều kết quả cho các hàng TableA.

Tôi không chắc liệu một UDF, sử dụng COALESCE hoặc thứ gì đó tương tự có thể hoạt động không?

Câu trả lời:


134

Ngay cả điều này sẽ phục vụ mục đích

Dữ liệu mẫu

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'

Truy vấn:

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

Đầu ra:

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

13
Không chắc tại sao điều này đã không được chọn vì nó giải quyết vấn đề mà không yêu cầu chức năng người dùng. Bạn có thể thấy cùng một ý tưởng được thể hiện ở đây codecorner.galanter.net/2009/06/25/… có trước câu trả lời này và vì vậy có thể là "nguyên bản"
Paul D'Ambra

1
Ở đây cũng vậy, không chắc tại sao cái này không được xếp hạng cao hơn
Marcel

1
Xin chào priyanka, bạn có thể cho tôi biết nếu và tại sao mệnh đề "và t1.name = t2.name" là cần thiết ở đây?
Koen

2
Thật tuyệt vời. Tôi đang tìm cách tối ưu hóa chức năng UDF như được liệt kê trong câu trả lời được chấp nhận, điều này đang giết chết máy chủ của tôi. Tôi đi từ một 102 thứ hai tìm kiếm xuống dưới 1. Kế hoạch Thực hiện so sánh là 78% -22% nhưng điều đó không liên quan đến thời gian thực hiện ...
toxaq

Chỉ cần một lời nhắc rằng bạn sẽ cần "hàng đầu" đó, nếu không bạn sẽ kết thúc với dấu ngoặc nhọn trong đầu ra của mình.
Tim Scarborough

45

1. Tạo 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. Sử dụng trong truy vấn con:

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

3. Nếu bạn đang sử dụng thủ tục được lưu trữ, bạn có thể làm như sau:

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
Điều này vẫn cảm thấy giống như một hack. Vì vậy, tôi vẫn đang sử dụng truy vấn phụ, vẫn còn rất nhiều xử lý bổ sung đang diễn ra. Tôi chắc chắn rằng có một giải pháp tốt hơn (tái cấu trúc bảng, hoặc một cách khác để xem xét vấn đề).
Donnie Thomas

1
Tôi sẽ không gọi đây là một vụ hack. Nó hiệu quả hơn con trỏ và nó thiếu chi phí cần thiết để tạo một bảng tạm thời với dữ liệu được cấu trúc theo cách bạn muốn.
Scott Lawrence

1
Xấu hổ là các cột không thể là tham số. Như vậy, bạn sẽ cần tạo một hàm cho mọi mối quan hệ con cái!
John Paul Jones

1
Không sao cả - tôi chỉ cần kết hợp các cột cụ thể này. Phần còn lại là các phép nối 'truyền thống'.
Donnie Thomas

Tôi không nhớ cách tốt nhất để làm điều này mà không có phương pháp này.
aF.

11

Tôi nghĩ rằng bạn đang đi đúng hướng với COALESCE. Xem ở đây để biết ví dụ về cách xây dựng một chuỗi được phân tách bằng dấu phẩy:

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


2
Tuyệt vời! Tôi đã thấy một số liên kết thảo luận về COALESCE, nhưng chúng liên quan đến việc tạo UDF với các trình kích hoạt. Liên kết bạn đã gửi có khóa, với một câu lệnh SELECT. Tôi đang thêm một câu trả lời với giải pháp chính xác để những người khác dễ dàng tìm thấy hơn. Cảm ơn!
Donnie Thomas

1
Xin chào Ben, tôi nghĩ câu trả lời cần chi tiết hơn một chút, cụ thể là cách tạo UDF, v.v. Khi tôi tìm ra điều này, tôi sẽ thêm giải pháp dưới dạng câu trả lời có thể chỉnh sửa của cộng đồng. Vui lòng chỉnh sửa nó, sau đó tôi sẽ chấp nhận đó là câu trả lời. Xin lỗi vì sự nhầm lẫn.
Donnie Thomas

11

Trong MySQL có một hàm group_concat sẽ trả về những gì bạn đang yêu cầu.

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

1
Điều này sẽ là hoàn hảo, nếu có một chức năng tương tự trong SQL Server. Như hiện tại, tôi đang sử dụng giải pháp của Ben để kết hợp những gì tôi muốn.
Donnie Thomas

0

Bạn có thể cần cung cấp thêm một số chi tiết để có câu trả lời chính xác hơn.

Vì tập dữ liệu của bạn có vẻ hơi hẹp, bạn có thể cân nhắc việc chỉ sử dụng một hàng cho mỗi kết quả và thực hiện xử lý hậu kỳ ở máy khách.

Vì vậy, nếu bạn thực sự muốn làm cho máy chủ thực hiện công việc, hãy trả về một tập hợp kết quả như

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

tất nhiên là THAM GIA INNER đơn giản trên ID

Sau khi bạn có tập kết quả trở lại ứng dụng khách, hãy duy trì một biến có tên là CurrentName và sử dụng biến đó làm kích hoạt khi nào ngừng thu thập SomeColumn vào việc hữu ích bạn muốn nó làm.


Tôi đã nghĩ đến điều này, nhưng không chắc liệu đây có phải là một giải pháp thanh lịch hay không - Tôi muốn SQL Server trả về tập kết quả được xây dựng đúng cách, chứ không phải thứ gì đó sẽ cần được xử lý thêm. Bạn có yêu cầu chi tiết bổ sung? Tôi đã đơn giản hóa cấu trúc bảng, nhưng tôi nghĩ bạn đã hiểu.
Donnie Thomas

0

Giả sử bạn chỉ có mệnh đề WHERE trên bảng A, hãy tạo một thủ tục được lưu trữ, do đó:

SELECT Id, Name From tableA WHERE ...

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

Sau đó điền vào một ds DataSet. Sau đó

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

Cuối cùng, bạn có thể thêm một bộ lặp lại trong trang đặt dấu phẩy cho mỗi dòng

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

Theo cách này, bạn sẽ thực hiện ở phía máy khách nhưng chỉ với một truy vấn, truyền dữ liệu tối thiểu giữa cơ sở dữ liệu và giao diện người dùng


0

Tôi đã thử giải pháp mà priyanka.sarkar đã đề cập và nó không hoàn toàn hoạt động như OP yêu cầu. Đây là giải pháp tôi đã kết thúc:

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

Giải pháp bên dưới:

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

Sử dụng điều này, bạn cũng có thể thay đổi các Tham gia


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

Điều này sẽ hoạt động để chọn từ bảng khác nhau bằng cách sử dụng truy vấn phụ.


-1

Tôi đã xem xét tất cả các câu trả lời. Tôi nghĩ rằng trong việc chèn cơ sở dữ liệu nên như sau:

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

Dấu phẩy phải ở cuối trước và thực hiện tìm kiếm bằng cách như %,X,%

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.