1

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

1 Answer 1

1

The JOIN "table" and "columns" can't be in backticks. You have to use raw expressions:

->join(DB::raw('(SELECT @rownum := 0) r'), DB::raw('1'), '=', DB::raw('1'))
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks, have tried that but got a different error - see above. It seems that the query builder assumes a real table is being joined. Removing , '1', '=', '1' results in a "too few argumants" error.
You also have to use raw expressions for the "columns". I updated my answer.
Sorry, shouldn't have been so quick to accept - doesn't work when inserted into the outer query - see above.
That's a different issue. You can use ->where('is_archived', DB::raw('1')). Or better: fromSub()

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.