본문 바로가기

데이터베이스: 쿼리 빌더

업데이트됨

번역일: 2026년 11월 8일

이 페이지는 원문이 업데이트되어 번역이 갱신되었습니다.

원문 수정
2026년 11월 5일
번역 갱신
2026년 11월 8일

데이터베이스: 쿼리 빌더

소개

Laravel의 데이터베이스 쿼리 빌더는 데이터베이스 쿼리를 만들고 실행하기 위한 편리한 플루언트(fluent) 인터페이스를 제공합니다. 애플리케이션에서 필요한 대부분의 데이터베이스 작업을 수행할 수 있으며, Laravel이 지원하는 모든 데이터베이스 시스템에서 동일하게 동작합니다.

Laravel 쿼리 빌더는 PDO 파라미터 바인딩을 사용해 SQL 인젝션 공격으로부터 애플리케이션을 보호합니다. 쿼리 바인딩으로 전달하는 문자열은 별도로 정제하거나 이스케이프할 필요가 없습니다.

WARNING

PDO는 컬럼 이름 바인딩을 지원하지 않습니다. 따라서 "order by" 컬럼을 포함해, 쿼리에서 참조하는 컬럼 이름을 사용자 입력값으로 결정하도록 허용해서는 안 됩니다.

데이터베이스 쿼리 실행

테이블의 모든 행 조회하기

쿼리를 시작할 때는 DB 파사드가 제공하는 table 메서드를 사용합니다. table 메서드는 지정한 테이블에 대한 플루언트 쿼리 빌더 인스턴스를 반환하므로, 여기에 제약 조건을 계속 이어 붙인 다음 마지막에 get 메서드로 결과를 조회할 수 있습니다.

<?php namespace App\Http\Controllers; use Illuminate\Support\Facades\DB; use Illuminate\View\View; class UserController extends Controller { /** * 애플리케이션의 모든 사용자 목록을 보여줍니다. */ public function index(): View { $users = DB::table('users')->get(); return view('user.index', ['users' => $users]); } }

get 메서드는 쿼리 결과를 담은 Illuminate\Support\Collection 인스턴스를 반환하며, 각 결과는 PHP의 stdClass 객체입니다. 각 컬럼의 값은 객체의 프로퍼티처럼 접근할 수 있습니다.

use Illuminate\Support\Facades\DB; $users = DB::table('users')->get(); foreach ($users as $user) { echo $user->name; }

NOTE

Laravel 컬렉션은 데이터를 매핑하고 리듀스하는 매우 강력한 메서드를 다양하게 제공합니다. 컬렉션에 대한 자세한 내용은 컬렉션 문서를 참고하세요.

테이블에서 단일 행 / 컬럼 조회하기

테이블에서 행 하나만 필요하다면 DB 파사드의 first 메서드를 사용할 수 있습니다. 이 메서드는 하나의 stdClass 객체를 반환합니다.

$user = DB::table('users')->where('name', 'John')->first(); return $user->email;

행 하나를 조회하되, 일치하는 행이 없을 때 Illuminate\Database\RecordNotFoundException 예외를 던지고 싶다면 firstOrFail 메서드를 사용합니다. 이 예외를 잡지 않으면 클라이언트에는 404 HTTP 응답이 자동으로 반환됩니다.

$user = DB::table('users')->where('name', 'John')->firstOrFail();

행 전체가 필요 없고 레코드의 값 하나만 필요하다면 value 메서드로 해당 컬럼의 값을 바로 가져올 수 있습니다.

$email = DB::table('users')->where('name', 'John')->value('email');

id 컬럼 값으로 단일 행을 조회하려면 find 메서드를 사용합니다.

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

컬럼 값 목록 조회하기

단일 컬럼의 값들을 담은 Illuminate\Support\Collection 인스턴스가 필요하다면 pluck 메서드를 사용합니다. 다음 예제에서는 사용자 직함(title)의 컬렉션을 조회합니다.

use Illuminate\Support\Facades\DB; $titles = DB::table('users')->pluck('title'); foreach ($titles as $title) { echo $title; }

pluck 메서드에 두 번째 인자를 전달하면, 결과 컬렉션의 키로 사용할 컬럼을 지정할 수 있습니다.

$titles = DB::table('users')->pluck('title', 'name'); foreach ($titles as $name => $title) { echo $title; }

결과 청크 처리

수천 건 이상의 데이터베이스 레코드를 다뤄야 한다면 DB 파사드의 chunk 메서드를 고려해 보세요. 이 메서드는 결과를 작은 덩어리(청크) 단위로 가져와 각 청크를 클로저에 넘겨 처리합니다. 예를 들어 users 테이블 전체를 100건씩 나누어 조회해 보겠습니다.

use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; DB::table('users')->orderBy('id')->chunk(100, function (Collection $users) { foreach ($users as $user) { // ... } });

클로저에서 false를 반환하면 이후 청크의 처리를 중단할 수 있습니다.

DB::table('users')->orderBy('id')->chunk(100, function (Collection $users) { // 레코드를 처리합니다... return false; });

청크 처리 도중 데이터베이스 레코드를 업데이트하면 청크 결과가 예기치 않게 달라질 수 있습니다. 조회한 레코드를 함께 업데이트할 계획이라면 항상 chunkById 메서드를 사용하는 것이 좋습니다. 이 메서드는 레코드의 기본 키를 기준으로 결과를 자동으로 페이지네이션합니다.

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]); } });

chunkById와 lazyById 메서드는 실행되는 쿼리에 자체적인 "where" 조건을 추가하므로, 직접 작성한 조건은 일반적으로 클로저 안에서 논리적으로 그룹화하는 것이 좋습니다.

DB::table('users')->where(function ($query) { $query->where('credits', 1)->orWhere('credits', 2); })->chunkById(100, function (Collection $users) { foreach ($users as $user) { DB::table('users') ->where('id', $user->id) ->update(['credits' => 3]); } });

WARNING

청크 콜백 안에서 레코드를 업데이트하거나 삭제할 때 기본 키나 외래 키가 변경되면 청크 쿼리에 영향을 줄 수 있습니다. 그 결과 일부 레코드가 청크 결과에 포함되지 않을 수 있습니다.

결과를 지연 스트리밍하기

lazy 메서드는 쿼리를 청크 단위로 실행한다는 점에서 chunk 메서드와 비슷하게 동작합니다. 다만 각 청크를 콜백에 넘기는 대신, lazy() 메서드는 LazyCollection을 반환하므로 전체 결과를 하나의 스트림처럼 다룰 수 있습니다.

