📜 ⬆️ ⬇️

Automatic sorting of rows with auxiliary ordinal column - using MySQL

sort
Recently I had to perform a scam from the database, which, at first glance, seems completely impossible with MySQL. Before my eyes I had a table of products, the sorting of which is carried out by the auxiliary column `order_num` ('order number'): it allows you to set the manual sorting of goods.
But it was necessary to automatically fill this column so that the goods were sorted by name: that is, with a number of restrictions, change the ʻorder_num` column in the entire table. I really wanted to do without MySQL using any additional tools, and the task was solved :)

The complexity of the task is also in the fact that MySQL does not know how to make UPDATE tables and at the same time read from it: in MyISAM, the table is exclusively locked when writing and there is no possibility to read in the subquery.


Task


There is a table of goods of an ordinary online store:
CREATE TABLE `products` (
`product_id` INT NOT NULL PRIMARY KEY COMMENT 'id ' ,
`model` VARCHAR (255) NOT NULL COMMENT ' ' ,
`order_num` INT NOT NULL COMMENT ' ' ,
) COMMENT '' ;


* This source code was highlighted with Source Code Highlighter .

Initially the goods are sorted the devil knows how. We need to sort them in the database by `model`: that is, so that the sorting column` order_num` of goods increases in parallel to the sorting by `model`.
As a result, the table should look like this:
`product_id``model``order_num`
70Ahhten
tenBbb20
thirtyIbvthirty
20Yy70

The engine of this store is buggy if several products have `order_num` the same. Sort items by `model` in the output can not: lost the ability to sort manually.
')

Idea



In the sort column `order_num` there should be the same product id` product_id`, but in the correct order. So we will precisely avoid duplication of sorting values, and “ORDER BY` order_num` ”will display products sorted by` model` - that is, correct :)

I note that in the particular case, goods of one category were sorted, therefore it is impossible to use ordinal 0,1,2, ... - only the id of the product is suitable for use.

So, there is the task of distributing the id array of goods on the ʻorder_num` column.

α-Solution


First you need to create a temporary table `by_model`, in which we place the` product_id` of goods sorted by `model`. In addition, you need to add one more column: row counter. The result will look like this:
`rowid``product_id`
one70
2ten
3thirty
four20

This is almost trivial. It is only necessary to add a column `rowid` with line numbers:
SET @n_row := 0; #
CREATE TEMPORARY TABLE `by_model`
SELECT @n_row:=@n_row+1 AS `rowid`, `product_id`
FROM `products`
ORDER BY `model` ASC ;


* This source code was highlighted with Source Code Highlighter .


Similarly, another temporary table `by_prod` is created: a list of all` product_id`, sorted in ascending order. Similarly, a column counter is added:
`rowid``ord`
oneten
220
3thirty
four70

SET @n_ord := 0;
CREATE TEMPORARY TABLE `by_model`
SELECT @n_ord:=@n_ord+1 AS `rowid`, `product_id` AS `ord`
FROM `products`
ORDER BY `product_id` ASC ;


* This source code was highlighted with Source Code Highlighter .


Let us try to declare these two tables on the common column `rowid`:
CREATE TEMPORARY TABLE `products-sort`
SELECT `product_id`, `ord`
FROM `by_model` NATURAL JOIN `by_prod`;


* This source code was highlighted with Source Code Highlighter .


And we get the following data set:
`product_id``order_num`
70ten
ten20
thirtythirty
2070


It becomes obvious that if you assign a serial number from the second column to products with id's from the first column of the `products-sort` table, then the goal will be reached :)
Like this:
UPDATE `products` NATURAL JOIN `products-sort` SET `order_num`=`ord`;

* This source code was highlighted with Source Code Highlighter .


Final Implementation & Optimization



Instead of creating three temporary tables, you can get by with only `products-sort`, and place the rest as a subquery inside the WHERE clause. And this is how it will look like, collected in a heap:
#
SET @n_row := 0, @n_ord := 0;
;;;
# , `ord` `product_id`. .
CREATE TEMPORARY TABLE `products-sort`
SELECT `product_id`, `ord` FROM
# : + id , `model`
( SELECT @n_row:=@n_row+1 AS `rowid`, `product_id`
FROM `products`
ORDER BY `model` ASC
) AS `by_model` # :
# : `rowid`
NATURAL JOIN
# : + id , id
( SELECT @n_ord:=@n_ord+1 AS `rowid`, `product_id` AS `ord`
FROM `products`
ORDER BY `product_id` ASC
) AS `by_prod`;
;;;
# `products-sort`
UPDATE `products` NATURAL JOIN `products-sort` SET `order_num`=`ord`;


* This source code was highlighted with Source Code Highlighter .


Only three requests, and pure MySQL.
In this case it is not necessary to take care of the speed of execution, but nevertheless it is very decent :)

Source: https://habr.com/ru/post/81394/


All Articles