Czy tabele łącznikowe powinny mieć swoje własne ID?

1

Cześć.
Tworzę moduł zadań i jestem na etapie projektowania tabel:

screenshot-20240406225843.png
Chcę, aby użytkownik mógł powiązać z każdym zadaniem kilku klientów. Stworzyłem więc drugą tabelę:
screenshot-20240406230407.png
... i teraz pytanie - czy ta druga tabela powinna zawierać swoje własne id? W końcu nie ma ona żadnych pól, które trzeba edytować. Jeśli użytkownik zmieni listę klientów, to będę mógł po prostu usunąć wszystkie powiązania dla danego zadania:

DELETE FROM zad_zadaniaKlienci WHERE idZadanie = @idZadanie;

i później dodać wszystkie elementy jeszcze raz.

Boje się, że przez dużą ilość zadań i klientów, nastąpi overflow ID (... proszę o wyrozumiałość, to moja pierwsza większa aplikacja : )

Z góry dzięki

1

Nie.

4
bbhzp napisał(a):

... i teraz pytanie - czy ta druga tabela powinna zawierać swoje własne id? W końcu nie ma ona żadnych pól, które trzeba edytować. Jeśli użytkownik zmieni listę klientów, to będę mógł po prostu usunąć wszystkie powiązania dla danego zadania:

Wszystko powinno mieć cel. Jeśli nie używałbyś tego id do niczego to nie ma specjalnego powodu żeby go dodawać. A jak będziesz chciał go dodać później, to wtedy się doda.

bbhzp napisał(a):

Boje się, że przez dużą ilość zadań i klientów, nastąpi overflow ID (... proszę o wyrozumiałość, to moja pierwsza większa aplikacja : )

YAGNI. Nie rozwiązuj problemów których (jeszcze) nie masz.

0

Chyba tak, bo, żeby kombinacje się nie powtórzyły. Takie ID myślę jak by co pozwoli precyzyjniej namierzyć miejsce i czas.

1

W wordpress wszystko ma swój ID - chyba jest tego powód. A powodem jest filtrowanie dzięki temu na tysiące sposobów danych na różne potrzeby.

4

@johnny_Be_good nieprawda, żeby to działało to już tą para musi być unikalna więc dodatkowy, nic nie wnoszący id tylko będzie przeszkadzał.
@Cimron ciekawe podejście - dodać id żeby filtrować 🤦‍♂️

2

Przecież gość chce zrobić tabelę łącznikową, po co mu id.

0

Dziękuję wszystkim za pomoc, po prostu słyszałem kilka razy że każda tabela powinna mieć swoje id, stąd pytanie.

2

Z punktu widzenia teorii tabela realizująca połączenie M:N nie musi mieć oddzielnego klucza unikalnego (stanowi go para kluczy obcych). Natomiast patrząc od strony praktycznej, niektóre środowiska nazwijmy je frontendowe mają problemy z taką reprezentacją tabeli i po prostu oczekują klucza głównego na jednym polu w każdej tabeli. Z dwupolowymi kluczami unikalnymi sobie po prostu nie radzą.
Jeśli więc nie masz takich potrzeb, to nie dodawaj tego ID. Natomiast zwykle warto dodać jakiś timestamp z datą utworzenia tego powiązania między zadaniem a klientem. Choćby po to, żeby posortować je wg kolejności tworzenia, jeśli w aplikacji jest to istotne (np. od momentu przydzielenia zadania do klienta mierzysz czas reakcji na to zadanie czy coś w tym rodzaju).

1

By the book, w drugiej tabeli masz jedynie klucze obce dla klientów i zadań + unikalny indeks na 2 pola.

1

W opisanym przypadku nie ma takiej potrzeby. Gdyby ta tabela łącznikowa realizowała jakiś inny cel, np. umożliwiałaby modelowanie powiązania elementów w czasie w ramach różnych ról, wówczas mogłaby mieć osobny PK (prawdopodobnie byłaby to osobna biznesowa encja, a nie po prostu techniczna realizacja N:M) i wiersze postaci:

