Laravel fromSubquery () Перевод синтаксиса. Postgres Заявление - PullRequest
0 голосов
/ 17 июня 2020

Начиная с laravel 6, появилась возможность добавлять операторы From Subquery.

https://github.com/laravel/framework/pull/29602

Примеры синтаксиса следующие:

$query->select('name')
    ->from(function ($query) {
        $query->something();
    }, 'users')
    ->get();

DB::table(function ($query) {
    $query->something();
}, 'users')->get();

Я очень запутался, в чем разница между ними и как это реализовать. Учитывая, что у меня есть следующий запрос postgresSQL, чтобы найти полосы (последовательные даты), в которых пользователь выполняет сеанс.

SELECT COUNT(*) streak, SUM(amount) streakAmount, MIN(date) startDate, MAX(date) endDate, dateMinusRow dateMinusRow
FROM (
    SELECT COUNT(*) amount, date_trunc('day', start) date,
           date_trunc('day', start) - INTERVAL '1' DAY * DENSE_RANK() OVER (ORDER BY date_trunc('day', start)) dateMinusRow
    FROM sessions
    WHERE user_id = ".$this->user->id."
    GROUP BY date_trunc('day', start)
) groupedDays
GROUP BY dateMinusRow

Мне сложно понять, как добавить окружающий запрос, я Думаю, я получил часть from () вниз:

DB::table(function ($query) {
    $query->selectRaw("
        COUNT(*) amount,
        date_trunc('day', start) date,
        date_trunc('day', start) - INTERVAL '1' DAY * DENSE_RANK() OVER (ORDER BY date_trunc('day', start)) dateMinusRow"
            )
        ->from('sessions')
        ->where('user_id', $this->user->id)
        ->groupByRaw("date_trunc('day', start)");
}, 'groupedDays')

Но я не понимаю, как я должен поместить предложения верхнего уровня select и groupBy

1 Ответ

1 голос
/ 17 июня 2020

Вы не обязаны использовать этот синтаксис.

  • from() используется для указания таблицы , когда вы используете подзапрос.
  • table() используется для указания основной запрашиваемой таблицы.

Вы можете передать необязательный псевдоним в качестве второго параметра в обоих этих методах.

Примеры:

DB::table('users', 'u')->select('id as ID')->get();
// select id as ID from users as u
DB::table(function($sq) {
    $sq->select('id')->from('users'); // must specify from when using subquery
})->select('id')->get();
// select id from (select id from users)
DB::table(function($sq) {
    $sq->select('id'); // not specifying from leads to a bad query
})->select('id')->get();
// select id from (select id)

Если вы предпочитаете отделить запросы друг от друга, вы также можете сделать это

$subquery = DB::table('users')->select('id'); // $subquery will be a Query Builder instance you can pass to table() or from() instead of a Closure.
$query    = DB::table($subquery, 'alias')->select('id');
$results  = $query->get();
// select id from (select id from users) as alias

Мой совет: используйте метод toSql(), чтобы убедиться, что ваши запросы верны . Начните с простого выполнения подзапроса.

SELECT
  COUNT(*) streak,
  SUM(amount) streakAmount,
  MIN(date) startDate,
  MAX(date) endDate,
  dateMinusRow
FROM
# Start Subquery
(
  SELECT
    COUNT(*) amount,
    date_trunc('day', start) date,
    date_trunc('day', start) - INTERVAL '1' DAY * DENSE_RANK() OVER (ORDER BY date_trunc('day', start)) dateMinusRow
  FROM sessions
  WHERE user_id = ".$this->user->id."
  GROUP BY date_trunc('day', start)
) groupedDays
# End Subquery
GROUP BY dateMinusRow
$subquery = DB::table('sessions')
  ->selectRaw(
    "COUNT(*) as amount, "
    ."date_trunc('day', start) as date, "
    ."date_trunc('day', start) - INTERVAL '1' DAY * DENSE_RANK() OVER (ORDER BY date_trunc('day', start)) as dateMinusRow "
  )
  ->where('user_id', $this->user->id)
  ->groupByRaw("date_trunc('day', start)");
// Verify the subquery is correct by dumping $subquery->toSql();
$query = DB::table($subquery, 'groupedDays')
  ->selectRaw(
    "COUNT(*) as streak, "
    ."SUM(amount) as streakAmount, "
    ."MIN(date) as startDate, "
    ."MAX(date) as endDate, "
    ."dateMinusRow "
  )
  ->groupBy('dateMinusRow');
// Verify the query is correct by dumping $query->toSql();
$results = $query->get();

Я не эксперт по postgres, поэтому предполагаю, что исходный запрос работает.

Изменить

from() отсутствует в моем исходном ответе, потому что я не использую подзапрос Closure, а передаю экземпляр Query Builder. Вот как это выглядело бы, если бы я использовал from(). На мой взгляд, работать с ним сложнее.

$query2 = DB::table(function($subquery) {
  $subquery->from('sessions')
    ->selectRaw(
      "COUNT(*) as amount, "
      ."date_trunc('day', start) as date, "
      ."date_trunc('day', start) - INTERVAL '1' DAY * DENSE_RANK() OVER (ORDER BY date_trunc('day', start)) as dateMinusRow "
    )
    ->where('user_id', $this->user->id)
    ->groupByRaw("date_trunc('day', start)");
}, 'groupedDays')
  ->selectRaw(
    "COUNT(*) as streak, "
    ."SUM(amount) as streakAmount, "
    ."MIN(date) as startDate, "
    ."MAX(date) as endDate, "
    ."dateMinusRow "
  )
  ->groupBy('dateMinusRow');
// Verify the query is correct by dumping $query2->toSql();
$results = $query->get();

Сбрасывая оба запроса, единственное отличие, которое я, кажется, получаю в Sql, - это использование обратных кавычек.

>>> $query->toSql()
=> "select COUNT(*) as streak, SUM(amount) as streakAmount, MIN(date) as startDate, MAX(date) as endDate, dateMinusRow  from (select COUNT(*) as amount, date_trunc('day', start) as d
ate, date_trunc('day', start) - INTERVAL '1' DAY * DENSE_RANK() OVER (ORDER BY date_trunc('day', start)) as dateMinusRow  from "sessions" where "user_id" = ? group by date_trunc('day
', start)) as "groupedDays" group by "dateMinusRow""
>>> $query2->toSql()
=> "select COUNT(*) as streak, SUM(amount) as streakAmount, MIN(date) as startDate, MAX(date) as endDate, dateMinusRow  from (select COUNT(*) as amount, date_trunc('day', start) as d
ate, date_trunc('day', start) - INTERVAL '1' DAY * DENSE_RANK() OVER (ORDER BY date_trunc('day', start)) as dateMinusRow  from `sessions` where `user_id` = ? group by date_trunc('day
', start)) as `groupedDays` group by `dateMinusRow`"

Проблема с запросом может быть связана с одной из опций драйвера базы данных. Я не знаю точно, но, чтобы привести пример, некоторые запросы group by не работают с Laravel, даже если сгенерированный код sql правильный , если только строгий режим не отключен в mysql драйвер.

Что касается того, почему я использую ключевое слово as, то базы данных, с которыми я работаю, обычно либо mysql, либо postgresql, поэтому я предпочитаю использовать общую нотацию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...