Skocz do zawartości
Kaz

MySQL - poradnik.

Rekomendowane odpowiedzi

Poradnik dotyczący MySQL

Spis treści:
1). Wprowadzenie
2). Wymagania
3). Transakcje:
- omówienie
- polecenia
3). Tworzenie tabel
4). Typy pól
5). Łączenie z bazą
6). Wysyłanie zapytania
7). Omówienie kilku funkcji dot. MySQL
8). Obsługa danymi:
- stworzenie rekordu
- pobranie rekordu
- zapisanie danych
9). Rady, zakończenie

 

 

Wprowadzenie:

MySQL to inaczej wolnodostępny system zarządzania bazą danych. Jest obsługiwany przez wiele systemów, dzięki czemu nie mamy problemu z utrzymaniem kompatybilności. Dodatkowo jest wydany wersji źródłowej, co umożliwia skompilowanie go dla dowolnej platformy systemowej. Istnieje kilka typów mechanizmów każdy z nich służy do innego zastosowania:
- MyISAM - domyślny mechanizm (nie obsługuje transakcji ani kluczy obcych, umożlwia wyszukiwanie pełnotekstowe)
- InnoDB - także domyślna opcja (obsługuje transakcje, klucze obce oraz zakładanie blokad na poziomie wierszy)
Jest jeszcze kilka typów, ale nimi się nie będziemy interesować, ponieważ zostały wycofane.

 

 

 

 

Wymagania(informacje dla osób chcących założyć hosting baz mysql):

