Dlaczego SQL nie można bardziej refaktoryzować? [Zamknięte]


39

Wszyscy wiedzą, że nowi programiści piszą długie funkcje. W miarę postępów coraz lepiej radzisz sobie z dzieleniem kodu na mniejsze części, a doświadczenie uczy Cię, jak to robić.

Wpisz SQL. Tak, SQLowe myślenie o kodzie różni się od proceduralnego myślenia o kodzie, ale ta zasada wydaje się równie odpowiednia.

Powiedzmy, że mam zapytanie w postaci:

select * from subQuery1 inner join subQuerry2 left join subquerry3 left join join subQuery4 

Używanie niektórych identyfikatorów lub dat itp.

Te podkwerendy same w sobie są złożone i mogą zawierać własne podkwerendy. W żadnym innym kontekście programowania nie sądzę, że logika dla skomplikowanych zapytań 1-4 należy do mojego zapytania nadrzędnego, które łączy je wszystkie. Wydaje się to tak proste, że te podzapytania powinny być zdefiniowane jako widoki, tak jak byłyby funkcjami, gdybym pisał kod proceduralny.

Dlaczego więc nie jest to powszechna praktyka? Dlaczego ludzie tak często piszą te długie monolityczne zapytania SQL? Dlaczego SQL nie zachęca do szerokiego użycia widoku, tak jak programowanie proceduralne zachęca do szerokiego użycia funkcji. (W wielu środowiskach korporacyjnych tworzenie widoków nie jest nawet łatwym zadaniem. Wymagane są żądania i zatwierdzenia. Wyobraź sobie, że inni programiści musieli składać żądania za każdym razem, gdy tworzyli funkcję!)

Pomyślałem o trzech możliwych odpowiedziach:

  1. Jest to już powszechne i pracuję z niedoświadczonymi ludźmi

  2. Doświadczeni programiści nie piszą złożonego SQL, ponieważ wolą rozwiązywać problemy związane z przetwarzaniem twardych danych za pomocą kodu proceduralnego

  3. Coś innego


12
Istnieją organizacje, które pozwalają zapytać o bazę danych tylko poprzez widoki i modyfikować ją za pomocą procedur przechowywanych.
Pieter B

3
SQL stał się dla mnie o wiele przyjemniejszy, kiedy w końcu uznałem, że nigdy nie będzie tak SUCHY jak mój normalny kod proceduralny.
Graham

1
4. SQL jest naprawdę stary i od dziesięcioleci nie był istotnie aktualizowany. W przypadku bardzo skomplikowanych rzeczy wiele zespołów wybiera procedury składowane. Możesz do tego dodać różne klauzule. Czasami wystarczy uruchomić zadania, aby wprowadzić dane do tabeli tymczasowej, a następnie dołączyć do tego. Zobacz, jak różne są języki deklaratywne i proceduralne.
Berin Loritsch

8
Jednym z powodów jest to, że występuje okropny problem z wydajnością zwany „łączeniem trójkątnym”, który może się zdarzyć podczas korzystania z widoków (oczywiście przez przypadek). Jeśli zapytanie łączy widok A i widok B, ale widok A również w swojej implementacji ponownie wykorzystuje widok B, zaczynasz widzieć ten problem. Tak więc ludzie często zaczynają od napisania pojedynczego monolitycznego zapytania, aby móc zobaczyć, co faktycznie będzie najlepsze pod względem refaktoryzacji do wyświetleń, a następnie ich ostatecznego terminu upływa, a monolit trafia do produkcji. Coś w stylu 98% wszystkich programistów, naprawdę :) :)
Stephen Byrne

3
„Wyobraź sobie, że inne typy programistów musiały składać żądania za każdym razem, gdy tworzyły funkcję” ... umm. Nie robisz recenzji kodu?
svidgen

Odpowiedzi:


25

Myślę, że głównym problemem jest to, że nie wszystkie bazy danych obsługują typowe wyrażenia tabelowe.

Mój pracodawca używa DB / 2 do wielu rzeczy. Najnowsze wersje obsługują CTE, dzięki czemu jestem w stanie wykonywać następujące czynności:

