Czy przechowywanie rozdzielonej listy w kolumnie bazy danych jest naprawdę takie złe?

Wyobraź sobie formularz internetowy z zestawem pól wyboru (dowolne lub wszystkie z nich można wybrać). Zdecydowałem się zapisać je na oddzielonej przecinkami liście wartości przechowywanych w jednej kolumnie tabeli bazy danych.

Teraz wiem, że poprawnym rozwiązaniem byłoby utworzenie drugiej tabeli i prawidłowa normalizacja bazy danych. Wdrożenie łatwego rozwiązania było szybsze, a ja chciałem mieć proof-of-concept tej aplikacji szybko i bez konieczności spędzania nad nią zbyt dużo czasu.

I thought zaoszczędzony czas i prostszy kod był tego wart w mojej sytuacji, czy jest to Defensywny wybór projektu, czy powinienem go znormalizować od początku?

Trochę więcej kontekstu, jest to mała wewnętrzna aplikacja, która zasadniczo zastępuje plik Excel, który został zapisany w folderze udostępnionym. Pytam również, ponieważ myślę o oczyszczeniu programu i uczynieniu go bardziej łatwym do utrzymania. Są tam pewne rzeczy, z których nie jestem do końca zadowolony, jedna z nich jest tematem tego pytania.

Author: Mad Scientist, 2010-09-06

9 answers

Oprócz naruszenia pierwszej normalnej postaci ze względu na powtarzającą się grupę wartości przechowywanych w jednej kolumnie, listy rozdzielane przecinkami mają wiele innych, bardziej praktycznych problemów:

  • nie można zapewnić, że każda wartość jest odpowiednim typem danych: nie można zapobiec 1,2,3, banan,5
  • nie można używać ograniczeń klucza obcego do łączenia wartości z tabelą wyszukiwania; nie można wymusić integralności odniesień.
  • nie można wymusić wyjątkowości: nie można zapobiec 1,2,3,3,3,5
  • nie można usunąć wartości z listy bez pobierania całej listy.
  • nie można przechowywać listy dłuższej niż ta, która mieści się w kolumnie string.
  • trudno wyszukać wszystkie encje o danej wartości na liście; trzeba użyć nieefektywnego skanowania tabeli. Może trzeba uciekać się do wyrażeń regularnych, na przykład w MySQL:
    idlist REGEXP '[[:<:]]2[[:>:]]'*
  • trudno zliczyć elementy na liście lub wykonać inne zapytania zbiorcze.
  • trudno połączyć wartości z / align = "left" /
  • trudno pobrać listę w posortowanej kolejności.
  • Przechowywanie liczb całkowitych jako ciągów zajmuje około dwa razy więcej miejsca niż przechowywanie liczb binarnych. Nie wspominając o spacji zajmowanej przez znaki przecinka.

Aby rozwiązać te problemy, musisz napisać mnóstwo kodu aplikacji, odkrywając na nowo funkcjonalność, którą RDBMS zapewnia już znacznie wydajniej.

Listy rozdzielane przecinkami są na tyle błędne, że zrobiłem z tego pierwszy rozdział w moja książka: SQL Antipatterns: unikanie pułapek programowania baz danych .

Są chwile, kiedy trzeba zastosować denormalizację, ale jak wspomina @OMG kucyki , są to wyjątki. Każda Nie relacyjna "optymalizacja" przynosi korzyści jednemu rodzajowi zapytań kosztem innych zastosowań danych, więc upewnij się, że wiesz, które z zapytań należy traktować tak specjalnie, aby zasługiwały na denormalizację.


* MySQL 8.0 nie obsługuje już tego słowo-składnia wyrażenia granicznego.

 492
Author: Bill Karwin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-08 20:38:46

Jest wiele pytań na temat SO asking:

  • Jak uzyskać liczbę konkretnych wartości z listy rozdzielonej przecinkami
  • Jak uzyskać rekordy, które mają tylko tę samą wartość 2/3 / etc z listy rozdzielonej przecinkami

Kolejnym problemem z listą rozdzielaną przecinkami jest zapewnienie spójności wartości - przechowywanie tekstu oznacza możliwość literówek...

Są to wszystkie objawy denormalizowanych danych i podkreślają, dlaczego zawsze należy modelować dla znormalizowane dane. Denormalizacja Może być optymalizacją zapytań, być stosowane, gdy rzeczywiście pojawia się potrzeba.

 37
Author: OMG Ponies,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-06 18:17:38

"jednym z powodów było lenistwo".

To brzmi alarmowo. Jedynym powodem, dla którego powinieneś zrobić coś takiego, jest to, że wiesz, jak to zrobić "we właściwy sposób", ale doszedłeś do wniosku, że istnieje konkretny powód, aby tego nie robić w ten sposób.

Powiedziawszy to: jeśli dane, które zdecydujesz się przechowywać w ten sposób, są danymi, których nigdy nie będziesz musiał sprawdzać, wtedy może istnieć przypadek przechowywania ich w sposób, który wybrałeś.

