Bazy danych SQLite w PHP

Autor: Tomasz Jędrzejewski
Data publikacji: 12.11.2003, 09:16 | Ostatnia modyfikacja: 29.10.2006, 16:21

Z bazÄ… danych SQLite, oraz jej obsÅ‚ugÄ… w PHP warto zapoznać siÄ™ z dwóch powodów. Pierwszy - nie wymaga instalacji Å»ADNYCH dodatkowych programów; Drugi - od PHP 5 zastÄ…pi ona MySQL'a jako wbudowany na staÅ‚e moduÅ‚ obsÅ‚ugi baz danych.



Z pewnoÅ›ciÄ… wielu chciaÅ‚oby przetestować możliwoÅ›ci innych systemów baz danych. Na ich drodze piÄ™trzy siÄ™ jednak sporo trudnoÅ›ci: brak dostÄ™pu do odpowiednich materiaÅ‚ów, maÅ‚a popularność danej bazy, a wreszcie trudnoÅ›ci ze zwykle skomplikowanÄ… instalacjÄ… odpowiednich programów zewnÄ™trznych. Jednak od niedawna PHP dysponuje znakomitym narzÄ™dziem, które nadaje siÄ™ do wiÄ™kszoÅ›ci zwykÅ‚ych zastosowaÅ„, a przy tym praktycznie nie wymaga instalacji. Chodzi o SQLite i tym pakietem zajmiemy siÄ™ w czytanym przez Ciebie tekÅ›cie.

SQLite bardzo dobrze wspiera zapewne znany ci już jÄ™zyk komunikacji z bazami danych zwany SQL (Structured Query Language). Co wiÄ™cej - obsÅ‚uguje kilka rozwiÄ…zaÅ„, których np. MySQL'owi po prostu brakuje:

  1. Obsługa zapytań zagnieżdżonych
  2. ObsÅ‚uga widoków
  3. Obsługa transakcji
  4. ObsÅ‚uga triggerów (acz jeszcze niepeÅ‚na)
  5. Możliwość definiowania własnych funkcji.

Jednak najważniejszym faktem dla Ciebie, programisty PHP jest to, że nie będziesz musiał instalować praktycznie żadnego dodatkowego serwera tejże bazy! Dlaczego? Bo sam moduł php_sqlite jest tym serwerem, a to oznacza, że serwer SQLite jest po prostu wbudowany w PHP :). Ponadto od PHP 5 będzie on już na stałe w niego wbudowany i nie będzie możliwości wyłączyć jego obsługi (do niedawna miejsce to dzierżył moduł dla baz MySQL).

SQLite przechowuje swoje dane w jednym pliku, który zwykle znajduje siÄ™ zaraz obok twoich skryptów PHP. Nie jest on jednak szyfrowany i dlatego SQLite niezbyt nadaje siÄ™, jeÅ›li zależy Ci na bezpieczeÅ„stwie swych danych. Z drugiej strony dziÄ™ki takiemu krokowi, wedÅ‚ug zapewnieÅ„ autorów, SQLite dziaÅ‚a prawie dwa razy szybciej niż MySQL i PostgreSQL. Tak wiÄ™c jeżeli bezpieczeÅ„stwo stawiasz na miejscu pierwszym - wybierz innÄ… bazÄ™ danych. W przeciwnym wypadku - warto spróbować. Być może kiedyÅ› bÄ™dzie to dla Ciebie jedyna alternatywa, gdy dostaniesz serwer bez obsÅ‚ugi żadnych innych baz danych?

MuszÄ™ także wspomnieć o pewnej rzeczy zwiÄ…zanej z obsÅ‚ugÄ… jÄ™zyka SQL w omawianej bazie - zapytania ALTER musisz odÅ‚ożyć na póÅ‚kÄ™, bowiem SQLite ich po prostu nie obsÅ‚uguje.

Instalacja

