Laravel Query Builder를 사용하여 하위 쿼리에서 선택하는 방법은 무엇입니까?


102

Eloquent ORM을 사용하여 다음 SQL로 가치를 얻고 싶습니다.

-SQL

 SELECT COUNT(*) FROM 
 (SELECT * FROM abc GROUP BY col1) AS a;

그런 다음 다음을 고려했습니다.

-코드

 $sql = Abc::from('abc AS a')->groupBy('col1')->toSql();
 $num = Abc::from(\DB::raw($sql))->count();
 print $num;

더 나은 솔루션을 찾고 있습니다.

가장 간단한 해결책을 알려주세요.

답변:


133

@delmadord의 답변과 귀하의 의견 외에도 :

현재 FROM절에 하위 쿼리를 생성하는 방법이 없으므로 원시 문을 수동으로 사용해야하며 필요한 경우 모든 바인딩을 병합합니다.

$sub = Abc::where(..)->groupBy(..); // Eloquent Builder instance

$count = DB::table( DB::raw("({$sub->toSql()}) as sub") )
    ->mergeBindings($sub->getQuery()) // you need to get underlying Query Builder
    ->count();

올바른 순서로 바인딩병합 해야합니다 . 다른 바운드 절이있는 경우 다음 뒤에 넣어야합니다 mergeBindings.

$count = DB::table( DB::raw("({$sub->toSql()}) as sub") )

    // ->where(..) wrong

    ->mergeBindings($sub->getQuery()) // you need to get underlying Query Builder

    // ->where(..) correct

    ->count();

3
참고이 같은 복잡한 쿼리가있는 경우 있음 belongsToMany부속으로 당신은 추가해야 getQuery()두 번 =>$sub->getQuery()->getQuery()
조르디 Puigdellívol

1
@Skyzer 당신은 내가 쓴 것을 읽고 있지 않습니다. 전화를 걸어도 탈출 할 수있는 것은 없습니다 toSql. PDO에 대해 읽어 php.net/manual/en/book.pdo.php 및 결과보고$query->toSql()
Jarek Tkaczyk

5
에 관하여 > mergeBindings가 ($ 하위> getQuery ()) - 단지 할 -> mergeBindings ($ 하위)
지미 Ilenloa

1
@JimmyIlenloa $sub쿼리가 Eloquent Builder 인 경우 여전히 ->getQuery()파트 가 필요합니다 . 그렇지 않으면이 메서드가 Query\Builder클래스 에 대해 유형 힌트되므로 오류가 발생 합니다.
Jarek Tkaczyk 2015

1
@Kannan 아니. 제가 생각하기에 PR 후보이지만 결국에는 그다지 일반적인 사용 사례가 아닙니다. 아마이이 일에이를 가지고 있지 이유는 ..
Jarek Tkaczyk

80