with custs as (
    select acct# as accountNumber, cfname as firstName, clname as lastName,
    from wrdCsts
    where -- various criteria
)
, accounts as (
    select acct# as accountNumber, crBal as currentBalance
    from crzyAcctTbl
)
select firstName, lastName, currentBalance
from custs
inner join accounts on custs.accountNumber = accounts.accountNumber

W rezultacie możemy mieć mocno skrócone nazwy tabel / pól i zasadniczo tworzę widoki tymczasowe, z bardziej czytelnymi nazwami, których mogę następnie użyć. Jasne, zapytanie staje się dłuższe. Ale wynik jest taki, że mogę napisać coś, co jest dość wyraźnie oddzielone (używając CTE w sposób, w jaki używasz funkcji do OSUSZANIA) i skończyć na całkiem czytelnym kodzie. A ponieważ jestem w stanie wyłamać moje podzapytania i mieć jedno odniesienie do drugiego, nie wszystkie są „wbudowane”. Czasami napisałem jeden CTE, potem miałem cztery inne CTE, które odwołują się do niego, a następnie miałem główną kwerendę wyników tych czterech ostatnich.

Można to zrobić za pomocą:

  • DB / 2
  • PostGreSQL
  • Wyrocznia
  • MS SQL Server
  • MySQL (najnowsza wersja; wciąż trochę nowa)
  • prawdopodobnie inni

Ale robi to DŁUGĄ drogę do uczynienia kodu czystszym, bardziej czytelnym, bardziej SUCHYM.

Opracowałem „standardową bibliotekę” CTE, które mogę podłączyć do różnych zapytań, dzięki czemu mogę szybko rozpocząć nowe zapytanie. Niektórych z nich zaczynają obejmować także inni deweloperzy w mojej organizacji.

Z czasem sensowne może być przekształcenie niektórych z nich w widoki, tak aby ta „standardowa biblioteka” była dostępna bez potrzeby kopiowania / wklejania. Ale moje CTE ulegają drobiazgowym modyfikacjom, z powodu różnych potrzeb, tak że nie byłem w stanie użyć jednego CTE TAK SZEROKIE, bez modów, że warto stworzyć widok.

Wydaje się, że częścią twojego problemu jest „dlaczego nie wiem o CTE?” lub „dlaczego moja baza danych nie obsługuje CTE?”

Jeśli chodzi o aktualizacje ... tak, możesz używać CTE, ale z mojego doświadczenia wynika, że ​​musisz ich używać w klauzuli set ORAZ w klauzuli where. Byłoby miło, gdybyś mógł zdefiniować jedną lub więcej przed całą instrukcją aktualizacji, a następnie po prostu mieć części „główne zapytanie” w klauzulach set / where, ale to nie działa w ten sposób. I nie można uniknąć niejasnych nazw tabel / pól w aktualizowanym stole.

Do usuwania można użyć CTE. Określenie wartości PK / FK dla rekordów, które chcesz usunąć z tej tabeli, może zająć wiele CTE. Ponownie nie można uniknąć niejasnych nazw tabel / pól w modyfikowanej tabeli.

O ile można dokonać zaznaczenia we wstawce, można użyć CTE dla wstawek. Jak zawsze możesz mieć do czynienia z niejasnymi nazwami tabel / pól w tabeli, którą modyfikujesz.

SQL NIE pozwala ci tworzyć odpowiednika obiektu domeny, zawijając tabelę za pomocą getters / setters. W tym celu będziesz musiał użyć pewnego rodzaju ORM, wraz z bardziej proceduralnym / OO językiem programowania. Pisałem takie rzeczy w Javie / Hibernacji.


4
Mieliśmy człowieka Big CTE, który pisał najgorsze SQL. Problem polegał na tym, że współczynniki CTE były kiepskie w wyborze abstrakcji, a optymalizator nie mógł cofnąć każdego wprowadzonego algorytmu z kośćmi.
Joshua

3
ORM może również robić pewne ohydne rzeczy pod względem wydajności ... szczególnie, gdy używasz tylko programów pobierających i ustawiających w celu pobrania wielu danych. Hibernacja jest znana z używania setek pojedynczych zapytań zamiast jednego dużego połączonego zapytania, co stanowi problem, gdy każde zapytanie ma narzut.
user3067860

