I have the following SQL which provides me with the position of a particular record in the database given specific filters and ordering.
\DB::select("SELECT
t.id,
t.position
FROM (
SELECT estimations.id,
@rownum := @rownum + 1 AS position
FROM estimations
JOIN (SELECT @rownum := 0) r
WHERE estimations.is_archived = 1
ORDER BY estimations.start desc, estimations.name) t
WHERE t.id = " . $this->id);
For the inner query, I want to use the eloquent model Estimation so that I can use its query scopes. I want to use the model so that if the scope is updated down the line, this query will reflect the change...
$innerQuery = Estimation::select('id')
->addSelect(\DB::raw('@rownum := @rownum + 1 AS position'))
// Need to add "JOIN (SELECT @rownum := 0) r"
->archived();
Attempt 1 - failed
Use the join method and pass in some generic arguments...
$innerQuery = Estimation::select('id')
->addSelect(\DB::raw('@rownum := @rownum + 1 AS position'))
->join('(SELECT @rownum := 0) r', '1', '=', '1')
->archived();
While the above gives me the correct SQL if I dump it, $innerQuery->toSql() it fails when I run it, $innerQuery->get() with the following error...
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'my_db.(select @rownum := 0) r' doesn't exist (SQL: SELECT temp.id, temp.position FROM (select `id`, @rownum := @rownum + 1 AS position from `estimations` inner join `(SELECT @rownum := 0) r` on `1` = `1` where `is_archived` = ? order by `start` desc, `name` asc) temp WHERE temp.id = 4)
Attempt 2 - failed
As above but using DB::raw() for the join() (suggested by Jonas Staudenmeir),
$innerQuery = Estimation::select('id')
->addSelect(\DB::raw('@rownum := @rownum + 1 AS position'))
->join(\DB::raw('(SELECT @rownum := 0) r'), '1', '=', '1')
->archived();
Running $innerQuery->get() results in the following error...
SQLSTATE[42S22]: Column not found: 1054 Unknown column '1' in 'on clause' (SQL: select `id`, @rownum := @rownum + 1 AS position from `estimations` inner join (SELECT @rownum := 0) r on `1` = `1` where `is_archived` = 1 order by `start` desc, `name` asc)
Attempt 3 - failed
As above, adding DB::raw() for all join() values (thanks again Jonas Staudenmeir),
$innerQuery = Estimation::select('id')
->addSelect(\DB::raw('@rownum := @rownum + 1 AS position'))
->join(\DB::raw('(SELECT @rownum := 0) r'), \DB::raw('1'), '=', \DB::raw('1'))
->archived();
This works on its own $innerQuery->get();, but when inserted into the outer query,
$res = \DB::select('SELECT
t.id,
t.position
FROM (' . $innerQuery->toSql() . ') t
WHERE t.id = ' . $this->id);
I get the error...
SQLSTATE[HY000]: General error: 2031 (SQL: SELECT t.id, t.position FROM (select `id`, @rownum := @rownum + 1 AS position from `estimations` inner join (SELECT @rownum := 0) r on 1 = 1 where `is_archived` = ? order by `start` desc, `name` asc) t WHERE t.id = 3)
Is this because scopeArchived() filters by a value, which is shown as a ? in the error? How can I avoid this?
Here's the code for scopeArchived(),
public function scopeArchived($query)
{
return $query->where('is_archived', true)->ordered('start', 'desc');
}
Removing ->where('is_archived', true) results in no error, but obviously not the results I'm after.
Attempt 4 - solved
(Thanks Jonas Staudenmeir) - use fromSub() for the outer query. Here's the complete code...
$innerQuery = Estimation::select('id')
->addSelect(\DB::raw('@rownum := @rownum + 1 AS position'))
->join(\DB::raw('(SELECT @rownum := 0) r'), \DB::raw('1'), '=', \DB::raw('1'))
->archived();
$res = \DB::query()->fromSub($innerQuery, 'temp')
->select('id', 'position')
->where('id', $this->id)
->first();