Làm cách nào để chọn từ truy vấn con bằng Laravel Query Builder?


102

Tôi muốn nhận giá trị bằng SQL sau đây sử dụng Eloquent ORM.

- SQL

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

Sau đó, tôi xem xét những điều sau đây.

- Mã

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

Tôi đang tìm kiếm một giải pháp tốt hơn.

Xin vui lòng cho tôi biết giải pháp đơn giản nhất.

Câu trả lời:


131

Ngoài câu trả lời của @ delmadord và nhận xét của bạn:

Hiện tại không có phương pháp nào để tạo truy vấn con trong FROMmệnh đề, vì vậy bạn cần sử dụng câu lệnh thô theo cách thủ công, sau đó, nếu cần, bạn sẽ hợp nhất tất cả các ràng buộc:

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

Lưu ý rằng bạn cần phải hợp nhất các ràng buộc theo đúng thứ tự . Nếu bạn có các mệnh đề ràng buộc khác, bạn phải đặt chúng sau 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
Lưu ý rằng nếu bạn có một truy vấn phức tạp như một belongsToManynhư subselect bạn phải thêm getQuery()hai lần =>$sub->getQuery()->getQuery()
Jordi Puigdellívol

1
@Skyzer Bạn không đọc những gì tôi viết. Không có gì được thoát ra khi bạn gọi toSql. Đọc về PDO php.net/manual/en/book.pdo.php và xem kết quả của bạn$query->toSql()
Jarek Tkaczyk 23/09/15

5
Liên quan đến -> mergeBindings ($ phụ> getQuery ()) chỉ làm -> mergeBindings ($ sub)
Jimmy Ilenloa

1
@JimmyIlenloa Nếu $subtruy vấn là Eloquent Builder , thì bạn vẫn cần ->getQuery()phần này, nếu không, bạn sẽ gặp lỗi, vì phương thức này được đánh chữ dựa trên Query\Builderlớp.
Jarek Tkaczyk

1
@Kannan nope. Tôi đoán đó là một ứng cử viên cho PR, nhưng cuối cùng thì đó không phải là trường hợp sử dụng phổ biến. Có lẽ đây là lý do vì không có nó ở đó cho đến hôm nay ..
Jarek Tkaczyk

76

Đã thêm Laravel v5.6.12 (2018-03-14) fromSub()fromRaw()các phương thức để tạo truy vấn (# 23476) .

Câu trả lời được chấp nhận là đúng nhưng có thể được đơn giản hóa thành:

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

Đoạn mã trên tạo ra SQL sau:

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

15

Giải pháp của @JarekTkaczyk chính xác là những gì tôi đang tìm kiếm. Điều duy nhất tôi nhớ là cách thực hiện khi bạn đang sử dụng DB::table()các truy vấn. Trong trường hợp này, đây là cách tôi thực hiện:

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

Đặc biệt chú ý làm thế nào để thực hiện mergeBindingsmà không sử dụng getQuery()phương pháp


Sử dụng DB::raw()không công việc cho tôi
Nino Škopac

7

Từ laravel 5.5 có một phương thức dành riêng cho các truy vấn con và bạn có thể sử dụng nó như sau:

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

hoặc là

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

1
Có vẻ như subSelect chỉ có thể được sử dụng để thêm một truy vấn phụ vào SELECT, không phải FROM.
hagabaka

1
Call to undefined method subSelect()dường như subSelectkhông tồn tại.
Maruf Alom

3
Cảm ơn vì đã thông báo cho tôi điều này, tôi đã viết sai tên, lẽ ra phải như vậy selectSub. Tôi đã cập nhật câu trả lời của mình ngay bây giờ.
Sasa Blagojevic

3

Tôi thích làm điều gì đó như thế này:

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

Nó không phải là rất thanh lịch, nhưng nó đơn giản.


Cảm ơn điều này đã làm việc cho tôi, như một lưu ý phụ, hãy cẩn thận với nội dung được chọn vì laravel đã thêm một số dấu ngoặc kép và tôi phải sử dụng -> select (\ DB :: raw ('Your select')) để loại bỏ chúng.
Wak

2

Tôi không thể tạo mã của bạn để thực hiện truy vấn mong muốn, AS là bí danh chỉ cho bảng abc, không phải cho bảng dẫn xuất. Laravel Query Builder không hỗ trợ ngầm các bí danh bảng dẫn xuất, DB :: raw rất có thể cần thiết cho việc này.

Giải pháp đơn giản nhất mà tôi có thể đưa ra gần giống với giải pháp của bạn, tuy nhiên lại tạo ra truy vấn như bạn yêu cầu:

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

Truy vấn được tạo là

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

Cảm ơn bạn đã trả lời. Có một vấn đề trong phương thức "Abc :: from (???) và DB :: table (???)". $ sql = Abc :: where ('id', '=', $ id) -> groupBy ('col1') -> toSql (); $ count = DB :: table (DB :: raw ("($ sql) AS a")) -> count (); Lỗi SQL xảy ra trong đoạn mã trên. - chỉ định ở đâu và tham số!
quenty658

2

Cách đúng được mô tả trong câu trả lời này: https://stackoverflow.com/a/52772444/2519714 Câu trả lời phổ biến nhất tại thời điểm hiện tại không hoàn toàn đúng.

Cách này https://stackoverflow.com/a/24838367/2519714 không đúng trong một số trường hợp như: sub select có nơi ràng buộc, sau đó tham gia bảng để chọn phụ, sau đó thêm các wheres khác vào tất cả truy vấn. Ví dụ truy vấn: select * from (select * from t1 where col1 = ?) join t2 on col1 = col2 and col3 = ? where t2.col4 = ? Để thực hiện truy vấn này, bạn sẽ viết mã như:

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

Trong khi thực hiện truy vấn này, phương thức của anh ta $query->getBindings()sẽ trả về các ràng buộc theo thứ tự không chính xác như ['val3', 'val1', 'val4']trong trường hợp này thay vì đúng ['val1', 'val3', 'val4']cho sql thô được mô tả ở trên.

Một cách chính xác nữa để làm điều này:

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

Ngoài ra, các ràng buộc sẽ được hợp nhất tự động và chính xác với truy vấn mới.


Cảm ơn rất nhiều! Nó đã giúp nhiều!
Hasnat Babur
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.