Query Builder vs конкатенация запроса ручками

На какой стороне ты?

  • QB / ORM / etc

    Голосов: 18 78,3%
  • Пилю все ручками, не обламываюсь.

    Голосов: 5 21,7%
  • ЭОС

    Голосов: 0 0,0%

  • Всего проголосовало
    23

AmdY

Пью пиво
Команда форума
Активист, я велосипеды пишу для себя под скатерть, для тренировки, повторяя готовые решения. а в реальной жизни мне полностью хватает laravel-вского, тем более в нём тоже не любят джойны. И этой балалайки хватает как на сайты визитки. так на crm и сеть интернет магазинов. При этом на проекте спокойно могут работать джуниоры и ребята не сильно разбирающиеся в sql. Сложность и специфические требования обычно растут из багов в голове разработчиков, которые ищут свой путь.
 

Активист

Активист
Команда форума
Юзал я qb года два три назад, и после танцев с бубном в нетипичных ситуациях, психанул и выпилил все запросы построенные на qb (из-за их его же бажности).
Где плюшка? Писать blabla()->table('foo')->where('a', '>', 'b')->limit(0,10)->fetch() или все же использовать результат работы ведущих специалистов и заюзать ANSI SQL и не усложнять проект вводя туда стороний продукт (который молодые еще и видят в первый раз) и плодя баги ? А "ребята не сильно разбирающиеся в sql" на join'ян, что SQL будет долбить /tmp создавая временные таблицы. В код молодых разработчиков вообще без слез порой смотреть невозможно. Джойнить из 1м записей без использования индексов это самое малое на что они способны.
 

AmdY

Пью пиво
Команда форума
Активист, запросы не пишут ведущие DBA, а динамически строятся в зависимости от требований приложений. Твой пример в реальном мире выглядит ближе к
PHP:
$model = new Foo();
// в реальности дёргался бы метод модели в который передаются данные
if ($b = $this->request()->get('b')) { // данные берутся из запроса
    $b->whereAGreaterThan($b);
}
if ($с = $this->request()->get('с')) {
    $b->with('foo2')->whereC($c); // здесь динамический join по требованию
}
$model->offset($this->request()->get('page')); // страница берётся из запроса и обкладывается проверками и кастингом
$model->limit($this->config('foo.prepage')); // это из конфига
в реальном мире даже за IBM-вскими DBA приходится запросы править, дописывать, где-то денормализировать, где-то избавляться от джойнов, где-то делать ленивую подгрузку, где-то тащить из кеша и т.д.
 

Lewik

Новичок
/me записал в блокнотик: "синглтон головного мозга"
Активист походу не видел код старых программистов =)
 

fixxxer

К.О.
Партнер клуба
AmdY, ага, уж на что я любитель своих велосипедов, и то на 99% перешел на Laravel. :)

Активист, да пусть пишут как хотят, только чтобы работало. Главное правило (в случае laravel) - все делать в скоупах, без размазывания говна по контроллерам. А дальше в профайлере все видно и код легко поправить. Посмотрят диффы, научатся со временем сразу писать нормально.
 

AmdY

Пью пиво
Команда форума
fixxxer, кстати, прелесть в том, код на laravel джуников от мидлов пришедших не с symfony практически не отличается, главное жёсткий ревью и рефакторинг на первых этапах, чтобы стиль вырабатывали.
а вот скоупы я не люблю из-за проблем с автокомплитом, а мучаться с phpdoc не хочется, предпочитаю обычные методы.
 

Активист

Активист
Команда форума
Активист, запросы не пишут ведущие DBA, а динамически строятся в зависимости от требований приложений. Твой пример в реальном мире выглядит ближе к
PHP:
$model = new Foo();
// в реальности дёргался бы метод модели в который передаются данные
if ($b = $this->request()->get('b')) { // данные берутся из запроса
    $b->whereAGreaterThan($b);
}
if ($с = $this->request()->get('с')) {
    $b->with('foo2')->whereC($c); // здесь динамический join по требованию
}
$model->offset($this->request()->get('page')); // страница берётся из запроса и обкладывается проверками и кастингом
$model->limit($this->config('foo.prepage')); // это из конфига
в реальном мире даже за IBM-вскими DBA приходится запросы править, дописывать, где-то денормализировать, где-то избавляться от джойнов, где-то делать ленивую подгрузку, где-то тащить из кеша и т.д.
Охрененно красиво:
if ($b = $this->request()->get('b')) { // данные берутся из запроса
$b->whereAGreaterThan($b);
}

А что там положили в whereAGreaterThan($b) еще нужно узнать.