1,partyA,partyB,role#1,fromX,toY
2,partyA,partyB,role#2,fromU,toV

Idąc w osobny klucz główny w tabeli łączącej, powinieneś wziąć pod uwagę możliwość wystąpienia duplikatów (w tym sensie, że połączenia między A i B mogą pojawić się wielokrotnie pod różnym kluczem głównym) i zabezpieczyć się przed tym np. przez dodanie unique constrainta na te dwa pola. Do tego zastanowić czy null w którymś z pól nie narobi Ci szkód (dodać constraint not null).

Boje się, że przez dużą ilość zadań i klientów, nastąpi overflow ID (... proszę o wyrozumiałość, to moja pierwsza większa aplikacja : )

Jakiego typu danych używasz w bazie oraz w aplikacji, że obawiasz się tego przepełnienia?

1

Logika bazy nie wymaga aby tabela łącznikowa miała pole ID będącym sztucznym kluczem Ale dla zasady, zawsze w każdej tabeli dodaję pole ID ze sztucznym kluczem główny na na tym polu, a w przypadku tabeli łącznikowej dodaję unique constraint albo unique indeks na polach łączących

1

Nadmienię iż Id przydaje się gdy chcesz cos kasować/ lub edytować - tutaj musiałbyś szukać po tytule i opisie.
jesli boisz się że ci się int skończy to mozesz użyć UUID'a. https://symfony.com/doc/current/components/uid.html

"..będę mógł po prostu usunąć wszystkie powiązania dla danego zadania:i później dodać wszystkie elementy jeszcze raz..."
Ostrzegam takie manipulacje danymi są problematyczne.

  1. siada performace
  2. konieczność użycia transakcji (bo bez transakcji ponownie ci sie np doda pół a potem apka wyleci w powitrze i zostaniesz z niekompletnymi danymi.
  3. jesli masz jakiekolwek logi albo powiaazania zmenia ci sie id :)
0

@Piotrekdp tak i zamiast mieć jeden indeks PK to masz index PK i index unikalny na pozostałych dwóch polach... Tak trudno przy usuwaniu wpisać pole1 = 1 and pole2 = 2?

0
abrakadaber napisał(a):

@Piotrekdp tak i zamiast mieć jeden indeks PK to masz index PK i index unikalny na pozostałych dwóch polach... Tak trudno przy usuwaniu wpisać pole1 = 1 and pole2 = 2?

No ja nie mówie ze trudno ale autor sie bał ze mu zabraknie miejsca na incie - wnioskuje wiec ze rekordów bedzie bardzo dużo.. kolumna moze miec index i byc indexowana a szukanie po dwóch polach to zawsze porównanie dwóch wartosci.. które zajmuje czas. (performance). oczywiscie wprowadzenie indexu spowolni zapis. ale przyspieszy odczyt.

0
Piotrekdp napisał(a):
abrakadaber napisał(a):

@Piotrekdp tak i zamiast mieć jeden indeks PK to masz index PK i index unikalny na pozostałych dwóch polach... Tak trudno przy usuwaniu wpisać pole1 = 1 and pole2 = 2?

No ja nie mówie ze trudno ale autor sie bał ze mu zabraknie miejsca na incie - wnioskuje wiec ze rekordów bedzie bardzo dużo.. kolumna moze miec index i byc indexowana a szukanie po dwóch polach to zawsze porównanie dwóch wartosci.. które zajmuje czas. (performance). oczywiscie wprowadzenie indexu spowolni zapis. ale przyspieszy odczyt.

