Zum Hauptinhalt springen

Was ist der Query Builder

Der Query Builder von Laravel stellt eine flüssige Schnittstelle zum Aufbauen und Ausführen von Datenbankabfragen bereit. Sie starten mit der Methode table() der Fassade DB und kombinieren die Abfrage per Method-Chaining.
use Illuminate\Support\Facades\DB;

$users = DB::table('users')->get();
Intern werden PDO-Parameterbindings verwendet – SQL-Injection wird also automatisch verhindert.
Der Query Builder funktioniert auf allen von Laravel unterstützten Datenbanken (MySQL, MariaDB, PostgreSQL, SQLite, SQL Server). Der Code bleibt beim Wechsel der Datenbank unverändert.

Wann Query Builder, wann Eloquent

SituationEmpfehlung
Modelle und Beziehungen erforderlichEloquent
Komplexe Aggregationen und ReportsQuery Builder
Performance-kritische MassendatenQuery Builder
Einfache Operationen auf bestehenden TabellenQuery Builder
Innerhalb von Migrationen oder SeedernQuery Builder

Daten abfragen

Alle Datensätze holen

$users = DB::table('users')->get();

foreach ($users as $user) {
    echo $user->name;
}
get() liefert eine Illuminate\Support\Collection zurück. Jeder Datensatz ist ein PHP-stdClass-Objekt.

Einen Datensatz holen

// Ersten Datensatz holen (null, wenn nicht gefunden)
$user = DB::table('users')->where('name', 'Max Mustermann')->first();

// Exception werfen, wenn nicht gefunden (liefert automatisch eine 404)
$user = DB::table('users')->where('name', 'Max Mustermann')->firstOrFail();

// Nur den Wert einer bestimmten Spalte
$email = DB::table('users')->where('name', 'Max Mustermann')->value('email');

// Per ID
$user = DB::table('users')->find(3);

Werte einer Spalte als Liste

// Collection der E-Mail-Werte
$emails = DB::table('users')->pluck('email');

// Assoziative Collection: Name als Schlüssel, E-Mail als Wert
$emailByName = DB::table('users')->pluck('email', 'name');

Große Datenmengen in Blöcken verarbeiten

use Illuminate\Support\Collection;

// In Blöcken von 100 verarbeiten
DB::table('users')->orderBy('id')->chunk(100, function (Collection $users) {
    foreach ($users as $user) {
        // Verarbeitung...
    }
});

// Beim Aktualisieren während des Chunking chunkById verwenden
DB::table('users')->where('active', false)
    ->chunkById(100, function (Collection $users) {
        foreach ($users as $user) {
            DB::table('users')
                ->where('id', $user->id)
                ->update(['active' => true]);
        }
    });
Wenn Sie während des Chunking Datensätze aktualisieren oder löschen, verwenden Sie statt chunk() immer chunkById(). Mit chunk() können Datensätze verschoben werden und übersprungen werden.

Streaming (LazyCollection)

DB::table('users')->orderBy('id')->lazy()->each(function (object $user) {
    // Datensatz für Datensatz verarbeiten
});

Aggregate

$count   = DB::table('users')->count();
$maxAge  = DB::table('users')->max('age');
$minAge  = DB::table('users')->min('age');
$avgAge  = DB::table('users')->avg('age');
$total   = DB::table('orders')->sum('amount');

// Mit Bedingung
$avgPremium = DB::table('orders')
    ->where('plan', 'premium')
    ->avg('amount');

Existenz prüfen

if (DB::table('orders')->where('finalized', 1)->exists()) {
    // Datensatz existiert
}

if (DB::table('orders')->where('finalized', 1)->doesntExist()) {
    // Datensatz existiert nicht
}

SELECT

// Spalten auswählen
$users = DB::table('users')
    ->select('name', 'email as user_email')
    ->get();

// Duplikate entfernen
$users = DB::table('users')->distinct()->get();

// Spalten nachträglich hinzufügen
$query = DB::table('users')->select('name');
$users = $query->addSelect('age')->get();

WHERE

Grundlegende Bedingungen

// Gleichheit (das `=` kann entfallen)
$users = DB::table('users')->where('votes', 100)->get();

// Vergleichsoperator angeben
$users = DB::table('users')->where('votes', '>=', 100)->get();
$users = DB::table('users')->where('name', 'like', 'Max%')->get();

// Mehrere Bedingungen (AND)
$users = DB::table('users')
    ->where('status', 'active')
    ->where('age', '>', 20)
    ->get();

// OR
$users = DB::table('users')
    ->where('votes', '>', 100)
    ->orWhere('name', 'Max Mustermann')
    ->get();

Bedingungen gruppieren

use Illuminate\Database\Query\Builder;

// OR-Bedingungen zusammenklammern und mit AND kombinieren
$users = DB::table('users')
    ->where('active', true)
    ->where(function (Builder $query) {
        $query->where('role', 'admin')
              ->orWhere('role', 'moderator');
    })
    ->get();
// WHERE active = 1 AND (role = 'admin' OR role = 'moderator')

whereIn / whereBetween / whereNull

