Bir alt sorudaki birden çok sonucu virgülle ayrılmış tek bir değerde birleştirin


84

İki masam var:

TableA
------
ID,
Name

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

İlişki bir satırdır TableA- çoğu TableB.

Şimdi şöyle bir sonuç görmek istiyorum:

ID     Name      SomeColumn

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

Bu işe yaramaz (bir alt sorguda birden çok sonuç):

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

İşlemi istemci tarafında yaparsam bu önemsiz bir sorundur. Ancak bu, her sayfada X sorgusu çalıştırmam gerektiği anlamına gelecektir, burada X sonuçlarının sayısıdır TableA.

Satırları için birden çok sonuç döndüreceğinden, bir GROUP BY veya benzer bir şey yapamayacağımı unutmayın TableA.

COALESCE veya benzeri bir şey kullanan bir UDF'nin işe yarayıp yaramayacağından emin değilim?

Yanıtlar:


135

Bu bile amaca hizmet edecek

Örnek veri

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'

Sorgu:

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

Çıktı:

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

13
Bir kullanıcı işlevi gerektirmeden sorunu çözdüğü için bunun neden seçilmediğinden emin olun. Aynı fikrin burada ifade edildiğini görebilirsiniz codecorner.galanter.net/2009/06/25/… ki bu cevap bu cevabın öncesidir ve bu yüzden "orijinal" olabilir
Paul D'Ambra

1
Aynı burada, neden daha yüksek derecelendirilmediğinden emin değilim
Marcel

1
Merhaba priyanka, burada "ve t1.name = t2.name" cümlesinin gerekli olup olmadığını ve nedenini bana söyleyebilir misiniz?
Koen

2
Bu mükemmel. Kabul edilen yanıtta listelendiği gibi sunucumu öldüren bir UDF işlevini optimize etmek istiyordum. 102 saniyelik bir aramadan 1'in altına düştüm. Yürütme planı karşılaştırması% 78 -% 22 idi, ancak bu yürütme süresiyle ilgili değil ...
toxaq

Sadece o öncü '' ya ihtiyacınız olacağını, aksi takdirde çıktınızda açılı parantezlerle sonuçlanacağınızı hatırlatmak isteriz.
Tim Scarborough

45

1. UDF'yi oluşturun:

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. Alt sorguda kullanın:

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

3. Depolanan yordamı kullanıyorsanız, şu şekilde yapabilirsiniz:

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
Bu hala bir hack gibi geliyor. Hala alt sorgular kullanıyorum, bu yüzden hala çok fazla ekstra işlem devam ediyor. Eminim daha iyi bir çözüm vardır (masayı yeniden yapılandırma veya soruna başka bir bakış açısı).
Donnie Thomas

1
Ben buna hile demezdim. Bir imlecin olacağından daha etkilidir ve istediğiniz şekilde yapılandırılmış verilerle geçici bir tablo oluşturmak için gerekli olan ek yükten yoksundur.
Scott Lawrence

1
Utanç, sütunlar parametre olamaz. Hali hazırda her çocuk ilişkisi için bir işlev yapmanız gerekecek!
John Paul Jones

1
Sorun değil - Yalnızca bu belirli sütunları birleştirmem gerekiyor. Gerisi 'geleneksel' birleşimlerdir.
Donnie Thomas

Bunu bu yöntem olmadan yapmanın en iyi yolunu hatırlamıyorum.
aF.

11

COALESCE ile doğru yolda olduğunuzu düşünüyorum. Virgülle ayrılmış bir dize oluşturma örneği için buraya bakın:

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


2
Harika! COALESCE'yi tartışan bazı bağlantılar görmüştüm, ancak tetikleyicilerle UDF'ler oluşturmayı içeriyorlardı. Gönderdiğiniz bağlantı, tek bir SELECT ifadesiyle birlikte anahtara sahiptir. Başkalarının bulması daha kolay olsun diye doğru çözüme sahip bir yanıt ekliyorum. Teşekkürler!
Donnie Thomas

1
Merhaba Ben, sanırım yanıtın biraz daha ayrıntıya ihtiyacı var, yani UDF'nin nasıl oluşturulacağı vb. Bunu anladıktan sonra, çözümü topluluk tarafından düzenlenebilir bir yanıt olarak ekleyeceğim. Lütfen düzenlemekten çekinmeyin, daha sonra bunu yanıt olarak kabul edeceğim. Karışıklık için özür dilerim.
Donnie Thomas

11

MySQL'de , istediğinizi döndürecek bir group_concat işlevi vardır.

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'da benzer bir işlev olsaydı bu mükemmel olurdu. Hal böyle olunca, istediğimi bir araya getirmek için Ben'in çözümünü kullanıyorum.
Donnie Thomas

0

Daha kesin bir yanıt için biraz daha ayrıntı sağlamanız gerekebilir.

Veri kümeniz biraz dar göründüğünden, sonuç başına bir satır kullanmayı ve istemcide son işlemeyi gerçekleştirmeyi düşünebilirsiniz.

Dolayısıyla, gerçekten sunucunun işi yapmasını istiyorsanız, aşağıdaki gibi bir sonuç kümesi döndürür:

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

hangisi elbette ID'de basit bir INNER JOIN

Sonuç kümesini istemcide geri aldığınızda, CurrentName adlı bir değişkeni koruyun ve bunu yapmak istediğiniz yararlı şeyde SomeColumn toplamayı durdurduğunuzda bunu bir tetikleyici olarak kullanın.


Bunu düşündüm, ancak bunun zarif bir çözüm olup olmadığından pek emin değildim - SQL Server'ın daha fazla işlenmesi gerekecek bir şey değil, düzgün bir şekilde oluşturulmuş sonuç kümesini döndürmesini istiyorum. Ek ayrıntılara ihtiyacınız var mı? Masa yapısını sadeleştirdim ama sanırım anladınız.
Donnie Thomas

0

Tablo A'da yalnızca WHERE yan tümcelerinizin olduğunu varsayarsak, böylece bir saklı yordam oluşturun:

SELECT Id, Name From tableA WHERE ...

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

Ardından bir DataSet ds'yi bununla doldurun. Sonra

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

Son olarak, sayfaya her satıra virgül koyan bir tekrarlayıcı ekleyebilirsiniz.

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

Bu şekilde, istemci tarafında, ancak tek bir sorgu ile, veritabanı ile ön uç arasında minimum veri aktarımı yapacaksınız.


0

Priyanka.sarkar'ın bahsettiği çözümü denedim ve OP'nin istediği gibi tam olarak çalışmadı. İşte bulduğum çözüm:

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

Aşağıdaki çözüm:

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

Bunu kullanın, ayrıca Birleştirmeleri de değiştirebilirsiniz


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

Bu, alt sorgu kullanarak farklı tablodan seçim yapmak için çalışacaktır.


-1

Tüm cevapları inceledim. Veritabanına eklemenin şöyle olması gerektiğini düşünüyorum:

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

Virgül önceki sonunda olmalı ve benzer şekilde arama yapmalıdır %,X,%

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.