use Illuminate\Support\Facades\DB; DB::table('users')->orderBy('id')->lazy()->each(function (object $user) { // ... });

이 경우에도 조회한 레코드를 순회하면서 업데이트할 계획이라면 lazyById 또는 lazyByIdDesc 메서드를 사용하는 것이 가장 좋습니다. 이 메서드들은 레코드의 기본 키를 기준으로 결과를 자동으로 페이지네이션합니다.

DB::table('users')->where('active', false) ->lazyById()->each(function (object $user) { DB::table('users') ->where('id', $user->id) ->update(['active' => true]); });

WARNING

레코드를 순회하면서 업데이트하거나 삭제할 때 기본 키나 외래 키가 변경되면 청크 쿼리에 영향을 줄 수 있습니다. 그 결과 일부 레코드가 결과에 포함되지 않을 수 있습니다.

집계

쿼리 빌더는 count, max, min, avg, sum 같은 집계 값을 조회하는 메서드도 다양하게 제공합니다. 쿼리를 구성한 뒤 이 메서드 중 원하는 것을 호출하면 됩니다.

use Illuminate\Support\Facades\DB; $users = DB::table('users')->count(); $price = DB::table('orders')->max('price');

물론 다른 절과 조합해 집계 값이 계산되는 방식을 세밀하게 조정할 수도 있습니다.

$price = DB::table('orders') ->where('finalized', 1) ->avg('price');

레코드 존재 여부 확인하기

쿼리 조건에 일치하는 레코드가 있는지 확인할 때는 count 메서드 대신 exists와 doesntExist 메서드를 사용할 수 있습니다.

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

Select 구문

Select 절 지정하기

테이블의 모든 컬럼을 조회하고 싶지 않은 경우도 많습니다. select 메서드를 사용하면 쿼리에 사용자 정의 "select" 절을 지정할 수 있습니다.

use Illuminate\Support\Facades\DB; $users = DB::table('users') ->select('name', 'email as user_email') ->get();

distinct 메서드를 사용하면 쿼리가 중복 없는 결과를 반환하도록 강제할 수 있습니다.

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

이미 쿼리 빌더 인스턴스가 있고 기존 select 절에 컬럼을 추가하고 싶다면 addSelect 메서드를 사용합니다.

$query = DB::table('users')->select('name'); $users = $query->addSelect('age')->get();

Raw 표현식

쿼리에 임의의 문자열을 그대로 넣어야 할 때가 있습니다. raw 문자열 표현식을 만들려면 DB 파사드가 제공하는 raw 메서드를 사용합니다.

$users = DB::table('users') ->select(DB::raw('count(*) as user_count, status')) ->where('status', '<>', 1) ->groupBy('status') ->get();

WARNING

Raw 구문은 문자열 그대로 쿼리에 삽입되므로, SQL 인젝션 취약점이 생기지 않도록 각별히 주의해야 합니다.

Raw 메서드

DB::raw 메서드 대신 아래 메서드들을 사용해 쿼리의 여러 부분에 raw 표현식을 삽입할 수도 있습니다. raw 표현식을 사용하는 쿼리는 Laravel이 SQL 인젝션 방어를 보장할 수 없다는 점을 기억하세요.

`selectRaw`

selectRaw 메서드는 addSelect(DB::raw(/* ... */))를 대신해 사용할 수 있습니다. 이 메서드는 두 번째 인자로 바인딩 배열을 선택적으로 받습니다.

$orders = DB::table('orders') ->selectRaw('price * ? as price_with_tax', [1.0825]) ->get();

`whereRaw / orWhereRaw`

whereRaw와 orWhereRaw 메서드는 쿼리에 raw "where" 절을 삽입할 때 사용합니다. 이 메서드들은 두 번째 인자로 바인딩 배열을 선택적으로 받습니다.

$orders = DB::table('orders') ->whereRaw('price > IF(state = "TX", ?, 100)', [200]) ->get();

`havingRaw / orHavingRaw`

havingRaw와 orHavingRaw 메서드는 "having" 절의 값으로 raw 문자열을 제공할 때 사용합니다. 이 메서드들은 두 번째 인자로 바인딩 배열을 선택적으로 받습니다.

$orders = DB::table('orders') ->select('department', DB::raw('SUM(price) as total_sales')) ->groupBy('department') ->havingRaw('SUM(price) > ?', [2500]) ->get();

`orderByRaw`

orderByRaw 메서드는 "order by" 절의 값으로 raw 문자열을 제공할 때 사용합니다.

$orders = DB::table('orders') ->orderByRaw('updated_at - created_at DESC') ->get();

`groupByRaw`

groupByRaw 메서드는 group by 절의 값으로 raw 문자열을 제공할 때 사용합니다.

$orders = DB::table('orders') ->select('city', 'state') ->groupByRaw('city, state') ->get();

조인

Inner Join 절

쿼리 빌더로 쿼리에 조인 절을 추가할 수도 있습니다. 기본적인 "inner join"을 수행하려면 쿼리 빌더 인스턴스에서 join 메서드를 사용합니다. join 메서드의 첫 번째 인자는 조인할 테이블 이름이고, 나머지 인자는 조인에 사용할 컬럼 조건을 지정합니다. 하나의 쿼리에서 여러 테이블을 조인할 수도 있습니다.

use Illuminate\Support\Facades\DB; $users = DB::table('users') ->join('contacts', 'users.id', '=', 'contacts.user_id') ->join('orders', 'users.id', '=', 'orders.user_id') ->select('users.*', 'contacts.phone', 'orders.price') ->get();

Left Join / Right Join 절

"inner join" 대신 "left join"이나 "right join"을 수행하려면 leftJoin 또는 rightJoin 메서드를 사용합니다. 이 메서드들의 시그니처는 join 메서드와 같습니다.

$users = DB::table('users') ->leftJoin('posts', 'users.id', '=', 'posts.user_id') ->get(); $users = DB::table('users') ->rightJoin('posts', 'users.id', '=', 'posts.user_id') ->get();

Cross Join 절

"cross join"을 수행하려면 crossJoin 메서드를 사용합니다. 크로스 조인은 첫 번째 테이블과 조인된 테이블 사이의 데카르트 곱(cartesian product)을 생성합니다.

$sizes = DB::table('sizes') ->crossJoin('colors') ->get();

고급 Join 절

더 복잡한 조인 절을 지정할 수도 있습니다. 먼저 join 메서드의 두 번째 인자로 클로저를 전달합니다. 이 클로저는 Illuminate\Database\Query\JoinClause 인스턴스를 받으며, 이를 통해 "join" 절에 대한 조건을 지정할 수 있습니다.

DB::table('users') ->join('contacts', function (JoinClause $join) { $join->on('users.id', '=', 'contacts.user_id')->orOn(/* ... */); }) ->get();

조인에 "where" 절을 사용하고 싶다면 JoinClause 인스턴스가 제공하는 where와 orWhere 메서드를 사용할 수 있습니다. 이 메서드들은 두 컬럼을 비교하는 대신 컬럼과 값을 비교합니다.

DB::table('users') ->join('contacts', function (JoinClause $join) { $join->on('users.id', '=', 'contacts.user_id') ->where('contacts.user_id', '>', 5); }) ->get();

서브쿼리 조인

joinSub, leftJoinSub, rightJoinSub 메서드를 사용하면 쿼리를 서브쿼리와 조인할 수 있습니다. 각 메서드는 서브쿼리, 테이블 별칭, 그리고 연관 컬럼을 정의하는 클로저, 이렇게 세 개의 인자를 받습니다. 다음 예제에서는 각 사용자 레코드에 해당 사용자가 가장 최근에 발행한 블로그 글의 created_at 타임스탬프가 함께 담긴 사용자 컬렉션을 조회합니다.

$latestPosts = DB::table('posts') ->select('user_id', DB::raw('MAX(created_at) as last_post_created_at')) ->where('is_published', true) ->groupBy('user_id'); $users = DB::table('users') ->joinSub($latestPosts, 'latest_posts', function (JoinClause $join) { $join->on('users.id', '=', 'latest_posts.user_id'); })->get();

래터럴 조인

WARNING

래터럴 조인은 현재 PostgreSQL, MySQL 8.0.14 이상, SQL Server에서 지원됩니다.

joinLateral과 leftJoinLateral 메서드를 사용하면 서브쿼리와 "래터럴 조인"을 수행할 수 있습니다. 각 메서드는 서브쿼리와 테이블 별칭, 두 개의 인자를 받습니다. 조인 조건은 전달한 서브쿼리의 where 절 안에 지정해야 합니다. 래터럴 조인은 행마다 평가되며 서브쿼리 바깥의 컬럼을 참조할 수 있습니다.

다음 예제에서는 사용자와 각 사용자의 최근 블로그 글 세 개를 함께 조회합니다. 각 사용자는 결과 집합에서 최대 세 개의 행을 만들 수 있으며, 최근 글 하나당 한 행씩 생성됩니다. 조인 조건은 서브쿼리 안의 whereColumn 절로 지정하며, 이 절이 현재 사용자 행을 참조합니다.

$latestPosts = DB::table('posts') ->select('id as post_id', 'title as post_title', 'created_at as post_created_at') ->whereColumn('user_id', 'users.id') ->orderBy('created_at', 'desc') ->limit(3); $users = DB::table('users') ->joinLateral($latestPosts, 'latest_posts') ->get();

유니온

쿼리 빌더는 둘 이상의 쿼리를 "유니온"으로 결합하는 편리한 메서드도 제공합니다. 예를 들어 먼저 초기 쿼리를 만든 다음, union 메서드로 다른 쿼리와 결합할 수 있습니다.

use Illuminate\Support\Facades\DB; $usersWithoutFirstName = DB::table('users') ->whereNull('first_name'); $users = DB::table('users') ->whereNull('last_name') ->union($usersWithoutFirstName) ->get();

union 메서드 외에 unionAll 메서드도 제공됩니다. unionAll로 결합한 쿼리는 중복된 결과를 제거하지 않습니다. unionAll 메서드의 시그니처는 union 메서드와 같습니다.

기본 Where 절

Where 절

쿼리 빌더의 where 메서드를 사용해 쿼리에 "where" 절을 추가할 수 있습니다. 가장 기본적인 where 호출에는 세 개의 인자가 필요합니다. 첫 번째 인자는 컬럼 이름이고, 두 번째 인자는 데이터베이스가 지원하는 연산자 중 하나입니다. 세 번째 인자는 컬럼 값과 비교할 값입니다.

예를 들어 다음 쿼리는 votes 컬럼 값이 100이고 age 컬럼 값이 35보다 큰 사용자를 조회합니다.

$users = DB::table('users') ->where('votes', '=', 100) ->where('age', '>', 35) ->get();

편의상, 컬럼이 특정 값과 =인지 확인하려면 값을 where 메서드의 두 번째 인자로 바로 전달해도 됩니다. 이 경우 Laravel은 = 연산자를 사용하려는 것으로 간주합니다.

$users = DB::table('users')->where('votes', 100)->get();

where 메서드에 연관 배열을 전달해 여러 컬럼을 한 번에 조건으로 지정할 수도 있습니다.

$users = DB::table('users')->where([ 'first_name' => 'Jane', 'last_name' => 'Doe', ])->get();

앞서 언급했듯이 데이터베이스 시스템이 지원하는 모든 연산자를 사용할 수 있습니다.

$users = DB::table('users') ->where('votes', '>=', 100) ->get(); $users = DB::table('users') ->where('votes', '<>', 100) ->get(); $users = DB::table('users') ->where('name', 'like', 'T%') ->get();

where 함수에 조건 배열을 전달할 수도 있습니다. 배열의 각 요소는 where 메서드에 보통 전달하는 세 개의 인자를 담은 배열이어야 합니다.

$users = DB::table('users')->where([ ['status', '=', '1'], ['subscribed', '<>', '1'], ])->get();

WARNING

PDO는 컬럼 이름 바인딩을 지원하지 않습니다. 따라서 "order by" 컬럼을 포함해, 쿼리에서 참조하는 컬럼 이름을 사용자 입력값으로 결정하도록 허용해서는 안 됩니다.

WARNING

MySQL과 MariaDB는 문자열과 숫자를 비교할 때 문자열을 정수로 자동 형변환합니다. 이 과정에서 숫자가 아닌 문자열은 0으로 변환되어 예기치 않은 결과가 나올 수 있습니다. 예를 들어 테이블의 secret 컬럼 값이 aaa일 때 User::where('secret', 0)을 실행하면 해당 행이 반환됩니다. 이를 방지하려면 쿼리에 사용하기 전에 모든 값을 적절한 타입으로 형변환해 두세요.

Or Where 절

쿼리 빌더의 where 메서드를 연달아 호출하면 "where" 절은 and 연산자로 결합됩니다. 반면 orWhere 메서드를 사용하면 or 연산자로 절을 결합할 수 있습니다. orWhere 메서드는 where 메서드와 같은 인자를 받습니다.

$users = DB::table('users') ->where('votes', '>', 100) ->orWhere('name', 'John') ->get();

"or" 조건을 괄호로 묶어야 한다면 orWhere 메서드의 첫 번째 인자로 클로저를 전달합니다.

use Illuminate\Database\Query\Builder; $users = DB::table('users') ->where('votes', '>', 100) ->orWhere(function (Builder $query) { $query->where('name', 'Abigail') ->where('votes', '>', 50); }) ->get();

위 예제는 다음 SQL을 생성합니다.

select * from users where votes > 100 or (name = 'Abigail' and votes > 50)

WARNING

글로벌 스코프가 적용될 때 예기치 않은 동작을 피하려면 orWhere 호출은 항상 그룹화해야 합니다.

Where Not 절

whereNot과 orWhereNot 메서드는 주어진 쿼리 조건 그룹을 부정(negate)할 때 사용합니다. 예를 들어 다음 쿼리는 재고 정리(clearance) 상품이거나 가격이 10 미만인 상품을 제외합니다.

$products = DB::table('products') ->whereNot(function (Builder $query) { $query->where('clearance', true) ->orWhere('price', '<', 10); }) ->get();

Where Any / All / None 절

여러 컬럼에 같은 쿼리 조건을 적용해야 할 때가 있습니다. 예를 들어 지정한 컬럼 목록 중 어느 하나라도 특정 값과 LIKE로 일치하는 레코드를 모두 조회하고 싶을 수 있습니다. 이때는 whereAny 메서드를 사용합니다.

$users = DB::table('users') ->where('active', true) ->whereAny([ 'name', 'email', 'phone', ], 'like', 'Example%') ->get();

위 쿼리는 다음 SQL을 생성합니다.

SELECT * FROM users WHERE active = true AND ( name LIKE 'Example%' OR email LIKE 'Example%' OR phone LIKE 'Example%' )

비슷하게 whereAll 메서드는 지정한 모든 컬럼이 주어진 조건과 일치하는 레코드를 조회할 때 사용합니다.

$posts = DB::table('posts') ->where('published', true) ->whereAll([ 'title', 'content', ], 'like', '%Laravel%') ->get();

위 쿼리는 다음 SQL을 생성합니다.

SELECT * FROM posts WHERE published = true AND ( title LIKE '%Laravel%' AND content LIKE '%Laravel%' )

whereNone 메서드는 지정한 컬럼 중 어느 것도 주어진 조건과 일치하지 않는 레코드를 조회할 때 사용합니다.

$albums = DB::table('albums') ->where('published', true) ->whereNone([ 'title', 'lyrics', 'tags', ], 'like', '%explicit%') ->get();

위 쿼리는 다음 SQL을 생성합니다.

SELECT * FROM albums WHERE published = true AND NOT ( title LIKE '%explicit%' OR lyrics LIKE '%explicit%' OR tags LIKE '%explicit%' )

JSON Where 절

Laravel은 JSON 컬럼 타입을 지원하는 데이터베이스에서 JSON 컬럼 쿼리도 지원합니다. 현재 MariaDB 10.3 이상, MySQL 8.0 이상, PostgreSQL 12.0 이상, SQL Server 2017 이상, SQLite 3.39.0 이상이 이에 해당합니다. JSON 컬럼을 쿼리하려면 -> 연산자를 사용합니다.

$users = DB::table('users') ->where('preferences->dining->meal', 'salad') ->get(); $users = DB::table('users') ->whereIn('preferences->dining->meal', ['pasta', 'salad', 'sandwiches']) ->get();

whereJsonContains와 whereJsonDoesntContain 메서드를 사용하면 JSON 배열을 쿼리할 수 있습니다.

$users = DB::table('users') ->whereJsonContains('options->languages', 'en') ->get(); $users = DB::table('users') ->whereJsonDoesntContain('options->languages', 'en') ->get();

애플리케이션이 MariaDB, MySQL, PostgreSQL 데이터베이스를 사용한다면 whereJsonContains와 whereJsonDoesntContain 메서드에 값의 배열을 전달할 수 있습니다.

$users = DB::table('users') ->whereJsonContains('options->languages', ['en', 'de']) ->get(); $users = DB::table('users') ->whereJsonDoesntContain('options->languages', ['en', 'de']) ->get();

또한 whereJsonContainsKey 또는 whereJsonDoesntContainKey 메서드를 사용하면 특정 JSON 키를 포함하거나 포함하지 않는 결과를 조회할 수 있습니다.

$users = DB::table('users') ->whereJsonContainsKey('preferences->dietary_requirements') ->get(); $users = DB::table('users') ->whereJsonDoesntContainKey('preferences->dietary_requirements') ->get();

마지막으로 whereJsonLength 메서드를 사용하면 JSON 배열의 길이로 쿼리할 수 있습니다.

$users = DB::table('users') ->whereJsonLength('options->languages', 0) ->get(); $users = DB::table('users') ->whereJsonLength('options->languages', '>', 1) ->get();

추가 Where 절

whereLike / orWhereLike / whereNotLike / orWhereNotLike

whereLike 메서드는 패턴 매칭을 위한 "LIKE" 절을 쿼리에 추가합니다. 이 메서드들은 데이터베이스 종류에 관계없이 문자열 매칭 쿼리를 수행할 수 있게 해 주며, 대소문자 구분 여부도 전환할 수 있습니다. 기본적으로 문자열 매칭은 대소문자를 구분하지 않습니다.

$users = DB::table('users') ->whereLike('name', '%John%') ->get();

caseSensitive 인자를 사용하면 대소문자를 구분하는 검색을 활성화할 수 있습니다.

$users = DB::table('users') ->whereLike('name', '%John%', caseSensitive: true) ->get();

orWhereLike 메서드는 LIKE 조건을 가진 "or" 절을 추가합니다.

$users = DB::table('users') ->where('votes', '>', 100) ->orWhereLike('name', '%John%') ->get();

whereNotLike 메서드는 쿼리에 "NOT LIKE" 절을 추가합니다.

$users = DB::table('users') ->whereNotLike('name', '%John%') ->get();

마찬가지로 orWhereNotLike를 사용하면 NOT LIKE 조건을 가진 "or" 절을 추가할 수 있습니다.

$users = DB::table('users') ->where('votes', '>', 100) ->orWhereNotLike('name', '%John%') ->get();

WARNING

whereLike의 대소문자 구분 검색 옵션은 현재 SQL Server에서 지원되지 않습니다.

whereIn / whereNotIn / orWhereIn / orWhereNotIn

whereIn 메서드는 지정한 컬럼의 값이 주어진 배열에 포함되어 있는지 확인합니다.

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

whereNotIn 메서드는 지정한 컬럼의 값이 주어진 배열에 포함되어 있지 않은지 확인합니다.

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

whereIn 메서드의 두 번째 인자로 쿼리 객체를 전달할 수도 있습니다.

$activeUsers = DB::table('users')->select('id')->where('is_active', 1); $comments = DB::table('comments') ->whereIn('user_id', $activeUsers) ->get();

위 예제는 다음 SQL을 생성합니다.

select * from comments where user_id in ( select id from users where is_active = 1 )

WARNING

쿼리에 정수 바인딩이 담긴 큰 배열을 추가하는 경우, whereIntegerInRaw 또는 whereIntegerNotInRaw 메서드를 사용하면 메모리 사용량을 크게 줄일 수 있습니다.

whereBetween / orWhereBetween

whereBetween 메서드는 컬럼의 값이 두 값 사이에 있는지 확인합니다.

$users = DB::table('users') ->whereBetween('votes', [1, 100]) ->get();

whereNotBetween / orWhereNotBetween

whereNotBetween 메서드는 컬럼의 값이 두 값의 범위 밖에 있는지 확인합니다.

$users = DB::table('users') ->whereNotBetween('votes', [1, 100]) ->get();

whereBetweenColumns / whereNotBetweenColumns / orWhereBetweenColumns / orWhereNotBetweenColumns

whereBetweenColumns 메서드는 컬럼의 값이 같은 테이블 행에 있는 두 컬럼의 값 사이에 있는지 확인합니다.

$patients = DB::table('patients') ->whereBetweenColumns('weight', ['minimum_allowed_weight', 'maximum_allowed_weight']) ->get();

whereNotBetweenColumns 메서드는 컬럼의 값이 같은 테이블 행에 있는 두 컬럼의 값 범위 밖에 있는지 확인합니다.

$patients = DB::table('patients') ->whereNotBetweenColumns('weight', ['minimum_allowed_weight', 'maximum_allowed_weight']) ->get();

whereValueBetween / whereValueNotBetween / orWhereValueBetween / orWhereValueNotBetween

whereValueBetween 메서드는 주어진 값이 같은 테이블 행에 있는, 같은 타입의 두 컬럼 값 사이에 있는지 확인합니다.

$products = DB::table('products') ->whereValueBetween(100, ['min_price', 'max_price']) ->get();

whereValueNotBetween 메서드는 주어진 값이 같은 테이블 행에 있는 두 컬럼 값의 범위 밖에 있는지 확인합니다.

$products = DB::table('products') ->whereValueNotBetween(100, ['min_price', 'max_price']) ->get();

whereNull / whereNotNull / orWhereNull / orWhereNotNull

whereNull 메서드는 지정한 컬럼의 값이 NULL인지 확인합니다.

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

whereNotNull 메서드는 컬럼의 값이 NULL이 아닌지 확인합니다.

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

whereNullSafeEquals / orWhereNullSafeEquals

whereNullSafeEquals와 orWhereNullSafeEquals 메서드는 컬럼의 값을 주어진 값과 비교하되, 두 NULL 값을 같은 것으로 취급할 때 사용합니다.

$lastLoginIp = $request->input('last_login_ip'); $users = DB::table('users') ->whereNullSafeEquals('last_login_ip', $lastLoginIp) ->get();

whereDate / whereMonth / whereDay / whereYear / whereTime

whereDate 메서드는 컬럼의 값을 날짜와 비교할 때 사용합니다.

$users = DB::table('users') ->whereDate('created_at', '2016-12-31') ->get();

whereMonth 메서드는 컬럼의 값을 특정 월과 비교할 때 사용합니다.

$users = DB::table('users') ->whereMonth('created_at', '12') ->get();

whereDay 메서드는 컬럼의 값을 월 중 특정 일과 비교할 때 사용합니다.

$users = DB::table('users') ->whereDay('created_at', '31') ->get();

whereYear 메서드는 컬럼의 값을 특정 연도와 비교할 때 사용합니다.

$users = DB::table('users') ->whereYear('created_at', '2016') ->get();

whereTime 메서드는 컬럼의 값을 특정 시각과 비교할 때 사용합니다.

$users = DB::table('users') ->whereTime('created_at', '=', '11:20:45') ->get();

wherePast / whereFuture / whereToday / whereBeforeToday / whereAfterToday

wherePast와 whereFuture 메서드는 컬럼의 값이 과거인지 미래인지 판단할 때 사용합니다.

$invoices = DB::table('invoices') ->wherePast('due_at') ->get(); $invoices = DB::table('invoices') ->whereFuture('due_at') ->get();

whereNowOrPast와 whereNowOrFuture 메서드는 현재 날짜와 시각을 포함해 컬럼의 값이 과거인지 미래인지 판단할 때 사용합니다.

$invoices = DB::table('invoices') ->whereNowOrPast('due_at') ->get(); $invoices = DB::table('invoices') ->whereNowOrFuture('due_at') ->get();

whereToday, whereBeforeToday, whereAfterToday 메서드는 컬럼의 값이 각각 오늘인지, 오늘 이전인지, 오늘 이후인지 판단할 때 사용합니다.

$invoices = DB::table('invoices') ->whereToday('due_at') ->get(); $invoices = DB::table('invoices') ->whereBeforeToday('due_at') ->get(); $invoices = DB::table('invoices') ->whereAfterToday('due_at') ->get();

비슷하게 whereTodayOrBefore와 whereTodayOrAfter 메서드는 오늘 날짜를 포함해 컬럼의 값이 오늘 이전인지 또는 오늘 이후인지 판단할 때 사용합니다.

$invoices = DB::table('invoices') ->whereTodayOrBefore('due_at') ->get(); $invoices = DB::table('invoices') ->whereTodayOrAfter('due_at') ->get();

whereColumn / orWhereColumn

whereColumn 메서드는 두 컬럼의 값이 같은지 확인할 때 사용합니다.

$users = DB::table('users') ->whereColumn('first_name', 'last_name') ->get();

whereColumn 메서드에 비교 연산자를 함께 전달할 수도 있습니다.

$users = DB::table('users') ->whereColumn('updated_at', '>', 'created_at') ->get();

whereColumn 메서드에 컬럼 비교 조건의 배열을 전달할 수도 있습니다. 이 조건들은 and 연산자로 결합됩니다.

$users = DB::table('users') ->whereColumn([ ['first_name', '=', 'last_name'], ['updated_at', '>', 'created_at'], ])->get();

논리적 그룹화

쿼리가 의도한 논리 구조를 갖도록 여러 "where" 절을 괄호로 묶어야 할 때가 있습니다. 사실 예기치 않은 쿼리 동작을 피하려면 orWhere 메서드 호출은 일반적으로 항상 괄호로 그룹화하는 것이 좋습니다. 이를 위해 where 메서드에 클로저를 전달할 수 있습니다.

$users = DB::table('users') ->where('name', '=', 'John') ->where(function (Builder $query) { $query->where('votes', '>', 100) ->orWhere('title', '=', 'Admin'); }) ->get();

보다시피 where 메서드에 클로저를 전달하면 쿼리 빌더가 조건 그룹을 시작합니다. 클로저는 쿼리 빌더 인스턴스를 받으며, 이를 사용해 괄호 그룹 안에 들어갈 조건을 설정할 수 있습니다. 위 예제는 다음 SQL을 생성합니다.

select * from users where name = 'John' and (votes > 100 or title = 'Admin')

WARNING

글로벌 스코프가 적용될 때 예기치 않은 동작을 피하려면 orWhere 호출은 항상 그룹화해야 합니다.

고급 Where 절

Where Exists 절

whereExists 메서드를 사용하면 "where exists" SQL 절을 작성할 수 있습니다. whereExists 메서드는 쿼리 빌더 인스턴스를 받는 클로저를 인자로 받으며, 이 클로저 안에서 "exists" 절 내부에 들어갈 쿼리를 정의합니다.

$users = DB::table('users') ->whereExists(function (Builder $query) { $query->select(DB::raw(1)) ->from('orders') ->whereColumn('orders.user_id', 'users.id'); }) ->get();

클로저 대신 쿼리 객체를 whereExists 메서드에 전달할 수도 있습니다.

$orders = DB::table('orders') ->select(DB::raw(1)) ->whereColumn('orders.user_id', 'users.id'); $users = DB::table('users') ->whereExists($orders) ->get();

위 두 예제는 모두 다음 SQL을 생성합니다.

select * from users where exists ( select 1 from orders where orders.user_id = users.id )

서브쿼리 Where 절

서브쿼리의 결과를 주어진 값과 비교하는 "where" 절을 만들어야 할 때가 있습니다. 이때는 where 메서드에 클로저와 값을 전달하면 됩니다. 예를 들어 다음 쿼리는 특정 타입의 최근 "membership"을 가진 모든 사용자를 조회합니다.

use App\Models\User; use Illuminate\Database\Query\Builder; $users = User::where(function (Builder $query) { $query->select('type') ->from('membership') ->whereColumn('membership.user_id', 'users.id') ->orderByDesc('membership.start_date') ->limit(1); }, 'Pro')->get();

반대로 컬럼을 서브쿼리의 결과와 비교하는 "where" 절이 필요할 수도 있습니다. 이때는 where 메서드에 컬럼, 연산자, 클로저를 전달합니다. 예를 들어 다음 쿼리는 금액이 평균보다 작은 모든 수입(income) 레코드를 조회합니다.

use App\Models\Income; use Illuminate\Database\Query\Builder; $incomes = Income::where('amount', '<', function (Builder $query) { $query->selectRaw('avg(i.amount)')->from('incomes as i'); })->get();

전문 검색 Where 절

WARNING

전문 검색(full text) where 절은 현재 MariaDB, MySQL, PostgreSQL에서 지원됩니다.

whereFullText와 orWhereFullText 메서드를 사용하면 전문 검색 인덱스가 있는 컬럼에 대해 전문 검색 "where" 절을 쿼리에 추가할 수 있습니다. 이 메서드들은 Laravel이 사용 중인 데이터베이스 시스템에 맞는 SQL로 변환합니다. 예를 들어 MariaDB나 MySQL을 사용하는 애플리케이션에서는 MATCH AGAINST 절이 생성됩니다.

$users = DB::table('users') ->whereFullText('bio', 'web developer') ->get();

벡터 유사도 절

NOTE

벡터 유사도 절은 현재 pgvector 확장을 사용하는 PostgreSQL 연결과 MariaDB 11.7 이상에서 지원됩니다. 벡터 컬럼과 인덱스를 정의하는 방법은 마이그레이션 문서를 참고하세요.

whereVectorSimilarTo 메서드는 주어진 벡터와의 코사인 유사도로 결과를 필터링하고, 관련도 순으로 결과를 정렬합니다. minSimilarity 임계값은 0.0에서 1.0 사이의 값이어야 하며, 1.0은 완전히 동일함을 의미합니다.

$documents = DB::table('documents') ->whereVectorSimilarTo('embedding', $queryEmbedding, minSimilarity: 0.4) ->limit(10) ->get();

벡터 인자로 일반 문자열을 전달하면, Laravel이 Laravel AI SDK를 사용해 해당 문자열의 임베딩을 자동으로 생성합니다.

$documents = DB::table('documents') ->whereVectorSimilarTo('embedding', 'Best wineries in Napa Valley') ->limit(10) ->get();

기본적으로 whereVectorSimilarTo는 결과를 거리순(가장 유사한 항목이 먼저)으로 정렬합니다. order 인자에 false를 전달하면 이 정렬을 비활성화할 수 있습니다.

$documents = DB::table('documents') ->whereVectorSimilarTo('embedding', $queryEmbedding, minSimilarity: 0.4, order: false) ->orderBy('created_at', 'desc') ->limit(10) ->get();

더 세밀한 제어가 필요하다면 selectVectorDistance, whereVectorDistanceLessThan, orderByVectorDistance 메서드를 각각 독립적으로 사용할 수 있습니다.

$documents = DB::table('documents') ->select('*') ->selectVectorDistance('embedding', $queryEmbedding, as: 'distance') ->whereVectorDistanceLessThan('embedding', $queryEmbedding, maxDistance: 0.3) ->orderByVectorDistance('embedding', $queryEmbedding) ->limit(10) ->get();

PostgreSQL을 사용할 때는 vector 컬럼을 생성하기 전에 pgvector 확장이 로드되어 있어야 합니다.

Schema::ensureVectorExtensionExists();

정렬, 그룹화, Limit 및 Offset

정렬

`orderBy` 메서드

orderBy 메서드는 쿼리 결과를 지정한 컬럼 기준으로 정렬합니다. orderBy 메서드의 첫 번째 인자는 정렬 기준 컬럼이며, 두 번째 인자는 정렬 방향으로 asc 또는 desc 중 하나입니다.

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

여러 컬럼으로 정렬하려면 필요한 만큼 orderBy를 여러 번 호출하면 됩니다.

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

정렬 방향은 선택 사항이며 기본값은 오름차순입니다. 내림차순으로 정렬하려면 orderBy 메서드의 두 번째 매개변수로 방향을 지정하거나 orderByDesc를 사용하면 됩니다.

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

마지막으로, -> 연산자를 사용하면 JSON 컬럼 안의 값을 기준으로 결과를 정렬할 수 있습니다.

$corporations = DB::table('corporations') ->where('country', 'US') ->orderBy('location->state') ->get();

`latest`와 `oldest` 메서드

latest와 oldest 메서드를 사용하면 날짜 기준으로 결과를 손쉽게 정렬할 수 있습니다. 기본적으로 테이블의 created_at 컬럼을 기준으로 정렬하며, 정렬 기준으로 삼을 컬럼 이름을 직접 전달할 수도 있습니다.

$user = DB::table('users') ->latest() ->first();

순서 지정 정렬

inOrderOf 메서드를 사용하면 문자열이나 enum 같은 값의 나열 순서대로 쿼리 결과를 정렬할 수 있습니다.

$orders = DB::table('orders') ->inOrderOf('status', [OrderStatus::Shipped, OrderStatus::Delivered, OrderStatus::Cancelled]) ->get();

무작위 정렬

inRandomOrder 메서드는 쿼리 결과를 무작위로 정렬할 때 사용합니다. 예를 들어 무작위 사용자 한 명을 가져올 때 활용할 수 있습니다.

$randomUser = DB::table('users') ->inRandomOrder() ->first();

기존 정렬 제거하기

reorder 메서드는 쿼리에 이전에 적용된 모든 "order by" 절을 제거합니다.

$query = DB::table('users')->orderBy('name'); $unorderedUsers = $query->reorder()->get();

reorder 메서드를 호출할 때 컬럼과 정렬 방향을 전달하면, 기존의 모든 "order by" 절을 제거하고 완전히 새로운 정렬을 쿼리에 적용할 수 있습니다.

$query = DB::table('users')->orderBy('name'); $usersOrderedByEmail = $query->reorder('email', 'desc')->get();

편의를 위해 reorderDesc 메서드를 사용하면 쿼리 결과를 내림차순으로 다시 정렬할 수 있습니다.

$query = DB::table('users')->orderBy('name'); $usersOrderedByEmail = $query->reorderDesc('email')->get();

그룹화

`groupBy`와 `having` 메서드

예상하셨겠지만, groupBy와 having 메서드로 쿼리 결과를 그룹화할 수 있습니다. having 메서드의 시그니처는 where 메서드와 비슷합니다.

$users = DB::table('users') ->groupBy('account_id') ->having('account_id', '>', 100) ->get();

havingBetween 메서드를 사용하면 지정한 범위 안의 결과로 필터링할 수 있습니다.

$report = DB::table('orders') ->selectRaw('count(id) as number_of_orders, customer_id') ->groupBy('customer_id') ->havingBetween('number_of_orders', [5, 15]) ->get();

groupBy 메서드에 여러 인자를 전달하면 여러 컬럼으로 그룹화할 수 있습니다.

$users = DB::table('users') ->groupBy('first_name', 'status') ->having('account_id', '>', 100) ->get();

더 복잡한 having 구문을 작성하려면 havingRaw 메서드를 참고하세요.

Limit 및 Offset

limit과 offset 메서드를 사용하면 쿼리에서 반환되는 결과의 수를 제한하거나, 지정한 개수만큼 결과를 건너뛸 수 있습니다.

$users = DB::table('users') ->offset(10) ->limit(5) ->get();

조건부 절

다른 조건에 따라 특정 쿼리 절을 적용하고 싶을 때가 있습니다. 예를 들어 들어온 HTTP 요청에 특정 입력값이 있을 때만 where 구문을 적용하고 싶을 수 있습니다. 이럴 때는 when 메서드를 사용합니다.

$role = $request->input('role'); $users = DB::table('users') ->when($role, function (Builder $query, string $role) { $query->where('role_id', $role); }) ->get();

when 메서드는 첫 번째 인자가 true일 때만 전달된 클로저를 실행합니다. 첫 번째 인자가 false이면 클로저는 실행되지 않습니다. 따라서 위 예제에서 when 메서드에 전달된 클로저는 들어온 요청에 role 필드가 있고 그 값이 true로 평가될 때만 호출됩니다.

when 메서드의 세 번째 인자로 또 다른 클로저를 전달할 수도 있습니다. 이 클로저는 첫 번째 인자가 false로 평가될 때만 실행됩니다. 이 기능이 어떻게 쓰이는지 보여주기 위해, 쿼리의 기본 정렬을 설정하는 예제를 살펴보겠습니다.

$sortByVotes = $request->boolean('sort_by_votes'); $users = DB::table('users') ->when($sortByVotes, function (Builder $query, bool $sortByVotes) { $query->orderBy('votes'); }, function (Builder $query) { $query->orderBy('name'); }) ->get();

Insert 구문

쿼리 빌더는 데이터베이스 테이블에 레코드를 삽입하는 insert 메서드도 제공합니다. insert 메서드는 컬럼 이름과 값의 배열을 받습니다.

DB::table('users')->insert([ 'email' => 'kayla@example.com', 'votes' => 0 ]);

배열의 배열을 전달하면 여러 레코드를 한 번에 삽입할 수 있습니다. 각 배열은 테이블에 삽입할 하나의 레코드를 나타냅니다.

DB::table('users')->insert([ ['email' => 'picard@example.com', 'votes' => 0], ['email' => 'janeway@example.com', 'votes' => 0], ]);

insertOrIgnore 메서드는 레코드를 삽입하는 동안 발생하는 오류를 무시합니다. 이 메서드를 사용할 때는 중복 레코드 오류가 무시될 뿐만 아니라, 데이터베이스 엔진에 따라 다른 종류의 오류도 무시될 수 있다는 점을 알아 두어야 합니다. 예를 들어 insertOrIgnore는 MySQL의 strict 모드를 우회합니다.

DB::table('users')->insertOrIgnore([ ['id' => 1, 'email' => 'sisko@example.com'], ['id' => 2, 'email' => 'archer@example.com'], ]);

insertUsing 메서드는 서브쿼리로 삽입할 데이터를 결정하면서 테이블에 새 레코드를 삽입합니다.

DB::table('pruned_users')->insertUsing([ 'id', 'name', 'email', 'email_verified_at' ], DB::table('users')->select( 'id', 'name', 'email', 'email_verified_at' )->where('updated_at', '<=', now()->minus(months: 1)));

자동 증가 ID

테이블에 자동 증가 id가 있다면 insertGetId 메서드로 레코드를 삽입한 뒤 해당 ID를 바로 가져올 수 있습니다.

$id = DB::table('users')->insertGetId( ['email' => 'john@example.com', 'votes' => 0] );

WARNING

PostgreSQL을 사용할 때 insertGetId 메서드는 자동 증가 컬럼의 이름이 id라고 가정합니다. 다른 "시퀀스"에서 ID를 가져오려면 컬럼 이름을 insertGetId 메서드의 두 번째 매개변수로 전달하세요.

Upsert

upsert 메서드는 존재하지 않는 레코드는 삽입하고, 이미 존재하는 레코드는 지정한 새 값으로 업데이트합니다. 첫 번째 인자는 삽입하거나 업데이트할 값이고, 두 번째 인자는 연관된 테이블에서 레코드를 고유하게 식별하는 컬럼(들)의 목록입니다. 세 번째이자 마지막 인자는 일치하는 레코드가 이미 데이터베이스에 있을 때 업데이트할 컬럼의 배열입니다.

DB::table('flights')->upsert( [ ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99], ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150] ], ['departure', 'destination'], ['price'] );