2
@Joshua Możesz napisać zły kod w dowolnym języku. W tym SQL. Ale dokonanie poprawnego refaktoryzacji do CTE może tworzyć oddolne projekty, które są łatwiejsze do przeanalizowania przez ludzi. Uważam to za pożądaną cechę, niezależnie od tego, z jakim językiem mam do czynienia :-)
Meower68

2
Inne odpowiedzi są świetne, ale tego osobiście szukałem. „Dlaczego nie wiem o CTE” to większość mojego problemu.
ebrts

2
@ Meower68 Czy nie istnieje ryzyko, że częste korzystanie z CTE powstrzyma ludzi od uczenia się prawidłowego dołączania i uczenia się o dobrym projekcie bazy danych? Popieram wartość CTE, ale sprawia też, że zbyt łatwa jest praca z podkwerendami, gdzie nie powinieneś.
Pieter B

36

Blokowanie tworzenia widoków bazy danych jest często wykonywane przez organizacje paranoiczne z powodu problemów z wydajnością bazy danych. Jest to kwestia kultury organizacyjnej, a nie problem techniczny z SQL.

Poza tym duże monolityczne zapytania SQL są pisane wiele razy, ponieważ przypadek użycia jest tak specyficzny, że bardzo niewiele kodu SQL można naprawdę ponownie wykorzystać w innych zapytaniach. Jeśli potrzebne jest złożone zapytanie, zwykle dotyczy ono zupełnie innego przypadku użycia. Kopiowanie SQL z innego zapytania jest często punktem wyjścia, ale z powodu innych pod-zapytań i JOIN w nowym zapytaniu, w końcu modyfikujesz skopiowany SQL tylko na tyle, aby przerwać jakąkolwiek abstrakcję, którą „funkcja” w innym języku używany do. Co prowadzi mnie do najważniejszego powodu, dla którego SQL jest trudny do refaktoryzacji.

SQL zajmuje się tylko konkretnymi strukturami danych, a nie abstrakcyjnym zachowaniem (lub abstrakcją w jakimkolwiek znaczeniu tego słowa). Ponieważ SQL jest napisany wokół konkretnych pomysłów, nie ma nic do wyodrębnienia w module wielokrotnego użytku. Widoki bazy danych mogą w tym pomóc, ale nie na tym samym poziomie co „funkcja” w innym języku. Widok bazy danych jest nie tyle abstrakcją, co zapytaniem. W rzeczywistości widok bazy danych jest zapytaniem. Zasadniczo jest używany jak tabela, ale wykonywany jak zapytanie podrzędne, więc znowu masz do czynienia z czymś konkretnym, a nie abstrakcyjnym.

Dzięki abstrakcji kod staje się łatwiejszy do refaktoryzacji, ponieważ abstrakcja ukrywa szczegóły implementacji przed konsumentem tej abstrakcji. Prosty SQL nie zapewnia takiej separacji, chociaż rozszerzenia proceduralne SQL, takie jak PL / SQL dla Oracle lub Transact-SQL dla SQL Server, zaczynają nieco zacierać linie.


„SQL zajmuje się tylko konkretnymi strukturami danych, a nie abstrakcyjnym zachowaniem (lub abstrakcją w jakimkolwiek znaczeniu tego słowa)”. To dziwne stwierdzenie, ponieważ z mojego punktu widzenia SQL zajmuje się wyłącznie abstrakcyjnym zachowaniem, a nie konkretnym programowaniem w żadnym znaczeniu tego słowa! Wystarczy rozważyć wszystkie ogromne stopnie złożoności, które są wyodrębnione w prostym słowie „DOŁĄCZ”: mówisz, że chcesz scalony wynik z dwóch różnych zestawów danych, i pozostaw to DBMS określenie konkretnych zaangażowanych technik, radzenie sobie z indeksowanie, radzenie sobie z różnicą między tabelami i podkwerendami itp.
Mason Wheeler

5
@MasonWheeler: Chyba bardziej myślałem o SQL z punktu widzenia danych, na których on działa, a nie implementacji funkcji językowych. Tabele w bazie danych nie wydają się abstrakcją. Są konkretne, ponieważ w tabeli o nazwie „numery_ telefoniczne” zawiera numery telefonów. Numer telefonu nie jest pojęciem abstrakcyjnym.
Greg Burghardt

12