не наглядней ли будет?
if ($b = $this->request()->get('b')) { // данные берутся из запроса
$where.= " and a > $b";
}
 

fixxxer

К.О.
Партнер клуба
а вот скоупы я не люблю из-за проблем с автокомплитом, а мучаться с phpdoc не хочется, предпочитаю обычные методы.
а я придумал смешной хак, как наобмануть сторм :)

* @method static Builder chapterId(int $chapterId)
* @method Builder \x01chapterId(int $chapterId)

в смысле - прям вот так символ \x01.

при сканировании считает за уникальный, а при комплите левые символы отрезаются :)))

понятное дело, что под это патчится ideHelper
 
  • Like
Реакции: AmdY

AmdY

Пью пиво
Команда форума
Активист,
>>А что там положили в whereAGreaterThan($b) еще нужно узнать.
это потому что параметр назывался A, в реальности это
whereDistanceGreaterThan() - и мне не нужно знать что в запросе, там скорее всего формула расчёта дистанции и разбираться с ней не нужно, пока работает. принцип чёрного ящика.
>>$where.= " and a > $b";
это sql injection, нужно помнить о типах, может там int, а может char(20), нужно проверять был ли where заполнен до этого. ну и ты пропустил важную часть - поджойнивается дополнительная таблица и ищется по ней.
ах. ещё же коллизии с именами и select * не пройдёт, так как может пересекаться с полями из поджойненных данных
 

Активист

Активист
Команда форума
Активист,
>>А что там положили в whereAGreaterThan($b) еще нужно узнать.
это потому что параметр назывался A, в реальности это
whereDistanceGreaterThan() - и мне не нужно знать что в запросе, там скорее всего формула расчёта дистанции и разбираться с ней не нужно, пока работает. принцип чёрного ящика.
>>$where.= " and a > $b";
это sql injection, нужно помнить о типах, может там int, а может char(20), нужно проверять был ли where заполнен до этого. ну и ты пропустил важную часть - поджойнивается дополнительная таблица и ищется по ней.
ах. ещё же коллизии с именами и select * не пройдёт, так как может пересекаться с полями из поджойненных данных
ооо)) об SQL иньекции это да, конструктивное замечание)) Естественно же что $b нужно обработать.
 

Активист

Активист
Команда форума
Активист,
>>А что там положили в whereAGreaterThan($b) еще нужно узнать.
это потому что параметр назывался A, в реальности это
whereDistanceGreaterThan() - и мне не нужно знать что в запросе, там скорее всего формула расчёта дистанции и разбираться с ней не нужно, пока работает. принцип чёрного ящика.
>>$where.= " and a > $b";
это sql injection, нужно помнить о типах, может там int, а может char(20), нужно проверять был ли where заполнен до этого. ну и ты пропустил важную часть - поджойнивается дополнительная таблица и ищется по ней.
ах. ещё же коллизии с именами и select * не пройдёт, так как может пересекаться с полями из поджойненных данных
А вот по поводу коллизий)) Какие коллизии, о чем??))
Вот метод раскидывает выборку по таблицам в ассоциативный двумерный массив (удобно при создании связанных объектов без доп запросов, значительно уменьшив количество запросов к серверу). Или это новая информация?)) Откроете для себя возможность дергать одним SQL-лем всю структуру связанных объектов по индексам для создания сущностей объектов.

Для MySQL
PHP:
/**
    * (non-PHPdoc)
    * @see app_db_interface::fetch_qualified_array()
    */
    function fetch_qualified_array()
    {
        if (!$this->_result instanceof mysqli_result)
        {
            return null;
        }
 
        $return = array();
 
        if ($row = $this->_result->fetch_row())
        {
     
            $fields = $this->_result->fetch_fields();
     
            foreach ($row as $_key => $_value)
            {
                $return[ $fields[$_key]->table ][ $fields[$_key]->name ] = $_value;
            }
     
            return $return;
        }
 
        return false;
    }
 
Последнее редактирование:

fixxxer

К.О.
Партнер клуба
Нет смысла что-то объяснять человеку, который пришел поучать с видом знатока. Пойми, мы тут это все уже проходили много лет назад. Впрочем, насчет "одним sql-запросом" отвечу: cache hit/miss ratio.
 

Активист

Активист
Команда форума
Нет смысла что-то объяснять человеку, который пришел поучать с видом знатока. Пойми, мы тут это все уже проходили много лет назад. Впрочем, насчет "одним sql-запросом" отвечу: cache hit/miss ratio.
Да я тут много лет уже)) Что Вы меня за кретина держите? И Как это - нет смысла что-то объяснять? Что за неуважительное отношение?