위 예제에서 Laravel은 두 개의 레코드를 삽입하려고 시도합니다. 같은 departure와 destination 컬럼 값을 가진 레코드가 이미 존재하면, Laravel은 해당 레코드의 price 컬럼을 업데이트합니다.

WARNING

SQL Server를 제외한 모든 데이터베이스에서는 upsert 메서드의 두 번째 인자에 지정한 컬럼에 "primary" 또는 "unique" 인덱스가 있어야 합니다. 또한 MariaDB와 MySQL 데이터베이스 드라이버는 upsert 메서드의 두 번째 인자를 무시하고, 기존 레코드를 찾을 때 항상 테이블의 "primary"와 "unique" 인덱스를 사용합니다.

Update 구문

쿼리 빌더는 레코드를 삽입하는 것 외에, update 메서드로 기존 레코드를 업데이트할 수도 있습니다. update 메서드는 insert 메서드와 마찬가지로 업데이트할 컬럼과 값의 쌍으로 이루어진 배열을 받습니다. update 메서드는 영향을 받은 행의 수를 반환합니다. where 절을 사용해 update 쿼리의 대상을 제한할 수 있습니다.

$affected = DB::table('users') ->where('id', 1) ->update(['votes' => 1]);

Update or Insert

데이터베이스에 있는 기존 레코드를 업데이트하되, 일치하는 레코드가 없으면 새로 생성하고 싶을 때가 있습니다. 이런 경우 updateOrInsert 메서드를 사용합니다. updateOrInsert 메서드는 두 개의 인자를 받습니다. 하나는 레코드를 찾기 위한 조건 배열이고, 다른 하나는 업데이트할 컬럼과 값의 쌍으로 이루어진 배열입니다.

updateOrInsert 메서드는 첫 번째 인자의 컬럼과 값 쌍을 사용해 일치하는 데이터베이스 레코드를 찾으려고 시도합니다. 레코드가 존재하면 두 번째 인자의 값으로 업데이트합니다. 레코드를 찾을 수 없으면 두 인자의 속성을 합친 값으로 새 레코드를 삽입합니다.

DB::table('users') ->updateOrInsert( ['email' => 'john@example.com', 'name' => 'John'], ['votes' => '2'] );

updateOrInsert 메서드에 클로저를 전달하면, 일치하는 레코드가 존재하는지 여부에 따라 업데이트하거나 삽입할 속성을 직접 지정할 수 있습니다.

DB::table('users')->updateOrInsert( ['user_id' => $user_id], fn ($exists) => $exists ? [ 'name' => $data['name'], 'email' => $data['email'], ] : [ 'name' => $data['name'], 'email' => $data['email'], 'marketable' => true, ], );

JSON 컬럼 업데이트

JSON 컬럼을 업데이트할 때는 -> 구문을 사용해 JSON 객체의 해당 키를 업데이트해야 합니다. 이 작업은 MariaDB 10.3 이상, MySQL 5.7 이상, PostgreSQL 9.5 이상에서 지원됩니다.

$affected = DB::table('users') ->where('id', 1) ->update(['options->enabled' => true]);

증가 및 감소

쿼리 빌더는 지정한 컬럼의 값을 증가시키거나 감소시키는 편리한 메서드도 제공합니다. 두 메서드 모두 최소한 하나의 인자, 즉 수정할 컬럼을 받습니다. 두 번째 인자를 전달하면 컬럼을 얼마만큼 증가 또는 감소시킬지 지정할 수 있습니다.

DB::table('users')->increment('votes'); DB::table('users')->increment('votes', 5); DB::table('users')->decrement('votes'); DB::table('users')->decrement('votes', 5);

필요하다면 증가 또는 감소 작업을 수행하는 동안 업데이트할 추가 컬럼도 지정할 수 있습니다.

DB::table('users')->increment('votes', 1, ['name' => 'John']);

또한 incrementEach와 decrementEach 메서드를 사용하면 여러 컬럼을 한 번에 증가 또는 감소시킬 수 있습니다.

DB::table('users')->incrementEach([ 'votes' => 5, 'balance' => 100, ]);

Delete 구문

쿼리 빌더의 delete 메서드로 테이블에서 레코드를 삭제할 수 있습니다. delete 메서드는 영향을 받은 행의 수를 반환합니다. delete 메서드를 호출하기 전에 "where" 절을 추가해 삭제 대상을 제한할 수 있습니다.

$deleted = DB::table('users')->delete(); $deleted = DB::table('users')->where('votes', '>', 100)->delete();

비관적 잠금

쿼리 빌더에는 select 구문을 실행할 때 "비관적 잠금(pessimistic locking)"을 구현하도록 돕는 함수도 몇 가지 포함되어 있습니다. "공유 잠금(shared lock)"으로 구문을 실행하려면 sharedLock 메서드를 호출합니다. 공유 잠금은 트랜잭션이 커밋될 때까지 선택된 행이 수정되지 않도록 막습니다.

DB::table('users') ->where('votes', '>', 100) ->sharedLock() ->get();

또는 lockForUpdate 메서드를 사용할 수도 있습니다. "for update" 잠금은 선택된 레코드가 수정되거나, 다른 공유 잠금으로 선택되는 것을 막습니다.

DB::table('users') ->where('votes', '>', 100) ->lockForUpdate() ->get();

필수는 아니지만, 비관적 잠금은 트랜잭션으로 감싸는 것을 권장합니다. 이렇게 하면 전체 작업이 끝날 때까지 조회한 데이터가 데이터베이스에서 변경되지 않음이 보장됩니다. 실패하는 경우에는 트랜잭션이 모든 변경 사항을 롤백하고 잠금을 자동으로 해제합니다.

