2

I have mysql database query and i want to implementing laravel query builder

with recursive dates as (
    select date('2021-01-01') date
    union all
    select dates.date + interval 1 day from dates where dates.date < '2021-01-31'
)
select dates.date, count(payments.id)
from dates
left join payments on date(payments.created)=dates.date
group by 1;

1 Answer 1

6

From what I can tell, the only way to add a WITH clause to a query is to use raw sql.

$rawSql = <<<SQL
with recursive dates as (
    select date('2021-01-01') date
    union all
    select dates.date + interval 1 day from dates where dates.date < '2021-01-31'
)
select dates.date, count(payments.id)
from dates
left join payments on date(payments.created)=dates.date
group by 1;
SQL;

$results = DB::select($rawSql);

If you need to change some values, you can do so with bindings.

$rawSql = <<<SQL
with recursive dates as (
    select date('?') date
    union all
    select dates.date + interval 1 day from dates where dates.date < '?'
)
select dates.date, count(payments.id)
from dates
left join payments on date(payments.created)=dates.date
group by 1;
SQL;

$results = DB::select($rawSql, ['2021-01-01', '2021-01-31']);

EDIT

Using staudenmeir/laravel-cte, you can write the WITH Clause like this:

withRecursiveExpression('dates', function ($rec) {
    $rec->selectRaw("date('2021-01-01') date")
        ->unionAll(function ($uni) {
            $uni->selectRaw('dates.date + interval 1 day')
                ->from('dates')
                ->whereRaw("dates.date < '2021-01-31'");
        });
})

The recursive part of the query kept throwing an error if I used bindings, so unfortunately, the dates are passes in raw.

In theory, this syntax should work for the complete query.

$res = DB::query()
    ->withRecursiveExpression('dates', function ($rec) {
        $rec->selectRaw("date('2021-01-01') date")
            ->unionAll(function ($uni) {
                $uni->selectRaw('dates.date + interval 1 day')
                    ->from('dates')
                    ->whereRaw("dates.date < '2021-01-31'");
            });
    })
    ->select('dates.date', DB::raw('count(payments.id)'))
    ->from('dates')
    ->leftJoin('payments', DB::raw('date(payments.created)'), 'dates.date')
    ->groupByRaw('1')
    ->get();

The generated SQL is slightly different.

with recursive `dates` as (
    (select date('2021-01-01') date)
    union all
    (select dates.date + interval 1 day from `dates` where dates.date < '2021-01-31')
)
select `dates`.`date`, count(payments.id)
from `dates`
left join `payments` on date(payments.created) = `dates`.`date`
group by 1
Sign up to request clarification or add additional context in comments.

5 Comments

OP's just using the WITH clause to generate a list of dates; that could easily be generated on the Laravel end and provided with a whereIn clause. A direct translation isn't necessary.
In this case, yes. But that might not be a great idea if the date list grows too big. whereIn adds a binding per element.
I'd like to see if/how the laravel cte extension github.com/staudenmeir/laravel-cte could be used for this
@ysth there you go.
Thanks. I commonly used staudenmeir/laravel-cte to build the query. but i just want to do with laravel query builder purly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.