// IN
$users = DB::table('users')
    ->whereIn('id', [1, 2, 3])
    ->get();

$users = DB::table('users')
    ->whereNotIn('id', [1, 2, 3])
    ->get();

// BETWEEN
$users = DB::table('users')
    ->whereBetween('age', [20, 40])
    ->get();

// NULL-Prüfung
$users = DB::table('users')->whereNull('deleted_at')->get();
$users = DB::table('users')->whereNotNull('email_verified_at')->get();

whereLike (Musterabgleich)

// Standard: nicht case-sensitiv
$users = DB::table('users')
    ->whereLike('name', '%Mustermann%')
    ->get();

// Case-sensitiv
$users = DB::table('users')
    ->whereLike('name', '%Mustermann%', caseSensitive: true)
    ->get();

whereAny / whereAll (gleiche Bedingung auf mehreren Spalten)

// Eine der Spalten erfüllt die LIKE-Bedingung
$users = DB::table('users')
    ->where('active', true)
    ->whereAny(['name', 'email', 'bio'], 'like', '%Laravel%')
    ->get();

// Alle Spalten erfüllen die LIKE-Bedingung
$posts = DB::table('posts')
    ->whereAll(['title', 'content'], 'like', '%Laravel%')
    ->get();

whereNullSafeEquals (NULL-sicherer Gleichheitsvergleich)

whereNullSafeEquals und orWhereNullSafeEquals behandeln zwei NULL-Werte als gleich. Beim gewöhnlichen =-Operator ist NULL = NULL false, whereNullSafeEquals betrachtet NULL und NULL als gleich. Es entspricht dem Operator <=> in MySQL und IS NOT DISTINCT FROM in PostgreSQL.
$lastLoginIp = $request->input('last_login_ip');

// Auch wenn $lastLoginIp null ist, werden Benutzer mit last_login_ip IS NULL gefunden
$users = DB::table('users')
    ->whereNullSafeEquals('last_login_ip', $lastLoginIp)
    ->get();
Ein normales where('column', null) wird zu WHERE column IS NULL; whereNullSafeEquals('column', $value) verhält sich hingegen konsistent, egal ob der gebundene Wert null ist oder nicht. Besonders praktisch, wenn Nutzereingaben null sein können.

JOIN

// INNER JOIN
$users = DB::table('users')
    ->join('orders', 'users.id', '=', 'orders.user_id')
    ->select('users.name', 'orders.amount')
    ->get();

// LEFT JOIN
$users = DB::table('users')
    ->leftJoin('orders', 'users.id', '=', 'orders.user_id')
    ->get();

// Mehrere Tabellen joinen
$users = DB::table('users')
    ->join('contacts', 'users.id', '=', 'contacts.user_id')
    ->join('orders', 'users.id', '=', 'orders.user_id')
    ->select('users.*', 'contacts.phone', 'orders.amount')
    ->get();

Subquery-JOIN

// JOIN mit einer Subquery
$latestOrders = DB::table('orders')
    ->select('user_id', DB::raw('MAX(created_at) as last_order_at'))
    ->groupBy('user_id');

$users = DB::table('users')
    ->joinSub($latestOrders, 'latest_orders', function ($join) {
        $join->on('users.id', '=', 'latest_orders.user_id');
    })
    ->get();

Sortieren, gruppieren, limitieren

// Sortieren
$users = DB::table('users')
    ->orderBy('name', 'asc')
    ->get();

// Nach mehreren Spalten sortieren
$users = DB::table('users')
    ->orderBy('last_name')
    ->orderBy('first_name', 'desc')
    ->get();

// Zufällige Reihenfolge
$users = DB::table('users')->inRandomOrder()->get();

// Gruppieren
$orders = DB::table('orders')
    ->select('status', DB::raw('COUNT(*) as count'))
    ->groupBy('status')
    ->get();

// HAVING
$orders = DB::table('orders')
    ->select('user_id', DB::raw('SUM(amount) as total'))
    ->groupBy('user_id')
    ->having('total', '>', 10000)
    ->get();

// LIMIT und OFFSET
$users = DB::table('users')
    ->skip(10)   // OFFSET
    ->take(5)    // LIMIT
    ->get();

Subqueries

// Subquery in einer WHERE-Klausel
$activeUsers = DB::table('users')->select('id')->where('is_active', 1);

$comments = DB::table('comments')
    ->whereIn('user_id', $activeUsers)
    ->get();

// Subquery in einer SELECT-Klausel
$users = DB::table('users')
    ->select('name')
    ->selectSub(function ($query) {
        $query->from('orders')
              ->selectRaw('COUNT(*)')
              ->whereColumn('orders.user_id', 'users.id');
    }, 'order_count')
    ->get();

Raw-Ausdrücke

Raw-Ausdrücke werden als SQL-String direkt eingebettet. Nutzerinput hier ungeprüft weiterzugeben, birgt SQL-Injection-Risiko. Verwenden Sie unbedingt Bindings.
// DB::raw() — beliebigen SQL-Ausdruck einbetten
$users = DB::table('users')
    ->select(DB::raw('count(*) as user_count, status'))
    ->groupBy('status')
    ->get();