DB::transaction(function () { $sender = DB::table('users') ->lockForUpdate() ->find(1); $receiver = DB::table('users') ->lockForUpdate() ->find(2); if ($sender->balance < 100) { throw new RuntimeException('Balance too low.'); } DB::table('users') ->where('id', $sender->id) ->update([ 'balance' => $sender->balance - 100 ]); DB::table('users') ->where('id', $receiver->id) ->update([ 'balance' => $receiver->balance + 100 ]); });

재사용 가능한 쿼리 구성 요소

애플리케이션 곳곳에서 같은 쿼리 로직이 반복된다면, 쿼리 빌더의 tap과 pipe 메서드를 사용해 그 로직을 재사용 가능한 객체로 추출할 수 있습니다. 애플리케이션에 다음과 같은 두 가지 쿼리가 있다고 가정해 보겠습니다.

use Illuminate\Database\Query\Builder; use Illuminate\Support\Facades\DB; $destination = $request->query('destination'); DB::table('flights') ->when($destination, function (Builder $query, string $destination) { $query->where('destination', $destination); }) ->orderByDesc('price') ->get(); // ... $destination = $request->query('destination'); DB::table('flights') ->when($destination, function (Builder $query, string $destination) { $query->where('destination', $destination); }) ->where('user', $request->user()->id) ->orderBy('destination') ->get();