W jakim sensie przyśpieszy odczyt jak nigdzie ten id nie będzie używany? On nie będzie nigdzie FK, żeby się po nim łączyć. Dodatkowo, żeby go użyć przy usuwaniu to trzeba go pobrać za każdym razem.
Co do obaw autora to jeśli chodzi o pola idZadania i idKlienta to one będą istnieć przed wstawieniem do tabeli łączącej więc jeśli wystąpi problem z przekręceniem licznika to nie tutaj.

0

aa dobra przepraszam faktyczne nie doczytałem o tabelę ... @abrakadaber patrzyłem na id zadanie i id klient :D przepraszam ale zasugerowalem sie wpisem o przekreceniu.
tak to w przypadku tabeli łaczacej z samymi idkami nie ma to sensu.

2

Też obracam się w środowisku, w którym id dorzuca się bezrefleksyjnie do każdej tabeli, z czym nieszczególnie się zgadzam.
Ale widzę tu dwa poruszone problemy:

  1. Czy tworzyć ID?
    Jak już zostało powiedziane, nie ma to większego sensu
  2. Jak się chronić przed przepełnieniem INTa?
    I tu padła nawet sugestia, by używać UUID. A po co?
    W większości rozwiązań INT, który może przyjąć ponad 4 miliardy różnych wartości, w zupełności wystarcza. A jeśli przestanie wystarczać, to wystarczy go zamienić na BigINT, który rozszerza pulę do ponad 18 trylionów - prędzej umrze projekt i jego deweloperzy, niż to się skończy (zakładam, że nikt, kto zadaje podstawowe pytania na tym forum, nie pisze właśnie bazy dla następcy googla).

Pomysł z usuwaniem rekordów i wstawianiem ich na nowo jest równie "ciekawy". Skoro pobierasz sobie do aplikacji zestaw par kluczy z tabeli powiązań i chcesz jedną usunąć, to przecież wiesz, którą chcesz usunąć i tylko tę usuwasz. Po co robić sobie i bazie dodatkową robotę?

0
Fac napisał(a):
  1. Jak się chronić przed przepełnieniem INTa?
    I tu padła nawet sugestia, by używać UUID. A po co?

Ja bym poszedł o krok dalej i zasugerował tu użycie SHA jako id. Możliwe że ty nigdy nie znajdziesz wytłumaczenia dla takiego zastosowania ale pomyśl ile CO2 ktoś zaoszczędzi gdy w przyszłości ta baza wycieknie i ktoś będzie potrzebował znaleźć odpowiadającą kolizję dla innego ciągu znaków.

Myśl o przyszłości a nie o tu i teraz.

2

Masz źle zrobione nazewnictwo.

Zawsze używaj liczby pojedynczej w nazwach tabel i kolumn:

zad_zadania -> zad_zadanie

Nie używaj kamelizacji w nazwach, wszystko z małej litery, najlepiej jeżeli klucze obce zaczynają się od nazwy obcej tabeli + _id

zad_zdaniaKlienci -> zad_zadanie_klient
idZadania -> zad_zadanie_id
idKlient -> klient_id

To bardzo zwiększa czytelność i ma inne zalety

0

@Fac, @Piotrekdp przykład z usuwaniem wszystkich danych i dodawania ich na nowo dałem żeby uprościć trochę zrozumienie mojego pytania : ) Przy edycji program zapisuje "zmiany", np:

  1. Usunąć klientów o id 1, 2, 3
  2. Dodać klienta o id 5

I później na podstawie tego wysyła zoptymalizowane zapytanie.

... a jeśli chodzi o przepełnienie id to faktycznie trochę przesadziłem : )

4

@bbhzp

Boje się, że przez dużą ilość zadań i klientów, nastąpi overflow ID

Jeśli ID będzie typu INT64 (zakładam że ze znakiem czyli 63 bity na wartości i jeden bit znaku) to przyjmując że w sekundę generujesz 1000 k nowych ID ( 1000000/s) to masz 292471 lat do overflow

1 użytkowników online, w tym zalogowanych: 0, gości: 1