Laravelクエリビルダーを使用してサブクエリから選択する方法


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;

より良い解決策を探しています。

最も簡単な解決策を教えてください。

回答:


131

@delmadordの回答とコメントに加えて:

現在、FROM節でサブクエリを作成する方法はないため、rawステートメントを手動で使用する必要があります。その後、必要に応じて、すべてのバインディングをマージします。

$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()2回追加する必要があることに注意してください=>$sub->getQuery()->getQuery()
JordiPuigdellívol15年

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

1
@かんなんいや。それは私が推測するPRの候補ですが、結局それはあまり一般的なユースケースではありません。おそらくこれは...この日にそこにそれを持っていない理由である
Jarek Tkaczyk

76

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`

15

@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();

方法mergeBindingsを使用せずに作る方法の特別な注意getQuery()


使用DB::raw()して仕事ができました
NinoŠkopac19年

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にサブクエリを追加するためにのみ使用できるようです。
ハガバカ2018年

1
Call to undefined method subSelect()subSelect存在しないようです。
Maruf Alom 2018

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(???)and DB :: table(???)」のメソッドに問題があります。$ sql = Abc :: where( 'id'、 '='、$ id)-> groupBy( '​​col1')-> toSql(); $ count = DB :: table(DB :: raw( "($ sql)AS a"))-> count(); 上記のコードでSQLエラーが発生します。-どこで、パラメータが割り当てられます!
quenty658 2014

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.