Instrukcje odnoÅ›nie instalacji w systemach Unix znajdujÄ… siÄ™ w pliku INSTALL dostÄ™pnym razem z pakietem kodu źródÅ‚owego PHP. Posiadacze PHP 5 mogÄ… spokojnie ten rozdziaÅ‚ pominąć, natomiast ci, którzy używajÄ… wciąż PHP 4 (UWAGA: Koniecznie wersja 4.3.x; jeÅ›li masz starszÄ…, to zrób update i nie popeÅ‚niaj wiÄ™cej grzechu zaniedbania tejże kwestii :)), muszÄ… pobrać plik php_sqlite.dll spod adresu: http://snaps.php.net/win32/PECL_STABLE/php_sqlite.dll, skopiować go do folderu "extensions" w katalogu ze swoim PHP, po czym otworzyć plik php.ini. Tam należy zlokalizować zbiór linijek z napisami "extension=php_xxx.dll" i pod nim dopisać "extension=php_sqlite.dll".

Do góry

Tworzenie nowej bazy danych

Na poczÄ…tku zapoznamy siÄ™ z samym "łączeniem siÄ™" do bazy SQLite, oraz zakÅ‚adaniem w niej tabel. Połączenie realizujemy przy pomocy funkcji sqlite_open(nazwa_pliku). Jako parametr podajemy nazwÄ™ pliku, w której znajduje siÄ™ baza danych, natomiast po jej wykonaniu otrzymujemy uchwyt połączenia, który bÄ™dzie potrzebny praktycznie we wszystkich innych funkcjach. Połączenie zamykamy poleceniem sqlite_close(), gdzie za parametr podajemy uchwyt. Zapytanie możemy wysÅ‚ać komendÄ… sqlite_query(uchwyt, zapytanie). A oto przykÅ‚ad numer 1, dziÄ™ki któremu stworzymy bazÄ™ potrzebnÄ… nam w późniejszych przykÅ‚adach.

