Jakie są najczęstsze anty-wzorce SQL? [Zamknięte]

232

Wszyscy, którzy pracują z relacyjnymi bazami danych, nauczyli się (lub uczą się), że SQL jest inny. Uzyskanie pożądanych rezultatów i zrobienie tego wydajnie wymaga żmudnego procesu częściowo charakteryzującego się poznaniem nieznanych paradygmatów i odkryciem, że niektóre z naszych najbardziej znanych wzorców programowania nie działają tutaj. Jakie są typowe antypatterny, które widziałeś (lub sam popełniłeś)?

le dorfier
źródło
To pytanie jest niezgodne z nowszymi standardami na temat tego, jaki typ pytania jest odpowiedni dla Przepełnienia stosu. Gdy został zapytany, może to nie być prawda.
David Manheim
@casperOne nie istnieje jakaś klauzula „historycznego znaczenia”, która sprawiłaby, że dziadek to pytanie stałby się akceptowalny?
Amy B,
26
Jest mi smutno, że jedno z najbardziej przydatnych pytań na stronie wohole jest zamknięte, ponieważ nie jest konstruktywne.
HLGEM
11
@HLGEM Całkowicie się zgadzam. To pytanie jest doskonałym przykładem wszystkiego, co jest nie tak z StackExchange
Kevin Morse
1
Temat jest absolutnie ważny i istotny. Pytanie jest jednak zbyt otwarte i dlatego odpowiedzi na wszystkie pytania opisują indywidualną usterkę anty-wzorcową poszczególnych inżynierów.
Shane

Odpowiedzi:

156

Konsekwentnie rozczarowuje mnie tendencja większości programistów do mieszania logiki interfejsu użytkownika w warstwie dostępu do danych:

SELECT
    FirstName + ' ' + LastName as "Full Name",
    case UserRole
        when 2 then "Admin"
        when 1 then "Moderator"
        else "User"
    end as "User's Role",
    case SignedIn
        when 0 then "Logged in"
        else "Logged out"
    end as "User signed in?",
    Convert(varchar(100), LastSignOn, 101) as "Last Sign On",
    DateDiff('d', LastSignOn, getDate()) as "Days since last sign on",
    AddrLine1 + ' ' + AddrLine2 + ' ' + AddrLine3 + ' ' +
        City + ', ' + State + ' ' + Zip as "Address",
    'XXX-XX-' + Substring(
        Convert(varchar(9), SSN), 6, 4) as "Social Security #"
FROM Users

Zwykle programiści robią to, ponieważ zamierzają powiązać swój zestaw danych bezpośrednio z siatką, a po prostu wygodnie jest mieć format SQL Server po stronie serwera niż format na kliencie.

Zapytania takie jak powyższe są wyjątkowo kruche, ponieważ ściśle łączą warstwę danych z warstwą interfejsu użytkownika. Ponadto ten styl programowania całkowicie uniemożliwia ponowne użycie procedur przechowywanych.

Julia
źródło
10
Dobry wzór plakat-dziecko dla maksymalnego sprzężenia na jak największej liczbie warstw / abstrakcji.
dkretz,
3
Może to nie być dobre do rozłączania, chociaż ze względu na wydajność często robiłem takie rzeczy, iteracyjne zmiany wprowadzane przez SQL Server są szybsze niż przez kod w środkowej warstwie. Nie dostaję punktu ponownego użycia - nic nie stoi na przeszkodzie, aby uruchomić SP i zmienić nazwę cols, jeśli chcesz.
Joe Pineda,
54
Moim ulubionym jest, gdy ludzie osadzają HTML i javascript, np. WYBIERZ „<a href=... onclick="">„ + name ”</a>”
Matt Rogish
15
Przy pomocy takich zapytań możesz edytować siatkę w witrynie za pomocą prostej instrukcji alter. Lub zmień treść eksportu lub ponownie sformatuj datę w raporcie. To sprawia, że ​​klienci są szczęśliwi i oszczędza mi czas. Więc dziękuję, ale nie, dziękuję, pozostanę przy takich zapytaniach.
Andomar
4
@Matt Rogish - Jezu, ktoś to robi?
Axarydax
118

Oto moje 3 najlepsze.

Numer 1. Brak określenia listy pól. (Edycja: aby uniknąć nieporozumień: jest to reguła kodu produkcyjnego. Nie dotyczy to jednorazowych skryptów analitycznych - chyba że jestem autorem).

SELECT *
Insert Into blah SELECT *

Powinien być

SELECT fieldlist
Insert Into blah (fieldlist) SELECT fieldlist

Numer 2. Używając kursora i pętli while, gdy wykona się pętla while ze zmienną pętli.

DECLARE @LoopVar int

SET @LoopVar = (SELECT MIN(TheKey) FROM TheTable)
WHILE @LoopVar is not null
BEGIN
  -- Do Stuff with current value of @LoopVar
  ...
  --Ok, done, now get the next value
  SET @LoopVar = (SELECT MIN(TheKey) FROM TheTable
    WHERE @LoopVar < TheKey)
END

Numer 3. DateLogic poprzez typy ciągów.

--Trim the time
Convert(Convert(theDate, varchar(10), 121), datetime)

Powinien być

--Trim the time
DateAdd(dd, DateDiff(dd, 0, theDate), 0)

Widziałem ostatnio skok „Jedno zapytanie jest lepsze niż dwa, prawda?”

