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.