поэтому я пытаюсь создать красноречивый запрос Laravel. Это немного сложно и имеет кучу предложений, все они работают хорошо, и запрашивает одну таблицу. Но я хочу добавить конкретное условие, описанное ниже.
->where('date', '>', Carbon::now())
Это условие работает нормально, но я хочу, чтобы оно применялось только к определенным строкам! Предположим, что условие, к которому я хочу применить приведенное выше предложение where, равно:
->where('row_type', '=', 'someType')
Теперь я не хочу фильтровать ни все строки, где row_type = 'someType', ни все строки, где дата > Carbon::now().
Я хочу фильтровать только строки с датой > Carbon::now(), КОГДА row_type = 'someType'.
Конечно, и «дата», и «row_type» являются столбцами в моей таблице.
Теперь, чтобы упростить логику, я хочу в основном ИСКЛЮЧИТЬ строки, где оба (date < Carbon::now() AND row_type = 'someType') верны.
Возможно ли это сделать в одном запросе в красноречивом без вставки необработанного sql?
Я смог воспроизвести свой запрос в необработанном sql:
select id, date, row_type from my_table where case when row_type = 'someType' then date > '2019-03-01%' end;
Следующее редактирование: в Laravel есть метод whereRaw(), поэтому вы можете перенести этот case when ... end; в этот, и он должен работать в Eloquent.
Спасибо @TimLewis Поместит это как необработанный sql, если нет возможности сделать это с помощью красноречивых функций.
Без проблем! Насколько мне известно, в Laravel нет готовой реализации CASE WHEN ..., поэтому я предлагаю подход raw. Возможно, он появился недавно, но я не уверен в этом.






Now to simplify the logic, what I want to do basically is EXCLUDE rows where both (date < Carbon::now() AND row_type = 'someType') are true.
Либо примените формальную логическую логику !(A and B) = !A or !B, либо просто заметьте, что «исключить, если оба верны» эквивалентно «включить, если одно из них ложно». Таким образом, мы включаем те строки, где либо дата не является прошлой (т.е. является будущей), либо тип не является SomeType.
->where('row_type', '!=', 'someType')->orWhere('date', '>', Carbon::now())
Если у вас также есть другие условия, включая то, что orWhere испортит их, вы должны просто использовать вложение для этого:
// ...other wheres...
->where(function($query) {
$query->where('date', '>', Carbon::now())->orWhere('row_type', '!=', 'someType');
})
->where( // ...other wheres...
Я попытаюсь пройти через SQL, чтобы показать, что это работает.
CREATE TABLE my_table(Id integer PRIMARY KEY, row_type text, date date);
/* First row is someType and past - it should be excluded */
INSERT INTO my_table VALUES(1,'someType', '2019-03-01');
INSERT INTO my_table VALUES(2,'someType', '2019-03-31');
INSERT INTO my_table VALUES(3,'otherType', '2019-03-01');
INSERT INTO my_table VALUES(4,'otherType', '2019-03-01');
COMMIT;
Запрос в op такой:
SELECT 'Cases from the OP' as '';
SELECT id, row_type, date
FROM my_table
WHERE
CASE
WHEN row_type = 'someType'
THEN date > '2019-03-22%'
END;
/* returns */
2|someType|2019-03-31
Это даже не то, что вы говорите словами. Он также исключает каждую строку, где row_type не является someType. Это эквивалентно row_type = 'someType' AND date > '2019-03-22'. Чтобы исключить то, что, как вы сказали, должно быть исключено, вам нужно сделать его еще более сложным:
SELECT id, row_type, date
FROM my_table
WHERE
CASE
WHEN row_type = 'someType'
THEN date > '2019-03-22'
ELSE 1
END;
/* returns */
2|someType|2019-03-31
3|otherType|2019-03-01
4|otherType|2019-03-01
Но было бы проще и уместнее (уместны случаи, когда на самом деле случаев несколько) просто написать так:
SELECT ' ' as '';
SELECT 'The same using OR' as '';
SELECT id, row_type, date
FROM my_table
WHERE
(row_type != 'someType' OR date > '2019-03-22');
/* returns */
2|someType|2019-03-31
3|otherType|2019-03-01
4|otherType|2019-03-01
Я заключил условие в круглые скобки, так как вы сказали, что хотите добавить и другие операторы. И это то, что where(function($q) {$q->...}) сделал бы.
Это не работает, потому что я не хочу исключать все результаты, где row_type = 'someType'. И если строка не является «someType», я не хочу, чтобы применялся фильтр даты.
@IbrahimChawa, вот почему это ИЛИ. Первый where включает все строки, которые не являются someType, а второй применяет фильтр даты к оставшимся (те, которые являются someType). Я думаю, что вы просто подходите к своим условиям не с того конца.
Ну, мой запрос намного сложнее, чем этот пример. У меня есть куча других статей where. Он включает в себя, где row_type в (некоторые другие типы), и я не могу поставить условие, например илиГде дата > сейчас
См. Комментарий выше, мне нужно было красноречивое предложение WHERE CASE, но, думаю, его нет. В любом случае спасибо.
Вы можете делать все, что хотите, но я думаю, что все еще могу избавить вас от использования необработанного оператора. Смотрите мой обновленный ответ. Любой оператор CASE можно заменить комбинацией И/ИЛИ. И это легко и читабельно для однократного случая. Может быть, вложенность wheres - это то, чего вам не хватало?
that's why it's OR. First where includes all rows that are not someType and the second applies date filter to remaining (those that are someType) .. это умственная гимнастика .. это не читается вообще .. ужасный обходной путь, на мой взгляд, я бы предпочел иметь читаемый необработанный sql, чем слишком сильно пытаться с красноречивым
@abbood Я не могу согласиться с тем, что CASE WHEN было бы легче понять, чем ... AND (row_type != some_type OR date > '2019-03-22'). Использование прецедентов для проверки одного условия вводит в заблуждение. Кроме того, SQL-запрос в OP даже не делает того, что требуется, см. мой обновленный ответ.
Если комбинации
->where()и->orWhere()не работают, может быть, подойдет какая-нибудьCASE WHEN ...? Я бы посоветовал посмотреть, сможете ли вы получить желаемый результат в ручном SQL-запросе (используя любую программу БД, которую вы используете), а затем попытаться перевести это на красноречивый.