Co prawda MySQL nie potrzebuje dużych wymagań z takiego powodu iż sam w trakcie wykonywania procesów, stara się je sam optymalizować. To także dotyczy się tabel oraz baz danych. Z doświadczenia nauczyłem się, że w teorii bazy potrzebują dużo pamięci RAM i szybkie dyski, można też trafić na to, że procesory też są brane pod uwagę. Głównie MySQL opiera się na cache'ch, bo ponad 90% wykonywanych zapytań trafia właśnie tam.
Konfiguracja dla osób, które chcą w przyszłości otworzyć firmę oferująca bazy danych:
- Procesor: sempron 3000+
- Pamięć: 2 GM RAM
- Dysk twardy (główny): 2x250 GB (RE, 16 MB cache) z RAID
- Dysk twardy (poboczny): 1x400 GB (wolniejsze dyski, dla przeprowadzanych backup'ów)
Druga konfiguracja "optymalna" dla użytkownika:
- Procesor: 1000+
- Pamięć: 256/512 MB RAM
- Dysk twardy: 20 GB z (RE, 16 MB cache i RAID)

 

 

 

 

Transakcje:

Transakcja - co to w ogóle jest? Otóż jest to zbiór operacji na bazie danych, które stanowią w istocie pewną całość i jako takie powinny być wykonane wszystkie lub żadna z nich. Przykładem takiej transakcji jest bankowa - przelew. Muszą zostać spełnione 2 operacje - zabranie pieniędzy z jednego konta oraz dopisanie ich do drugiego. W przypadku niepowodzenia żadna z tych operacji nie powinna być zatwierdzona, gdyż zajście tylko jednej powodowałoby nieprawidłowości w bazie danych (pojawienie się lub zniknięcie pieniędzy).
Transakcja składa się z trzech elementów:

 

 

  • rozpoczęcia
  • wykonania
  • zamknięcia

W systemach bazodanowych istotne jest, aby transakcja trwała jak najkrócej, ponieważ równolegle może być dokonywanych wiele transakcji. Każdy etap transakcji jest logowany, dzięki czemu w razie awarii systemu można odtworzyć stan bazy danych sprzed transakcji, która nie została zamknięta.
 

Transakcje w SQL

W systemach baz danych realizujących standard SQL następujące polecenia dotyczą transakcji:

  • BEGIN lub BEGIN WORK - rozpoczęcie transakcji;
  • COMMIT - zatwierdzenie zmian wykonanych w obrębie transakcji;
  • ROLLBACK - odrzucenie zmian wykonanych w obrębie transakcji;
  • SAVEPOINT nazwa - zdefiniowanie punktu pośredniego o określonej nazwie;
  • RELEASE SAVEPOINT nazwa - skasowanie punktu pośredniego;
  • ROLLBACK TO SAVEPOINT nazwa - wycofanie transakcji do stanu zapamiętanego;

Tworzenie tabel:

Jest to główny proces obsługi naszej bazy, wymaga utworzenia bazy i połączenia się, ale o łączeniu w następnym rozdziale. Możemy zacząć wprowadzać dane, najpierw jednak trzeba utworzyć tabele, robimy to według następującego schematu:

CREATE TABLE nazwa_tabeli (nazwa_pola1 typ_pola1 [atrybuty], nazwa_pola2 typ_pola2 [atrybuty], nazwa_pola3 typ_pola3 [atrybuty], PRIMARY KEY(nazwa_polaX))

Przykład:

CREATE TABLE NBA (id int NOT NULL AUTO_INCREMENT, imie char(30), lata char(3), punkty char(3), mistrzostwa char(3), PRIMARY KEY(id))

Każda tabela musi posiadać co najmniej jedno pole, dodatkowo co najmniej jedno pole, które będzie jednoznacznie identyfikuje wiersz w tabeli - tak zwany klucz główny. Jeśli jest masa danych, a Ty znasz klucz główny jednego z wierszy wtedy możesz bez problemu dostać się do tego wiersza. W naszym przypadku kluczem głównym jest pole pierwsze - id.
Przeważnie nadaje jako klucz główny określa się pierwsze pole tabeli. Dodatkowo klucz główny posiada dwa atrybuty:
pierwszy - NOT NULL - oznaczający, że wartość tego pola nigdy nie może być pusta;
drugi - AUTO_INCREMENT* - oznaczający, że wartość pola będzie automatycznie zwiększania przez bazę danych przy dodaniu rekordu;

* Atrybut AUTO_INCREMENT - możemy stosować tylko do pól typu całkowitoliczbowego.

 

 

 

Typy pól:

Dostępnych jest wiele typów pól, najpopularniejsze poniżej:
- CHAR(x) / VARCHAR(x) - ciąg znaków o maksymalnej dł. X, gdzie X nie może być większy od 255;
- BLOB - binarny ciąg znaków o dł. ograniczonej przez pamięc Twojego serwera;
- TINYINT(ilość znaków) - liczby z zakresu od -128 do 127 lub liczby dodatnie od 0 do 255;
- SMALLINT(ilość znaków) - liczby z zakresu od -32768 do 32767 lub liczby dodatnie od 0 do 65535;
- MEDIUMINT(ilość znaków) - liczby z zakresu od -8388608 do 8388607 lub liczby dodatnie od 0 do 16777215;
- INTEGER / INT - liczba całkowita z przedziału -2147483647 do 2147483647 lub liczby dodatnie od 0 do 4294967295;
- TEXT - tekstowy ciąg znaków o dł. ograniczonej przez pamięć Twojego serwera;
- DATE - data w formacie określonym przez ustawienia serwera;
- ENUM - enumeracja (wyliczenie). W kolumnie może się znaleźć jedna z podanych wartości;
- YEAR - rok, jeśli zostanie podany zły, jego wartość zmieni się w 0000;
- DECIMAL(x, y) - liczba dziesiętna, gdzie X oznacza maksymalną liczbe cyfr, a Y maksymalną liczbę cyfr po przecinku;

 

 

 

 

Łączenie z bazą:

Za pomocą poniższego kodu połączymy się poprawnie z naszą bazą (plugin: MySQL StrickenKid):

#define SQL_HOST "localhost"
#define SQL_USER "user"
#define SQL_PASSWORD "password"
#define SQL_DB "datebase"

new MySQL:sql_init;
public OnFilterScriptInit()
{
sql_init = mysql_init(1);
new sql_handle = mysql_connect(SQL_HOST, SQL_USER, SQL_PASSWORD, SQL_DB, sql_init, .auto_reconnect=1);
if (sql_handle) // mysql_connect jeśli nastąpi poprawne połączenie z bazą zwróci nam wynik "true"
{
// jeśli warunek się wykona
printf("Połączono z bazą danych, gratulacje!");
} else {
// jeśli warunek się nie wykona
printf("Niepołączono z bazą danych!!!");
SendRconCommand("exit"); // zamykamy serwer
}
return 1;
}

public OnFilterScriptExit() return mysql_close(sql_init);

I tak oto poprawnie połączyliśmy się z naszą bazą!

 

 

 

Wysyłanie zapytania:

W tym kroku nauczymy się wykonywać polecenia/zapytania, które wyślemy do naszej bazy.
Dla przykładu pobierzemy z naszej przykładowej tabeli lata oraz punkty, którego wyszukamy za pomocą imienia.

new buffer[127], playerNick[MAX_PLAYER_NAME];
GetPlayerName(playerid, playerNick, MAX_PLAYER_NAME);
mysql_real_escape_string(playerNick, playerNick); // sprawdzamy, czy nie ma zadnych podejrzanych znakow, ktore moga naszej bazie zaszkodzic
format(buffer, 127, "SELECT lata, punkty FROM NBA WHERE imie='%s'", playerNick);
if (mysql_query(buffer)) mysql_ping(); // Ta linijka pozwoli nam podtrzymać nasze połączenie
mysql_store_result(); // zapisujemy do pamięci
if (mysql_num_rows() && mysql_fetch_row(buffer, "|"))
{
new lata, punkty;
sscanf(buffer, "p<|>dd", lata, punkty);
format(buffer, 127, "Masz %d punktów i %d lat.", punkty, lata);
SendClientMessage(playerid, -1, buffer);
}
mysql_free_result(); // czyścimy pamięć

Jeśli nasz rekord będzie istniał otrzymamy wiadomość np.

Masz 14 punktów i 19 lat.

Jak widzimy bardzo łatwo jest pobrać informacje posiadając jedno z nich - tutaj imię.

 

 

 

Omówienie kilku funkcji dot. MySQL:

 

Ogólnie w MySQL posiadamy bardzo dużo funkcji, które przydają się nam w różnych problemach można znaleźć rozwiązania dzięki nim.
Podam tylko te związane z datą, które mogą posłużyć nam do stworzenia systemu Premium:

-------------------------------------------------
| Funkcja | Przykład użycia | Opis |
-------------------------------------------------
HOUR() | HOUR(kolumna) | Zwraca samą godzine, ze wskazanej daty
MINUTE() | MINUTE(kolumna) | Zwraca same minuty, ze wskazanej daty.
SECOND() | SECOND(kolumna) | Zwraca same sekundy, ze wskazanej daty.
DAYNAME() | DAYNAME(kolumna) | Zwraca nazwę dnia tygodnia.
DAYOFMONTH() | DAYOFMONTH(kolumna) | Zwraca sam dzień miesiąca, ze wskazanej daty (wyrażone liczbą).
MONTHNAME() | MONTHNAME(kolumna) | Zwraca nazwę miesiąca występującego we wskazanej dacie.
MONTH() | MONTH(kolumna) | Zwraca sam miesiąc ze wskazanej daty (wyrażony liczbą).
YEAR() | YEAR(kolumna) | Zwraca sam rok ze wskazanej daty.
ADDDATE() | ADDDATE(kolumna INTERVAL x typ) | Dodaje do daty przechowywanej w kolumnie x jednostek i zwraca wynik
SUBDATE() | SUBDATE(kolumna INTERVAL x typ) | Odejmuje od daty przechowywanej w kolumnie x jednostek i zwraca wynik.
UNIX_TIMESTAMP()| UNIX_TIMESTAMP(data) | Zwraca liczbę sekund jaka upłynęła od początku tzw. epoki unixa lub od wskazanej daty.

Obsługa danymi:

- stworzenie rekordu
W życiu codziennej pracy MySQL, przyjdzie taka pora, że trzeba będzie stworzyć REKORD.
A więc napiszmy taką, sugerując się wcześniejszą tabelą
Przykład 1).

