J'aimerais que le code SQL suivant utilise Eloquent ORM.
- SQL
SELECT COUNT(*) FROM
(SELECT * FROM abc GROUP BY col1) AS a;
Alors j'ai considéré le suivant.
- Code
$sql = Abc::from('abc AS a')->groupBy('col1')->toSql();
$num = Abc::from(\DB::raw($sql))->count();
print $num;
Je cherche une meilleure solution.
S'il vous plaît dites-moi la solution la plus simple.
En plus de la réponse de @ delmadord et de vos commentaires:
Il n'existe actuellement aucune méthode pour créer une sous-requête dans la clause FROM
. Vous devez donc utiliser manuellement une instruction brute. Si nécessaire, vous allez fusionner toutes les liaisons:
$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();
Notez que vous devez fusionner les liaisons dans le bon ordre. Si vous avez d'autres clauses liées, vous devez les mettre après 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();
Laravel v5.6.12 (2018-03-14) a ajouté les méthodes fromSub()
et fromRaw()
au constructeur de requêtes (# 23476) .
La réponse acceptée est correcte mais peut être simplifiée comme suit:
DB::query()->fromSub(function ($query) {
$query->from('abc')->groupBy('col1');
}, 'a')->count();
L'extrait ci-dessus produit le code SQL suivant:
select count(*) as aggregate from (select * from `abc` group by `col1`) as `a`
La solution de @JarekTkaczyk est exactement ce que je cherchais. La seule chose qui me manque, c'est comment le faire lorsque vous utilisez les requêtes DB::table()
. Dans ce cas, voici comment je le fais:
$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();
Attention particulière sur la manière de rendre la mergeBindings
sans utiliser la méthode getQuery()
De laravel 5.5, il existe une méthode dédiée aux sous-requêtes et vous pouvez l'utiliser comme ceci:
Abc::selectSub(function($q) { $q->select('*')->groupBy('col1'); }, 'a')->count('a.*');
ou
Abc::selectSub(Abc::select('*')->groupBy('col1'), 'a')->count('a.*');
Je ne pouvais pas faire votre code pour faire la requête désirée, l'AS n'est un alias que pour la table abc
, pas pour la table dérivée. Laravel Query Builder ne prend pas implicitement en charge les alias de tables dérivés, DB :: raw est probablement nécessaire pour cela.
La solution la plus simple que j'ai pu trouver est presque identique à la vôtre, mais produit la requête que vous avez demandée:
$sql = Abc::groupBy('col1')->toSql();
$count = DB::table(DB::raw("($sql) AS a"))->count();
La requête produite est
select count(*) as aggregate from (select * from `abc` group by `col1`) AS a;
J'aime faire quelque chose comme ça:
Message::select('*')
->from(DB::raw("( SELECT * FROM `messages`
WHERE `to_id` = ".Auth::id()." AND `isseen` = 0
GROUP BY `from_id` asc) as `sub`"))
->count();
Ce n'est pas très élégant, mais c'est simple.