Laravel Query Builder kullanarak alt sorgudan nasıl seçim yapılır?


104

Eloquent ORM kullanarak aşağıdaki SQL ile değer elde etmek istiyorum.

- SQL

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

Sonra şunu düşündüm.

- Kod

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

Daha iyi bir çözüm arıyorum.

Lütfen bana en basit çözümü söyleyin.

Yanıtlar:


136

@ Delmadord'un cevabına ve yorumlarınıza ek olarak:

Şu anda FROMyan tümcede alt sorgu oluşturmak için bir yöntem yoktur , bu nedenle ham ifadeyi manuel olarak kullanmanız gerekir, ardından gerekirse tüm bağlamaları birleştirirsiniz:

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

Bağlamaları doğru sırada birleştirmeniz gerektiğini unutmayın . Başka bağlayıcı maddeleriniz varsa, bunları aşağıdakilerden sonra koymalısınız 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
belongsToManygetQuery()$sub->getQuery()->getQuery()
Alt seçim

1
@Skyzer Yazdıklarımı okumuyorsun. Aradığınızda hiçbir şey kaçmaz toSql. PDO hakkında okuyun php.net/manual/en/book.pdo.php ve sonucunu görmek$query->toSql()
Jarek Tkaczyk

5
İle ilgili olarak > mergeBindings ($ alt> getQuery ()) - sadece bunu -> mergeBindings ($ alt)
Jimmy Ilenloa

1
@JimmyIlenloa $subSorgu bir Eloquent Builder ise , o zaman ->getQuery()parçaya hala ihtiyacınız var , aksi takdirde bu yöntem sınıfa karşı yazılacağı için hata alırsınız Query\Builder.
Jarek Tkaczyk

1
@Kannan hayır. bu bir PR adayı sanırım, ama sonuçta bu çok yaygın bir kullanım durumu değil. Muhtemelen bu güne kadar orada olmamasının nedeni budur ..
Jarek Tkaczyk

84

Laravel v5.6.12 (2018-03-14) eklendi fromSub()ve fromRaw()oluşturucuyu sorgulama yöntemleri (# 23476) .

Kabul edilen cevap doğrudur, ancak basitleştirilebilir:

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

Yukarıdaki kod parçası aşağıdaki SQL'i üretir:

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

16

@JarekTkaczyk çözümü tam olarak aradığım şeydi. Tek özlediğim şey, DB::table()sorguları kullanırken bunu nasıl yapacağım . Bu durumda, ben böyle yaparım:

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

Yöntemi mergeBindingskullanmadan nasıl yapılacağına özel dikkatgetQuery()


DB::raw()İşi kullanmak
işimi yaptı

7

Laravel 5.5'ten alt sorgular için özel bir yöntem vardır ve bunu şu şekilde kullanabilirsiniz:

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

veya

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

1
Görünüşe göre subSelect yalnızca SELECT'e bir alt sorgu eklemek için kullanılabilir, FROM değil.
hagabaka

1
Call to undefined method subSelect()subSelectyok gibi görünüyor .
Maruf Alom

3
Bunu bildirimime getirdiğin için teşekkürler, adı yanlış yazdım, olmalıydı selectSub. Cevabımı şimdi güncelledim.
Sasa Blagojevic

3

Bunun gibi bir şey yapmayı seviyorum:

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

Çok zarif değil ama basit.


Teşekkürler bu benim için çalıştı, bir yan not olarak, seçilen içeriğe dikkat edin çünkü laravel bazı tırnak işaretleri ekledi ve onlardan kurtulmak için -> select (\ DB :: raw ('Your select')) kullanmak zorunda kaldım.
Wak

2

Kodunuzu istediğiniz sorguyu yapacak şekilde yapamadım, AS abctüretilmiş tablo için değil, yalnızca tablo için bir takma addır . Laravel Query Builder, türetilmiş tablo diğer adlarını örtük olarak desteklemez, DB :: raw büyük olasılıkla bunun için gereklidir.

Bulabildiğim en düz çözüm sizinkiyle hemen hemen aynıdır, ancak sorguyu istediğiniz gibi üretir:

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

Üretilen sorgu

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

Cevabın için teşekkürler. "Abc :: from (???) ve DB :: table (???)" yönteminde bir sorun var. $ sql = Abc :: burada ('id', '=', $ id) -> groupBy ('col1') -> toSql (); $ count = DB :: table (DB :: raw ("($ sql) AS a")) -> count (); Yukarıdaki kodda SQL hatası oluşuyor. - nereye ve parametreler atanır!
quenty658

2

Bu cevapta açıklanan doğru yol: https://stackoverflow.com/a/52772444/2519714 Şu anda en popüler cevap tam olarak doğru değil.

Bu şekilde https://stackoverflow.com/a/24838367/2519714 aşağıdaki gibi bazı durumlarda doğru değildir: alt seçim, nerede bağlamalara sahiptir, ardından tabloyu alt seçime katmak, ardından tüm sorguya eklenen diğer yerler. Örneğin sorgu: select * from (select * from t1 where col1 = ?) join t2 on col1 = col2 and col3 = ? where t2.col4 = ? Bu sorguyu yapmak için aşağıdaki gibi kod yazacaksınız:

$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');

Bu sorguyu yürütürken, yöntemi $query->getBindings()bağlamaları ['val3', 'val1', 'val4']bu durumda olduğu gibi yanlış sırada döndürecektir, bunun yerine ['val1', 'val3', 'val4']yukarıda açıklanan ham sql için düzeltecektir .

Bunu yapmanın bir kez daha doğru yolu:

$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');

Ayrıca bağlamalar otomatik olarak ve doğru bir şekilde yeni sorguya birleştirilecektir.


Çok teşekkürler! Çok yardımcı oldu!
Hasnat Babur
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.