INSERT INTO NBA (imie, punkty, lata, mistrzostwa) VALUES ('Imie', '17', '2', '1');

Przykład 2).

INSERT INTO NBA SET imie='Imie', punkty='17', lata='2', mistrzostwa='1';

Przykład 3).

INSERT INTO NBA ('Imie', '17', '2', '1');

Jak widzimy, można zrobić to na wiele sposobów. Najlepszym (według mnie) rozwiązaniem jest przykład drugi, ponieważ możemy łatwo zobaczyć co i jak ustawiamy. W pierwszym przykładzie jest to o tyle utrudnione, że wszystko "leci" po kolei, a w ostatnim nie wiemy, czy dobrze stworzyliśmy rekord.

- pobranie rekordu
Ten krok pozwoli się nam przyjrzeć jak powinno wyglądać poprawne pobranie rekordu.
Stwórzmy sobie funkcje, która wyśle zapytanie, czy dane konto o nicku np. 'Polak' istnieje...

public OnPlayerConnect(playerid)
{
if (SprawdzCzyKontoIstnieje(playerid))
{
SendClientMessage(playerid, -1, "Twoje konto istnieje w bazie danych!");
} else {
SendClientMessage(playerid, -1, "Twoje konto nie istnieje w bazie danych!");
}
return true;
}

