@shtepsil
IT специалист в вэбразработке

Yii2 построитель SQL запросов. Один и тот же запрос, но разные результаты выборки?

Коллеги выручайте ПЛЫЫЫЗЗ!
Уже две недели не могу победить одну странность в Yii2.

В общем создаю запрос через построитель SQL запросов Yii2 (имею ввиду - строю SQL запрос используя методы Yii2, а не createCommand)

Создаю вот такой запрос:
$obj = Customers::find()
    ->select(['customers.*', 'ps.price'])
    ->join('LEFT JOIN', 'product ps', 'ps.customer_id = customers.id')
    ->orderBy(['ps.price' => SORT_DESC])
    ->offset(1)->limit(6)
    ->asArray();

$obj->all();


Результат выборки такого запроса вот такой:
0 => Array (id=>3, name=>user3, price=>43000), 
1 => Array (id=>4, name=>user4, price=>42099), 
2 => Array (id=>11, name=>user11, price=>25783), 
3 => Array (id=>7, name=>user7, price=>25783)


Не то что нужно. А нужно получить 6 строк!!!

Далее, используя команду:
Эта команда показывает SQL строку, построенную через построитель запросов Yii (который в самом верху моего вопроса).
$obj->createCommand()->sql;

Получаю вот такую SQL строку:
SELECT `customers`.*, `ps`.`price` FROM `customers` LEFT JOIN `product` `ps` ON ps.customer_id = customers.id ORDER BY `ps`.`price` DESC LIMIT 6 OFFSET 2


