Упорядочить по вложенному запросу SQL - PullRequest
2 голосов
/ 04 мая 2020

Я хотел бы получить вашу помощь по поводу странного факта, с которым я столкнулся при использовании инструкции «упорядочить по» в MySQL

Давайте посмотрим на следующую таблицу:

CREATE TABLE `test_nested_order_by` (
  `id` int(11) NOT NULL,
  `timestamp` int(11) NOT NULL COMMENT 'Timestamp',
  `index_continuity_month` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

insert into test_nested_order_by (id,timestamp,index_continuity_month) values (1,1583772141,0) ;
insert into test_nested_order_by (id,timestamp,index_continuity_month) values (1,1583708400,0) ;
insert into test_nested_order_by (id,timestamp,index_continuity_month) values (5,1583708400,0) ;
insert into test_nested_order_by (id,timestamp,index_continuity_month) values (4,1583708400,0) ;
insert into test_nested_order_by (id,timestamp,index_continuity_month) values (5,1583794800,0) ;
insert into test_nested_order_by (id,timestamp,index_continuity_month) values (4,1583794800,0) ;

Как видите, значение 0 установлено для столбца index_continuity_month в каждой строке.

Теперь я хотел бы установить это значение следующим образом: уникальное значение, которое будет увеличиваться в порядке возрастания идентификатора и отметки времени столбцы. Таблица будет выглядеть так:

mysql>  select * from test_nested_order_by :

+----+------------+------------------------+
| id | timestamp  | index_continuity_month |
+----+------------+------------------------+
|  1 | 1583772141 |                      2 |
|  1 | 1583708400 |                      1 |
|  5 | 1583708400 |                      5 |
|  4 | 1583708400 |                      3 |
|  5 | 1583794800 |                      6 |
|  4 | 1583794800 |                      4 |
+----+------------+------------------------+
6 rows in set (0,00 sec)

Или, если хотите:

mysql> select * from test_nested_order_by order by id,timestamp ;
+----+------------+------------------------+
| id | timestamp  | index_continuity_month |
+----+------------+------------------------+
|  1 | 1583708400 |                      1 |
|  1 | 1583772141 |                      2 |
|  4 | 1583708400 |                      3 |
|  4 | 1583794800 |                      4 |
|  5 | 1583708400 |                      5 |
|  5 | 1583794800 |                      6 |
+----+------------+------------------------+

Для этого я использую этот запрос:

UPDATE  test_nested_order_by t1,
(SELECT
id,
timestamp,
@last_continuity_month := @last_continuity_month +1, @last_continuity_month AS index_continuity_month

FROM test_nested_order_by, (

SELECT @last_continuity_month :=0
)SQLVars
ORDER BY id , timestamp) t2

SET t1.index_continuity_month = t2.index_continuity_month

WHERE t1.id = t2.id
      AND t1.timestamp = t2.timestamp;

Но когда я посмотрите результат, похоже, не работает:

mysql> select * from test_nested_order_by order by id,timestamp ;
+----+------------+------------------------+
| id | timestamp  | index_continuity_month |
+----+------------+------------------------+
|  1 | 1583708400 |                      2 |
|  1 | 1583772141 |                      1 |
|  4 | 1583708400 |                      4 |
|  4 | 1583794800 |                      6 |
|  5 | 1583708400 |                      3 |
|  5 | 1583794800 |                      5 |
+----+------------+------------------------+
6 rows in set (0,00 sec)

Я подозреваю, что инструкция "заказ по" не учитывается (и если я удалю ее из запроса, то результат будет точно таким то же самое).

Мы можем заметить, что увеличение index_continuity_month выполняется не в порядке возрастания столбцов id и timestamp, а в том порядке, в котором строки были вставлены в таблицу.

НО, если я запускаю только вложенную часть запроса:

SELECT
id,
timestamp,
@last_continuity_month := @last_continuity_month +1, @last_continuity_month AS index_continuity_month

FROM test_nested_order_by, (

SELECT @last_continuity_month :=0
)SQLVars
ORDER BY id , timestamp;

+----+------------+-----------------------------------------------------+------------------------+
| id | timestamp  | @last_continuity_month := @last_continuity_month +1 | index_continuity_month |
+----+------------+-----------------------------------------------------+------------------------+
|  1 | 1583708400 |                                                   1 |                      1 |
|  1 | 1583772141 |                                                   2 |                      2 |
|  4 | 1583708400 |                                                   3 |                      3 |
|  4 | 1583794800 |                                                   4 |                      4 |
|  5 | 1583708400 |                                                   5 |                      5 |
|  5 | 1583794800 |                                                   6 |                      6 |
+----+------------+-----------------------------------------------------+------------------------+

Результат хороший!

Есть ли кто-нибудь, кто мог бы мне объяснить, в чем проблема? И, более конкретно, почему запрос SQL не ведет себя так, когда он вложен в другой запрос?

Большое спасибо!

Ответы [ 2 ]

1 голос
/ 05 мая 2020

Для начала: если вы используете MySQL 8.0, это очень просто делается с помощью row_number():

update test_nested_order_by t
inner join (
    select 
        t.*, 
        row_number() over(order by id, timestamp) rn 
    from test_nested_order_by t
) t1 on t1.id = t.id and t1.timestamp = t.timestamp
set t.index_continuity_month  = t1.rn 

В более ранних версиях пользовательские переменные действительно являются решением; однако использовать их с order by довольно сложно. Это потому, что order by обычно обрабатывается после предложения select, поэтому нет гарантии, что «правильное» значение будет присвоено каждой строке. Чтобы обойти это, вам нужно сначала упорядочить таблицу в подзапросе, а затем установить переменную:

update test_nested_order_by t
inner join (
    select t.*, @rn := @rn + 1 rn
    from (select * from test_nested_order_by order by id, timestamp) t
    cross join (select @rn := 0) x
) t1 on t1.id = t.id and t1.timestamp = t.timestamp
set t.index_continuity_month  = t1.rn 

Demo on DB Fiddle - оба update запросов дают следующие результаты:

select * from test_nested_order_by order by id, timestamp
id |  timestamp | index_continuity_month
-: | ---------: | ---------------------:
 1 | 1583708400 |                      1
 1 | 1583772141 |                      2
 4 | 1583708400 |                      3
 4 | 1583794800 |                      4
 5 | 1583708400 |                      5
 5 | 1583794800 |                      6
1 голос
/ 04 мая 2020

Результат подзапроса - неупорядоченный набор строк. Поэтому оптимизатор MySQL может игнорировать ORDER BY в подзапросе.

...