SELECT *
FROM blah
WHERE (blah.Name = @name OR @name is null)
  AND (blah.Purpose = @Purpose OR @Purpose is null)

To zapytanie wymaga dwóch lub trzech różnych planów wykonania w zależności od wartości parametrów. Generowany jest tylko jeden plan wykonania i umieszczany w pamięci podręcznej dla tego tekstu sql. Ten plan będzie stosowany niezależnie od wartości parametrów. Powoduje to sporadyczne słabe działanie. O wiele lepiej jest napisać dwa zapytania (jedno zapytanie na planowany plan wykonania).

David B.
źródło
7
hmmm, dam ci +1 za same punkty 2 i 3, ale programiści przeceniają zasadę 1. Czasami ma swoje miejsce.
annakata
1
Jakie jest uzasadnienie nr 1?
lipiec
29
Kiedy używasz select *, dostajesz wszystko, co jest w tabeli. Te kolumny mogą zmieniać nazwy i kolejność. Kod klienta często opiera się na nazwach i kolejności. Co 6 miesięcy jestem pytany, jak zachować porządek kolumn podczas modyfikowania tabeli. Jeśli zasada będzie przestrzegana, nie będzie to miało znaczenia.
Amy B,
Czasem używałem # 2, inni wybrałem trasę kursora (chociaż najpierw zapisuję wyniki zapytania w tabeli var, otwieram na nim kursor). Zawsze zastanawiałem się, czy ktoś przeprowadził test wydajności obu.
Joe Pineda,
4
... ale oczywiście kursory powinny prawie zawsze być ostatecznością, po tym, jak nie udało się ustalić, jak wykonać zadanie za pomocą SQL opartego na zestawie. Kiedyś spędziłem około 45 minut ostrożnie analizując przerażający, gigantyczny kursor PL / SQL w procedurze przechowywanej (rysowałem diagramy zepsutej rzeczy), która wypełniła duży stół tymczasowy, a następnie wybrała zawartość tabeli tymczasowej z powrotem do osoby dzwoniącej raport. Uruchomienie zajęło 8,5 minuty na znacznym sprzęcie. Po zilustrowaniu tego wszystkiego byłem w stanie zastąpić go pojedynczym zapytaniem, które zwróciło te same wyniki w niecałe 2 sekundy. Kursory, stary ...
Craig,
71
  • Pola hasła czytelne dla człowieka , np. Wyjaśniające.

  • Korzystanie LIKE przed indeksowanych kolumnach, a ja prawie pokusie tylko powiedzieć, jak w ogóle.

  • Recykling wygenerowanych przez SQL wartości PK.

  • Niespodzianka, nikt jeszcze nie wspomniał o boskim stole . Nic nie mówi „organiczne” jak 100 kolumn flag bitowych, dużych ciągów i liczb całkowitych.

  • Następnie jest wzorzec „Tęsknię za plikami .ini” : przechowywanie plików CSV, ciągów rozdzielanych potokami lub innych wymaganych danych w dużych polach tekstowych.

  • A w przypadku serwera MS SQL w ogóle korzystanie z kursorów . Jest lepszy sposób na wykonanie dowolnego zadania kursora.

Edytowane, ponieważ jest ich tak wiele!

annakata
źródło
19
źle o kursorach, wahałbym się przed powiedzeniem, że robienie jakiejkolwiek konkretnej rzeczy jest w 100% słuszne lub w 100% złe
Shawn
4
Jak dotąd każdy przykład obrony kursora, jaki widziałem, używa niewłaściwego narzędzia do pracy. Ale jeśli wszystko, co wiesz, to SQL, albo używasz go niewłaściwie, albo uczysz się pisać inne rodzaje oprogramowania.
dkretz,
3
@tuinstoel: W jaki sposób LIKE '% blah%' może korzystać z indeksu? Indeksowanie opiera się na porządku i ten przykład wyszukuje losową środkową pozycję ciągu. (Indeksuje kolejność według 1. znaku na 1., a więc spojrzenie na środkowe 4 znaki daje praktycznie losową kolejność ...)
MatBailie
12
Na większości serwerów baz danych (przynajmniej tych, których używałem), LIKE może korzystać z indeksów .. pod warunkiem, że jest to wyszukiwanie prefiksów (LIKE „xxx%”) - o ile znaki wieloznaczne nie są na pierwszym miejscu w ciągu wyszukiwania. Myślę, że trochę tu rozmawiasz na różne sposoby.
Cowan
10
To tak, jakbyś nie lubił LIKE '%LIKE'.
Johan
62

Nie musisz za to głęboko sięgać: nie używaj przygotowanych instrukcji.

stesch
źródło
3
Tak. Z mojego doświadczenia wynika, że ​​obserwowałem go ściśle w tym samym kontekście, z „nie pułapkami błędów”.
dkretz,
1
@stesch: To nic w porównaniu do korzystania z widoków i zmiennej daty raportowania. Widoki są antypatternem, jeśli masz zmienną datę raportowania (zakładam, że większość aplikacji ma). Dodałbym to w osobnej odpowiedzi, ale niestety jest zamknięte.
Stefan Steiger,
56

Używanie bez znaczenia aliasów tabeli:

from employee t1,
department t2,
job t3,
...

Sprawia, że ​​czytanie dużej instrukcji SQL jest o wiele trudniejsze niż to konieczne

