Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Problem z zaprojektowaniem bazy danych.
Forum PHP.pl > Forum > Bazy danych > MySQL
exseerius
Witam, mam problem, z optymalizacją.

Mam strone, gdzie loguja się uzytkownicy i w swoim profilu maja kilka opcji do wyboru (ok 15).

przykładowo:

ulubiony kolor:
[_] zielony
[_] niebieski
[_] czerwony itp.

do tej pory rozwiazałem problem tak, że w bazie danych mam kolumne kolory i tam zapis w postaci 1.0.0.1.1.1.1.0.1.0...
1 odpowiada włączonej opcji, 0 wyłaczonej

W sumie działa jak powinno, ale musze te dane obrabiać, explodem i jest problem z kolejnością, bo dane muszą zawsze wyswietlać się w kolejnosci jaka pierwotnie została podana w array zadeklarowanym w dokumencie. Druga sprawa to problem z wyszukiwaniem, bo stworzenie zapytania gdzie WHERE wygląda np tak.

WHERE kolor = 0.0.0.0.0.0.1.1.0.0.0.0 OR 1.1.1.1.1.0.0.0.0.0 itp jest trochę na około.

Czy jest mozliwość rozwiązania tego w inny sposób?

Wymysliłem dwa rozwiązania, ale nie wiem czy bedą one optymalne:

#1 dwie tabele, jedna z definicją kolorów

id | kolor
1 | niebieski
2 | zielony

druga wybory
id | user | wybor_1 | wybor_2 | wybor_3
1 | 1 | true | flase | true

php sprawdza opcje wyszukujac kluczy "WHERE wybor_"kolor.id - trochę lepsze rozwiązanie, ale chyba nadal jest to nie to co ma być, bo dopisanie kolejnego koloru dowyboru musze dopisać kolejną kolumne w "wyborach"

#2 drugie rozwiązanie siedzi u mnie tylko w głowie, bo nie wiem jak do końca to rozwiązać.
Mianowicie utworzenie tabeli np. wybory, gdzie jako nazwy kolumn będa opcje możliwe do wyboru (niektóre zawierają polskie znaki, znaki /\-=+).

np.

id | user | niebieski | zielony | czerwony | jasno-żółty
1 | 1 | true | true | false | true

i pytanie co jest lepsze w filtrowaniu danych. Jak wyświetlić na stronie tylko nagłówki kolumn z pominieciem id i user.


Ewentualnie jaki jeszcze inny sposób proponujecie na rozwiązanie tej zagwostki w taki sposób, aby łatwo było napisać pod to wyszukiwarkę.
darko
To może tak:

tabela colors:

id_color (PK)
color_name varchar(50)

tabela kolorywybór (colors_choice)

colors_choice
id_color_choice (PK)
id_color (FK)
id_user (FK)
color_choice_status tinyint(1)

tabela user:

id_user (PK)
id_color_choice (FK)
(...)

i teraz jeśli chcesz wyciągnąć kolory wybrane przez usera to wystarczy wyciągnać tablicę idków barw:

  1. SELECT id_color FROM colors_choice WHERE id_user=X AND color_choice_status=1


Natomiast jeśli user zmieni swój wybór to robimy update na rekordach w tabeli color_choice dla id_user=X i zmieniamy wartość pól color_choice_status z 1 na 0, a następnie inserty z nowym wyborem kolorów:

  1. UPDATE color_choice SET color_choice_status=0 WHERE id_user=x AND color_choice_status=1


  1. // tablica tu trzymamy nowowybrane idki kolorów przesłane forumarzem
  2. $id_colors = array();
  3. // a tu id_user
  4. $id_user
  5. // (...)
  6. foreach($id_colors as $cid) {
  7. $query="insert into colors_choice values($cid, $id_user, 1)";
  8. // tu wykonujemy zapytanie:
  9. if(!mysql_query($query)) {
  10. die("db insert error");
  11. }
  12. }



W ten sposób wyciągasz dane po kluczach obcych, wszystko - moim skromnym zdaniem - w sposób łatwy, a przy tym czytelny i wydajny
Ges
Czemu na upartego robić to po stronie PHP ? Wszystko na około :]

Nie prościej będzie skorzystać z typu SET w SQL ?
http://dev.mysql.com/tech-resources/articl...t-datatype.html
myset SET('zielony','czerwony','niebieski')

Na danych w SET pracujesz jak na flagach:
zielony 00000001
czerwony 00000010
niebieski 00000100