두 쿼리에 공통으로 들어 있는 목적지(destination) 필터링을 재사용 가능한 객체로 추출할 수 있습니다.

<?php namespace App\Scopes; use Illuminate\Database\Query\Builder; class DestinationFilter { public function __construct( private ?string $destination, ) { // } public function __invoke(Builder $query): void { $query->when($this->destination, function (Builder $query) { $query->where('destination', $this->destination); }); } }

그런 다음 쿼리 빌더의 tap 메서드로 이 객체의 로직을 쿼리에 적용할 수 있습니다.

use App\Scopes\DestinationFilter;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
DB::table('flights')
->when($destination, function (Builder $query, string $destination) { //
$query->where('destination', $destination); //
}) //
->tap(new DestinationFilter($destination)) //
->orderByDesc('price')
->get();
// ...
DB::table('flights')
->when($destination, function (Builder $query, string $destination) { //
$query->where('destination', $destination); //
}) //
->tap(new DestinationFilter($destination)) //
->where('user', $request->user()->id)
->orderBy('destination')
->get();

쿼리 파이프

tap 메서드는 항상 쿼리 빌더를 반환합니다. 쿼리를 실행하고 다른 값을 반환하는 객체를 추출하고 싶다면 pipe 메서드를 대신 사용할 수 있습니다.

애플리케이션 전반에서 사용되는 공통 페이지네이션 로직을 담은 다음 쿼리 객체를 살펴보겠습니다. 쿼리에 조건만 적용하는 DestinationFilter와 달리, Paginate 객체는 쿼리를 실행하고 페이지네이터 인스턴스를 반환합니다.

<?php namespace App\Scopes; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Database\Query\Builder; class Paginate { public function __construct( private string $sortBy = 'timestamp', private string $sortDirection = 'desc', private int $perPage = 25, ) { // } public function __invoke(Builder $query): LengthAwarePaginator { return $query->orderBy($this->sortBy, $this->sortDirection) ->paginate($this->perPage, pageName: 'p'); } }

쿼리 빌더의 pipe 메서드를 사용하면 이 객체로 공통 페이지네이션 로직을 적용할 수 있습니다.

$flights = DB::table('flights') ->tap(new DestinationFilter($destination)) ->pipe(new Paginate);

디버깅

쿼리를 작성하는 도중 dd와 dump 메서드를 사용하면 현재 쿼리의 바인딩과 SQL을 출력할 수 있습니다. dd 메서드는 디버그 정보를 표시한 뒤 요청 실행을 중단합니다. dump 메서드는 디버그 정보를 표시하되 요청이 계속 실행되도록 둡니다.

DB::table('users')->where('votes', '>', 100)->dd(); DB::table('users')->where('votes', '>', 100)->dump();

쿼리에 dumpRawSql과 ddRawSql 메서드를 호출하면 모든 파라미터 바인딩이 올바르게 치환된 SQL을 출력할 수 있습니다.

DB::table('users')->where('votes', '>', 100)->dumpRawSql(); DB::table('users')->where('votes', '>', 100)->ddRawSql();

이 문서는 Laravel 공식 문서(MIT)를 한국 개발자를 위해 번역·재구성한 것입니다.

번역일: 2026년 11월 8일