mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-04 15:24:07 +01:00
571 lines
25 KiB
Plaintext
571 lines
25 KiB
Plaintext
Relacyjny Rekord Aktywny
|
|
========================
|
|
|
|
Dotychczas zobaczyliśmy jak używać Rekordu Aktywnego (AR) aby wybierać dane z jednej
|
|
tabeli bazodanowej. W tej sekcji opiszemy jak używać AR aby złączyć kilka powiązanych
|
|
tabel baz danych i zwrócić połączony zbiór danych.
|
|
|
|
W celu używania relacyjnego AR, wymagane jest, aby relacja dla obcego klucza głównego
|
|
była dobrze zdefiniowana pomiędzy tabelami, które będą łączone. AR bazuje na metadanych
|
|
tych relacji gdy decyduje jak połączyć tabele.
|
|
|
|
> Note|Uwaga: Poczynając od wersji 1.0.1, możesz używać relacyjnego AR nawet jeśli
|
|
> nie zdefiniowałeś kluczy obcych w swojej bazie danych.
|
|
|
|
Dla uproszczenia, będziemy używali schematu bazy danych pokazanego na następnym
|
|
diagramie zależności encji (ER) celem zilustrowania przykładów w tej sekcji.
|
|
|
|

|
|
|
|
> Info|Info: Wsparcie dla ograniczeń kluczy obcych różni sie w różnych DBMS.
|
|
>
|
|
> SQLite < 3.6.19 nie wspiera ograniczeń kluczy obcych, lecz wciąż możesz deklarować
|
|
> ograniczenia, podczas tworzenia tabel. AR może wykorzystać te deklaracje
|
|
> aby prawidłowo wspierać relacyjne zapytania.
|
|
>
|
|
> MySQL wspiera ograniczenia kluczy obcych gdy używany jest silnik InnoDB,
|
|
> w przeciwieństwie do silnika MyISAM. Zalecamy zatem używanie InnoDB dla twoich
|
|
> baz danych MySQL.
|
|
> Podczas używania MyISAM, możesz wykorzystać następujący trik
|
|
> aby móc wykonywać relacyjne zapytania przy użyciu AR:
|
|
> ~~~
|
|
> [sql]
|
|
> CREATE TABLE Foo
|
|
> (
|
|
> id INTEGER NOT NULL PRIMARY KEY
|
|
> );
|
|
> CREATE TABLE bar
|
|
> (
|
|
> id INTEGER NOT NULL PRIMARY KEY,
|
|
> fooID INTEGER
|
|
> COMMENT 'CONSTRAINT FOREIGN KEY (fooID) REFERENCES Foo(id)'
|
|
> );
|
|
> ~~~
|
|
> Powyżej użyliśmy słowa kluczowego `COMMENT` aby opisać ograniczenia klucza obcego
|
|
> co może zostać przeczytanie przez AR aby rozponać opisywaną relację.
|
|
|
|
Deklarowanie relacji
|
|
----------------------
|
|
|
|
Zanim użyjemy AR aby wykonać relacyjne zapytanie, musimy powiedzieć AR jak
|
|
jedna klasa AR jest powiązana z drugą.
|
|
|
|
Relacja pomiędzy dwoma klasami AR jest bezpośrednio związana z relacją pomiędzy
|
|
tabelami bazy danych reprezentowanych przez klasę AR. Z punkty widzenia bazy danych
|
|
relacja pomiędzy dwoma tabelami A i B może występować w 3 wariantach:
|
|
jeden-do-wielu (ang. one-to-many; np. `tbl_user` i `tbl_post`), jeden-do-jednego (ang. one-to-one;
|
|
np. `tbl_user` i `tbl_profile`) oraz wiele-do-wielu (ang. many-to-many; np. `tbl_category` i `tbl_post`).
|
|
W AR występują cztery rodzaje relacji:
|
|
|
|
- `BELONGS_TO` (należy do): jeśli relacja pomiędzy tabelą A i B to jeden-do-jednego,
|
|
wtedy B należy do A (np. post `tbl_post` należy do użytkownika `tbl_user`);
|
|
|
|
- `HAS_MANY` (posiada wiele): jeśli relacja pomiędzy tabelą A i B to jeden-do-wielu
|
|
wtedy A ma wiele B (np. użytkownik `tbl_user` ma wiele postów `tbl_post`);
|
|
|
|
- `HAS_ONE` (posiada jedną): to jest specjalny przypadek relacji `HAS_MANY` gdzie A posiada
|
|
co najwyżej jedno B (np. użytkownik `tbl_user` ma co najwyżej jeden profil `tbl_profile`);
|
|
|
|
- `MANY_MANY` (wiele do wielu): odpowiada relacji bazodanowej wiele-do-wielu.
|
|
Aby rozbić relację wiele-do-wielu na jeden-do-wielu potrzebna jest tablica asocjacyjna,
|
|
gdyż wiele DBMS nie wspiera bezpośrednio relacji wiele-do-wielu.
|
|
W naszym przykładzie schemat bazy danych `tbl_post_category` zostanie użyty w tym celu.
|
|
W terminologii AR, możemy wytłumaczyć. W terminologii AR, możemy wytłumaczyć relację
|
|
wiele-do-wielu jako kombinację `BELONGS_TO` oraz `HAS_MANY`. Na przykład,
|
|
post `tbl_post` należy do wielu kategorii `tbl_category` a kategoria `tbl_category` posiada wiele postów `tbl_post`.
|
|
|
|
Deklarowanie relacji w AR wymaga nadpisania metody [relations()|CActiveRecord::relations]
|
|
z [CActiveRecord]. Metoda zwraca tablicę konfiguracji relacji. Każdy element tablicy
|
|
reprezentuje pojedynczą relację zapisaną w następującym formacie:
|
|
|
|
~~~
|
|
[php]
|
|
'NazwaZmiennej'=>array('TypRelacji', 'NazwaKlasy', 'KluczObcy', ...dodatkowe opcje)
|
|
~~~
|
|
|
|
gdzie `NazwaZmiennej` jest nazwą relacji; `TypRelacji` specyfikuje typ relacji i posiada
|
|
jedną z czterech stałych wartości:
|
|
`self::BELONGS_TO`, `self::HAS_ONE`, `self::HAS_MANY` oraz
|
|
`self::MANY_MANY`; `NazwaKlasy` jest nazwą klasy AR powiązanej z tą klasą; oraz
|
|
`KluczObcy` określa klucz(e) obcy(e) powiązane z tą relacją. Dodatkowe opcje moga być
|
|
określone na końcu każdej relacji (więcej szczegółów w dalszej części).
|
|
|
|
Następujący kod pokazuje jak możemy zadeklarować relację dla klasy użytkownika `User`
|
|
oraz postu `Post`.
|
|
|
|
~~~
|
|
[php]
|
|
class Post extends CActiveRecord
|
|
{
|
|
......
|
|
|
|
public function relations()
|
|
{
|
|
return array(
|
|
'author'=>array(self::BELONGS_TO, 'User', 'author_id'),
|
|
'categories'=>array(self::MANY_MANY, 'Category',
|
|
'tbl_post_category(post_id, category_id)'),
|
|
);
|
|
}
|
|
}
|
|
|
|
class User extends CActiveRecord
|
|
{
|
|
......
|
|
|
|
public function relations()
|
|
{
|
|
return array(
|
|
'posts'=>array(self::HAS_MANY, 'Post', 'author_id'),
|
|
'profile'=>array(self::HAS_ONE, 'Profile', 'owner_id'),
|
|
);
|
|
}
|
|
}
|
|
~~~
|
|
|
|
> Info|Info: Klucz obcy może być kluczem złożonym, zawierającym dwie lub więcej kolumn.
|
|
W takim przypadku, powinniśmy złączyć nazwy kolumn dla kluczy obcych rozdzielając przecinkiem.
|
|
Dla typu relacji `MANY_MANY`, nazwa tablicy asocjacyjnej musi
|
|
również być określona w kluczu obcym. Na przykład, relacja kategorii `categories` w `tbl_post`
|
|
jest określona przez klucz obcy `tbl_post_category(post_id, category_id)`.
|
|
|
|
Deklaracja relacji w klasie AR domyślnie dodaje właściwość do klasy
|
|
dla każdej relacji. Po tym jak zapytanie relacyjne jest wykonywane, odpowiadająca
|
|
właściwość będzie wypełniona odpowiadającymi instancjami AR. Na przykład, jeśli `$author`
|
|
reprezentuje instancję AR `User`, możemy użyć `$author->posts` aby dostać się do
|
|
powiązanych instancji `Post`.
|
|
|
|
Wykonywanie zapytań relacyjnych
|
|
---------------------------
|
|
Najprostszym sposobem wykonania relacyjnego zapytania jest odczytanie relacyjnej
|
|
właściwości instancji AR. Jeśli właściwość nie była wcześniej odczytywana,
|
|
relacyjne zapytanie zostanie zainicjalizowane za pomocą którego złączymy dwie połączone z sobą
|
|
tabele i odfiltrujemy dane przy użyciu klucza głównego aktualnej instancji AR. Wynik zapytania
|
|
będzie zapisany we właściwości jako instancja(e) powiązanej klasy AR. Jest to znane
|
|
jako technika *opóźnionego ładowania* (ang. *lazy loading* approach), np. zapytanie
|
|
relacyjne jest wykonywane tylko wtedy, gdy powiązane obiekty są odczytywane są po
|
|
raz pierwszy. Poniższy przykład pokazuje jak używać tej techniki:
|
|
|
|
~~~
|
|
[php]
|
|
// zwróć post, którego ID wynosi 10
|
|
$post=Post::model()->findByPk(10);
|
|
// zwróć autora posta: tutaj będzie wykonane zapytanie relacyjne
|
|
$author=$post->author;
|
|
~~~
|
|
|
|
> Info|Info: Jeśli nie istnieją żadne powiązane instancje dla relacji,
|
|
odpowiednie właściwości mogą być pustą tablicą bądź wartością null.
|
|
Dla relacji `BELONGS_TO` oraz `HAS_ONE` wynikiem jest wartość null; dla
|
|
`HAS_MANY` oraz `MANY_MANY` jest to pusta tablica.
|
|
|
|
Zauważ, że relacje `HAS_MANY` oraz `MANY_MANY` zwracają tablicę obiektów, dlatego
|
|
będziesz musiał przejść w pętli przez zwrócony wynik, jeśli będziesz chciał uzyskać dostęp
|
|
do jakiejkolwiek właściwości obiektu. W przeciwnym przypadku, możesz uzyskać błąd: "Próbujesz
|
|
skorzystać z właściwości elementu nie będącego obiektem" (ang. "Trying to get property of non-object" errors).
|
|
|
|
Technika opóźnionego ładowania jest bardzo poręczne w użyciu, ale nie zawsze jest wydajne
|
|
we wszystkich scenariuszach. Dla przykładu, jeśli chcemy uzyskać dostęp do informacje o autorze dla
|
|
`N` postów, używając techniki opóźnionego ładowania wykonamy `N` zapytań z użyciem join.
|
|
W tych warunkach powinniśmy uciec się do tak zwanej techniki *zachłannego ładowania*
|
|
(ang. eager loading approach).
|
|
|
|
Technika zachłannego ładowania zwraca powiązane instancje AR razem z główną(ymi) instancją(ami) AR.
|
|
Osiągamy to poprzez użycie metody [with()|CActiveRecord::with] razem z jedną z metod AR
|
|
[find|CActiveRecord::find] lub [findAll|CActiveRecord::findAll]. Na przykład,
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->with('author')->findAll();
|
|
~~~
|
|
|
|
Powyższy kod zwróci tablicę instancji `Post`. W odróżnieniu od techniki leniwego ładowania
|
|
właściwość `author` w każdej instancji `Post` jest już wypełniona odpowiednią instancją
|
|
`User` przed jej pierwszym odczytaniem. Zamiast wywoływania zapytania z join dla
|
|
każdego posty, technika zachłannego ładowania zwraca wszystkie posty razem wraz z autorami
|
|
za pomocą jednego zapytania z join!
|
|
|
|
Możemy używać jednocześnie wielu nazw relacji w metodzie [with()|CActiveRecord::with]
|
|
a technika zachłannego łączenia dostarczy nam je z powrotem za jednym razem.
|
|
Na przykład, następujący kod zwróci posty razem z ich autorami oraz kategoriami:
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->with('author','categories')->findAll();
|
|
~~~
|
|
|
|
Możemy również korzystać z zagnieżdżonych zachłannych ładowań. Zamiast listy nazw relacji,
|
|
przekazujemy w postaci hierarchicznej nazwy relacji do metody [with()|CActiveRecord::with]
|
|
w następujący sposób:
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->with(
|
|
'author.profile',
|
|
'author.posts',
|
|
'categories')->findAll();
|
|
~~~
|
|
|
|
Powyższy przykład zwróci wszystkie posty razem z ich autorami oraz kategoriami.
|
|
Zwróci również dla każdego autora profil oraz posty.
|
|
|
|
Poczynając od wersji 1.1.0, zachłanne ładowanie może zostać również wywołane poprzez
|
|
określenie właściwości [CDbCriteria::with] w następujący sposób:
|
|
|
|
~~~
|
|
[php]
|
|
$criteria=new CDbCriteria;
|
|
$criteria->with=array(
|
|
'author.profile',
|
|
'author.posts',
|
|
'categories',
|
|
);
|
|
$posts=Post::model()->findAll($criteria);
|
|
~~~
|
|
|
|
lub
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->findAll(array(
|
|
'with'=>array(
|
|
'author.profile',
|
|
'author.posts',
|
|
'categories',
|
|
)
|
|
);
|
|
~~~
|
|
|
|
Opcje zapytań relacyjnych
|
|
------------------------
|
|
|
|
Wspominaliśmy, że dodatkowe opcje mogą być dookreślone w deklaracji relacji.
|
|
Opcje te zapisane jako pary nazw i wartości używane są w celu dostosowania do własnych
|
|
potrzeb zapytań relacyjnych. Poniżej znajdziemy ich zestawienie.
|
|
|
|
- `select`: lista kolumn, które będą zwrócone dla powiązanej klasy AR.
|
|
Domyślną wartością jest '*', oznaczająca wszystkie kolumny. Nazwy kolumn
|
|
do których się odwołujemy w tej opcji powinny zostać ujednoznacznione.
|
|
|
|
- `condition`: klauzula `WHERE`. Domyślnie pusta. Nazwy kolumn
|
|
do których się odwołujemy w tej opcji powinny zostać ujednoznacznione.
|
|
|
|
- `params`: parametry, które zostaną przypięte do wygenerowanego wyrażenia SQL.
|
|
Powinny zostać przekazane jako tablica par nazwa-wartość. Opcja ta jest dostępna
|
|
od wersji 1.0.3.
|
|
|
|
- `on`: klauzula `ON`. Warunki określone tutaj będą dołączone do warunków złączenia
|
|
przy użyciu operatora `AND`. Nazwy kolumn do których się odwołujemy w tej opcji
|
|
powinny zostać ujednoznacznione. Opcja ta jest dostępna od wersji 1.0.2.
|
|
|
|
- `order`: klauzula `ORDER BY`. Nazwy kolumn do których się odwołujemy
|
|
w tej opcji powinny zostać ujednoznacznione
|
|
|
|
- `with`: lista obiektów potomnych, które powinny zostać załadowane wraz z tym
|
|
obiektem. Należy pamiętać, że niewłaściwe użycie tej opcji może skutkować utworzeniem
|
|
nigdy niekończącej się pętli.
|
|
|
|
- `joinType`: typ złączenia dla relacji. Domyślna wartość to `LEFT OUTER JOIN`.
|
|
|
|
- `alias`: alias dla tablicy powiązanej z relacją. Opcja ta jest dostępna
|
|
od wersji 1.0.1. Domyślnie posiada wartość null, co oznacza iż alias tablicy
|
|
jest taki sam jak nazwa relacji.
|
|
|
|
- `together`: używana jeśli tabele powiązane mają zostać zmuszone do połączenia się razem
|
|
z główną tabelą. Opcja ta ma znaczenie dla relacji `HAS_MANY` oraz `MANY_MANY`.
|
|
Jeśli opcja ta ma wartość ustawioną jako false, wtedy tabela związana z relacją `HAS_MANY`
|
|
lub `MANY_MANY` zostanie połączona z tabelą główną w osobnym zapytaniu SQL, co może zwiększyć
|
|
ogólną wydajność zapytania, gdyż zwracana jest mniejsza ilość powtarzających się danych.
|
|
Jeśli ta opcja posiada wartość true, powiązana tabela będzie zawsze dołączona
|
|
do pierwszej tabeli za pomocą pojedyńczego zapytania SQL, nawet jeśli pierwsza tabela
|
|
jest stronicowana. Jeśli ta opcja nie jest ustawiona, powiązana tabela będzie dołączona
|
|
do pierwszej tabeli za pomocą pojedyńczego zapytania SQL tylko wtedy kiedy pierwsza tabela
|
|
nie jest stronicowana. Aby uzyskać więcej szczegółow, zobacz do działu "Wydajność zapytań relacyjnych".
|
|
|
|
- `join`: dodatkowa klauzula `JOIN`. Domyślnie pusta. Opcja ta jest dostępna od wersji 1.1.3.
|
|
|
|
- `group`: klauzula `GROUP BY`. Domyślnie pusta. Nazwy kolumn
|
|
do których się odwołujemy w tej opcji powinny zostać ujednoznacznione.
|
|
|
|
- `having`: klauzula `HAVING`. Domyślnie pusta. Nazwy kolumn
|
|
do których się odwołujemy w tej opcji powinny zostać ujednoznacznione.
|
|
Uwaga, opcja ta jest dostępna od wersji 1.0.1.
|
|
|
|
- `index`: nazwa kolumny, której wartość powinna zostać użyta jako klucz tablicy, która zawiera powiązane obiekty.
|
|
Bez ustawienia tej opcji, tablica obiektów powiązanych będzie używała indeksu całkowitoliczbowego rozpoczynającego się od liczby zero.
|
|
Opcja ta może zotać ustawiona tylko dla relacji `HAS_MANY` oraz `MANY_MANY`. Opcja ta jest dostępna od wesji 1.0.7.
|
|
|
|
Dodatkowo, następujące opcje są dostępne dla wybranych relacji podczas opóźnionego
|
|
ładowania:
|
|
|
|
- `limit`: ogranicza ilość zwracanych wierszy. Opcja ta nie ma zastosowania
|
|
dla relacji `BELONGS_TO`.
|
|
|
|
- `offset`: offset dla zwracanych wierszy. Opcja ta nie ma zastosowania
|
|
dla relacji `BELONGS_TO`.
|
|
|
|
Poniżej zmodyfikujemy deklarację relacji `posts` w klasie `User` poprzez
|
|
wybranie części z powyższych opcji.
|
|
|
|
~~~
|
|
[php]
|
|
class User extends CActiveRecord
|
|
{
|
|
public function relations()
|
|
{
|
|
return array(
|
|
'posts'=>array(self::HAS_MANY, 'Post', 'author_id',
|
|
'order'=>'posts.create_time DESC',
|
|
'with'=>'categories'),
|
|
'profile'=>array(self::HAS_ONE, 'Profile', 'owner_id'),
|
|
);
|
|
}
|
|
}
|
|
~~~
|
|
|
|
Teraz jeśli odczytamy `$author->posts`, powinniśmy otrzymać autorów postów
|
|
posortowanych malejąco wg czasu ich utworzenia. Każda instancja postu będzie
|
|
również posiadała wczytaną odpowiadającą jej kategorię.
|
|
|
|
Ujednoznacznianie nazw kolumn
|
|
---------------------------
|
|
|
|
Kiedy nazwa kolumny pojawia się w dwóch lub więcej tabelach, które będą łączone
|
|
należy ją ujednoznacznić (rozróżnić). Robi się to poprzedzając nazwę kolumny
|
|
prefiksem będącym aliasem nazwy tabeli.
|
|
|
|
Domyślnie, w relacyjnych zapytaniach AR alias nazwy dla głównej tabeli ustalony jest
|
|
jako `t`, gdy zaś alias nazwy dla tabeli relacyjnej jest taki sam jak odpowiadająca
|
|
Na przykład, w kolejnym wyrażeniu aliasem nazwy dla postu `Post` oraz komentarza `Comment`
|
|
są odpowiednio `t` oraz `comments`:
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->with('comments')->findAll();
|
|
~~~
|
|
|
|
Załóżmy teraz, że zarówno `Post` oraz `Comment` posiadają kolumnę czas utworzenia `create_time`
|
|
postu lub komentarza i chcemy pobrać posty razem z ich komentarzami, najpierw sortując po czasie
|
|
utworzenia postów a następnie po czasie utworzenia komentarzy. Potrzebujemy ujednoznacznić
|
|
kolumnę czasu utworzenia `create_time` w następujący sposób:
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->with('comments')->findAll(array(
|
|
'order'=>'t.create_time, comments.create_time'
|
|
));
|
|
~~~
|
|
|
|
> Note|Uwaga: sposób ujednoznaczniania kolumn zmienił się od wersji 1.1.0.
|
|
> Poprzednie w wersji 1.0.x, Yii domyślnie generowało w sposób automatyczny
|
|
> alias tabeli sla każdej tabeli relacyjnej i musieliśmy używać prefiksu `??.`
|
|
> w celu odniesienia się do automatycznie wygenerowanego aliasu. Również, w wersji
|
|
> 1.0.x, alias nazwy tabeli głównej był nazwą samej tabeli.
|
|
|
|
Dynamiczne opcje pytań relacyjnych
|
|
--------------------------------
|
|
|
|
Wraz z wersją 1.0.2 możemy używać dynamicznych opcji zapytań relacyjnych zarówno w metodzie
|
|
[with()|CActiveRecord::with] jak i opcji `with`. Dynamiczne opcje nadpiszą istniejące
|
|
opcje tak jak zostało to określone w metodzie [relations()|CActiveRecord::relations].
|
|
Na przykład, dla powyższego modelu `User`, jeśli chcemy używać techniki zachłannego ładowania
|
|
aby zwrócić posty należące do autora sortujące jest rosnąco
|
|
(opcja `order` w specyfikacji relacji jest malejąca), możemy zrobić to następująco:
|
|
|
|
~~~
|
|
[php]
|
|
User::model()->with(array(
|
|
'posts'=>array('order'=>'posts.create_time ASC'),
|
|
'profile',
|
|
))->findAll();
|
|
~~~
|
|
|
|
Poczynając od wersji 1.0.5, opcje dynamicznych zapytań mogą być również używane dla leniwego ładowania
|
|
w celu wykonywania zapytań relacyjnych. Aby to zrobić, powinniśmy wywołać metodę, której nazwa jest identyczna
|
|
z nazwą relacji oraz przekazać opcje dynamicznego zapytania jako parametr metody.
|
|
Na przykład, następujący kod zwróci posty użytkownika, których `status` wynosi 1:
|
|
|
|
~~~
|
|
[php]
|
|
$user=User::model()->findByPk(1);
|
|
$posts=$user->posts(array('condition'=>'status=1'));
|
|
~~~
|
|
|
|
Wydajność relacyjneych zapytań
|
|
----------------------------
|
|
|
|
Jak opisaliśmy powyżej, zachłanne ładowanie jest używant w scenariuszu, gdy potrzebujemy otrzymać
|
|
dostęp do wielu powiązanych obiektów. Generuje one duże, złożone zapytanie SQL
|
|
poprzed połączenie wszystkich potrzebnych tabel. Duże zapytanie SQL
|
|
jest prefereoane w wielu przypadkach, ponieważ upraszcza filtrowanie
|
|
oparte na kolumnie w relacyjnych tabelach. Jednakże może ono być
|
|
nieefektowne w niektórych przypadkach.
|
|
|
|
Rozważmy przypadek, gdzie potrzebujemy znaleźć ostatnie posrt raze z ich komentarzami.
|
|
Zakładając, że każdy post posiada 10 kometarzy, używanie pojedynczego, dużego zapytania SQL,
|
|
zwróci wiele powtarzającycj się danych postów, ze względu na to, że każdy post będzie powtórzony
|
|
dla każdego posiadanego komentarza.
|
|
Z nowym podejściem potrzebujemy wywołać dwa zapytanie SQL. Korzyścią z tego płynącą jest brak
|
|
redundancji w zwróconych rezultach zapytania.
|
|
|
|
Zatem, które podejście jest bardziej wydajne? Nie ma jednoznacznej odpowiedzi.
|
|
Wywołanie pojedynczego, dużego zapytania SQL może być bardziej wydajne ponieważ
|
|
powoduje mniejsze obciążenie DBMS podczas parsowania i wykonywania zapytania SQL.
|
|
Z drugiej strony używajać pojedynczego zapytania SQL, mamy w końcu więcej redundantnych
|
|
dancyh i w ten sposób potrzebujemy więcej czasu by je odczytać i przetworzyć.
|
|
|
|
Z tego powodu Yii dostarcza opcję zapytania `together`, tak by można było wybierać pomiędzy
|
|
oboma podejściami w zależności od potrzeb. Domyślnie przyjmuje pierwsze podejście, czyli
|
|
tworzy jedno zapytanie SQL aby wykonać zachłanne ładowanie. Możemy ustawić opcję `together`
|
|
jako false w deklaracji zapytania, tak że część tabel będzie połączona w oddzielnym zapytaniu SQL.
|
|
Na przykład odpytując o ostanie posty wraz z ich komentarzami, w celu użycia drugiego podejścia,
|
|
możemy zadeklarować relację `comments` w klasie `Post` następująco:
|
|
|
|
~~~
|
|
[php]
|
|
public function relations()
|
|
{
|
|
return array(
|
|
'comments' => array(self::HAS_MANY, 'Comment', 'post_id', 'together'=>false),
|
|
);
|
|
}
|
|
~~~
|
|
|
|
Możemy również dynamicznie ustawić tą opcję kiedy wykonujemy zachłanne ładowanie:
|
|
|
|
~~~
|
|
[php]
|
|
$posts = Post::model()->with(array('comments'=>array('together'=>false)))->findAll();
|
|
~~~
|
|
|
|
> Note|Uwaga: W wersji 1.0.x, domyślnym zachowaniem Yii jest generowanie i wykonywanie
|
|
> `N+1` zapytań SQL, jeśli występuje `N` relacji `HAS_MANY` lub `MANY_MANY`.
|
|
> Każda relacja `HAS_MANY` lub `MANY_MANY` posiada swoje własne zapytanie SQL. Poprzez wywołanie
|
|
> metody `together()` po `with()`, możemy narzucić wygenerowania i wykonanie się tylko
|
|
> jednego zapytania SQL. Na przykład:
|
|
>
|
|
> ~~~
|
|
> [php]
|
|
> $posts=Post::model()->with(
|
|
> 'author.profile',
|
|
> 'author.posts',
|
|
> 'categories')->together()->findAll();
|
|
> ~~~
|
|
|
|
|
|
Zapytania statystyczne (ang. Statistical Query)
|
|
-----------------
|
|
|
|
> Note|Uwaga: Zapytania statystyczne są wspierane od wersji 1.0.4
|
|
|
|
Poza zapytaniami relacyjnymi opisanymi powyżej, Yii wspiera również tak zwane zapytania statystyczne (lub zapytania agregacyjne). Odnoszą się one do uzyskiwania agregowanych
|
|
informacji o powiązanych obiektach, takich jak liczba komentarzy dla każdego postu, średni ocena dla każdego produktu, itp. Zapytania statystyczne mogą być wykonywane dla obiektów
|
|
wskazywanych w relacji `HAS_MANY` (np. post ma wiele komentarzy) lub `MANY_MANY` (np. post należy do wielu kategorii a kategoria ma wiele postów).
|
|
|
|
Wykonywania zapytań statystycznych jest bardzo podobne do wykonywania opisanych wcześniej zapytań relacyjnych. Najpierw musimy zdefiniować zapytanie statystyczne w metodzie
|
|
[relations()|CActiveRecord::relations] klasy [CActiveRecord] tak jak robimy to dla zapytań relacyjnych.
|
|
|
|
|
|
~~~
|
|
[php]
|
|
class Post extends CActiveRecord
|
|
{
|
|
public function relations()
|
|
{
|
|
return array(
|
|
'commentCount'=>array(self::STAT, 'Comment', 'post_id'),
|
|
'categoryCount'=>array(self::STAT, 'Category', 'post_category(post_id, category_id)'),
|
|
);
|
|
}
|
|
}
|
|
~~~
|
|
|
|
Powyżej zadeklarowaliśmy dwa zapytania statystyczne: `commentCount` oblicza ilość komentarzy należących do postu a `categoryCount` oblicza ilość kategorii do których należy post.
|
|
Zauważ, że relacja pomiędzy `Post` a `Comment` jest relacją `HAS_MANY`, podczas gdy relacja pomiędzy `Post` a `Category` jest relacją `MANY_MANY`
|
|
(wraz z łączącą je tabelą `post_category`). Jak możemy zauważyć, deklaracja jest bardzo podobna do tych z relacji opisywanych we wcześniejszych podpunktach.
|
|
Jedyną różnicą jest to, że typem relacji jest tutaj `STAT`.
|
|
|
|
Przy użyciu powyższej deklaracji możemy otrzymać ilość komentarzy dla postu przy użyciu wyrażenia `$post->commentCount`. Jeśli użyjemy tej właściwości po raz pierwszy,
|
|
wyrażenie SQL zostanie wywołane w ukryciu w celu uzyskania pożądanego rezultatu. Jak już wiemy, jest to tak zwane *leniwe ładowanie*.
|
|
Możemy również używać *zachłannego ładowania* jeśli chcemy dowiedzieć uzyskać ilość komentarzy dla wielu postów.
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->with('commentCount', 'categoryCount')->findAll();
|
|
~~~
|
|
|
|
Powyższe wyrażenie wykona trzy zapytania SQL aby zwrócić wszystkie posty razem z policzonymi komentarzami oraz policzonymi kategoriami. Używając podejścia leniwego ładowania
|
|
skończylibyśmy z `2*N+1` zapytaniami SQL, gdzie `N` to ilość postów.
|
|
|
|
Domyślnie zapytanie statystyczne użyje wyrażenia `COUNT` do obliczeń (i w ten sposób policzymy komentarze oraz kategorie w powyższych przykładach). Możemy je dostosować
|
|
do własnych potrzeb poprzez określenie dodatkowych opcji podczas deklarowania go w metodzie [relations()|CActiveRecord::relations]. Podsumowanie dostępnych opcji znajduje się poniżej.
|
|
|
|
|
|
- `select`: wyrażenie statystyczne. Domyślnie `COUNT(*)`, co oznacza liczenie obiektów potomnych.
|
|
|
|
- `defaultValue`: wartość jaka będzie przypisana do tych rekordów, dla których nie zostaną zwrócone rezultaty zapytania statystycznego. Na przykład, jeśli post nie posiada
|
|
żadnych komentarzy, jego `commentCount` otrzyma tą wartość. Wartość domyślna dla tej opcji to zero.
|
|
|
|
- `condition`: klauzula `WHERE`. Domyślnie jest pusta.
|
|
|
|
- `params`: parametry, które mają zostać związane z wygenerowanym zapytaniem SQL. Powinny być one przekazane jako tablica par nazwa-wartość.
|
|
|
|
- `order`: klauzula `ORDER BY`. Domyślnie jest pusta.
|
|
|
|
- `group`: klauzula `GROUP BY`. Domyślnie jest pusta.
|
|
|
|
- `having`: klauzula `HAVING`. Domyślnie jest pusta.
|
|
|
|
|
|
Relacyjne zapytania z nazwanymi podzbiorami
|
|
----------------------------------
|
|
|
|
> Note|Uwaga: Wsparcie dla nazwanych podzbiorów zostało wprowadzone wraz z wersją 1.0.5.
|
|
|
|
Relacyjne zapytania mogą być wykonywane w połączeniu z [nazwanymi zbiorami](/doc/guide/database.ar#named-scopes).
|
|
Rozróżniamy dwa przypadki. W pierwszym, nazwany podzbiór stosowany jest do głównego modelu. W drugim, nazwany podzbiór stosowany jest
|
|
do powiązanego modelu.
|
|
|
|
Następujący kod pokazuje, jak zastosować nazwane podzbiory do głównego modelu.
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->published()->recently()->with('comments')->findAll();
|
|
~~~
|
|
|
|
Przypomina to bardziej nierelacyjne zapytania. Jedyna różnica polega na tym, że mamy wywołanie metody `with()`
|
|
po łańcuchu wywołań nazwanych podzbiorów. Zapytanie zwróci ostatnio opublikowane posty wraz z ich komentarzami.
|
|
|
|
Następujący kod pokazuje, jak zastosować nazwane podzbiory do modelu powiązanego.
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->with('comments:recently:approved')->findAll();
|
|
~~~
|
|
|
|
Powyższe zapytanie zwróci wszystkie posty wraz z zatwierdzonymi komentarzami. Zauważ, że `comments` jest referencją
|
|
do nazwy relacji, a `recently` oraz `approved` referuje do dwóch nazwanych podzbiorów w klasie modelu `Comment`.
|
|
Nazwa relacji oraz nazwane podzbiory powinny być rozdzielone dwukropkiem.
|
|
|
|
Nazwane podzbiory mogą również zostać zdefiniowane w opcji `with` reguły relacyjnej zadeklarowanej
|
|
w [CActiveRecord::relations()]. W następnym przykładzie, jeśli odczytamy `$user->posts`,
|
|
zwróci nam wszystkie zatwierdzone komentarze (*approved*) postu.
|
|
|
|
[php]
|
|
class User extends CActiveRecord
|
|
{
|
|
public function relations()
|
|
{
|
|
return array(
|
|
'posts'=>array(self::HAS_MANY, 'Post', 'author_id',
|
|
'with'=>'comments:approved'),
|
|
);
|
|
}
|
|
}
|
|
~~~
|
|
|
|
> Note|Uwaga: Nazwane podzbiory zastosowane dla modelów w relacji muszą zostać określone
|
|
w [CActiveRecord::scopes]. W rezultacie, nie mogą one zostać sparametryzowane.
|
|
|
|
|
|
<div class="revision">$Id: database.arr.txt 2782 2010-12-28 16:18:06Z qiang.xue $</div> |