Rzeczą, która moim zdaniem może być pominięta w twoim pytaniu / punkcie widzenia, jest to, że SQL wykonuje operacje na zestawach (używając operacji na zestawach itp.).

Kiedy pracujesz na tym poziomie, oczywiście rezygnujesz z pewnej kontroli nad silnikiem. Nadal możesz wymusić kod proceduralny za pomocą kursorów, ale jak pokazuje doświadczenie 99/100 razy, nie powinieneś tego robić.

Refaktoryzacja SQL jest możliwa, ale nie korzysta z tych samych zasad refaktoryzacji kodu, jakie mamy w kodzie aplikacji. Zamiast tego optymalizujesz sposób korzystania z samego silnika SQL.

Można to zrobić na różne sposoby. Jeśli korzystasz z Microsoft SQL Server, możesz użyć SSMS, aby uzyskać przybliżony plan wykonania i możesz go użyć, aby zobaczyć, jakie kroki możesz zrobić, aby dostroić kod.

W przypadku podziału kodu na mniejsze moduły, jak wspomniano w @ greg-burghardt, SQL jest zasadniczo specjalnie zbudowanym kawałkiem kodu, w wyniku czego. Robi to, czego potrzebujesz, i nic więcej. Jest zgodny z literą S w SOLID, ma tylko jeden powód do zmiany / wpływu i wtedy potrzebujesz tego zapytania, aby zrobić coś innego. Reszta akronimu (OLID) nie ma tutaj zastosowania (AFAIK nie ma wstrzykiwania zależności, interfejsów ani zależności jako takich w SQL), w zależności od smaku używanego SQL, możesz rozszerzyć niektóre zapytania poprzez ich zawijanie w procedurze przechowywanej / funkcji tabeli lub używaniu ich jako podkwerend, więc powiedziałbym, że zasada otwartego zamknięcia nadal miałaby zastosowanie, w pewnym sensie. Ale dygresję.

Myślę, że musisz zmienić paradygmat pod względem sposobu wyświetlania kodu SQL. Ze względu na jego ustalony charakter nie jest w stanie zapewnić wielu funkcji, jakie mogą mieć języki aplikacji (ogólne itp.). SQL nigdy nie został zaprojektowany w taki sposób, jest to język do przeszukiwania zestawów danych, a każdy zestaw jest unikalny na swój sposób.

Biorąc to pod uwagę, istnieją sposoby na poprawienie wyglądu kodu, jeśli czytelność ma wysoki priorytet w organizacji. Przechowywanie bitów często używanych bloków SQL (wspólnych zestawów danych, których używasz) w procedurach przechowywanych / funkcjach wartości tabeli, a następnie wysyłanie zapytań i przechowywanie ich w tymczasowych tabelach / zmiennych tabeli, a następnie używanie ich do łączenia elementów w jedną ogromną transakcję że inaczej byś napisał, jest opcją. IMHO nie warto robić czegoś takiego z SQL.

Jako język został zaprojektowany tak, aby był czytelny i zrozumiały dla każdego, nawet dla osób niebędących programistami. W związku z tym, chyba że robisz coś bardzo sprytnego, nie ma potrzeby przekształcania kodu SQL na mniejsze bajty. Osobiście napisałem ogromne zapytania SQL podczas pracy nad hurtownią danych ETL / Reporting i wszystko było nadal bardzo jasne pod względem tego, co się działo. Wszystko, co mogłoby wyglądać nieco dziwnie dla kogokolwiek innego, otrzyma krótki zestaw komentarzy, które zapewnią krótkie wyjaśnienie.

Mam nadzieję, że to pomoże.


6

W twoim przykładzie zamierzam skupić się na „podkwerendach”.

Dlaczego są tak często używane? Ponieważ używają naturalnego sposobu myślenia o osobie: mam ten zestaw danych i chcę wykonać akcję na jego podzbiorze i połączyć go z podzbiorem innych danych. 9 na 10 razy, gdy widzę podzapytanie, jest źle używane. Mój żart na temat podkwerend to: ludzie, którzy boją się dołączeń, używają podkwerend.

Jeśli zobaczysz takie podzapytania, często oznacza to nieoptymalny projekt bazy danych.