Można robić & i || (np: zielony & niebieski = 5).
Jedyny limit to 64 elementy w tym typie danych, ale z opisu Twojego problemu wynika, że w tym przypadku jest to idealne rozwiązanie.

Nie będę tu przeklejał tutoriala. Pocztaj linka którego dałem powyżej.
Dodam tylko, że jeszcze jedna ważna sprawa to możliwość wyszukiwania pomiędzy wartościami od razu na bazie.
Wyciąganie tego do PHPa i explode strasznie nieekonomiczne, bo wyciągasz z bazy wiersze których możesz nigdy nie użyć
bo po zrobieniu explode może się okazać, że wiersz ten nie spełnia Twoich oczekiwań.

W przypadku proponowanego przeze mnie rozwiązania masz to gotowe:
SELECT * FROM tabela WHERE myset LIKE '%zielony%niebieski%';
SELECT * FROM tabela WHERE myset & 5 = 5;

Myślę, że reszte doczytasz sam. Chciałem tylko naprowadzić Cię na rozwiązanie smile.gif

Aha no i oczywiście najlepiej sobie podefiniować zmienne define('') w PHPie zgodne z SQL do wygodnego korzystania smile.gif
darko
Ges... no nie wiem... po to są relacyjne bazy danych, żeby z nich korzystać. Poza tym co jeśli do iluś tam kolorów dojdzie jeszcze mega innych danych? Twoja idea pada, a tu tworzysz nową tabelę, tworzysz relację i wszystko śmiga.
Ges
Hmm. Jak będzie miał powyżej 64 elementów, to może wtedy niech zacznie się martwić o stosowanie innego rozwiązania. Moim zdaniem SET w opisanym przypadku jest idealnym rozwiązaniem i najprostszym.

Jak dojdzie "ileś tam danych" to myślę, że założy wtedy nowy topic smile.gif W tym wypadku wg opisu chodziło o zapisanie zestawu BOOLEANÓW i ich kombinacji. Co jest z definicji SETem w SQL.

Co do robienia kolejnych tablic i robienia kolejnych JOIN, to jest dość krótkowzroczne myślenie wg mnie. Każdy JOIN (w Twoim rozwiązaniu potrójny) jest mega kosztowną operacją i w przypadku gdy dojdzie do tych tabel około 100k wierszy userów taki JOIN może zabić bazę i będzie czekał porenaście sekund na odpowiedź. Jak będzie miał więcej opcji niż teraz, to ja bym skusił się nawet o założenie kolejnej kolumny SETów które pomieści kolejne 64 booleany, a JOINy omijałbym szerokim łukiem. Ale to może moje zboczenie myślenia o optymalizacji każdego rozwiązania, a nie robienia wszystkiego na pierwszy wymyślony sposób smile.gif

Poza tym zamiast 1 INSERTa w Twoim rozwiązaniu jest ich tyle ile opcji (sic !). Kolejne samobójstwo smile.gif
No i UPDATE poprzez ustawienie wszystkiego na 0 i potem mase INSERTÓW ? Naprawdę nie widzisz w takim rowiązaniu przerostu ? smile.gif

Oczywiście dobór rozwiązania zależy od jego twórcy i to on powinnien ocenić co dla niego będzie korzystniejsze. Ja bym tylko polecał dobór jak najprostszego i najbardziej dopasowanego rozwiązania do jego problemu, a nie robienie czegoś na wyrost.
darko
(OT?) Ges tak czytam ten artykuł, do którego wkleiłeś link i częściowo potwierdza on to, co napisałem wcześniej, częściowo wzbogaca moją wiedzę (i za to dzięki):

Cytat
Why You Shouldn't Use SET

The MySQL SET datatype is not commonly used for a few reasons; First, using the MySQL SET datatype limits you to 64 elements. While you could get around this by using multiple SETs, this still represents a loss of versatility. Second, you cannot include commas in your set elements, as the comma is an element separator.Third, using a set means your data is not normalized. In our above example, we are tracking a person's interests for a hypothetical dating site. In a normalized schema, there should be three tables: one for the person, one for all possible interests, and one that links a person to their particular interests. Fourth, an INDEX on a set datatype is going to refer to the set as a whole and will not be used for searching individual elements (this may or may not be a problem for certain applications).