Tony Andrews
źródło
49
skróty? piekło Widziałem takie nazwy kolumn
annakata
10
Krótkie aliasy są w porządku. Jeśli chcesz mieć sensowną nazwę, w ogóle nie używaj aliasu.
Joel Coehoorn,
43
Nie powiedział „zwięzły”, powiedział „bez znaczenia”. W mojej książce nie byłoby nic złego w używaniu e, d i j jako aliasów w przykładowym zapytaniu.
Robert Rossney,
11
Absolutnie, Robert - e, d i j byłyby ze mną w porządku.
Tony Andrews,
8
Używałbym
53
var query = "select COUNT(*) from Users where UserName = '" 
            + tbUser.Text 
            + "' and Password = '" 
            + tbPassword.Text +"'";
  1. Ślepo ufający wkład użytkownika
  2. Nieużywane sparametryzowane zapytania
  3. Hasła tekstu jawnego
woli
źródło
Wszystkim tym można z powodzeniem się zająć, używając warstwy abstrakcji bazy danych (dowolnego) rodzaju.
dkretz,
@doofledorfer: Zgadzam się, środkowy poziom byłby zdecydowanie lepszy w takim przypadku, a także zapewnienie buforowania wyników jako miłego efektu ubocznego.
Joe Pineda,
Świetny przykład. Jeśli deweloper zastanawia się, jak zastąpić to dobrym rozwiązaniem, jest w połowie drogi do stania się przyzwoitym deweloperem SQL.
Steve McLeod,
46

Moje błędy to 450 kolumn z tabelami dostępu, które zostały zebrane przez 8-letniego syna najlepszego przyjaciela dyrektora zarządzającego, pielęgnatora psów i podejrzana tabela przeglądowa, która istnieje tylko dlatego, że ktoś nie wie, jak poprawnie znormalizować strukturę danych.

Zazwyczaj ta tabela odnośników wygląda następująco:

ID INT,
Nazwa NVARCHAR (132),
IntValue1 INT,
IntValue2 INT,
CharValue1 NVARCHAR (255),
CharValue2 NVARCHAR (255),
Data 1 DATETIME,
Data 2 DATETIME

Straciłem rachubę liczby klientów, którzy widziałem, którzy mają systemy oparte na takich ohydach.

Pete OHanlon
źródło
1
Co gorsza, czytam, że w najnowszej wersji programu Access, która faktycznie jest obsługiwana automatycznie, co, obawiam się, zachęci więcej do tego fetyszyzmu kolumn Value1, Value2, Value3 ...
Joe Pineda
Poczekaj - więc 8-letni syn jest synem pielęgnatora psów?
barrypicker
28

Te, których najbardziej nie lubię, to

  1. Używanie spacji podczas tworzenia tabel, sproków itp. Nie mam nic przeciwko CamelCase lub under_scores i liczby pojedynczej lub mnogiej i DUŻYM lub małym literom, ale muszę odwoływać się do tabeli lub kolumny [ze spacjami], zwłaszcza jeśli [jest dziwnie rozmieszczony] (tak, Wpadłem na to) naprawdę mnie denerwuje.

  2. Dane zdormalizowane. Tabela nie musi być doskonale znormalizowana, ale kiedy napotkam tabelę pracowników, która ma informacje o ich bieżącym wyniku oceny lub ich podstawowej naturze, mówi mi, że prawdopodobnie będę musiał kiedyś stworzyć osobną tabelę i następnie spróbuj je zsynchronizować. Najpierw znormalizuję dane, a następnie, jeśli zobaczę miejsce, w którym pomaga denormalizacja, rozważę to.

  3. Nadużywanie widoków lub kursorów. Widoki mają swój cel, ale kiedy każdy stół jest zawinięty w widok, jest to zbyt wiele. Musiałem użyć kursorów kilka razy, ale ogólnie możesz do tego użyć innych mechanizmów.

  4. Dostęp. Czy program może być anty-wzorcem? W mojej pracy mamy SQL Server, ale wiele osób korzysta z dostępu ze względu na jego dostępność, „łatwość użycia” i „przyjazność” dla użytkowników nietechnicznych. Jest tu zbyt wiele, aby się w nie zaangażować, ale jeśli byłeś w podobnym środowisku, wiesz.

Jamal Hansen
źródło
2
# 4 - istnieje inny wątek tylko dla <a href=' stackoverflow.com/questions/327199/…> :).
dkretz,
4
Dostęp NIE jest DBMS. Jest to środowisko RAD z bardzo prostym menedżerem baz danych. SQL Server, Oracle i in. będzie nigdy zastąpić go, chyba że dodasz VB-jak język i Crystal Reports, takich jak placówki.
Joe Pineda,
26

użyj SP jako prefiksu nazwy procedury składowania, ponieważ najpierw przeszuka ona lokalizację procedur systemowych, a nie niestandardowe.

Oscar Cabrero
źródło
1
Można go również rozszerzyć na użycie dowolnego innego wspólnego przedrostka dla wszystkich procedur przechowywanych, co utrudnia wybranie posortowanej listy.
dkretz,
7
+1 za komentarz doofledorfer !! Widziałem to wiele, uważam ten idiotyczny i rzeczywiście sprawiają, szukając konkretnego SP bardzo trudnej !!! Rozszerzono również na „vw_” dla widoków, „tbl_” dla tabel i tym podobne, jak ich nienawidzę!
Joe Pineda,
1
Prefiksy mogą być przydatne, jeśli piszesz obiekty w plikach (np. Do kontroli źródła, wdrażania lub migracji)
Rick
1
Dlaczego, do diabła, miałoby być użyteczne, aby każdą pojedynczą procedurę składowaną poprzedzać sp lub usp? Utrudnia to skanowanie listy w poszukiwaniu tej, którą chcesz.
Ryan Lundy,
25

Nadużywanie tymczasowych tabel i kursorów.

Rockcoder
źródło
2
Dobry dowód na to, że „wszystko, co wiem, to języki proceduralne”.
dkretz,
2
Nadużywanie czegokolwiek jest z definicji niepożądane. Pomocny byłby konkretny przykład tego, gdzie użycie tabel / kursorów tymczasowych nie byłoby potrzebne.
Jace Rhea
6
Najczęściej widzę, że tabele tymczasowe nie są używane. z SQL Server często zyskujesz na wydajności, robiąc rzeczy z wieloma tabelami tymczasowymi zamiast jednego monolitycznego zapytania.
Cervo,
24

Do przechowywania wartości czasu należy używać tylko strefy czasowej UTC. Nie należy używać czasu lokalnego.

Frank Schwieterman
źródło
3
Nadal nie znalazłem dobrego prostego rozwiązania do konwersji czasu UTC na czas lokalny dla dat, kiedy trzeba wziąć pod uwagę czas letni, z różnymi datami zmian w poszczególnych latach i krajach, a także ze wszystkimi wyjątkami w poszczególnych krajach. Tak więc UTC nie oszczędza cię przed złożonością konwersji. Ważne jest jednak, aby znać sposób na strefę czasową każdej przechowywanej daty i godziny.
ckarras
1
@CsongorHalmai Wiele miejsc ćwiczy oszczędzanie czasu, więc wartości czasu w ciągu godziny od zmiany czasu mogą być niejednoznaczne.
Frank Schwieterman,
Z pewnością jest to właściwe dla teraźniejszości i przeszłości, ale w przyszłości, szczególnie w dość odległej przyszłości, wyraźne strefy czasowe są często koniecznością. Jeśli masz 30-letnią opcję, która właśnie została napisana i wygasa za 2049-09-27T17: 00: 00 czasu nowojorskiego, nie możesz po prostu ślepo założyć, że będzie to 21: 00: 00Z. Kongres USA może równie dobrze zmienić zasady DST. Musisz oddzielić czas lokalny i prawdziwą strefę czasową (Ameryka / Nowy_Jork).
John Cowan
23

używając @@ IDENTITY zamiast SCOPE_IDENTITY ()

Cytat z tej odpowiedzi :

  • @@ IDENTITY zwraca ostatnią wartość tożsamości wygenerowaną dla dowolnej tabeli w bieżącej sesji we wszystkich zakresach. Musisz tu być ostrożny, ponieważ jest to w wielu zakresach. Możesz uzyskać wartość z wyzwalacza, zamiast z bieżącego wyciągu.
  • SCOPE_IDENTITY zwraca ostatnią wartość tożsamości wygenerowaną dla dowolnej tabeli w bieżącej sesji i bieżący zakres. Ogólnie to, czego chcesz użyć.
  • IDENT_CURRENT zwraca ostatnią wartość tożsamości wygenerowaną dla określonej tabeli w dowolnej sesji i dowolnym zakresie. Pozwala to określić, z której tabeli chcesz uzyskać wartość, na wypadek gdyby dwie powyższe nie były dokładnie tym, czego potrzebujesz (bardzo rzadko). Możesz użyć tego, jeśli chcesz uzyskać bieżącą wartość TOŻSAMOŚCI dla tabeli, do której nie wstawiłeś rekordu.
Brann
źródło
+1 bardzo prawda, może powodować błędy, które byłyby trudne do wyeliminowania
Axarydax 16.01.11
23

Ponowne użycie „martwego” pola do czegoś, do czego nie było przeznaczone (np. Przechowywanie danych użytkownika w polu „Faks”) - bardzo kuszące jako szybkie rozwiązanie!

FruitBreak
źródło
21
select some_column, ...
from some_table
group by some_column

i zakładając, że wynik zostanie posortowany według some_column. Widziałem to trochę w Sybase, gdzie założenie to obowiązuje (na razie).

Adrian Pronk
źródło
1
głosowanie za KIEDYKOLWIEK zakładającym porządek sortowania, tylko dlatego, że tak to kiedyś pokazało się w narzędziu do zapytań
Joel Coehoorn
3
Widziałem to nawet jako błąd więcej niż jeden raz.
dkretz,
6
w MySQL udokumentowano sortowanie. < dev.mysql.com/doc/refman/5.0/en/select.html >. Więc obwiniaj MySQL (ponownie).
derobert,
1
W Oracle nieposortowane wyniki (prawie) zawsze pasowały do ​​grupowania - aż do wersji 10G. Dużo przeróbek dla programistów, którzy pomijali ORDER BY!
Tony Andrews,
1
Byłem nawet na zajęciach szkoleniowych, w których stwierdzono to jako fakt dla SQL Server. Musiałem protestować naprawdę głośno. Aby zapisać tylko 20 znaków, musisz polegać na niejasnym lub nieudokumentowanym zachowaniu.
erikkallen
20
SELECT FirstName + ' ' + LastName as "Full Name", case UserRole when 2 then "Admin" when 1 then "Moderator" else "User" end as "User's Role", case SignedIn when 0 then "Logged in" else "Logged out" end as "User signed in?", Convert(varchar(100), LastSignOn, 101) as "Last Sign On", DateDiff('d', LastSignOn, getDate()) as "Days since last sign on", AddrLine1 + ' ' + AddrLine2 + ' ' + AddrLine3 + ' ' + City + ', ' + State + ' ' + Zip as "Address", 'XXX-XX-' + Substring(Convert(varchar(9), SSN), 6, 4) as "Social Security #" FROM Users