(niektórzy użytkownicy kwestionowaliby wypowiedź w poprzednim akapicie, mówiąc ,że "nigdy nie można wiedzieć, jakie wymagania zostaną dodane w przyszłości". Ci użytkownicy są albo błędni, albo stwierdzają przekonania religijne. Czasami korzystnie jest pracować zgodnie z wymaganiami, które masz przed sobą.)

 37
Author: Hammerite,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-22 17:04:18

Ogólnie rzecz biorąc wszystko można obronić, jeśli spełnia wymagania twojego projektu. Nie oznacza to, że ludzie zgodzą się lub będą chcieli bronić twojej decyzji...

Ogólnie rzecz biorąc, przechowywanie danych w ten sposób jest nieoptymalne (np. trudniejsze do wykonania wydajne zapytania) i może powodować problemy z konserwacją, jeśli zmodyfikujesz elementy w formularzu. Być może mógłbyś znaleźć środek i użyć liczby całkowitej reprezentującej zestaw bitowych FLAG zamiast tego?

 16
Author: bobbymcr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-06 18:20:56

Tak, powiedziałbym, że naprawdę jest tak źle. To dobry wybór, ale to nie czyni go dobrym.

Łamie pierwszą normalną formę.

Druga krytyka polega na tym, że umieszczanie surowych wyników wejściowych bezpośrednio w bazie danych, bez żadnej walidacji lub wiązania, pozostawia cię otwartym na ataki SQL injection.

To, co nazywasz lenistwem i brakiem znajomości SQL, to rzeczy, z których powstają neofity. Polecam poświęcić trochę czasu, aby zrobić to poprawnie i zobacz to jako okazję do nauki.

Lub zostaw to tak, jak jest i naucz się bolesnej lekcji ataku SQL injection.

 12
Author: duffymo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-06 18:16:09

Potrzebowałem kolumny wielowartościowej, można ją zaimplementować jako pole xml

W razie potrzeby można go przekonwertować na rozdzielany przecinkiem

Zapytania do listy XML w SQL server przy użyciu Xquery .

Będąc polem xml, można rozwiązać niektóre problemy.

Z CSV: nie można zapewnić, że każda wartość jest właściwym typem danych: nie można zapobiec 1,2,3,banana, 5

Z XML: wartości w znaczniku mogą być wymuszone na poprawności typ


W CSV: nie można używać ograniczeń klucza obcego do łączenia wartości z tabelą wyszukiwania; nie można wymusić integralności referencji.

Z XML: nadal problem


Z CSV: nie można wymusić wyjątkowości: nie można zapobiec 1,2,3,3,3,5

Z XML: nadal problem


W pliku CSV: nie można usunąć wartości z listy bez pobierania całej listy.

With XML: single elementy można usunąć


Z CSV: trudno wyszukać wszystkie encje o danej wartości na liście; trzeba użyć nieefektywnego skanowania tabeli.

Za pomocą XML: pole xml może być indeksowane


Z CSV: trudno zliczyć elementy na liście lub wykonać inne zapytania zbiorcze.**

Z XML: niezbyt trudne


Z CSV: trudno połączyć wartości z tabelą lookup one Referencja.**

Z XML: niezbyt trudne


Z CSV: trudno pobrać listę w posortowanej kolejności.

Z XML: niezbyt trudne


W CSV: Przechowywanie liczb całkowitych jako ciągów zajmuje około dwa razy więcej miejsca niż przechowywanie liczb binarnych.

Z XML: przechowywanie jest jeszcze gorsze niż csv


Z CSV: Plus dużo przecinków.

Z XML: tagi są używane zamiast przecinków


W skrócie, używanie XML rozwiązuje niektóre problemy z rozdzieloną listą i może być przekonwertowane do rozdzielonej listy w razie potrzeby

 7
Author: James A Mohler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:22

Tak, to jest aż tak źle. Moim zdaniem, jeśli nie lubisz używać relacyjnych baz danych, poszukaj alternatywy, która bardziej Ci odpowiada, istnieje wiele ciekawych projektów "NOSQL" z naprawdę zaawansowanymi funkcjami.

 6
Author: Robin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-06 18:40:03

Dobrze używam klucz / wartość para tab oddzielona listą w kolumnie NTEXT w SQL Server od ponad 4 lat i to działa. Tracisz elastyczność tworzenia zapytań, ale z drugiej strony, jeśli masz bibliotekę, która utrzymuje / derpersists pary wartości klucza, to nie jest to taki zły pomysł.

 5
Author: Raj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-06 18:16:38

Prawdopodobnie wziąłbym środek: przekształcić każde pole w CSV w osobną kolumnę w bazie danych, ale nie martw się zbytnio o normalizację (przynajmniej na razie). W pewnym momencie normalizacja może stać się interesująca, ale z wszystkimi danymi włożonymi do jednej kolumny praktycznie nie zyskujesz żadnych korzyści z korzystania z bazy danych. Musisz rozdzielić dane na logiczne pola/kolumny / cokolwiek chcesz je wywołać, zanim będziesz mógł je sensownie manipulować w wszystkie.

 0
Author: Jerry Coffin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-06 18:19:35