SprawdzCzyKontoIstnieje(graczid)
{
new buffer[127], nick[24], bool: istnieje = false;
GetPlayerName(graczid, nick, MAX_PLAYER_NAME);
mysql_real_escape_string(nick, nick);
format(buffer, 127, "SELECT 1 FROM NBA WHERE imie='%s'", nick);
if (mysql_query(buffer)) mysql_ping();
mysql_store_result();
if (mysql_num_rows()) istnieje=true;
mysql_free_result();
return istnieje; // jeśli znalazlo konto otrzymamy wynik true, jesli nie, to false
}

Proste, prawda? Według mnie nie trzeba tutaj nic tłumaczyć.

- zapisanie danych
Przyszła pora na zapisanie/aktualizację danych gracza
Użyjemy SEPARATORA "UPDATE":

enum p_info { bool: logged, imie[24], punkty, lata, mistrzostwa };
new pInfo[MAX_PLAYERS][p_info];

public OnPlayerDisconnect(playerid, reason)
{
if (pInfo[playerid][logged]) // Sprawdzamy czy zalogowany jest
{
new buffer[127];
GetPlayerName(playerid, pInfo[playerid][imie], MAX_PLAYER_NAME);
pInfo[playerid][punkty] = GetPlayerScore(playerid);
format(buffer, 127, "UPDATE NBA SET punkty='%d', lata='%d', mistrzostwa='%d' WHERE imie='%s'", pInfo[playerid][punkty], pInfo[playerid][lata], pInfo[playerid][mistrzostwa], pInfo[playerid][imie]);
mysql_query(buffer);
}
return true;
}

I tak oto, zapisaliśmy dane gracza, gdy opuścił serwer. Użyłem 2 przykładu omówionego w poprzednich podpunktach, ponieważ jest najprostszy.

 

 

 

 

 

Zakończenie:

Nadszedł koniec tego poradnika. Omówimy tutaj trochę informacji o bezpieczeństwie zapytań.
Najlepszym rozwiązaniem dotyczącym bezpieczeństwa jest przeczytanie tego klik!
Należy pamiętać o SQL Injection, można się przed tym zabezpieczyć stosując w formatach tj. '%s', '%d', '%f';
Podałem możliwe przykłady stosując do tego PAWN, aby było prościej zrozumieć ich znaczenie.
Pamiętaj MySQL nie odnosi się tylko do PAWN, w ten sam sposób można go użyć do innych języków programowania! :)
Przez to, że wiele języków programowania używa MySQL można z łatwością pobrać rekord z bazy danych w PHP, a wcześniej stworzyć go np. w PAWN.

© 2012, Górniczek. Szczególną pomoc dedykuję AXV.

Edytowane przez Gorniczek

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

@up zgaduję, że pewnie wgl. go nie przeczytałeś. Poradnik nie odnosi się tylko do samego kodu i wszystkich tych gotowców, ale również do ciekawostek i ważnych informacji, które są opisane. Nie patrz na przykłady, bo to tylko przykłady...

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Bardzo dobry poradnik. Ja osobiście dodałbym kilka screenów jak to się zapisuje w phpMyAdmin, bo nie wszyscy ogarniają to.

Tzn. gdzie tabele, gdzie nowe bazy, rekordy etc

Edytowane przez dawid4157

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

@up: Mało masz takich poradników na oficjalnym forum SA-MP'a? [Tutorial] Easy MySQL Register/Login system! - SA-MP Forums , [Tutorial] Dynamic Faction System [MySQL] - SA-MP Forums , [Tutorial] Setting up MySQL database [For newbies!] - SA-MP Forums Warto ruszyć mózgiem :). Po za tym wytłumaczył do czego służą typy kolumn.