Lub wkuwanie wszystkiego w jedną linię.

Radu
źródło
Użyłem zapytania z poprzedniego komentarza tylko dlatego, że było to pierwsze dostępne przeze mnie zapytanie SQL.
Jasper Bekkers
17
  • FROM TableA, TableB WHERESkładnia ŁĄCZY zamiastFROM TableA INNER JOIN TableB ON

  • Przyjmując założenie, że zapytanie zostanie zwrócone, posortowano w określony sposób bez wstawiania klauzuli ORDER BY, tylko dlatego, że tak to pokazało się podczas testowania w narzędziu zapytań.

Joel Coehoorn
źródło
5
Moje bazy danych Oracle DBA zawsze narzekają, że używam „złączeń ANSI”, czyli tego, co prezentujesz jako prawidłowy sposób. Ale ciągle to robię i podejrzewam, że w głębi duszy wiedzą, że to lepiej.
Steve McLeod,
1
Podejrzewam, że Oracle życzy odejścia standardowego SQL. :-) Ponadto, nie można mieszać niejawnych i jawnych JOINS (inaczej ANSI JOINs) w MySQL 5 - to nie działa. Co jest kolejnym argumentem za jawnymi JIONami.
staticsan
3
Powiedziałbym, że nawet A INNER JOIN B ON to anty wzór. Wolę ZA POMOCĄ WEWNĘTRZNEGO DOŁĄCZENIA B.
John Nilsson,
Oracle obsługuje teraz składnię ANSI, ale kiedyś mieli tę naprawdę dziwną składnię dla połączeń zewnętrznych i zbyt wiele osób nadal z niej korzysta.
Cervo,
cóż ... Oracle nadal nie pozwala używać sprzężeń ANSI do szybkich odświeżanych, zmaterializowanych widoków na
żądanie
14

Nauka języka SQL w ciągu pierwszych sześciu miesięcy ich kariery i nigdy nie uczenie się niczego przez następne 10 lat. W szczególności brak uczenia się lub efektywnego korzystania z funkcji okienkowania / analizy SQL. W szczególności użycie over () i podział według.

Funkcje okna, podobnie jak funkcje agregujące, wykonują agregację na zdefiniowanym zestawie (grupie) wierszy, ale zamiast zwracać jedną wartość na grupę, funkcje okna mogą zwracać wiele wartości dla każdej grupy.

Aby uzyskać ładny przegląd funkcji okienkowania, zobacz O'Reilly SQL Cookbook Dodatek A.

Brian
źródło
12

Muszę tu umieścić mojego obecnego faworyta, aby uzupełnić listę. Mój ulubiony antypattern nie testuje twoich zapytań .

Ma to zastosowanie, gdy:

  1. Twoje zapytanie dotyczy więcej niż jednej tabeli.
  2. Myślisz, że masz optymalny projekt zapytania, ale nie zawracaj sobie głowy testowaniem swoich założeń.
  3. Akceptujesz pierwsze zapytanie, które działa, bez pojęcia, czy w ogóle jest ono zoptymalizowane.

Wszelkie testy przeprowadzane na nietypowych lub niewystarczających danych się nie liczą. Jeśli jest to procedura składowana, umieść instrukcję testu w komentarzu i zapisz ją wraz z wynikami. W przeciwnym razie umieść go w komentarzu w kodzie z wynikami.

le dorfier
źródło
Bardzo przydatna technika dla minimalnego testu T-SQL: W pliku .SQL, w którym definiujesz SP, UDF itp., Natychmiast po utworzeniu testu blokowego, takiego jak IF 1 = 2 BEGIN (przykładowe przypadki dla twojego kodu, z oczekiwanymi wynikami jako komentarze) KONIEC
Joe Pineda,
SQL Server analizuje kod w bloku testowym, nawet jeśli nigdy nie jest wykonywany. Kiedy więc Twój obiekt zostanie zmodyfikowany i otrzyma więcej parametrów lub innego typu itp. Lub obiekt, od którego zależy, zostanie zmodyfikowany, otrzymasz błąd po prostu pytając o plan wykonania!
Joe Pineda,
Nie zawsze jest możliwe testowanie na prawdziwych danych. Często serwer deweloperski / serwer „testowy” jest niedopłacany i otrzymuje ułamek serwera działającego na żywo. Generalnie testy są niezadowolone z serwera na żywo. Niektóre miejsca są lepsze i mają serwer testowy lub testowy z danymi na żywo.
Cervo,
11

Tymczasowe nadużycie tabeli.

W szczególności tego rodzaju rzeczy:

SELECT personid, firstname, lastname, age
INTO #tmpPeople
FROM People
WHERE lastname like 's%'

DELETE FROM #tmpPeople
WHERE firstname = 'John'

DELETE FROM #tmpPeople
WHERE firstname = 'Jon'