Laravel v5.6.12 (2018-03-14) 추가 fromSub()fromRaw()쿼리 작성기에 메서드 (# 23476) .

허용되는 답변은 정확하지만 다음과 같이 단순화 할 수 있습니다.

DB::query()->fromSub(function ($query) {
    $query->from('abc')->groupBy('col1');
}, 'a')->count();

위의 스 니펫은 다음 SQL을 생성합니다.

select count(*) as aggregate from (select * from `abc` group by `col1`) as `a`

16

@JarekTkaczyk의 해결책은 정확히 내가 찾던 것입니다. 내가 놓친 유일한 것은 DB::table()쿼리를 사용할 때 수행하는 방법 입니다. 이 경우 다음과 같이합니다.

$other = DB::table( DB::raw("({$sub->toSql()}) as sub") )->select(
    'something', 
    DB::raw('sum( qty ) as qty'), 
    'foo', 
    'bar'
);
$other->mergeBindings( $sub );
$other->groupBy('something');
$other->groupBy('foo');
$other->groupBy('bar');
print $other->toSql();
$other->get();

특별 하 atention는 방법을 만들기 위해 mergeBindings사용하지 않고 getQuery()방법을


사용 DB::raw()나를 위해 작업 않았다
니노 Škopac

7

laravel 5.5에는 하위 쿼리에 대한 전용 메서드가 있으며 다음과 같이 사용할 수 있습니다.

Abc::selectSub(function($q) {
    $q->select('*')->groupBy('col1');
}, 'a')->count('a.*');

또는

Abc::selectSub(Abc::select('*')->groupBy('col1'), 'a')->count('a.*');

1
subSelect는 FROM이 아닌 SELECT에 하위 쿼리를 추가하는 데만 사용할 수 있습니다.
hagabaka

1
Call to undefined method subSelect()subSelect존재하지 않는 것 같습니다 .
Maruf Alom

3
신고 해 주셔서 감사합니다. 이름을 잘못 입력했습니다 selectSub. 지금 내 응답을 업데이트했습니다.
Sasa Blagojevic

3

나는 다음과 같은 것을 좋아합니다.

Message::select('*')
->from(DB::raw("( SELECT * FROM `messages`
                  WHERE `to_id` = ".Auth::id()." AND `isseen` = 0
                  GROUP BY `from_id` asc) as `sub`"))
->count();

매우 우아하지는 않지만 간단합니다.


감사합니다. 부수적으로 laravel이 따옴표를 추가하고 제거하기 위해-> select (\ DB :: raw ( 'Your select'))를 사용해야했기 때문에 선택 내용에주의하십시오.
Wak

2

원하는 쿼리를 수행하도록 코드를 만들 수 없습니다. AS는 abc파생 테이블이 아니라 테이블에 대한 별칭 입니다. Laravel Query Builder는 파생 테이블 별칭을 암시 적으로 지원하지 않으며 DB :: raw가이를 위해 가장 많이 필요합니다.

내가 생각해 낼 수있는 가장 직접적인 해결책은 당신의 것과 거의 동일하지만 당신이 요청한대로 쿼리를 생성합니다.

$sql = Abc::groupBy('col1')->toSql();
$count = DB::table(DB::raw("($sql) AS a"))->count();

생성 된 쿼리는

select count(*) as aggregate from (select * from `abc` group by `col1`) AS a;

당신의 답변에 감사드립니다. "Abc :: from (???) 및 DB :: table (???)"메서드에 문제가 있습니다. $ sql = Abc :: where ( 'id', '=', $ id)-> groupBy ( 'col1')-> toSql (); $ count = DB :: table (DB :: raw ( "($ sql) AS a"))-> count (); 위 코드에서 SQL 오류가 발생합니다. -위치와 매개 변수 할당!
quenty658

2

이 답변에 설명 된 올바른 방법 : https://stackoverflow.com/a/52772444/2519714 현재 가장 인기있는 답변은 완전히 정확하지 않습니다.

이렇게하면 https://stackoverflow.com/a/24838367/2519714 는 다음과 같은 경우에 올바르지 않습니다. 예제 쿼리 : select * from (select * from t1 where col1 = ?) join t2 on col1 = col2 and col3 = ? where t2.col4 = ? 이 쿼리를 작성하려면 다음과 같은 코드를 작성합니다.

$subQuery = DB::query()->from('t1')->where('t1.col1', 'val1');
$query = DB::query()->from(DB::raw('('. $subQuery->toSql() . ') AS subquery'))
    ->mergeBindings($subQuery->getBindings());
$query->join('t2', function(JoinClause $join) {
    $join->on('subquery.col1', 't2.col2');
    $join->where('t2.col3', 'val3');
})->where('t2.col4', 'val4');

이 쿼리를 실행하는 동안 그의 메서드 $query->getBindings()['val3', 'val1', 'val4']이 경우 와 같이 잘못된 순서로 바인딩을 반환합니다. 대신 ['val1', 'val3', 'val4']위에서 설명한 원시 SQL에 맞습니다 .

한 번 더 올바른 방법으로 수행하십시오.

$subQuery = DB::query()->from('t1')->where('t1.col1', 'val1');
$query = DB::query()->fromSub($subQuery, 'subquery');
$query->join('t2', function(JoinClause $join) {
    $join->on('subquery.col1', 't2.col2');
    $join->where('t2.col3', 'val3');
})->where('t2.col4', 'val4');

또한 바인딩은 새 쿼리에 자동으로 올바르게 병합됩니다.


감사합니다! 많은 도움이되었습니다!
Hasnat Babur
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.