Sam autor artykułu przyznaje, że zastosowanie setów nie jest drogą do normalizacji bazy danych. To tak jakbyś luźno wiązał ze sobą grupy do 64 rekordów w bazie. Według mnie - to się właśnie później zemści. Brak relacji, ograniczone możliwości wyszukiwania, czy to ma w czymś pomóc w przyszłości, czy takie podejście jest dalekowzroczne? Nie przekonałeś mnie. Twoja propozycja rozwiązania jest dobra w przypadku niewielkich, a nawet małych zbiorów rekordów, po za tym nie jest to rozwiązanie uniwersalne i narzuca pewne istotne ograniczenia (i wg. mnie tworzy też utrudnienia związane z wyszukiwaniem).

Dla mnie budowanie relacji na podstawie kluczy obcych to podstawowe zagadnienie związane z relacyjnymi bazami danych. Klucze służą do opisywania relacji pomiędzy tabelami i szczerze przyznaję, że nie chce mi się wierzyć, że zbiory setów są - przy dużej ilości rekordów - dużo bardziej wydajniejsze (a może są, ale na krótką metę, później zrobi się z bazy jeden wielki kocioł z bigosem).

Oczywiście trzeba Ci oddać, że generalnie operacje bitowe są przeprowadzanie szybciej niż wyciąganie kluczy obcych i tu się zgadzamy, ale nie proponowałbym takiego rozwiązania na dłuższą metę, a już na pewno nie określałbym go jako bardziej dalekowzrocznego niż relacje na FK.

exseerius - jeśli jesteś pewien, że ilość opcji, które ma do wyboru Twój user nie przekroczy 64, nie będziesz miał olbrzymiej bazy userów i nie potrzebujesz wyszukiwać opcji wyboru według różnych warunków, to śmiało rozważ zastosowanie proponowane przez Ges. W przeciwnym razie proponuję tworzenie prostych relacji pomiędzy tabelami i dalszą normalizację bazy (w zależności od rodzaju i ilości opcji wybieranych przez użytkowników). Oczywiście relacje zawarte w moim poście można jeszcze lepiej (wydajniej) określić, to była tylko propozycja, szkic.

Pozdrawiam.
Ges
Racja, obie opcje trzeba wziąć pod uwagę i wybrać odpowiedniejsza dla siebie wersję rozwiązania.
Jeżeli główną funkcją serwisu będzie opisywanie różnych przeróżnych opcji i ilość opcji może się rozrosnąć, bądź będzie też dość często zmieniana, to faktycznie zrobienie tego na tabelach będzie efektywniejszą i bezpieczniejszą opcją.

Jeśli natomiat mamy ileś tam opcji dla każdego uzytkownika i jest to zamknięty zbiór ktory nie będzie się rozbudowywał, polecam SETa.

Z opisu problemu w tym wątku myślę, że wybór jednego z 2 rozwiązań należy do autora wątku :]

Dzięki za kreatywną dyskusję ;]
Gapollo
...
#1 dwie tabele, jedna z definicją kolorów

id | kolor
1 | niebieski
2 | zielony
3 | czerwony

druga wybory (zawierająca tylko wybrane przez usera kolory a nie wszystkie z flagami true/false)

user | kolor
1 | 1
1 | 3

(wybrał niebieski i czerwony)

pokaż userów którzy wybrali czerowny i niebieski:
... WHERE kolor IN (3,1)

[/quote]
phpion
Cytat(Gapollo @ 16.11.2009, 12:34:27 ) *
pokaż userów którzy wybrali czerowny i niebieski:
... WHERE kolor IN (3,1)

Nie do końca: powyższe pokaże użytkowników, którzy wybrali kolor czerwony lub niebieski. Aby móc użyć i musiałbyś pogrupować dane oraz sprawdzić HAVING COUNT(pole) = 2.
Gapollo
Cytat(phpion @ 16.11.2009, 11:41:52 ) *
Nie do końca: powyższe pokaże użytkowników, którzy wybrali kolor czerwony lub niebieski. Aby móc użyć i musiałbyś pogrupować dane oraz sprawdzić HAVING COUNT(pole) = 2.


Tak, oczywiście wyraziłem się nieprecyzyjnie. pokaże tych uzytkowników, którzy wybrali kolor czerwony i tych, którzy wybrali kolor niebieski.
Nawiązywałem do "WHERE kolor = 0.0.0.0.0.0.1.1.0.0.0.0 OR 1.1.1.1.1.0.0.0.0.0"
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.