Znajduję sposób na agregowanie ciągów z różnych wierszy w jeden wiersz. Chcę to zrobić w wielu różnych miejscach, więc dobrze byłoby mieć funkcję ułatwiającą to. Wypróbowałem rozwiązania wykorzystujące COALESCE
i FOR XML
, ale one po prostu tego nie robią.
Agregacja ciągów mogłaby zrobić coś takiego:
id | Name Result: id | Names
-- - ---- -- - -----
1 | Matt 1 | Matt, Rocks
1 | Rocks 2 | Stylus
2 | Stylus
Rzuciłem okiem na funkcje agregujące zdefiniowane w CLR jako zamiennik COALESCE
i FOR XML
, ale najwyraźniej SQL Azure nie obsługuje elementów zdefiniowanych w CLR, co jest dla mnie uciążliwe, ponieważ wiem, że możliwość ich użycia rozwiązałaby wiele problemów problemy dla mnie.
Czy jest jakieś możliwe obejście lub podobnie optymalna metoda (która może nie być tak optymalna jak CLR, ale hej , wezmę to, co mogę uzyskać), której mogę użyć do zagregowania moich rzeczy?
for xml
nie działa to dla Ciebie?for xml
pokazuje 25% wykorzystania pod względem wydajności zapytań (większość zapytań!)for xml path
zapytania. Niektórzy szybciej niż inni. Może to zależeć od twoich danych, ale te, które używają,distinct
są z mojego doświadczenia wolniejsze niż używaniegroup by
. A jeśli używasz.value('.', nvarchar(max))
do uzyskania połączonych wartości, powinieneś zmienić to na.value('./text()[1]', nvarchar(max))
Odpowiedzi:
ROZWIĄZANIE
Definicja optymalnego może się różnić, ale oto sposób łączenia ciągów z różnych wierszy przy użyciu zwykłego języka Transact SQL, co powinno działać dobrze na platformie Azure.
WYJAŚNIENIE
Podejście sprowadza się do trzech kroków:
Ponumeruj wiersze, używając
OVER
iPARTITION
grupując je i porządkując zgodnie z potrzebami konkatenacji. Wynik toPartitioned
CTE. W każdej partycji przechowujemy liczbę wierszy, aby później filtrować wyniki.Używając rekurencyjnego CTE (
Concatenated
) iteruj po numerach wierszy (NameNumber
kolumn) dodającName
wartości doFullName
kolumny.Odfiltruj wszystkie wyniki oprócz tych z najwyższym
NameNumber
.Należy pamiętać, że aby zapytanie było przewidywalne, należy zdefiniować zarówno grupowanie (na przykład w scenariuszu wiersze z tym samym
ID
są konkatenowane), jak i sortowanie (przyjąłem, że przed konkatenacją po prostu sortujesz ciąg alfabetycznie).Szybko przetestowałem rozwiązanie na SQL Server 2012 z następującymi danymi:
Wynik zapytania:
źródło
Czy metody używające FOR XML PATH, jak poniżej, są naprawdę takie wolne? Itzik Ben-Gan pisze, że ta metoda ma dobre wyniki w swojej książce T-SQL Querying (moim zdaniem pan Ben-Gan jest źródłem godnym zaufania).
źródło
id
kolumnie, gdy rozmiar tabeli stanie się problemem.&
przełączony na&
itd.). Tutajfor xml
podano bardziej poprawne rozwiązanie .Dla tych z nas, którzy to znaleźli
i nie używają Azure SQL Database:STRING_AGG()
w PostgreSQL, SQL Server 2017 i Azure SQLhttps://www.postgresql.org/docs/current/static/functions-aggregate.html
https://docs.microsoft.com/en-us/sql/t-sql/ funkcje / string-agg-transact-sql
GROUP_CONCAT()
w MySQLhttp://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_group-concat
(Podziękowania dla @Brianjorden i @milanio za aktualizację platformy Azure)
Przykładowy kod:
SQL Fiddle: http://sqlfiddle.com/#!18/89251/1
źródło
STRING_AGG
został przesunięty z powrotem do 2017 r. Nie jest dostępny w 2016 r.Chociaż odpowiedź @serge jest poprawna, ale porównałem jego zużycie czasu z xmlpath i stwierdziłem, że xmlpath jest tak szybszy. Napiszę kod porównawczy i możesz to sprawdzić samodzielnie. To jest sposób @serge:
A to jest sposób xmlpath:
źródło
Aktualizacja: Ms SQL Server 2017+, Azure SQL Database
Można użyć:
STRING_AGG
.Użycie jest dość proste na żądanie OP:
Czytaj więcej
Cóż, mój stary brak odpowiedzi został słusznie usunięty (pozostawiony bez odpowiedzi poniżej), ale jeśli ktoś zdarzy się tu wylądować w przyszłości, jest dobra wiadomość. Zaimplementowali również STRING_AGG () w Azure SQL Database. Powinno to zapewnić dokładną funkcjonalność pierwotnie wymaganą w tym poście z natywną i wbudowaną obsługą. @hrobky wspomniał o tym wcześniej jako o funkcji SQL Server 2016 w tamtym czasie.
--- Stary post: Za mało reputacji, aby odpowiedzieć bezpośrednio @hrobky, ale STRING_AGG wygląda świetnie, jednak obecnie jest dostępny tylko w SQL Server 2016 vNext. Mamy nadzieję, że wkrótce trafi również do bazy danych Azure SQL Databse.
źródło
STRING_AGG()
ma zostać udostępniony w programie SQL Server 2017 na dowolnym poziomie zgodności. docs.microsoft.com/en-us/sql/t-sql/functions/ ...Możesz użyć + =, aby połączyć ciągi, na przykład:
jeśli wybierzesz @test, wszystkie nazwy zostaną połączone
źródło
select @test += name + ', ' from names
ORDER BY
w zapytaniu. Powinieneś użyć jednej z wymienionych alternatyw.Uznałem, że odpowiedź Serge'a jest bardzo obiecująca, ale napotkałem również problemy z wydajnością, tak jak zostało napisane. Jednak kiedy zrestrukturyzowałem go tak, aby używał tabel tymczasowych i nie zawierał podwójnych tabel CTE, wydajność wzrosła z 1 minuty 40 sekund do sekundy dla 1000 połączonych rekordów. Tutaj jest dla każdego, kto musi to zrobić bez FOR XML w starszych wersjach SQL Server:
źródło