Про cache hit/miss ratio, да конечно SQL сервер молодец, но вы забываете на время соединения, на проверки состояния таблиц и еще множество накладных ресурсов. Порой в раскрученных движках встретишь и 50 и 200 запросов, когда можно было сделать пару.

Скажем, есть тривиальная задача - вывести товары групп 1,2,3,4, при этом у товаров есть 20 свойств, 10 картинок, 3 склада и 4 типа цен. Собственно разные таблицы.

Все это должно превратиться в object relation сущности. Что мы можем делать? Мы можем дернуть 50 товаров, дернуть для них 20 свойств, потому еще 10 картинок , склады и типы цен. Все по IN (id). Выход. И это будет хорошо, что если сделают так.

А могут вообще, на каждую сущность товара поставить методы выборки свойств, картинок, складов и т.п. по первичному ключу / индексу. К чему это приведет? К возрастанию до 300-от запросов на выборку 50 товаров как минимум .

Конечно, утверждая что выборки быстры и проще сделать запрос по первичным ключам/индексам, но почему-то приложение вдруг стало отрабатывать не за 0.05 секунды, а уже 0.5 с 8-ю кратным превышением нагрузки на IO . После этого и начинается, кеши и т.п., хотя можно было просто правильно выстроить логику приложения используя возможности системы.

Хотя можно было просто сделать два хороших SQL запросов, отточенных профайлером до блеска.
 

Фанат

oncle terrible
Команда форума
Они ж, вроде, не спорят с этим.
Сейчас ведь разговор про QB, а не про ORM, который ты описываешь (и к которому у меня такие же претензии).

Просто помимо оптимальности работы приложения есть еще оптимальность кода. И в их подходах она всё-таки выше.

Не знаю, я бы сейчас делал, как описал выше:
- для простых случаев orm
- для сложных - билдер
- для исключительных - ручками.
 

fixxxer

К.О.
Партнер клуба
Активист, если бы за дурака держал - начал бы разжевывать, а ты не дурак, потому наводящими вопросами.

Ты же согласен, что, в целом, чем меньше запросов в базу, тем лучше (опустим что это не всегда так)? Окей, идеальный случай - 0 запросов.

Давай представь себе крайний случай - все, что нужно для рендеринга страницы, выдергивается одним сколь угодно сложным sql-запросом. Теперь представим себе другой крайний случай - на каждый entity делается по запросу по primary key. Теперь навешиваем на это дело внешний key-value кэш (скажем memcached), прикидываем как будем вычислять cache keys, и считаем hit ratio в обоих случаях ;)

Разумеется, истина посередине.
 

fixxxer

К.О.
Партнер клуба
Фанат, я бы не стал прям так разделять orm и builder, любой современный orm это настройка над билдером, и билдер при этом доступен и в методах модели.

Другое дело, что всё говно :) Не, eloquent неплох, но тоже шаг в сторону и DB::raw. Мне очень нравится питоновский SqlAlchemy, дающий практически всю полноту всех диалектов sql, но на php его "втупую" фиг портируешь - уж слишком там питоновская специфика используется на каждом шагу.
 

Активист

Активист
Команда форума
Активист, если бы за дурака держал - начал бы разжевывать, а ты не дурак, потому наводящими вопросами.

Ты же согласен, что, в целом, чем меньше запросов в базу, тем лучше (опустим что это не всегда так)? Окей, идеальный случай - 0 запросов.

Давай представь себе крайний случай - все, что нужно для рендеринга страницы, выдергивается одним сколь угодно сложным sql-запросом. Теперь представим себе другой крайний случай - на каждый entity делается по запросу по primary key. Теперь навешиваем на это дело внешний key-value кэш (скажем memcached), прикидываем как будем вычислять cache keys, и считаем hit ratio в обоих случаях ;)

Разумеется, истина посередине.
Спору нет, нужна золотая середина. Все зависит от частного случая, поскольку бывает что база 10 гб помещается (и будет помещаться) вся в ОЗУ, да и SQL отлично работает с cache, нужен ли memcached ?
 

Absinthe

жожо
fixxxer, кстати, прелесть в том, код на laravel джуников от мидлов пришедших не с symfony практически не отличается, главное жёсткий ревью и рефакторинг на первых этапах, чтобы стиль вырабатывали.
а вот скоупы я не люблю из-за проблем с автокомплитом, а мучаться с phpdoc не хочется, предпочитаю обычные методы.
У всех фреймворков не отличается, если делать ревью.
 
Сверху