// selectRaw — Raw-Ausdruck in SELECT
$orders = DB::table('orders')
    ->selectRaw('price * ? as price_with_tax', [1.10])
    ->get();

// whereRaw — Raw-Ausdruck in WHERE
$orders = DB::table('orders')
    ->whereRaw('price > IF(state = "JP", ?, 100)', [500])
    ->get();

// havingRaw — Raw-Ausdruck in HAVING
$orders = DB::table('orders')
    ->select('department', DB::raw('SUM(amount) as total'))
    ->groupBy('department')
    ->havingRaw('SUM(amount) > ?', [100000])
    ->get();

// orderByRaw — Raw-Ausdruck in ORDER BY
$orders = DB::table('orders')
    ->orderByRaw('updated_at - created_at DESC')
    ->get();

INSERT / UPDATE / DELETE

INSERT

// Ein Datensatz
DB::table('users')->insert([
    'email' => '[email protected]',
    'name'  => 'Max Mustermann',
]);

// Mehrere Datensätze
DB::table('users')->insert([
    ['email' => '[email protected]', 'name' => 'Max Mustermann'],
    ['email' => '[email protected]', 'name' => 'Erika Musterfrau'],
]);

// Nach dem Einfügen die AUTO_INCREMENT-ID abrufen
$id = DB::table('users')->insertGetId([
    'email' => '[email protected]',
    'name'  => 'Jan Beispiel',
]);

UPSERT (INSERT OR UPDATE)

// Aktualisieren, wenn vorhanden; sonst einfügen
DB::table('users')->upsert(
    [
        ['email' => '[email protected]', 'name' => 'Max Mustermann', 'votes' => 5],
        ['email' => '[email protected]', 'name' => 'Erika Musterfrau', 'votes' => 10],
    ],
    uniqueBy: ['email'],    // Spalten für die Duplikatsprüfung
    update: ['name', 'votes'] // Zu aktualisierende Spalten
);

UPDATE

// Aktualisieren mit Bedingung
$affected = DB::table('users')
    ->where('id', 1)
    ->update(['name' => 'Max M.', 'updated_at' => now()]);

// Inkrementieren/Dekrementieren
DB::table('users')->where('id', 1)->increment('votes');       // +1
DB::table('users')->where('id', 1)->increment('votes', 5);    // +5
DB::table('users')->where('id', 1)->decrement('votes');       // -1
DB::table('users')->where('id', 1)->decrement('balance', 100); // -100

DELETE

// Löschen mit Bedingung
$deleted = DB::table('users')->where('status', 'inactive')->delete();

// Gesamte Tabelle leeren (AUTO_INCREMENT wird zurückgesetzt)
DB::table('users')->truncate();

Bedingte Abfragen (when)

Für dynamische Bedingungen ist when() übersichtlicher.
$status = request('status');
$sortBy = request('sort', 'name');

$users = DB::table('users')
    ->when($status, function ($query, $status) {
        $query->where('status', $status);
    })
    ->when($sortBy === 'email', function ($query) {
        $query->orderBy('email');
    }, function ($query) {
        $query->orderBy('name');
    })
    ->get();

Debugging

// Erzeugten SQL-String anzeigen
$sql = DB::table('users')->where('active', true)->toSql();
// "select * from `users` where `active` = ?"

// SQL und Bindings prüfen
$bindings = DB::table('users')->where('active', true)->getBindings();

// Abfrage ausführen und dumpen (Ausführung fortsetzen)
DB::table('users')->where('active', true)->dump();

// Abfrage ausführen, dumpen und beenden
DB::table('users')->where('active', true)->dd();
dd() ist zum Debuggen praktisch, sollte in Produktion aber niemals eingesetzt werden. Sicher ist der Blick auf SQL und Bindings über toSql() und getBindings().

Zusammenfassung

MethodeBeschreibung
get()Alle Datensätze abrufen (Collection)
first()Ersten Datensatz abrufen
find($id)Datensatz per ID
value($column)Einzelnen Spaltenwert abrufen
pluck($column)Liste der Spaltenwerte
count()Anzahl
sum($col)Summe
avg($col)Mittelwert
max($col)Maximum
min($col)Minimum
exists()Existenz prüfen
insert([...])Einfügen
update([...])Aktualisieren
delete()Löschen
chunk($n, fn)Blockweise verarbeiten
when($cond, fn)Bedingte Abfrage
toSql()Erzeugten SQL anzeigen
Der Query Builder ist eine niedrigere Ebene als Eloquent und gibt statt Model-Instanzen stdClass-Objekte zurück. Wenn Sie Beziehungen oder Modell-Events (Observer) nicht benötigen, ist der Query Builder einfacher und schneller.
// Eloquent: Gibt User-Instanzen zurück
$users = User::where('active', true)->get();
echo $users[0]->name; // User-Objekt

// Query Builder: Gibt stdClass zurück
$users = DB::table('users')->where('active', true)->get();
echo $users[0]->name; // stdClass-Objekt
Zuletzt geändert am 13. Juli 2026