Jest to problem, nad którym w przeszłości spędziłem godziny. Wydaje mi się, że jest to coś, co powinno było zostać rozwiązane przez nowoczesne rozwiązania RDBMS , ale jak dotąd nie znalazłem niczego, co naprawdę odnosi się do tego, co uważam za niezwykle powszechną potrzebę w dowolnej aplikacji internetowej lub Windows z zapleczem bazy danych.
Mówię o sortowaniu dynamicznym. W moim świecie fantasy powinno to być tak proste, jak coś takiego:
ORDER BY @sortCol1, @sortCol2
Jest to kanoniczny przykład podawany przez początkujących programistów SQL i procedur składowanych na forach w Internecie. "Dlaczego nie jest to możliwe?" pytają. Niezmiennie ktoś w końcu przychodzi, aby pouczyć ich o skompilowanej naturze procedur składowanych, ogólnie o planach wykonania i wielu innych powodach, dla których nie jest możliwe umieszczenie parametru bezpośrednio w ORDER BY
klauzuli.
Wiem, co niektórzy z was już sobie myślą: „Pozwól więc klientowi sortować”. Oczywiście odciąża to pracę bazy danych. Jednak w naszym przypadku nasze serwery bazodanowe nie są nawet obciążone w 99% przypadków, a nawet nie są jeszcze wielordzeniowe ani nie są żadnymi innymi niezliczonymi ulepszeniami architektury systemu, które mają miejsce co 6 miesięcy. Tylko z tego powodu posiadanie przez nasze bazy danych obsługi sortowania nie byłoby problemem. Dodatkowo bazy danych są bardzodobry w sortowaniu. Są pod tym kątem zoptymalizowane i mieli lata, aby to zrobić dobrze, język do tego jest niesamowicie elastyczny, intuicyjny i prosty, a przede wszystkim każdy początkujący pisarz SQL wie, jak to zrobić, a co ważniejsze, umie go edytować wprowadzać zmiany, konserwować itp. Gdy Twoje bazy danych są dalekie od opodatkowania i chcesz po prostu uprościć (i skrócić!) czas programowania, wydaje się to oczywistym wyborem.
Jest też problem z siecią. Bawiłem się JavaScriptem, który wykonuje sortowanie tabel HTML po stronie klienta, ale nieuchronnie nie są one wystarczająco elastyczne dla moich potrzeb, a ponieważ moje bazy danych nie są nadmiernie opodatkowane i potrafią naprawdę bardzo łatwo sortować, ja Trudno mi uzasadnić czas potrzebny na ponowne napisanie lub zrolowanie własnego sortownika JavaScript. To samo dotyczy generalnie sortowania po stronie serwera, chociaż jest już prawdopodobnie znacznie preferowane niż JavaScript. Nie jestem osobą, która szczególnie lubi narzuty związane z DataSets, więc pozwól mi.
Ale to przypomina, że nie jest to możliwe - a raczej niełatwe. Z poprzednimi systemami zrobiłem niesamowity sposób na uzyskanie dynamicznego sortowania. Nie był ani ładny, ani intuicyjny, prosty ani elastyczny, a początkujący pisarz SQL zostałby stracony w ciągu kilku sekund. Już teraz wygląda to nie tyle na „rozwiązanie”, ale na „komplikację”.
Poniższe przykłady nie mają na celu ujawnienia jakichkolwiek najlepszych praktyk ani dobrego stylu kodowania ani niczego takiego, ani nie wskazują na moje umiejętności jako programisty T-SQL. Są tym, czym są i przyznaję, że są zagmatwane, mają złą formę i są po prostu hackami.
Przekazujemy wartość całkowitą jako parametr do procedury składowanej (nazwijmy parametr po prostu „sort”) i na tej podstawie określamy kilka innych zmiennych. Na przykład ... powiedzmy, że sortowanie to 1 (lub wartość domyślna):
DECLARE @sortCol1 AS varchar(20)
DECLARE @sortCol2 AS varchar(20)
DECLARE @dir1 AS varchar(20)
DECLARE @dir2 AS varchar(20)
DECLARE @col1 AS varchar(20)
DECLARE @col2 AS varchar(20)
SET @col1 = 'storagedatetime';
SET @col2 = 'vehicleid';
IF @sort = 1 -- Default sort.
BEGIN
SET @sortCol1 = @col1;
SET @dir1 = 'asc';
SET @sortCol2 = @col2;
SET @dir2 = 'asc';
END
ELSE IF @sort = 2 -- Reversed order default sort.
BEGIN
SET @sortCol1 = @col1;
SET @dir1 = 'desc';
SET @sortCol2 = @col2;
SET @dir2 = 'desc';
END
Możesz już zobaczyć, jak gdybym zadeklarował więcej zmiennych @colX, aby zdefiniować inne kolumny, naprawdę mógłbym uzyskać kreatywność z kolumnami do sortowania na podstawie wartości „sort”… aby go użyć, zwykle kończy się to następująco niesamowicie niechlujna klauzula:
ORDER BY
CASE @dir1
WHEN 'desc' THEN
CASE @sortCol1
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END DESC,
CASE @dir1
WHEN 'asc' THEN
CASE @sortCol1
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END,
CASE @dir2
WHEN 'desc' THEN
CASE @sortCol2
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END DESC,
CASE @dir2
WHEN 'asc' THEN
CASE @sortCol2
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END
Oczywiście jest to bardzo okrojony przykład. Prawdziwe rzeczy, ponieważ zwykle mamy cztery lub pięć kolumn do obsługi sortowania, każda z ewentualnymi drugorzędnymi lub nawet trzecimi kolumnami do sortowania oprócz tego (na przykład data malejąca, a następnie sortowana wtórnie według nazwy rosnąco) i każda obsługująca bi- sortowanie kierunkowe, które skutecznie podwaja liczbę przypadków. Tak ... bardzo szybko robi się owłosiony.
Chodzi o to, że można „łatwo” zmienić sortowanie przypadków, tak aby identyfikator pojazdu był sortowany przed czasem przechowywania ... ale pseudoelastyczność, przynajmniej w tym prostym przykładzie, naprawdę się kończy. Zasadniczo każdy przypadek, który nie przejdzie testu (ponieważ nasza metoda sortowania nie ma do niego zastosowania tym razem) renderuje wartość NULL. W ten sposób otrzymujesz klauzulę, która działa jak poniżej:
ORDER BY NULL DESC, NULL, [storagedatetime] DESC, blah blah
Masz pomysł. Działa, ponieważ SQL Server skutecznie ignoruje wartości null w kolejności według klauzul. Jest to niezwykle trudne do utrzymania, co prawdopodobnie zauważy każdy z podstawową wiedzą praktyczną na temat SQL. Jeśli straciłem kogokolwiek z was, nie czuj się źle. Zajęło nam dużo czasu, zanim zaczęło działać, a wciąż jesteśmy zdezorientowani, próbując go edytować lub tworzyć nowe, podobne do tego. Na szczęście nie trzeba go często zmieniać, w przeciwnym razie szybko stałby się „nie wart zachodu”.
A jednak zadziałało.
Moje pytanie brzmi zatem: czy jest lepszy sposób?
Nie przeszkadzają mi rozwiązania inne niż procedury składowane, ponieważ zdaję sobie sprawę, że może to nie być droga. Najlepiej chciałbym wiedzieć, czy ktokolwiek może zrobić to lepiej w ramach procedury składowanej, ale jeśli nie, jak wszyscy poradzicie sobie z umożliwieniem użytkownikowi dynamicznego sortowania tabel danych (również dwukierunkowo) za pomocą ASP.NET?
I dziękuję za przeczytanie (lub przynajmniej przejrzenie) tak długiego pytania!
PS: Ciesz się, że nie pokazałem mojego przykładu procedury składowanej, która obsługuje dynamiczne sortowanie, dynamiczne filtrowanie / wyszukiwanie tekstu kolumn, paginację za pomocą ROWNUMBER () OVER, AND spróbuj ... złapać wycofywanie transakcji na błędach ... „wielkości behemota” nawet nie zaczyna ich opisywać.
Aktualizacja:
- Chciałbym uniknąć dynamicznego SQL . Parsowanie łańcucha razem i uruchomienie na nim procedury EXEC podważa wiele z celów posiadania procedury składowanej w pierwszej kolejności. Czasami jednak zastanawiam się, czy wady zrobienia czegoś takiego nie byłyby tego warte, przynajmniej w tych specjalnych dynamicznych przypadkach sortowania. Mimo to zawsze czuję się brudny, gdy robię takie dynamiczne ciągi SQL - jakbym nadal żył w świecie klasycznej ASP.
- Głównym powodem, dla którego chcemy, aby procedury składowane były przede wszystkim, jest bezpieczeństwo . Nie mogę dzwonić w sprawach bezpieczeństwa, tylko sugeruję rozwiązania. Dzięki SQL Server 2005 możemy ustawić uprawnienia (w zależności od użytkownika, jeśli zajdzie taka potrzeba) na poziomie schematu dla poszczególnych procedur składowanych, a następnie bezpośrednio odmówić wszelkich zapytań dotyczących tabel. Krytyka zalet i wad tego podejścia to być może inna kwestia, ale znowu nie jest to moja decyzja. Jestem tylko małpą prowadzącą kod. :)
źródło
Odpowiedzi:
Tak, to ból, a sposób, w jaki to robisz, wygląda podobnie do tego, co ja:
Jest to dla mnie nadal znacznie lepsze niż budowanie dynamicznego SQL z kodu, co dla administratorów baz danych zamienia się w koszmar skalowalności i utrzymania.
To, co robię z kodu, to refaktoryzacja stronicowania i sortowania, więc przynajmniej nie mam tam wielu powtórzeń z wypełnianiem wartości dla
@SortExpr
i@SortDir
.Jeśli chodzi o SQL, zachowaj projekt i formatowanie takie same między różnymi procedurami składowanymi, aby było co najmniej schludne i rozpoznawalne, gdy wchodzisz do wprowadzania zmian.
źródło
Takie podejście zapobiega dwukrotnemu duplikowaniu sortowanych kolumn w kolejności i jest trochę bardziej czytelne w IMO:
źródło
Dynamiczny SQL jest nadal opcją. Musisz tylko zdecydować, czy ta opcja jest bardziej smaczna niż ta, którą masz obecnie.
Oto artykuł, który pokazuje, że: http://www.4guysfromrolla.com/webtech/010704-1.shtml .
źródło
Moje aplikacje często to robią, ale wszystkie dynamicznie budują SQL. Jednak kiedy mam do czynienia z procedurami składowanymi, robię to:
select * from dbo.fn_myData() where ... order by ...
aby dynamicznie określić tam porządek sortowania.W takim razie przynajmniej część dynamiczna jest w twojej aplikacji, ale baza danych nadal wykonuje ciężkie prace.
źródło
Technika procedury składowanej (hack?), Której używałem, aby uniknąć dynamicznego SQL dla niektórych zadań, polega na posiadaniu unikalnej kolumny sortowania. To znaczy,
Ten jest łatwy do pokonania podczas przesyłania - możesz łączyć pola w kolumnie mySort, odwracać kolejność za pomocą funkcji matematycznych lub dat itp.
Najlepiej jednak używać moich widoków siatki asp.net lub innych obiektów z wbudowanym sortowaniem, aby wykonać sortowanie za mnie PO pobraniu danych z Sql-Server. Lub nawet jeśli nie jest wbudowany - np. Zbiory danych itp. W asp.net.
źródło
Istnieje kilka różnych sposobów na włamanie się do tego.
Wymagania wstępne:
Następnie wstaw do tabeli tymczasowej:
Metoda nr 2: skonfiguruj połączony serwer z powrotem do siebie, a następnie wybierz z tego za pomocą zapytania otwartego: http://www.sommarskog.se/share_data.html#OPENQUERY
źródło
Może istnieć trzecia opcja, ponieważ twój serwer ma wiele zapasowych cykli - użyj procedury pomocniczej, aby wykonać sortowanie za pomocą tabeli tymczasowej. Coś jak
Uwaga: nie testowałem tego, ale „powinno” działać w SQL Server 2005 (który utworzy tymczasową tabelę z zestawu wyników bez wcześniejszego określania kolumn).
źródło
Czy w pewnym momencie nie warto odejść od procedur składowanych i po prostu używać zapytań parametrycznych, aby uniknąć tego rodzaju włamań?
źródło
Zgadzam się, używam strony klienta. Ale wygląda na to, że nie jest to odpowiedź, którą chcesz usłyszeć.
Tak więc jest doskonały, taki jaki jest. Nie wiem, dlaczego chciałbyś to zmienić, a nawet zapytać „Czy jest lepszy sposób”. Naprawdę powinno się nazywać „Drogą”. Poza tym wydaje się, że działa i pasuje do potrzeb projektu i prawdopodobnie będzie wystarczająco rozszerzalny przez wiele lat. Ponieważ Twoje bazy danych nie są opodatkowane, a sortowanie jest naprawdę łatwe , powinno tak pozostać przez wiele lat.
Nie przejmowałbym się tym.
źródło
Podczas stronicowania posortowanych wyników dobrym rozwiązaniem jest dynamiczny SQL. Jeśli masz paranoję co do iniekcji SQL, możesz użyć numerów kolumn zamiast nazwy kolumny. Zrobiłem to przed użyciem wartości ujemnych do zejścia. Coś takiego...
Następnie musisz tylko upewnić się, że liczba znajduje się w kolumnach od 1 do #. Można nawet rozszerzyć ten do listy numerów kolumn i analizować to pod tabelą wskazówki przy użyciu funkcji takich jak ten . Następnie budowałbyś kolejność po klauzuli w ten sposób ...
Wadą jest to, że musisz pamiętać o kolejności poszczególnych kolumn po stronie klienta. Szczególnie, gdy nie wyświetlasz wszystkich kolumn lub wyświetlasz je w innej kolejności. Gdy klient chce sortować, mapujesz nazwy kolumn na kolejność kolumn i generujesz listę liczb całkowitych.
źródło
Argumentem przeciwko sortowaniu po stronie klienta są duże ilości danych i paginacja. Gdy liczba wierszy przekroczy to, co możesz łatwo wyświetlić, często sortujesz jako część pominięcia / ujęcia, które prawdopodobnie chcesz uruchomić w języku SQL.
W przypadku Entity Framework można użyć procedury składowanej do obsługi wyszukiwania tekstu. Jeśli napotkasz ten sam problem z sortowaniem, rozwiązaniem, które widziałem, jest użycie zapisanego procesu do wyszukiwania, zwracając tylko zestaw kluczy id dla dopasowania. Następnie przeprowadź ponowne zapytanie (z sortowaniem) względem bazy danych, używając identyfikatorów z listy (zawiera). EF radzi sobie z tym całkiem dobrze, nawet jeśli zestaw identyfikatorów jest dość duży. Tak, są to dwie rundy, ale pozwala zawsze zachować sortowanie w bazie danych, co może być ważne w niektórych sytuacjach, i zapobiega zapisywaniu logiki w procedurze składowanej.
źródło
A co z sortowaniem po elementach wyświetlających wyniki - siatkach, raportach itp., A nie na SQL?
EDYTOWAĆ:
Aby wyjaśnić sprawę, ponieważ ta odpowiedź została wcześniej przegłosowana, opowiem trochę o tym ...
Powiedziałeś, że wiesz o sortowaniu po stronie klienta, ale chciałeś tego uniknąć. To oczywiście twoja decyzja.
Chcę jednak podkreślić, że robiąc to po stronie klienta, możesz pobrać dane RAZ, a następnie pracować z nimi w dowolny sposób - w przeciwieństwie do wielokrotnych podróży w tę iz powrotem na serwer za każdym razem sortowanie ulegnie zmianie.
Twój SQL Server nie jest teraz opodatkowany i to jest niesamowite. Nie powinno. Ale to, że nie jest jeszcze przeciążony, nie oznacza, że pozostanie tak na zawsze.
Jeśli używasz nowszych elementów ASP.NET do wyświetlania w Internecie, wiele z nich jest już gotowych.
Czy warto dodawać tyle kodu do każdej procedury składowanej, aby obsłużyć sortowanie? Znowu twoja decyzja.
To nie ja będę ostatecznie odpowiedzialny za wspieranie tego. Ale zastanów się, co będzie związane z dodawaniem / usuwaniem kolumn w różnych zestawach danych używanych przez procedury składowane (wymagające modyfikacji instrukcji CASE) lub gdy nagle zamiast sortować według dwóch kolumn, użytkownik zdecyduje, że potrzebują trzech - wymagające teraz zaktualizowania wszystkich procedur składowanych korzystających z tej metody.
Dla mnie warto uzyskać działające rozwiązanie po stronie klienta i zastosować je do kilku wyświetlanych przez użytkownika wyświetlaczy danych i skończyć z tym. Jeśli zostanie dodana nowa kolumna, jest już obsługiwana. Jeśli użytkownik chce sortować według wielu kolumn, może sortować według dwóch lub dwudziestu z nich.
źródło
Przepraszam, że spóźniłem się na imprezę, ale oto kolejna opcja dla tych, którzy naprawdę chcą uniknąć dynamicznego SQL, ale chcą oferowanej przez niego elastyczności:
Zamiast dynamicznie generować SQL w locie, napisz kod, aby wygenerować unikalny proces dla każdej możliwej odmiany. Następnie możesz napisać w kodzie metodę, która zajrzy do opcji wyszukiwania i wybierze odpowiedni proces do wywołania.
Jeśli masz tylko kilka odmian, możesz po prostu ręcznie utworzyć procesy. Ale jeśli masz wiele odmian, to zamiast utrzymywać je wszystkie, po prostu utrzymujesz generator procentów zamiast tego, aby je odtworzył.
Dodatkową korzyścią jest uzyskanie lepszych planów SQL zapewniających lepszą wydajność, robiąc to również w ten sposób.
źródło
To rozwiązanie może działać tylko w .NET, nie wiem.
Pobieram dane do C # z początkową kolejnością sortowania w klauzuli SQL order by, umieszczam te dane w DataView, buforuję je w zmiennej Session i używam ich do tworzenia strony.
Kiedy użytkownik klika nagłówek kolumny, aby posortować (lub stronę lub filtr), nie wracam do bazy danych. Zamiast tego wracam do mojego buforowanego DataView i ustawiam jego właściwość „Sort” na wyrażenie, które buduję dynamicznie, tak jak w przypadku dynamicznego SQL. (Filtrowanie wykonuję w ten sam sposób, używając właściwości „RowFilter”).
Możesz zobaczyć / poczuć, jak działa w wersji demonstracyjnej mojej aplikacji BugTracker.NET na http://ifdefined.com/btnet/bugs.aspx
źródło
Należy unikać sortowania SQL Server, chyba że jest to konieczne. Dlaczego nie posortować na serwerze aplikacji lub po stronie klienta? Również .NET Generics robi wyjątkowe sortowanie
źródło