Jesteśmy w połowie cyklu między wydaniami, gdzie nie słyszymy jeszcze o żadnej z planowanych funkcji SQL Server vNext. To prawdopodobnie najlepszy czas, aby naciskać na Microsoft w celu wprowadzenia ulepszeń, o ile możemy poprzeć nasze prośby uzasadnionymi sprawami biznesowymi. W SQL Server 2016, STRING_SPLIT
rozwiązał dawno brakującą lukę w języku, który wprawdzie nie był przeznaczony do skomplikowanego przetwarzania ciągów znaków. I to właśnie chcę dzisiaj poruszyć.
Na lata przed SQL Server 2016 (i przez lata później) pisaliśmy własne wersje, ulepszaliśmy je z biegiem czasu, a nawet kłóciliśmy się o to, kto jest najszybszy. Pisaliśmy na blogu o każdej mikrosekundzie, którą mogliśmy zyskać, a ja wielokrotnie powtarzałem:„to jest mój ostatni post o rozdzielaniu strun!” A jednak oto jesteśmy.
Zawsze będę twierdził, że parametry wyceniane w tabeli są właściwym sposobem na rozdzielanie ciągów. Ale gdy ja Uważam, że te oddzielone przecinkami fragmenty tekstu nigdy nie powinny być ujawniane w bazie danych w tej formie, dzielenie ciągów nadal jest powszechnym przypadkiem użycia — kilka moich postów na blogu znajduje się w pierwszej piątce wyświetleń każdego dnia .
Dlaczego więc ludzie nadal próbują dzielić ciągi za pomocą funkcji wycenianych w tabeli, gdy istnieje lepszy zamiennik? Niektóre, jestem tego pewien, ponieważ wciąż są w starszych wersjach, utknęły na starszym poziomie kompatybilności lub nie mogą w ogóle uciec od dzielenia ciągów, ponieważ TVP nie są obsługiwane przez ich język lub ORM. Co do reszty, podczas gdy STRING_SPLIT
jest zarówno wygodny, jak i wydajny, nie jest doskonały. Ma ograniczenia, które powodują pewne trudności i sprawiają, że zastąpienie istniejących wywołań funkcji wywołaniem natywnym jest kłopotliwe lub niemożliwe.
Oto moja lista.
Te ograniczenia nie są wyczerpujące, ale wymieniłem te ważne w moim kolejność priorytetowa (a Andy Mallon napisał o tym dzisiaj na blogu):
- Ogranicznik jednoznakowy
Wygląda na to, że funkcja została utworzona tylko z myślą o prostym przypadku użycia:CSV. Ludzie mają bardziej złożone ciągi niż1,2,3
lubA|B|C
i często są wprowadzane do ich baz danych z systemów znajdujących się poza ich kontrolą. Jak opisuję w tej odpowiedzi i tej wskazówce, istnieją sposoby na obejście tego (naprawdę nieefektywne operacje zastępowania), ale są one naprawdę brzydkie i, szczerze mówiąc, cofają wszystkie korzyści wydajnościowe oferowane przez natywną implementację. Ponadto niektóre problemy z tym sprowadzają się w szczególności do:„Cóż,string_to_array
PostgreSQL obsługuje wiele ograniczników znaków, więc dlaczego nie może SQL Server?"Implementacja:Zwiększ maksymalny rozmiarseparator
. - Brak wskazania kolejności wprowadzania
Wynikiem funkcji jest zestaw i z natury zestawy nie mają kolejności. I chociaż w większości przypadków zobaczysz ciąg wejściowy, taki jakbob,ted,frank
wychodzą w tej kolejności (bob
ted
frank
), nie ma gwarancji (z lub bez niechlujnego(ORDER BY (SELECT NULL))
włamać się). Wiele domowych funkcji zawiera kolumnę wyjściową, aby wskazać pozycję porządkową w ciągu, co może być ważne, jeśli lista jest ułożona w określonej kolejności lub dokładna pozycja porządkowa ma jakieś znaczenie.Implementacja:Dodaj opcję włączenia kolumny pozycji porządkowej w wyjście. - Typ wyjścia jest oparty tylko na wejściu
Kolumna wyjściowa funkcji jest ustalona navarchar
lubnvarchar
i jest określana dokładnie przez długość całego ciągu wejściowego, a nie długość najdłuższego elementu. Masz więc listę 25 liter, typ danych wyjściowych to co najmniejvarchar(51)
. W przypadku dłuższych ciągów może to sprowadzać się do problemów z przyznawaniem pamięci, w zależności od użycia, i może powodować problemy, jeśli konsument polega na wyprowadzaniu innego typu danych (powiedzmy,int
, które funkcje czasami określają, aby później uniknąć niejawnych konwersji). Jako obejście, użytkownicy czasami tworzą własne tabele tymczasowe lub zmienne tabeli i zrzucają tam dane wyjściowe funkcji przed interakcją z nią, co może prowadzić do problemów z wydajnością. Wdrożenie:Dodaj opcję, aby określić typ danych wyjściowychvalue
. - Nie można zignorować pustych elementów lub końcowych ograniczników
Gdy masz ciąg, taki jaka,,,b,
, możesz oczekiwać, że zostaną wyświetlone tylko dwa elementy, ponieważ pozostałe trzy są puste. Większość niestandardowych plików TVF, które widziałem, odcina końcowe ograniczniki i/lub odfiltrowuje ciągi o zerowej długości, aleSTRING_SPLIT
zwraca wszystkie 5 wierszy. Utrudnia to zamianę w funkcji natywnej, ponieważ musisz również dodać logikę zawijania, aby wyeliminować te encje. Implementacja:Dodaj opcję ignorowania pustych elementów. - Nie można filtrować duplikatów
Jest to prawdopodobnie mniej powszechne żądanie i łatwe do rozwiązania za pomocąDISTINCT
lubGROUP BY
, ale wiele funkcji robi to automatycznie. W takich przypadkach nie ma żadnej różnicy w wydajności, ale jest coś, o czym zapomniałeś dodać samodzielnie (pomyśl o dużej liście, z wieloma duplikatami, dołączanej do dużego stołu). Implementacja:Dodaj opcję odfiltrowywania duplikatów.
Oto uzasadnienie biznesowe.
To wszystko brzmi teoretycznie, ale oto uzasadnienie biznesowe, które zapewniam, że jest bardzo realne. W Wayfair dysponujemy pokaźnym zapleczem SQL Server i mamy dosłownie dziesiątki różnych zespołów, które przez lata tworzyły własne funkcje z wartościami tabelarycznymi. Niektóre są lepsze od innych, ale wszystkie są wywoływane z tysięcy wierszy kodu. Niedawno rozpoczęliśmy projekt, w którym próbujemy zastąpić je wywołaniami STRING_SPLIT
, ale natknęliśmy się na przypadki blokowania obejmujących kilka z powyższych ograniczeń.
Niektóre są łatwe do obejścia dzięki funkcji owijarki. Ale ogranicznik pojedynczego znaku ograniczenie zmusiło nas do oceny okropnego obejścia za pomocą REPLACE
, a to okazało się eliminować oczekiwane przez nas korzyści w zakresie wydajności, zmuszając nas do pompowania hamulców. I w takich przypadkach straciliśmy kluczową kartę przetargową w dążeniu do aktualizacji do poziomu kompatybilności (nie wszystkie bazy danych są na 130, nie mówiąc już o 140). W takich przypadkach tracimy nie tylko STRING_SPLIT
ulepszeń, ale także innych ponad 130 ulepszeń wydajności, z których moglibyśmy korzystać, gdyby STRING_SPLIT
sam w sobie był wystarczająco przekonujący, by naciskać na uaktualnienie poziomu zgodności.
Więc proszę o pomoc.
Odwiedź ten element opinii:
- STRING_SPLIT nie jest kompletny
Zagłosuj! Co ważniejsze, zostaw komentarz opisywanie rzeczywistych przypadków użycia, które sprawiają, że STRING_SPLIT
ból lub brak dla ciebie startu. Same głosy nie wystarczą, ale przy wystarczającej ilości namacalnych i jakościowych informacji zwrotnych istnieje szansa, że zaczną poważnie traktować te luki.
Mam ochotę wspierać wieloznakowe ograniczniki (nawet, powiedzmy, rozwijając z [n]varchar(1)
do [n]varchar(5)
) to nieinwazyjne ulepszenie, które odblokuje wiele osób, które podzielają mój scenariusz. Inne ulepszenia mogą być trudniejsze do zaimplementowania, niektóre wymagają przeciążeń i/lub ulepszeń językowych, więc nie oczekuję wszystkich tych poprawek w vNext. Ale nawet jedno drobne ulepszenie powtórzyłoby, że STRING_SPLIT
była opłacalną inwestycją i nie zostanie porzucona (jak, powiedzmy, zawarte bazy danych, jedna z bardziej znanych funkcji drive-by).
Dziękujemy za wysłuchanie!