DELETE FROM #tmpPeople
WHERE age > 35

UPDATE People
SET firstname = 'Fred'
WHERE personid IN (SELECT personid from #tmpPeople)

Nie buduj tabeli tymczasowej z zapytania, aby usunąć niepotrzebne wiersze.

I tak, widziałem strony kodu w tej formie w produkcyjnych bazach danych.

geofftnz
źródło
1
+1, zgadzam się. Chociaż znalazłem co najmniej jeden lub dwa przypadki, w których ta technika poprawiła wydajność - związane z tym zapytania były co najmniej złożone.
a'r
1
To prawda, że ​​mają miejsce, ale nie w każdym zapytaniu :)
geofftnz
1
Czasami musisz to zrobić, jeśli warunki są bardzo skomplikowane. To prawda, że ​​można go wykorzystywać do skrajności. Ale wiele razy proste usunięcie jest znacznie prostsze niż logika, aby uzyskać sprawę w pierwszym zapytaniu. Czasami również, jeśli klauzula nie jest możliwa do sarge, wstępne zapytanie zostanie spowolnione. Ale samo zrobienie tego na mniejszym stole temperaturowym jest bardziej wydajne. A innym razem dodajesz przypadki, które ludzie biznesu dodają po fakcie.
Cervo,
9

Pogląd przeciwny: nadmierna obsesja na punkcie normalizacji.

Większość systemów SQL / RBDB oferuje wiele funkcji (transakcji, replikacji), które są bardzo przydatne, nawet w przypadku nienormalizowanych danych. Miejsce na dysku jest tanie, a czasem może być prostsze (łatwiejszy kod, szybszy czas programowania) manipulowanie / filtrowanie / wyszukiwanie pobranych danych, niż pisanie schematu 1NF i radzenie sobie z wszystkimi zawartymi w nim problemami (złożone połączenia, nieprzyjemne podselekcje itp.).

Odkryłem, że nadmiernie znormalizowane systemy są często przedwczesną optymalizacją, szczególnie na wczesnych etapach rozwoju.

(więcej przemyśleń na ten temat ... http://writeonly.wordpress.com/2008/12/05/simple-object-db-using-json-and-python-sqlite/ )

Gregg Lind
źródło
22
Myślę, że nienormalizacja jest często przedwczesną optymalizacją.
tuinstoel
Czasami tak jest, czasem nie. Na szczęście często jest to łatwe do przetestowania, a różne opcje działają z różnymi potrzebami db.
Gregg Lind
17
Normalizacja dotyczy nie tylko oszczędności miejsca na dysku. Ma również na celu stworzenie wiarygodnego źródła danych. Jeśli dane są przechowywane tylko w jednym miejscu, to spójność nie jest produktem ubocznym starannego kodowania, ale jest produktem ubocznym projektu.
Grant Johnson,
Przechowywanie danych złożonych w formacie JSON to jedno: jest coraz więcej wsparcia i jest to świadomy kompromis. Używanie wartości oddzielonych przecinkami (lub cokolwiek) przy próbie uratowania jednego złączenia jest groźne i głupie.
John Cowan
Rozwiązania noSQL wykazują poprawę wydajności kosztem zduplikowanych danych, eliminując wyszukiwanie wielu tabel. Stawia na głowie całą kwestię normalizacji. W niektórych przykładach dane są gromadzone w wielu miejscach, aby zapewnić najszybszy możliwy czas reakcji jednego procesu. Oczywiście pojawiają się pytania dotyczące wiarygodnych źródeł.
barrypicker,
9

Właśnie złożyłem to razem, w oparciu o niektóre odpowiedzi SQL tutaj na SO.

Poważnym przeciwnikiem jest myślenie, że wyzwalacze dotyczą baz danych, podobnie jak procedury obsługi zdarzeń do OOP. Istnieje przekonanie, że dowolną starą logikę można wprowadzić w wyzwalacze, które zostaną zwolnione, gdy transakcja (zdarzenie) nastąpi na stole.

Nie prawda. Jedną z dużych różnic jest to, że wyzwalacze są synchroniczne - z zemstą, ponieważ są one synchroniczne na określonej operacji, a nie na wierszu. Po stronie OOP dokładnie odwrotnie - zdarzenia to skuteczny sposób na implementację transakcji asynchronicznych.

dkretz
źródło
8

Procedury przechowywane lub funkcje bez komentarzy ...

Bliek
źródło
I widoki;) Funkcje prawdziwe, z wyjątkiem funkcji o wartościach przechowywanych w tabeli (= widoki z parametrami).
Stefan Steiger,
7

1) Nie wiem, czy to „oficjalny” anty-wzór, ale nie lubię i staram się unikać literałów łańcuchowych jako magicznych wartości w kolumnie bazy danych.

Przykład z „obrazu” tabeli MediaWiki:

img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", 
    "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
img_major_mime ENUM("unknown", "application", "audio", "image", "text", 
    "video", "message", "model", "multipart") NOT NULL default "unknown",

(Po prostu zauważam inną obudowę, kolejną rzeczą, której należy unikać)

Projektuję takie przypadki jak wyszukiwania int w tabelach ImageMediaType i ImageMajorMime z int kluczami podstawowymi.

2) konwersja daty / łańcucha, która zależy od określonych ustawień NLS

CONVERT(NVARCHAR, GETDATE())

bez identyfikatora formatu

