Об’єднайте декілька результатів у підзапиті в одне значення, розділене комами


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 або щось подібне, може працювати?

Відповіді:


134

Навіть це буде служити цілі

Зразки даних

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/…, яка передує цій відповіді, і, отже, може бути "оригіналом"
Пол Д'Амбра

1
Те саме тут, не впевнений, чому це не оцінено вище
Марсель

1
Привіт, Приянка, ти можеш сказати мені, чи потрібно і чому тут передбачати пункт "and t1.name = t2.name"?
Koen

2
Це чудово. Я прагнув оптимізувати функцію UDF, як зазначено у прийнятій відповіді, яка вбивала мій сервер. Я перейшов із 102 секунди пошуку до менш ніж 1. Порівняння плану виконання склало 78% -22%, але це не стосується часу виконання ...
toxaq

Тільки нагадування про те, що вам знадобиться це провідне ',' інакше ви отримаєте кутові дужки у своєму висновку.
Тім Скарборо

45

1. Створіть ОДС:

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
Це все одно відчуває себе як хак. Я все ще використовую підзапити, тому ще багато додаткової обробки триває. Я впевнений, що існує краще рішення (реструктуризація таблиці або інший спосіб розглянути проблему).
Донні Томас

1
Я б не назвав це хакерством. Це ефективніше, ніж курсор, і йому не вистачає накладних витрат, необхідних для створення тимчасової таблиці з даними, структурованими так, як ви цього хочете.
Скотт Лоуренс

1
Соромно, що стовпці не можуть бути параметрами. У такому вигляді вам потрібно буде створити функцію для кожного дитячого спілкування!
Джон Пол Джонс,

1
Це нормально - мені потрібно об’єднати лише ці конкретні стовпці. Решта - це „традиційні” об’єднання.
Донні Томас

Я не пам’ятаю найкращого способу зробити це без цього методу.
aF.

11

Я думаю, що ви на правильному шляху з COALESCE. Дивіться тут приклад побудови рядка, розділеного комами:

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


2
Чудово! Я бачив кілька посилань, які обговорювали COALESCE, але вони передбачали створення ОДС за допомогою тригерів. Посилання, яке ви надіслали, містить ключ із єдиним оператором SELECT. Я додаю відповідь із правильним рішенням, щоб його було простіше знайти іншим. Дякую!
Донні Томас

1
Привіт, Бен, я думаю, що для відповіді потрібно трохи детальніше, а саме про те, як створити UDF тощо. Як тільки я це зрозумію, я додаю рішення як відповідь, яку можна редагувати у спільноті. Будь ласка, не соромтеся редагувати його, після чого я прийму це як відповідь. Вибачте за непорозуміння.
Донні Томас

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 була подібна функція. Поки що я використовую рішення Бена, щоб збити все, що я хочу.
Донні Томас

0

Можливо, вам доведеться надати детальну інформацію для більш точної відповіді.

Оскільки ваш набір даних виглядає начебто вузьким, ви можете розглянути можливість просто використовувати рядок для кожного результату та виконати подальшу обробку на клієнті.

Отже, якщо ви дійсно прагнете змусити сервер виконати роботу, поверніть набір результатів типу

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

що, звичайно, є простим ВНУТРІШНІМ ПРИЄДНАННЯМ за ідентифікатором

Після того, як ви отримаєте набір результатів у клієнта, підтримуйте змінну, яка називається CurrentName, і використовуйте її як тригер, коли потрібно припинити збір SomeColumn у корисну справу, яку ви хочете зробити.


Я думав про це, але не був дуже впевнений, що це елегантне рішення - я хотів би, щоб SQL Server повернув правильно побудований набір результатів, а не те, що потрібно буде обробляти далі. Вам потрібні додаткові деталі? Я спростив структуру таблиці, але, думаю, у вас є.
Донні Томас

0

Припускаючи, що у таблиці A є лише речення WHERE, створюємо збережену процедуру таким чином:

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, і не зовсім змусило його працювати, як просив ОП. Ось рішення, з яким я опинився:

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,%

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.