Im bardziej znormalizowana jest Twoja baza danych, tym więcej łączy, tym bardziej twoja baza danych wygląda jak duży arkusz programu Excel, tym więcej otrzymujesz podselekcji.

Refaktoryzacja w SQL ma często inny cel: uzyskanie większej wydajności, lepsze czasy zapytań, „unikanie skanowania tabeli”. Mogą nawet sprawić, że kod będzie mniej czytelny, ale są bardzo cenne.

Dlaczego więc widzisz tyle ogromnych monolitycznych zapytań bez refrakcji?

  • SQL pod wieloma względami nie jest językiem programowania.
  • Zły projekt bazy danych.
  • Ludzie niezbyt biegle posługujący się językiem SQL.
  • Brak władzy nad bazą danych (na przykład niedozwolone korzystanie z widoków)
  • Różne cele z refaktoryzacją.

(dla mnie, im bardziej mam doświadczenie w SQL, tym mniejsze są moje zapytania, SQL ma sposoby dla osób na wszystkich poziomach umiejętności, aby wykonywać swoje zadania bez względu na wszystko).


6
„Podkwerendy” mogą stanowić pewną agregację odpowiednio znormalizowanej bazy danych, podobnie jak normalizację ad hoc dla
nienormalizowanej bazy danych

@Caleth to taka prawda.
Pieter B

5
Nawet w dobrze znormalizowanych bazach danych nadal często konieczne jest łączenie się z podzapytaniami zamiast bezpośredniego łączenia z tabelami. Np. Jeśli chcesz dołączyć do danych zgrupowanych.
Barmar

1
@Barmar zdecydowanie, stąd mój komentarz 9 na 10. Zapytania podrzędne mają swoje miejsce, ale widzę, że są nadużywane przez niedoświadczonych ludzi.
Pieter B

Podoba mi się twoja metryka „liczby podkwerend” jako wskaźnik normalizacji bazy danych (lub jej braku).
Jason

2

Podział obowiązków

W duchu SQL baza danych jest wspólnym zasobem zawierającym dane firmy, a jej ochrona ma kluczowe znaczenie. Wchodzi do DBA jako strażnik świątyni.

Tworzenie nowego widoku w bazie danych ma służyć trwałemu celowi i powinno być udostępniane społeczności użytkowników. W widoku DBA jest to dopuszczalne tylko wtedy, gdy widok jest uzasadniony strukturą danych. Każda zmiana widoku jest następnie związana z ryzykiem dla wszystkich jego obecnych użytkowników, nawet tych, którzy nie korzystają z aplikacji, ale którzy odkryli widok. Wreszcie, tworzenie nowych obiektów wymaga zarządzania autoryzacjami, aw przypadku widoku spójnie z autoryzacjami bazowych tabel.

Wszystko to wyjaśnia, dlaczego DBA nie lubią dodawać widoków, które są tylko dla kodu jakiejś indywidualnej aplikacji.

Projekt SQL

Jeśli zdekomponujesz jedno z ładnych, złożonych zapytań, możesz dowiedzieć się, że podzapytania często wymagają parametru zależnego od innego podzapytania.

Dlatego przekształcanie podzapytań nie jest tak proste, jak podano. Musisz wyizolować parametry zmiennych i zaprojektować widok, aby parametry mogły zostać dodane jako kryteria wyboru w widoku.

Niestety robiąc to, czasami narzucasz dostęp do większej ilości danych i mniej efektywnie niż w zapytaniu dostosowanym do potrzeb.

Zastrzeżone rozszerzenia

Możesz mieć nadzieję na pewne refaktoryzowanie, przenosząc pewne obowiązki na rozszerzenia proceduralne SQL, takie jak PL / SQL lub T-SQL. Są one jednak zależne od dostawcy i tworzą zależność technologiczną. Ponadto rozszerzenia te działają na serwerze bazy danych, powodując większe obciążenie przetwarzania zasobu, który jest znacznie trudniejszy do skalowania niż serwer aplikacji.

Ale na czym polega problem?

Wreszcie, czy podział obowiązków i konstrukcja SQL z jego siłą i ograniczeniami są prawdziwym problemem? W końcu okazało się, że te bazy danych skutecznie i niezawodnie obsługują bardzo krytyczne dane, w tym w środowiskach o znaczeniu krytycznym.