devio
źródło
Nie ma też wcięć składniowych. Argghh.
dkretz,
2
Dlaczego to takie złe? z pewnością, jeśli próbujesz wyrazić zestaw wartości, działa to równie dobrze jak tabela odnośników i lepiej pasuje do kodu, który ją wywołuje. Zamiast tego mam enum w kodzie aplikacji, które odwzorowuje ograniczenie enum w mojej bazie danych, zamiast enum w kodzie aplikacji, który mapuje określone wiersze tabeli odnośników. Po prostu wydaje się czystszy.
Jack Ryan
@JackRyan: Jest to złe, ponieważ kiedy później zmienisz listę wyliczeń, musisz pamiętać, aby zmienić ją teraz w dwóch miejscach. Narusza SUCHO . Baza danych powinna być jedynym źródłem prawdy.
Gerrat
7

Identyczne podzapytania w zapytaniu.

EvilTeach
źródło
10
Niestety, czasem po prostu nie da się tego uniknąć - w SQL 2000 nie było słowa kluczowego „Z”, a użycie UDF do enkapsulacji typowych podzapytań czasami prowadzi do kar za wydajność, obwiniaj MS za to ...
Joe Pineda
Cóż, mam nadzieję, że uda im się dodać go w jednym z tych dni.
EvilTeach,
W SQL 2000 można używać zmiennych tabeli.
rekurencyjny
@recursive: nie można indeksować zmiennej tabeli, co często powoduje, że będzie wolniejsza niż podzapytanie. Można jednak użyć tabeli tymczasowej z niestandardowymi indeksami.
Rick
Fajnie, od lat pracuję z SQL i nawet nie wiedziałem, że istnieją wspólne wyrażenia tabelowe (choć bym ich potrzebował). Teraz ja robię! Dzięki!
sleske
7
  • Zmieniony widok - widok, który jest zmieniany zbyt często i bez uprzedzenia lub powodu. Zmiana zostanie zauważona w najbardziej nieodpowiednim czasie lub, co gorsza, będzie błędna i nigdy nie zauważona. Być może Twoja aplikacja ulegnie awarii, ponieważ ktoś wymyślił lepszą nazwę dla tej kolumny. Z reguły widoki powinny zwiększać użyteczność tabel podstawowych przy jednoczesnym utrzymaniu umowy z konsumentami. Napraw problemy, ale nie dodawaj funkcji ani gorzej zmieniaj zachowanie, aby utworzyć nowy widok. Aby ograniczyć ryzyko, nie udostępniaj widoków innym projektom i korzystaj z CTE, gdy pozwalają na to platformy. Jeśli twój sklep ma DBA, prawdopodobnie nie możesz zmienić poglądów, ale wszystkie twoje widoki będą nieaktualne i / lub bezużyteczne w takim przypadku.

  • ! Paramed - czy zapytanie może mieć więcej niż jeden cel? Prawdopodobnie, ale następna osoba, która ją przeczyta, nie dowie się o niej aż do głębokiej medytacji. Nawet jeśli nie potrzebujesz ich teraz, są szanse, że tak będzie, nawet jeśli „tylko” to debugowanie. Dodanie parametrów skraca czas konserwacji i utrzymuje SUCHO. Jeśli masz klauzulę where, powinieneś mieć parametry.

  • Sprawa bez CASE -

    SELECT  
    CASE @problem  
      WHEN 'Need to replace column A with this medium to large collection of strings hanging out in my code.'  
        THEN 'Create a table for lookup and add to your from clause.'  
      WHEN 'Scrubbing values in the result set based on some business rules.'  
        THEN 'Fix the data in the database'  
      WHEN 'Formating dates or numbers.'   
        THEN 'Apply formating in the presentation layer.'  
      WHEN 'Createing a cross tab'  
        THEN 'Good, but in reporting you should probably be using cross tab, matrix or pivot templates'   
    ELSE 'You probably found another case for no CASE but now I have to edit my code instead of enriching the data...' END  
Jason Saldo
źródło
Uwielbiałem ten trzeci. Już używam go lokalnie ...
alphadogg
Dzięki za rekwizyty. :)
jason saldo
5

Dwa, które najbardziej mi się podobają i mogą mieć znaczny koszt pod względem wydajności to:

  • Używanie kursorów zamiast wyrażenia opartego na zestawie. Myślę, że ten występuje często, gdy programista myśli w sposób proceduralny.

  • Używanie skorelowanych zapytań cząstkowych, gdy sprzężenie z tabelą pochodną może wykonać zadanie.

Mitch Pszenica
źródło
Zgadzam się, jeśli masz na myśli to, co myślę; chociaż skorelowane zapytanie cząstkowe jest rodzajem pochodnej tabeli IIRC.
dkretz,
1
Tabela pochodna jest operacją ustawiania, podczas gdy skorelowane podzapytanie jest uruchamiane dla każdego wiersza w zewnętrznym zapytaniu, co czyni go mniej wydajnym (9 razy na 10)
Mitch Wheat
Kilka lat temu zdziwiłem się, że SQL S. jest w jakiś sposób zoptymalizowany do obsługi skorelowanych zapytań: w przypadku prostych otrzymujesz taki sam plan wykonania, jak w przypadku logicznie równoważnego zapytania przy użyciu JOIN! Również skorelowane zapytania, które rzucają Oracle na kolana, działają bardzo wolno na SQL S.!
Joe Pineda,
Dlatego zawsze testuję to na dwa sposoby. I zwykle robię to w obie strony. W praktyce, w każdym razie dla SQL Server, zwykle uważam, że skorelowany sq nie jest wolniejszy.
dkretz,
3
PROSZĘ zrozumieć, że skorelowane podzapytanie i łączenie są IDENTYCZNE (w większości przypadków). Nie są to nawet różne rzeczy zoptymalizowane względem siebie, ale po prostu różne reprezentacje tekstowe tej samej operacji.
erikkallen
5