Далее, использую эту SQL строку в "createCommand" вот так:
$pns_command = Yii::$app->db->createCommand('
        SELECT `customers`.*, `ps`.`price` FROM `customers` LEFT JOIN `product` `ps` ON ps.customer_id = customers.id ORDER BY `ps`.`price` DESC LIMIT 6 OFFSET 1
        ');

            try {
                $pns = $pns_command->queryAll();
            } catch (\Exception $e) {
                d::pe($e->getMessage());
            }


SQL запрос ОДИН И ТОТ ЖЕ, только написан вручную, через "createCommand", НО!!!
Результат выборки уже другой. Вот такой:

0 => Array (id=>7, name=>user7, price=>43087), 
1 => Array (id=>3, name=>user3, price=>43000), 
2 => Array (id=>4, name=>user4, price=>42099), 
3 => Array (id=>4, name=>user4, price=>32999), 
4 => Array (id=>3, name=>user3, price=>31000), 
5 => Array (d=>11, name=>user11, price=>25783)


Получено 6 строк, то что нужно.

Хочу заметить то, что во втором результате, где получено 6 строк,
в сроках Array, ID 3 и 4 повторяются,
а в том результате, где получено 4 строки,
этих повторений нету.

Получено через построитель Yii
0 => Array (id=>3, name=>user3, price=>43000), 
1 => Array (id=>4, name=>user4, price=>42099), 
2 => Array (id=>11, name=>user11, price=>25783), 
3 => Array (id=>7, name=>user7, price=>25783)

Получено через "createCommand"
0 => Array (id=>7, name=>user7, price=>43087), 
1 => Array (id=>3, name=>user3, price=>43000), 
2 => Array (id=>4, name=>user4, price=>42099), 
3 => Array (id=>4, name=>user4, price=>32999), 
4 => Array (id=>3, name=>user3, price=>31000), 
5 => Array (d=>11, name=>user11, price=>25783)


Почему в одном и том же запросе получаются разные результаты?

Если из запроса убрать строку:
->offset(1)->limit(6)
То через построитель запросов получаем строки, где ни один ID не повторяется.
0 => Array (id=>11, name=>user11, price=>43087, 
1 => Array (id=>7, name=>user7, price=>43087, 
2 => Array (id=>3, name=>user3, price=>43000, 
3 => Array (id=>4, name=>user4, price=>42099, 
4 => Array (id=>9, name=>user9, price=>22999, 
5 => Array (id=>5, name=>user5, price=>22999, 
6 => Array (id=>2, name=>user2, price=>9001, 
7 => Array (id=>10, name=>user10, price=>7000, 
8 => Array (id=>6, name=>user6, price=>7000, 
9 => Array (id=>12, name=>user12, price=>6000, 
10 => Array (id=>8, name=>user8, price=>6000, 
11 => Array (id=>1, name=>user1, price=>3401,

А если через "createCommand", то в результате выборки есть повторяющиеся ID.
0 => Array (id=>11, name=>user11, price=>43087, 
1 => Array (id=>7, name=>user7, price=>43087, 
2 => Array (id=>3, name=>user3, price=>43000, 
3 => Array (id=>4, name=>user4, price=>42099, 
4 => Array (id=>4, name=>user4, price=>32999, 
5 => Array (id=>3, name=>user3, price=>31000, 
6 => Array (id=>11, name=>user11, price=>25783, 
7 => Array (id=>7, name=>user7, price=>25783, 
8 => Array (id=>11, name=>user11, price=>23000, 
9 => Array (id=>7, name=>user7, price=>23000, 
10 => Array (id=>9, name=>user9, price=>22999, 
11 => Array (id=>5, name=>user5, price=>22999, 
12 => Array (id=>4, name=>user4, price=>21008, 
13 => Array (id=>11, name=>user11, price=>21000, 
14 => Array (id=>7, name=>user7, price=>21000, 
15 => Array (id=>3, name=>user3, price=>21000, 
16 => Array (id=>4, name=>user4, price=>19000, 
17 => Array (id=>4, name=>user4, price=>15000, 
18 => Array (id=>9, name=>user9, price=>12888, 
19 => Array (id=>5, name=>user5, price=>12888, 
20 => Array (id=>2, name=>user2, price=>9001, 
21 => Array (id=>3, name=>user3, price=>8329, 
22 => Array (id=>10, name=>user10, price=>7000, 
23 => Array (id=>6, name=>user6, price=>7000, 
24 => Array (id=>10, name=>user10, price=>6000, 
25 => Array (id=>6, name=>user6, price=>6000, 
26 => Array (id=>12, name=>user12, price=>6000, 
27 => Array (id=>8, name=>user8, price=>6000, 
28 => Array (id=>9, name=>user9, price=>5912, 
29 => Array (id=>5, name=>user5, price=>5912, 
30 => Array (id=>10, name=>user10, price=>4500, 
31 => Array (id=>6, name=>user6, price=>4500, 
32 => Array (id=>10, name=>user10, price=>4399, 
33 => Array (id=>6, name=>user6, price=>4399, 
34 => Array (id=>11, name=>user11, price=>3900, 
35 => Array (id=>6, name=>user6, price=>3900, 
36 => Array (id=>1, name=>user1, price=>3401, 
37 => Array (id=>12, name=>user12, price=>2358, 
38 => Array (id=>7, name=>user7, price=>2358, 
39 => Array (id=>3, name=>user3, price=>2000, 
40 => Array (id=>1, name=>user1, price=>1500, 
41 => Array (id=>12, name=>user12, price=>1388, 
42 => Array (id=>8, name=>user8, price=>1388, 
43 => Array (id=>1, name=>user1, price=>1200, 
44 => Array (id=>12, name=>user12, price=>1200, 
45 => Array (id=>8, name=>user8, price=>1200, 
46 => Array (id=>2, name=>user2, price=>900, 
47 => Array (id=>1, name=>user1, price=>800, 
48 => Array (id=>12, name=>user12, price=>650, 
49 => Array (id=>8, name=>user8, price=>650, 
50 => Array (id=>12, name=>user12, price=>500, 
51 => Array (id=>8, name=>user8, price=>500, 
52 => Array (id=>9, name=>user9, price=>399, 
53 => Array (id=>5, name=>user5, price=>399, 
54 => Array (id=>2, name=>user2, price=>330, 
55 => Array (id=>1, name=>user1, price=>300, 
56 => Array (id=>2, name=>user2, price=>290, 
57 => Array (id=>2, name=>user2, price=>251, 
58 => Array (id=>10, name=>user10, price=>200, 
59 => Array (id=>5, name=>user5, price=>200,


Такой результат выборки мне и нужен!!!

Может при сборе информации, Yii собирая объект результата выборки, как то перезаписывает повторяющиеся ID, тем самым затирая одинаковые повторяющимися?

Но проблема в том, что мне нужно использовать НЕ "createCommand",
а именно построитель запросов через методы Yii.

Помогите пожалуйста.. Мозг весь уже сломал.
  • Вопрос задан
  • 367 просмотров
Решения вопроса 1
@shtepsil Автор вопроса
IT специалист в вэбразработке
Ответ сам пришел мне в голову.
В общем то решение все 2,5 недели было у меня перед глазами.

В общем когда построитель запросов Yii2 формировал объект выборки с данными из связной таблицы,
из связной таблицы выбиралось по несколько строк, которые присоединялись к каждой строке основной таблицы.
т.е. например к первой из строк основной таблицы присоединялось 3 строки из связной таблицы, ко второй строке основной таблицы присоединялось к примеру 7 строк из связной таблицы и тд.

Простой запрос выдал выборку с повторяющимися ID основной таблицы, вот так:
0 => Array (id=>7, name=>user7, price=>43087), 
1 => Array (id=>3, name=>user3, price=>43000), 
2 => Array (id=>4, name=>user4, price=>42099), 
3 => Array (id=>4, name=>user4, price=>32999), 
4 => Array (id=>3, name=>user3, price=>31000), 
5 => Array (d=>11, name=>user11, price=>25783)


А построитель запросов Yii2 давал вот это:
0 => Array (id=>3, name=>user3, price=>43000), 
1 => Array (id=>4, name=>user4, price=>42099), 
2 => Array (id=>11, name=>user11, price=>25783), 
3 => Array (id=>7, name=>user7, price=>25783)


Задвоенные ID 3 и 4 были перезаписаны.

Вообще моя цель была такая - по условиям фильтра выбрать данные из основной таблицы и к каждой выбранной строке присоединить по одной строке из связной таблицы.
И вывести на страницу 6 элементов, отсортированных по полю(price) из связной таблицы.

А так как к каждой строке основной таблицы - присоединялось по несколько строк из связной таблицы, то в объекте выборки - ID основной таблицы не были уникальны, они повторялись.
Получалось вот это:
// Строка основной таблицы
0 => Array (id=>7, name=>user7), 
                  // Строки связной таблицы
                  Array( price=>43087 ), 

// Строка основной таблицы
1 => Array (id=>3, name=>user3),
                  // Строки связной таблицы
                  Array(
                           price=>43000
                           price=>31000
                  ), 
// Строка основной таблицы
2 => Array (id=>4, name=>user4, price=>42099), 
                  // Строки связной таблицы
                  Array(
                           price=>42099
                           price=>32999
                  ),
// Строка основной таблицы
3 => Array (d=>11, name=>user11),
                  // Строки связной таблицы
                  Array( price=>25783  ),

Итоговый объект выборки смотрелся так:
0 => Array (id=>7, name=>user7, price=>43087), 
1 => Array (id=>3, name=>user3, price=>43000), 
2 => Array (id=>4, name=>user4, price=>42099), 
3 => Array (id=>4, name=>user4, price=>32999), 
4 => Array (id=>3, name=>user3, price=>31000), 
5 => Array (d=>11, name=>user11, price=>25783)


т.е. сначала у меня формировался объект выборки по LIMIT 6, далее из этих шести элементов - Yii2 переписывал повторяющиеся ID основной таблицы и в итоговой выборке на страницу попадало меньше чем 6 элементов.
Т.е. Yii2 формировал объект выборки так, чтобы в итоговой выборке - ID основной таблицы не повторялись, тем самым уменьшая количество строк меньше чем LIMIT 6.
Сделать это ДО LIMIT возможности нет!

Решение:
В основной запрос я добавил
->distinct()
и ->join() заменил на ->joinWith()

$obj = Customers::find()
    ->select(['customers.*', 'products.price'])
    ->distinct()
    ->joinWith('product')
    ->orderBy(['products.price' => SORT_DESC])
    ->offset(1)->limit(6)
    ->asArray();

$obj->all();


Тем самым Yii2 сформировал нужный мне конечный объект!
В объект выборки попало 6 элементов из основной таблицы, далее через связь hasOne() к каждой строке основной таблицы привязалось по ОДНОМУ элементу!
И я получил нужные мне 6 элементов без повторяющихся ID основной таблицы:
0 => Array (id=>7, name=>user7, price=>43120), 
1 => Array (id=>3, name=>user3, price=>43100), 
2 => Array (id=>9, name=>user9, price=>42099), 
3 => Array (id=>4, name=>user4, price=>32999), 
4 => Array (id=>15, name=>user15, price=>31000), 
5 => Array (d=>11, name=>user11, price=>25783)
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы