S
S
Sergey Brazhnikov2019-07-30 08:57:09
Yii
Sergey Brazhnikov, 2019-07-30 08:57:09

Yii2 SQL Query Builder. Same query but different fetch results?

Colleagues help out PLYYYZZ!
For two weeks now I can not defeat one oddity in Yii2.
In general, I create a query through the Yii2 SQL query builder (I mean, I build a SQL query using Yii2 methods, not createCommand)
I create the following query:

$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();

The result of such a query is as follows:
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)

Not what is needed. And it is necessary to receive 6 lines!!!
Next, using the command:
This command shows the SQL string built through the Yii Query Builder (which is at the very top of my question).
$obj->createCommand()->sql;
I get this SQL string:
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

Next, I use this SQL string in "createCommand" like this:
$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 query is the SAME, only written manually, through "createCommand", BUT!!!
The sampling result is different. Here's one:
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)

Received 6 lines, what you need.
I want to note that in the second result, where 6 rows are received,
in the terms Array, ID 3 and 4 are repeated,
and in the result where 4 rows are received,
these repetitions are not present.
Obtained via Yii builder
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)

Obtained via "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)

Why do the same query produce different results?
If we remove the line from the query: Then through the query builder we get lines where no ID is repeated.
->offset(1)->limit(6)
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,

And if through "createCommand", then as a result of the selection there are duplicate IDs.
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,

Such result of sampling also is necessary to me!!!
Maybe when collecting information, Yii, collecting the object of the result of the selection, somehow overwrites duplicate IDs, thereby overwriting the same ones with duplicates?
But the problem is that I need to use NOT "createCommand"
but the query builder via Yii methods.
Help please .. The whole brain has already broken.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
S
Sergey Brazhnikov, 2019-08-05
@shtepsil

The answer came to my mind.
In general, the decision was all 2.5 weeks before my eyes.
In general, when the Yii2 Query Builder generated a select object with data from a linked table,
several rows were selected from the linked table and attached to each row of the main table.
those. for example, 3 rows from a linked table were attached to the first row of the main table, for example, 7 rows from a linked table were attached to the second row of the main table, and so on.
A simple query returned a selection with duplicate main table IDs, like this:

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)

And the Yii2 query builder gave this:
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)

Doubled IDs 3 and 4 have been overwritten.
In general, my goal was this - according to the filter conditions, select data from the main table and attach one row from the linked table to each selected row.
And display 6 elements on the page, sorted by the field (price) from the linked table.
And since several rows from the linked table were attached to each row of the main table, then in the selection object, the IDs of the main table were not unique, they were repeated.
It turned out this:
// Строка основной таблицы
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  ),

The final sample object looked like this:
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)

those. first, I formed a selection object by LIMIT 6, then from these six elements, Yii2 overwrote the repeated IDs of the main table, and in the final selection, less than 6 elements fell on the page.
Those. Yii2 formed the selection object so that in the final selection - IDs of the main table were not repeated, thereby reducing the number of rows less than LIMIT 6.
There is no way to do this BEFORE LIMIT!
Solution:
I added
->distinct() to the main query
and replaced ->join() with ->joinWith()
$obj = Customers::find()
    ->select(['customers.*', 'products.price'])
    ->distinct()
    ->joinWith('product')
    ->orderBy(['products.price' => SORT_DESC])
    ->offset(1)->limit(6)
    ->asArray();

$obj->all();

Thus, Yii2 formed the final object I needed!
6 elements from the main table got into the selection object, then ONE element was attached to each row of the main table through the hasOne() connection!
And I got the 6 elements I needed without duplicate IDs of the main table:
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)

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question