Aby więc dokonać udanego refaktoryzacji:

  • rozważyć lepszą komunikację . Spróbuj zrozumieć ograniczenia DBA. Jeśli udowodnisz DBA, że nowy widok jest uzasadniony strukturami danych, że nie jest to obejście problemu i że nie ma on wpływu na bezpieczeństwo, z pewnością zgodzi się na jego utworzenie. Ponieważ wtedy byłby to wspólny interes.

  • najpierw posprzątaj swój dom : nic nie zmusza cię do generowania dużej ilości SQL w wielu miejscach. Zmodyfikuj kod aplikacji, aby oddzielić dostęp do SQL i utworzyć klasy lub funkcje, aby zapewnić wielokrotne zapytania wielokrotnego użytku, jeśli są one często używane.

  • poprawa świadomości zespołu : upewnij się, że Twoja aplikacja nie wykonuje zadań, które mogłyby być wydajniej wykonywane przez silnik DBMS. Jak słusznie wskazałeś, podejście proceduralne i podejście zorientowane na dane nie są w równym stopniu opanowane przez różnych członków zespołu. To zależy od ich pochodzenia. Ale aby zoptymalizować system jako całość, Twój zespół musi zrozumieć go jako całość. Stwórz więc świadomość, aby mieć pewność, że mniej doświadczeni gracze nie odkryją na nowo koła i podzielą się swoimi przemyśleniami z DB z bardziej doświadczonymi członkami.


+1 Kilka świetnych punktów tutaj. Biorąc pod uwagę, jak zły jest niektóre SQL, powściągliwość baz danych w celu umożliwienia widoków jest często całkowicie zrozumiała. Ponadto SQL może zdecydowanie skorzystać z wzajemnej oceny, jeśli jest zasobny i / lub będzie często uruchamiany.
Robbie Dee

1

Do punktów 1 i 3: Widoki nie są jedynym sposobem. Istnieją również tabele tymczasowe, marty, zmienne tabel, zagregowane kolumny, CTE, funkcje, procedury składowane i ewentualnie inne konstrukcje w zależności od RDBMS.

DBA (i mówię jako ktoś, kto był zarówno DBA, jak i programistą) mają tendencję do patrzenia na świat w dość binarny sposób, więc często sprzeciwiają się takim widokom i funkcjom z powodu postrzeganej obniżki wydajności.

Później potrzeba skomplikowanych połączeń zmniejszyła się, gdy uznano, że tabele zdenormalizowane, mimo że nie są optymalne z punktu widzenia NF , są wysoce wydajne.

Istnieje również tendencja do wykonywania zapytań po stronie klienta za pomocą technologii takich jak LINQ, które poruszasz w punkcie 2.

Chociaż zgadzam się, że SQL może być trudny do modularyzacji, poczyniono wielkie postępy, chociaż zawsze będzie dychotomia między kodem po stronie klienta a SQL - chociaż 4GL nieco zamazało linie.

Myślę, że to naprawdę zależy od tego, jak daleko twoi DBA / architekci / technicy są gotowi zrezygnować w tym zakresie. Jeśli odmówią zezwolenia na cokolwiek innego niż waniliowy SQL z dużą liczbą złączeń, mogą pojawić się ogromne zapytania. Jeśli utkniesz z tym, nie uderzaj głową w ścianę z cegieł, eskaluj ją. Są ogólnie lepsze sposoby robienia rzeczy przy odrobinie kompromisu - zwłaszcza jeśli możesz udowodnić korzyści.


1
Nigdy nie słyszałem o konstrukcie „mart”. Co to jest?
biskup

1
Marts to tylko podzbiór repozytorium (główna baza danych). Jeśli trzeba uruchomić określone złożone zapytania, można utworzyć specjalną bazę danych specjalnie do obsługi tych żądań. Bardzo częstym przykładem jest raportowanie mart.
Robbie Dee

1
Myliłem się, dlaczego zostało to odrzucone. Nie odpowiada bezpośrednio na pytanie, ale daje dość wyraźną domyślną odpowiedź na „opcję 3: istnieje wiele sposobów radzenia sobie z tym, które są szeroko stosowane”.
Dewi Morgan

TIL o centrach danych. Masz +1!
biskup
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.