Umieszczanie rzeczy w tabelach tymczasowych, szczególnie osoby, które przechodzą z SQL Server na Oracle, mają zwyczaj nadużywania tabel tymczasowych. Wystarczy użyć zagnieżdżonych instrukcji select.

tuinstoel
źródło
5

Programiści, którzy piszą zapytania, nie mając pojęcia, co powoduje, że aplikacje SQL (zarówno zapytania indywidualne, jak i systemy dla wielu użytkowników) są szybkie lub wolne. Obejmuje to ignorancję na temat:

  • fizyczne strategie minimalizacji We / Wy, biorąc pod uwagę, że wąskim gardłem większości zapytań jest We / Wy, a nie CPU
  • wpływ różnych rodzajów fizycznego dostępu do pamięci (np. wiele sekwencyjnych operacji we / wy będzie szybszych niż wiele małych losowych operacji we / wy, chociaż mniej, jeśli fizyczna pamięć jest dyskiem SSD!)
  • jak ręcznie dostroić zapytanie, jeśli DBMS tworzy zły plan zapytań
  • jak zdiagnozować niską wydajność bazy danych, jak „debugować” wolne zapytanie i jak odczytać plan zapytań (lub wyjaśnić, w zależności od wybranego DBMS)
  • strategie blokowania w celu optymalizacji przepustowości i uniknięcia zakleszczeń w aplikacjach dla wielu użytkowników
  • znaczenie grupowania i innych sztuczek w przetwarzaniu zestawów danych
  • projektowanie tabel i indeksów w celu jak najlepszego zrównoważenia przestrzeni i wydajności (np. obejmowanie indeksów, utrzymywanie indeksów tam, gdzie to możliwe, zmniejszanie typów danych do niezbędnego minimalnego rozmiaru itp.)
Justin Grant
źródło
3

Używanie SQL jako gloryfikowanego pakietu ISAM (Indexed Sequential Access Method). W szczególności zagnieżdżanie kursorów zamiast łączenia instrukcji SQL w jedną, choć większą, instrukcję. Jest to również liczone jako „nadużycie optymalizatora”, ponieważ tak naprawdę niewiele można zrobić. Można to połączyć z nieprzygotowanymi oświadczeniami dla maksymalnej nieefektywności:

DECLARE c1 CURSOR FOR SELECT Col1, Col2, Col3 FROM Table1

FOREACH c1 INTO a.col1, a.col2, a.col3
    DECLARE c2 CURSOR FOR
        SELECT Item1, Item2, Item3
            FROM Table2
            WHERE Table2.Item1 = a.col2
    FOREACH c2 INTO b.item1, b.item2, b.item3
        ...process data from records a and b...
    END FOREACH
END FOREACH

Prawidłowym rozwiązaniem (prawie zawsze) jest połączenie dwóch instrukcji SELECT w jedną:

DECLARE c1 CURSOR FOR
    SELECT Col1, Col2, Col3, Item1, Item2, Item3
        FROM Table1, Table2
        WHERE Table2.Item1 = Table1.Col2
        -- ORDER BY Table1.Col1, Table2.Item1

FOREACH c1 INTO a.col1, a.col2, a.col3, b.item1, b.item2, b.item3
    ...process data from records a and b...
END FOREACH

Jedyną zaletą wersji z podwójną pętlą jest to, że można łatwo dostrzec przerwy między wartościami w tabeli 1, ponieważ pętla wewnętrzna się kończy. Może to być czynnikiem w raportach z łamaniem kontroli.

Ponadto sortowanie w aplikacji jest zwykle nie-nie.

Jonathan Leffler
źródło
Ten styl, choć nie ta składnia, jest szczególnie rozpowszechniony w PHP z mojego doświadczenia.
dkretz,
Składnia to tak naprawdę IBM Informix-4GL - ale jest na tyle jasne, że nie trzeba wiele wyjaśniać (tak myślę). A styl jest powszechny w wielu programach SQL - niezależnie od języka programowania.
Jonathan Leffler,
Z wyjątkiem faktu, że używasz dobrze znanego antatatternu (niejawne sprzężenia) do zilustrowania swojego antatatternu, to w pewnym sensie nie ma sensu.
Johan
Oczywiście użycie kursorów w ogóle jest anty-wzorem SQl. Praktycznie wszystkie kursory można przepisać jako operacje oparte na zestawie. Nieliczni, którzy nie mogą, są tymi, którzy tylko DBA z wieloletnim doświadczeniem i którzy rozumieją, jak powinny działać wewnętrzne elementy bazy danych. Żaden programista aplikacji nie powinien nigdy pisać kursora SQL.
HLGEM
3

Używanie kluczy podstawowych jako surogatu dla adresów rekordów i używanie kluczy obcych jako surogatu dla wskaźników osadzonych w rekordach.

Walter Mitty
źródło