Mit Propel über LEFT JOIN nicht vorhandene Einträge via IS NULL finden, so gehts

Manchmal braucht man aus der Datenbank die Information darüber, welche Angaben bzw. Beziehungen nicht existieren. In der Propel ORM Dokumentation wird mit dem Beispiel eines Buchladens mit Autoren und Büchern gearbeitet. Daher nehme ich hier gerne auch einmal das Beispiel auf:

Wir wollen also einmal ermitteln welche Autoren keine Bücher haben.

Über reines SQL würde das so aussehen:

SELECT
    author.first_name,
    author.last_name
FROM
    author
LEFT JOIN
    book
    ON(
        author.id = book.author_id
    )
WHERE
    book.author_id IS NULL

Über Propel ist diese SQL ganz einfach zu realisieren:

// use Criteria (needed since propel 2.x)
use Propel\Runtime\ActiveQuery\Criteria;

// build relation
$authors = AuthorQuery::create()
    ->useBookQuery(null, Criteria::LEFT_JOIN)
        ->filterByAuthorId(null, Criteria::ISNULL)
    ->endUse()
    ->find()
;

// output example
foreach ($authors as $author) {
    printf("%d: %s %s\n", $author->getId(), $author->getFirstName(), $author->getLastName());
}

Dieses Beispiel funktioniert natürlich nur, wenn über das Schema über foreign-keys eine Beziehung (relation) zwischen den beiden Tabellen hergestellt wurde, stellt dann aber die einfachste Art der Abfrage dar und ist zudem noch sehr schön und einfach zu lesen.

Natürlich kann man ein LEFT JOIN aber auch „zu Fuß“ darstellen. Diese Variante funktioniert auch wenn keine direkten Beziehungen der Tabellen über das Schema definiert wurden:

use Propel\Runtime\ActiveQuery\Criteria;
$authors = AuthorQuery::create()
    ->leftJoinBook()
    ->where('Book.AuthorId ' . Criteria::ISNULL)
    ->find()
;

Wichtiger Hinweis: Ich rate (derzeit) von der Nutzung des „Alias“ Parameters in den leftJoinXxx() Methoden ab, da dieser beim Bauen der SQL Abfrage teilweise ein CROSS JOIN in die Abfrage einbaut (in MySQL ein Alias für INNER JOIN) und genau dieser Join-Typ soll es ja eben nicht sein. Im Zweifel immer einmal die Abfrage debuggen. Das ist in Propel über die toString() Methode sehr einfach. Wichtig ist jedoch, dass Ihr die Abfrage über die abschließenden Methoden wie find(), findOne(), etc. nicht schon ausgeführt habt. Hier ein Beispiel:

use Propel\Runtime\ActiveQuery\Criteria;
$authors = AuthorQuery::create()
    ->leftJoinBook('b')
    ->where('b.AuthorId ' . Criteria::ISNULL)
;

// debug query that will be built
print "<pre>";
print_r($authors->toString() . "\n");
print "</pre>";

// execute query (after debugging!)
$authors->find();

Noch ein Tipp zum Schluss: Manchmal sind die Abfragen natürlich ein wenig komplexer. Man kann über Propel so ziemlich jede SQL Abfrage nachbauen, so kann man beispielsweise die Join-Bedingungen natürlich auch ganz einfach erweitern:

use Propel\Runtime\ActiveQuery\Criteria;

// version using "useXxxQuery"
$authors = AuthorQuery::create()
    ->useBookQuery(null, Criteria::LEFT_JOIN)
        ->filterByAuthorId(null, Criteria::ISNULL)
    ->endUse()
    ->addJoinCondition('Book', 'Book.AuthorId > ?', 0) // doesn't make sense, just an example ;)
    ->find()
;

// version using "leftJoinXxx"
$authors = AuthorQuery::create()
    ->leftJoinBook()
    ->addJoinCondition('Book', 'Book.AuthorId > ?', 0) // doesn't make sense, just an example ;)
    ->where('Book.AuthorId ' . Criteria::ISNULL)
    ->find()
;

Als Schema Vorlage habe ich das altuelle Original Beispiel-Schema direkt von der Propel Builttime Seite genommen. Für den Fall, dass sich dieses einmal ändern sollte, füge ich es diesem Beitrag einmal an:

<?xml version="1.0" encoding="UTF-8"?>
<database name="test" defaultIdMethod="native">
    <table name="book" phpName="Book">
        <column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true"/>
        <column name="title" type="varchar" size="255" required="true" />
        <column name="isbn" type="varchar" size="24" required="true" phpName="ISBN"/>
        <column name="author_id" type="integer" required="true"/>
        <foreign-key foreignTable="author">
            <reference local="author_id" foreign="id"/>
        </foreign-key>
    </table>
    <table name="author" phpName="Author">
        <column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true"/>
        <column name="first_name" type="varchar" size="128" required="true"/>
        <column name="last_name" type="varchar" size="128" required="true"/>
    </table>
</database>

Ich hoffe dieser Beitrag ist für Euch hilfreich. Wenn ja oder Ihr es anders handhabt, schreibt mir natürlich gerne!

 

Das könnte dich auch interessieren …

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Bitte beachte die Hinweise zum Datenschutz