За последние 24 часа нас посетили 22949 программистов и 1262 робота. Сейчас ищут 723 программиста ...

Subquery в join

Тема в разделе "Laravel", создана пользователем vvas, 10 сен 2019.

Метки:
  1. vvas

    vvas Новичок

    С нами с:
    9 апр 2018
    Сообщения:
    50
    Симпатии:
    10
    Помогите выразить на Query Builder без DB::raw(), но через joinSub или подобный механизм вот такой непростой запрос:

    Код (SQL):
    1. SELECT
    2.    SUM(w.weight) AS weight
    3. FROM container AS c
    4. JOIN weight AS w ON w.id = ( -- First weight
    5.      SELECT MIN(id) FROM weight WHERE container_id = c.id
    6. )
    7. LEFT JOIN container_run AS cr ON cr.id = ( -- First link to process if any    
    8.     SELECT MIN(id) FROM container_run WHERE container_id = c.id
    9. )
    10. WHERE
    11.    cr.TYPE = 1 OR cr.TYPE IS NULL
    Запрос на сыром SQL работает как надо. Не хочется в Laravel писать всё через DB::RAW() это как-то неэстетично.

    Колдунство с
    JOIN xxx ON id = SELECT MIN(id) FROM xxx WHERE fk_id = yyy.id
    понадобилось так как надо прицепить первую из нескольких подходящих записей.

    Тому гуру Eloquent кто осилит перевод, от меня будет огромный респект!
     
  2. Алекс8

    Алекс8 Активный пользователь

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    Если нужно получить все контейнеры с их первым весом и которые уже имеют статус отправки 1 или null то как то так...
    PHP:
    1. Container::has('weight')->with(['weight' => function($q){
    2.      $q->orderBy('id', 'ASC')->take(1);
    3. }])
    4. ->whereHas('container_run', function($q){
    5.     $q->where(function($q){
    6.          $q->whereType(1)->orWhereNull('type');
    7.     });
    8. })->get();
    а если нужно получить ВЕС всех контейнеров которые имеют статус отправки 1 или null тогда лучше так))
    PHP:
    1. Weight::whereHas('container_run', function($q){
    2.     $q->where(function($q){
    3.          $q->whereType(1)->orWhereNull('type');
    4.     });
    5. })
    6. ->orderBy('id', 'ASC')
    7. ->orderBy('container_id', 'ASC')
    8. ->groupBy('container_id')->get();
    а потом уже через коллекцию посчитать сумму.. во втором случае запрос будет легче...
    не проверял ни первый ни второй)) написал по памяти))
     
  3. Алекс8

    Алекс8 Активный пользователь

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    второй вариант с ошибкой изза groupBy.... думаю как исправить.. distinct нужен))
     
    #3 Алекс8, 11 сен 2019
    Последнее редактирование: 11 сен 2019
  4. vvas

    vvas Новичок

    С нами с:
    9 апр 2018
    Сообщения:
    50
    Симпатии:
    10
    Спасибо. Но это всё не то.
    --- Добавлено ---
    Я не уверен надо ли описывать что вообще делается, потому что задача не в том, чтобы переписать запрос. А в том как его перевести на Eloquent. Именно этот запрос. Без использования raw.

    Если всё-таки описать словами, то каждый контейнер может быть взвешен несколько раз. В данном случае нам интересно только первое взвешивание. Каждый контейнер может использоваться для разных целей. Это выражено через таблицу-связку в которой есть поле Тип. Нам надо чтобы первый раз он использовался как тип=1. Если запись в этой таблице отсутствует (у нас здесь left join!), то мы считаем это как если бы тип был равен 1.
    Надо вычислить общий вес контейнеров по данным условиям.
     
    #4 vvas, 11 сен 2019
    Последнее редактирование: 11 сен 2019
  5. Алекс8

    Алекс8 Активный пользователь

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    первый запрос решает это задачу частично...
    PHP:
    1. Container::has('weight')->with(['weight' => function($q){
    2.      $q->orderBy('id', 'ASC')->take(1);
    3. }])
    4. ->whereHas('container_run', function($q){
    5.     $q->where(function($q){
    6.          $q->whereType(1)->orWhereNull('type');
    7.     });
    8. })->get();
    мы взяли все контейнеры у которых type доставки 1 или null
    плюс добавили связь к этим контейнерам и взяли первое взвешивание...
    получили коллекцию...
    и дальше уже можно в коллекции сложить общий весь контейнеров....

    вариант второй будет работать на mysql 5.6 и ниже, потому что начиная с версии 5.7 и выше включили ONLY_FULL_GROUP_BY по умолчанию.. и теперь или выключать этот режим... и собирать вес через group или делать так как в первом варианте))
     
  6. vvas

    vvas Новичок

    С нами с:
    9 апр 2018
    Сообщения:
    50
    Симпатии:
    10
    @Алекс8 здесь нет left join.
    --- Добавлено ---
    это кардинально меняет смысл условия
    --- Добавлено ---
    Подсказка: если вместо последнего ->get() применить ->toSql() то можно увидеть какой будет вызван запрос.
     
  7. keren

    keren Новичок

    С нами с:
    15 ноя 2017
    Сообщения:
    513
    Симпатии:
    42
    Это странно - последняя таблица никак не джойнится с предыдущими.
     
  8. vvas

    vvas Новичок

    С нами с:
    9 апр 2018
    Сообщения:
    50
    Симпатии:
    10
    @keren как это понимать? Слово join не работает почему-то? :)
     
  9. Алекс8

    Алекс8 Активный пользователь

    С нами с:
    18 май 2017
    Сообщения:
    1.730
    Симпатии:
    359
    left join связывает две таблицы делая основной ту которая слева))... а у Вас он не связывает ничего)
     
  10. vvas

    vvas Новичок

    С нами с:
    9 апр 2018
    Сообщения:
    50
    Симпатии:
    10
    Блин, пацаны :) Это невозможно, майсиквел будет возражать если вы попытаетесь не связать ничего. Будте более критичны к себе, плиз.
    --- Добавлено ---
    Конкретно слева там container, а справа container_run. Да, пара из container_run может отсутствовать и именно ЭТУ ситуацию ловит
    cr.type IS NULL
    а не то, что ты попытался изобразить в whereHas @Алекс8
     
  11. vvas

    vvas Новичок

    С нами с:
    9 апр 2018
    Сообщения:
    50
    Симпатии:
    10
    Короче, я пришел к выводу, что запросы подобные этому невозможно полностью перевести на eloquent. Какой-то кусок таки будет сырым.