@Down: Ale gościowi chodzi by dał screeny, ja podałem te tematy ponieważ tam są screeny w jaki sposób tworzyć tabele, kolumny. Jak wydasz poradniki o MySQL to z chęcią poczytam :). W dodatku temat Gorniczka bardzo mi pomógł z typami pól, wielkie dzięki :P.

Edytowane przez Człowiek123

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

@UP Takich badziewi z forum sampa nie oplaca sie czytac, czesto sa tam perfidne gotowce, do tego slabe i nie poradne, mowiac o poradnikach MySQL oczywiscie. ;-)

Jak bedzie troche wiecej czasu to osobiscie wydam kilka poradnikow na temat mysql.

Edytowane przez AXV

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

new buffer[127], playerNick[MAX_PLAYER_NAME];

GetPlayerName(playerid, playerNick, MAX_PLAYER_NAME);

mysql_real_escape_string(playerNick, playerNick); // sprawdzamy, czy nie ma zadnych podejrzanych znakow, ktore moga naszej bazie zaszkodzic

format(buffer, 127, "SELECT lata, punkty FROM NBA WHERE imie='%s'", playerNick);

if (mysql_query(buffer)) mysql_ping(); // Ta linijka pozwoli nam podtrzymać nasze połączenie

mysql_store_result(); // zapisujemy do pamięci

if (mysql_num_rows() && mysql_fetch_row(playerNick, ""))

{

new lata, punkty;

sscanf(playerNick, "dd", lata, punkty);

format(buffer, 127, "Masz %d punktów i %d lat.", punkty, lata);

SendClientMessage(playerid, -1, buffer);

}

mysql_free_result(); // czyścimy pamięć

Tutaj nie ma błędu w sscanfie? W bufferze, czy playerNick są zapisane punkty i lata?

Edytowane przez BaLL

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Ja szukam tego poradnika w dziale "Poradniki" a on jest w oczekujących... 

Dodajcie ten poradnik w końcu, bo on jest świetny i bardzo dużo można się z niego nauczyć. 

Pozdro.

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Gorniczek:

 

Naprawdę popraw to, bo tu jest błąd.

 

 

 

sscanf warning: Strings without a length are deprecated, please add a destination size.

 

new buffer[127], playerNick[MAX_PLAYER_NAME];
GetPlayerName(playerid, playerNick, MAX_PLAYER_NAME);
mysql_real_escape_string(playerNick, playerNick); // sprawdzamy, czy nie ma zadnych podejrzanych znakow, ktore moga naszej bazie zaszkodzic
format(buffer, 127, "SELECT lata, punkty FROM NBA WHERE imie='%s'", playerNick);
if (mysql_query(buffer)) mysql_ping(); // Ta linijka pozwoli nam podtrzymać nasze połączenie
mysql_store_result(); // zapisujemy do pamięci
if (mysql_num_rows() && mysql_fetch_row(playerNick, ""))
{
new lata, punkty;
sscanf(playerNick, "dd", lata, punkty);
format(buffer, 127, "Masz %d punktów i %d lat.", punkty, lata);
SendClientMessage(playerid, -1, buffer);
}
mysql_free_result(); // czyścimy pamięć
Edytowane przez dawid4157

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Znalazłem literówkę.

 

W tym kodzie:

 

 

 

SprawdzCzyKontoIstnieje(graczid)
{
new buffer[127], nick[24], bool: istnieje = false;
GetPlayerName(graczid, nick, MAX_PLAYER_NAME);
mysql_real_escape_string(nick, nick);
format(buffer, 127, "SELECT 1 FROM NBA WHERE imie='%s'", nick);
if (mysql_query(buffer)) mysql_ping();
mysql_store_result();
if (mysql_nom_rows()) istnieje=true;
mysql_free_result();
return istnieje; // jeśli znalazlo konto otrzymamy wynik true, jesli nie, to false
}
 

 

na końcu jest mysql_nom_rows

 

Powinno być raczej mysql_num_rows.

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

@up zauważ, że to samo to tylko zapytania, które pomagał mi robić AXV. Myślisz, że ja na pamięć znam wszystkie typy pól? Logiczne, że są takie same, więc nie widzę problemu.

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

  • Przeglądający   0 użytkowników

    Brak zarejestrowanych użytkowników przeglądających tę stronę.

×