<?php
 
   $db = sqlite_open('./sklep.sqlite');
   
   sqlite_query($db,'
   CREATE TABLE products (
      product_id INTEGER PRIMARY KEY,
      product_name VARCHAR(32),
      product_desc BLOB,
      product_price FLOAT
   );
   
   CREATE TABLE clients (
      client_id INTEGER PRIMARY KEY,
      client_name,
      client_address
   );
   
   INSERT INTO products VALUES (NULL, \'Spadochron do nurkowania\', \'Rewelacyjny spadochron dla nurków! Znakomicie zapobiega zderzeniu się z dnem!\', 250.99);
   INSERT INTO products VALUES (NULL, \'Wanna z drzwiami jednoskrzyd3owymi\', \'Dzięki wbudowanym drzwiom już wiecej nie pośliźniesz się wychodząc z wanny\', 599.8);
   INSERT INTO products VALUES (NULL, \'Używany papier toaletowy\', \'Tani, wygodny, ekologiczny (bo z odzysku) - czyli to, co ekolodzy lubią najbardziej\', 1.99);
   
   INSERT INTO clients VALUES (NULL, \'Mietek Żulewicz\', \'Prosektorium przy ul. Ofiar Hiroszimy; 00-000 Pcim Dolny\');
   INSERT INTO clients VALUES (NULL, \'Jan Kowalski\', \'ul. Stołeczna 13, 99-999 Bździągowo\');   
   ');
   
   echo 'Baza danych SQLite założona pomyślnie!';
   
   sqlite_close($db);
 
?>

Przyjrzyj siÄ™ wywoÅ‚aniu funkcji sqlite_query(), gdyż przedstawia ono parÄ™ ciekawych cech SQLite'a. Pierwsza z nich - możemy wysÅ‚ać dowolnie dużo zapytaÅ„ wywoÅ‚ujÄ…c funkcjÄ™ tylko raz - w tym wypadku kolejne zapytania oddzielone sÄ… Å›rednikami. Druga sprawa dotyczy samego tworzenia tabel (CREATE TABLE). Zauważ - w tabeli "clients" nie zdefiniowaliÅ›my typów dla pól. To nie jest błąd, bowiem SQLite dopuszcza takie rozwiÄ…zanie, a dziaÅ‚a ono podobnie do zmiennych w PHP - typ dobierany jest automatycznie. Warto zwrócić także uwagÄ™ na "INTEGER PRIMARY KEY". Kod ten informuje SQLite, że dane pole ma zawierać sekwencjÄ™. W PostgreSQL'u odpowiada to typowi SERIAL, natomiast w MySQL'u parametrowi AUTO_INCREMENT. Jeżeli chcesz zapoznać siÄ™ ze wszystkimi dostÄ™pnymi typami, wejdź pod adres http://sqlite.org/datatypes.html.

Do góry

Pobieranie danych z bazy

W tej sferze SQLite nie oferuje zbyt wielu niespodzianek - praktycznie wszystko przebiega podobnie, jak w innych bazach. Oto przykład:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Sklep</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-2">
</head>
 
<body>
<center>Produkty</center>
<?php
   $db = sqlite_open('./sklep.sqlite');
 
   $r = sqlite_query($db, 'SELECT * FROM products');
 
   echo 'Znaleziono '.sqlite_num_rows($r).' produktów<br>';
 
   while($row = sqlite_fetch_array($r, SQLITE_ASSOC)){
      echo '<b>'.$row['product_id'].'. '.$row['product_name'].'</b><br> Opis: '.$row['product_desc'].'<br>Cena: '.$row['product_price'].'<br><br>';
   }
?>
<hr>
<center>Klienci</center>
<?php
 
   $r = sqlite_query($db, 'SELECT * FROM clients');
 
   echo 'Znaleziono '.sqlite_num_rows($r).' klientów<br>';   
 
   while($row = sqlite_fetch_array($r, SQLITE_ASSOC)){
      echo '<b>'.$row['client_id'].'. '.$row['client_name'].'</b><br> Adres: '.$row['client_address'].'<br><br>';
   }
 
   sqlite_close($db);
?>
</body>
</html>

W zasadzie można zainteresować siÄ™ tu tylko drugim parametrem funkcji sql_fetch_array(). OkreÅ›la on, jak zostanÄ… okreÅ›lone nazwy pól w tabeli wynikowej. SQLITE_ASSOC oznacza, że bÄ™dziemy mogli odwoÅ‚ywać siÄ™ poprzez nazwy pól: $row['nazwa_pola']; SQLITE_NUM oznacza numeryczne oznaczenia: $row[1]; natomiast SQLITE_BOTH spowoduje, że tabela wynikowa bÄ™dzie zawieraÅ‚a i jedne, i drugie odwoÅ‚ania.

Do góry

Widoki

Widok to po prostu pojedyncze zapytanie SELECT, którego możemy używac tak jak zwykłą tabelÄ™ (z tego powodu jest zwany czasem wirtualnÄ… tabelÄ…). Stworzyć go możemy poleceniem CREATE VIEW nazwa AS zapytanie_select tak, jak w poniższym przykÅ‚adzie:

<?php
 
   $db = sqlite_open('./sklep.sqlite');
 
   sqlite_query($db, '
      CREATE TABLE products_to_clients (product_id, client_id);
 
      INSERT INTO products_to_clients VALUES(1,2);
      INSERT INTO products_to_clients VALUES(3,2);
      INSERT INTO products_to_clients VALUES(2,1);
 
      CREATE VIEW client_products AS SELECT pc.client_id AS client_id, p.product_name AS product_name FROM products_to_clients pc, products p WHERE pc.product_id = p.product_id;');
 
   $r = sqlite_query($db, 'SELECT product_name FROM client_products WHERE client_id=2');
   echo 'Produkty zamówione przez klienta 2:<br>';
   while($row = sqlite_fetch_array($r, SQLITE_ASSOC)){
      echo $row['product_name'].'<br>';
   }
 
   $r = sqlite_query($db, 'SELECT product_name FROM client_products');
   echo '<br><br>Wszystkie zamówione przynajmniej raz produkty<br>';
   while($row = sqlite_fetch_array($r, SQLITE_ASSOC)){
      echo $row['product_name'].'<br>';
   }
 
   sqlite_close($db);
?>

StworzyliÅ›my tutaj widok "client_products". Mówi on nam, że posiada dwa pola: client_id, oraz product_name (fragment SELECT pc.client_id AS client_id, p.product_name AS product_name). Wartość client_id pobierana jest z tabeli products_to_orders (informacje o tym, jakie produkty zamówiÅ‚ jaki klient), natomiast product_name z tabeli products, ale tylko wtedy, gdy speÅ‚nia warunek WHERE. Użytkownik nie widzi caÅ‚ego tego kombinowania z tym, skÄ…d pobrać wartoÅ›ci jakich pól - dla niego client_products to najzwyklejsza w Å›wiecie tabelka z dwoma polami. Jedynym "dziwnym" jej zachowaniem jest to, że jeżeli zmienimy nazwÄ™ jakiegoÅ› produktu w tabeli "products", to "zmieni" siÄ™ ona także w widoku.

UWAGA: W SQLite dozwolone jest tylko i wyłącznie czytanie z widoków - tak wiÄ™c naszego "client_products" nie bÄ™dziemy mogli użyć w zapytaniach typu INSERT, UPDATE, czy DELETE.

Widoki usuwamy poleceniem DROP VIEW nazwa_widoku.

Do góry

Tworzenie indeksów

Co to jest indeks, wie każdy, kto zabawiaÅ‚ siÄ™ PostgreSQL'em, czy MySQL'em - po prostu jest to kopia danego pola, gdzie wszystkie wartoÅ›ci sÄ… już posortowane, tak wiÄ™c przyspiesza siÄ™ wyszukiwanie, czy sortowanie, natomiast spowalnia dodawanie rekordów (z racji tego, że trzeba jeszcze zmodyfikować indeks), a także zwiÄ™ksza siÄ™ objÄ™tość bazy. Indeksów nie można zakÅ‚adać, jak popadnie - nie da siÄ™ też tego dobrze opisać. Dlatego proponujÄ™ pooglÄ…dać, gdzie sÄ… one zaÅ‚ożone w bazacch danych różnych znanych skryptów, np. phpBB, czy InvisionBoard. Indeks tworzymy w sposób przedstawiony na przykÅ‚adzie:

<?php
 
   $db = sqlite_open('./sklep.sqlite');
 
   sqlite_query($db, 'CREATE INDEX i ON products_to_clients (client_id, product_id);');
 
   if(!sqlite_last_error($db)){
      echo 'Indeks dodany!';
   }else{
      echo 'Błąd SQLite: '.sqlite_error_string(sqlite_last_error());
   }
 
   sqlite_close($db);
?>

Jedyne, co musimy określić w zapytaniu CREATE INDEX to nazwa indeksu, tabela, na jaką chcemy go założyć, oraz pola, jakich ma on dotyczyć.

PrzykÅ‚ad ten ilustruje dodatkowo, jak należy sprawdzać błędy w naszych zapytaniach. Jest to bardzo ważne, ponieważ czÄ™sto nieutworzenie uchwytu do wyników przez funkcjÄ™ sqlite_query() spowodowane jest wÅ‚aÅ›nie błędem w naszym zapytaniu!

Do góry

Triggery - co to takiego?

Trigger jest to zadanie (jedno, lub wiÄ™cej zapytaÅ„ SQL) wykonujÄ…ce siÄ™ w tle (czyli programista nawet nie wie, że ono siÄ™ wywoÅ‚aÅ‚o), gdy zajdzie okreÅ›lona okoliczność (np. dodanie rekordu do bazy). Daje nam on potężne możliwoÅ›ci. ZaprezentujÄ™ je na podstawie dwóch poniższych przykÅ‚adów. Pierwszy z nich utworzy trigger, natomiast drugi pokaże efekt jego dziaÅ‚ania :). Pod ogieÅ„ weźmiemy tabelÄ™ products_to_clients oraz clients. Co tu może dziać siÄ™ ciekawego? Ano, można usunąć klienta z bazy. Wtedy jednak zostanÄ… Å›mieci w pierwszej tabelce. Mamy dwa wyjÅ›cia - albo rÄ™cznie je usunąć, albo utworzyć trigger, który zrobi to za nas bez nas :).

<?php
 
   $db = sqlite_open('./sklep.sqlite');
   
   sqlite_query($db, '
      CREATE TRIGGER delete_orders BEFORE DELETE ON clients
      FOR EACH ROW
      BEGIN
         DELETE FROM products_to_clients WHERE client_id = OLD.client_id;
      END;
   ');
   
   if(!sqlite_last_error($db)){
      echo 'Trigger dodany!';
   }else{
      echo 'Błąd SQLite: '.sqlite_error_string(sqlite_last_error());
   }
   
   sqlite_close($db);
?>

Co my tu mamy? Ano, zdefiniowany trigger "delete_orders", który wykona siÄ™ przed skasowaniem rekordów z tabeli clients (BEFORE DELETE ON clients). Dla każdego kasowanego rekordu (FOR EACH ROW) wykona on zapytanie DELETE FROM products_to_clients WHERE client_id = OLD.client_id;, które usunie z tabeli products_to_orders odnoÅ›niki do produktów zamówionych przez klienta "OLD.client_id". OLD jest aliasem. SQLite daje nam jeszcze jeden taki alias: NEW, a oto ich użycie:

Polecam zapoznać siÄ™ ze skÅ‚adniÄ… polecenia CREATE TRIGGER na stronie: http://sqlite.org/lang.html#createtrigger, oraz w ogóle samodzielnie poeksperymentować z tym. ObsÅ‚uga triggerów jest tematem na caÅ‚y obszerny artykuÅ‚, a ja nie zamierzam siÄ™ aż tak tu rozpisywać :).

Ale wróćmy do rzeczywistoÅ›ci. Warto pokazać, co nasz trigger takiego potrafi:

<?php
 
   $db = sqlite_open('./proba_sklep.sqlite');
 
   sqlite_query($db, 'DELETE FROM clients WHERE client_id = 2');
   if(!sqlite_last_error($db)){
      echo 'Klient 2 usunięty!';
   }
 
   $r = sqlite_query($db, 'SELECT * FROM products_to_clients');
   echo 'Kontrola zamówień:<br>';
   while($row = sqlite_fetch_array($r, SQLITE_ASSOC)){
      echo 'Klient '.$row['client_id'].' zamówił produkt o ID '.$row['product_id'].'<br>';
   }
   
   sqlite_close($db);
 
?>

Uruchom przykÅ‚ad - co siÄ™ staÅ‚o? UsunÄ™liÅ›my tylko klienta z tabeli "clients", natomiast w tabeli "products_to_orders" nie ma także już informacji o zamówionych przez niego produktach. Po prostu w tle zadziaÅ‚aÅ‚ trigger, który posprzÄ…taÅ‚ za nas automatycznie.

Do góry

Tworzenie własnych funkcji

Aby stworzyć własną funkcję w PostgreSQL'u, należało być w nim samym nieźle otrzaskanym, gdyż zadanie to jest tam trudniejsze, niż przybicie galaretki do ściany :). Jednak SQLite jest wbudowany w PHP, nic nie stoi zatem na przeszkodzie, by korzystać z... udostępnianych przez niego funkcji. Oto przykład:

<?php
 
   function tralala(){
      return 'Jestem sobie funkcjÄ… PHP-SQLite!';
   }
 
   $db = sqlite_open('./sklep.sqlite');
 
   sqlite_create_function($db, 'nowa_funkcja', 'tralala');
 
   $r = sqlite_query($db, 'SELECT nowa_funkcja() AS wynik;');
   $row = sqlite_fetch_array($r, SQLITE_ASSOC);
 
   echo $row['wynik'];
 
   sqlite_close($db);
?>

UtworzyliÅ›my w PHP funkcjÄ™ "tralala", a nastÄ™pnie poleceniem sqlite_create_function poinformowaliÅ›my SQLite, że jeÅ›li natrafi w zapytaniu na funkcjÄ™ "nowa_funkcja", to ma wykonać stworzonÄ… przez nas "tralala". Niżej z resztÄ… widać, iż sposób ten po prostu dziaÅ‚a.

Do góry

Zakończenie

To już koniec artykułu o bazie SQLite. Mam nadzieję, że zainteresowałem Cię tym rozwiązaniem, zwłaszcza, że jest ono tego warte. Nie poprzestawaj na tym. Oto dwa bardzo przydatne adresy, pod którymi znajdziesz więcej informacji:

Jednocześnie zapewniam, że SQLite będzie często powracać w moich artykułach.

Autor: Tomasz "Zyx" Jędrzejewski, www.zyxist.com

Do góry

Waszym zdaniem:

Nikt jeszcze nie dodał swojego komentarza. Możesz być pierwszy!


Twoim zdaniem:

Reklama

banner

Partnerzy

CityDesign.pl
phpSolutions