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:
Jest to już powszechne i pracuję z niedoświadczonymi ludźmi
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
Coś innego
Odpowiedzi:
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:
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ą:
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.
źródło
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.
źródło
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.
źródło
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?
(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).
źródło
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.
źródło
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.
źródło