Trying to sort rows from lowest to highest continually, or rather repeatedly using MySql. For example: if a column has the following values: 1,3,2,4,2,1,4,3,5, then it should end up like this 1,2,3,4,5,1,2,3,4. So it goes from lowest to highest, but tries to sort again from lowest to highest multiple times.
For large sets, the semi-JOIN operation (the approach in the answer from Strawberry) may create an unwieldy resultset. (Then again, MySQL may have some optimizations in there.)
Another alternative available in MySQL is to use "user variables", like this:
SELECT r.mycol
FROM ( SELECT IF(q.mycol=#prev,#seq := #seq + 1,#seq := 1) AS seq
, #prev := q.mycol AS mycol
FROM mytable q
JOIN (SELECT #prev := NULL, #seq := NULL) p
ORDER BY q.mycol
) r
ORDER BY r.seq, r.mycol
Let me unpack that a bit, and explain what it's doing, starting with the inner query (inline view aliased as r.) We're telling MySQL to get the column (mycol) containing the values you want to sort, e.g. 1,3,2,4,2,1,4,3,5 and we're telling MySQL to order these in ascending sequence: 1,1,2,2,3,3,4,4,5.
The "trick" now is to use a MySQL user variable, so that we can compare the mycol value from the current row to the mycol value from the previous row, and we use that to assign an ascending sequence value, from 1..n on each distinct value.
With that resultset, we can tell MySQL to order by that assigned sequence value first, and then by the value from mycol.
If there is a unique id on each row, then a correlated subquery can be used to get an equivalent result (although this approach is very unlikely to perform as well on large sets)
SELECT r.mycol
FROM mytable r
ORDER
BY ( SELECT COUNT(1)
FROM mytable q
WHERE q.mycol = r.mycol
AND q.id <= r.id
)
, r.mycol
Here's the setup for the test case:
CREATE TABLE mytable (id INT, mycol INT);
INSERT INTO mytable (id, mycol) VALUES
(1,1),(2,3),(3,2),(4,4),(5,2),(6,1),(7,4),(8,3),(9,5);
there is no order two time just this
ORDER BY column ASC
Let's pretend that the PK is a unique integer. Consider the following...
CREATE TABLE seq(id INT NOT NULL PRIMARY KEY,val INT);
INSERT INTO seq VALUES (8,1),(4,2),(1,3),(2,4),(7,0),(6,1),(3,2),(5,5);
SELECT * FROM seq ORDER BY val;
+----+------+
| id | val |
+----+------+
| 7 | 0 |
| 6 | 1 |
| 8 | 1 |
| 3 | 2 |
| 4 | 2 |
| 1 | 3 |
| 2 | 4 |
| 5 | 5 |
+----+------+
SELECT x.*
, COUNT(*) rank
FROM seq x
JOIN seq y
ON y.val = x.val
AND y.id <= x.id
GROUP
BY id
ORDER
BY rank
, val;
+----+------+------+
| id | val | rank |
+----+------+------+
| 7 | 0 | 1 |
| 6 | 1 | 1 |
| 3 | 2 | 1 |
| 1 | 3 | 1 |
| 2 | 4 | 1 |
| 5 | 5 | 1 |
| 8 | 1 | 2 |
| 4 | 2 | 2 |
+----+------+------+
Related
I am trying to create a query that can get First Non-null value for selected columns from the table.
I cant fire multiple queries and union it per every column as I have so many columns. I tried to create a query using answers form some SO questions. but it doesn't work for me.
Example Table
| orders| id | default_address |
|-------|------|-----------------|
| 1 | 1 | null |
| 2 | null | null |
| 3 | 2 | 3 |
Expected Result
| id | default_address |
|----|-----------------|
| 1 | 3 |
Another Example Table
| orders| id | default_address |
|-------|------|-----------------|
| 1 | 1 | null |
| 2 | null | 5 |
| 3 | 2 | 3 |
Expected Result
| id | default_address |
|----|-----------------|
| 1 | 5 |
What i tried is here
http://sqlfiddle.com/#!9/84a5c/1
http://sqlfiddle.com/#!9/574481/2
Here is one way to do this using analytic functions, assuming you are using MySQL 8+:
WITH cte AS (
SELECT *,
COUNT(id) OVER (ORDER BY orders) cnt_id,
COUNT(default_address) OVER (ORDER BY orders) cnt_addr
FROM yourTable
)
SELECT
MAX(CASE WHEN cnt_id = 1 THEN id END) AS id,
MAX(CASE WHEN cnt_addr = 1 THEN default_address END) AS default_address
FROM cte;
This assumes that there actually exist a third column orders which generates the ordering shown in your sample data.
This trick works because the COUNT() function by default only counts non NULL values. So, used a window function with the ordering given by the orders column, it would only equal 1 at the first non NULL value.
For earlier MySQL versions:
SELECT
MAX(id) AS id,
MAX(default_address) AS default_address
FROM
(
SELECT
CASE WHEN (SELECT COUNT(t2.id) FROM yourTable t2
WHERE t2.orders <= t1.orders) = 1 THEN id END AS id,
CASE WHEN (SELECT COUNT(t2.default_address) FROM yourTable t2
WHERE t2.orders <= t1.orders) = 1 THEN default_address END AS default_address
FROM yourTable t1
) t;
I have a table like this
|num|id|name|prj|
| 1 | 1|abc | 1 |
| 2 | 1|efg | 1 |
| 3 | 1|cde | 1 |
| 4 | 2|zzz | 1 |
I want to run a query like this:
SELECT * FROM table WHERE prj=1 ORDER BY name
but printing out repeated values only once. I want to keep all the rows and I would like to do this at database level and not on the presentation layer (I know how to do it in php).
Desired result is
|num|id|name|prj|
| 1 | 1|abc | 1 |
| 3 | |cde | 1 |
| 2 | |efg | 1 |
| 4 | 2|zzz | 1 |
any hint on where to start from to build that query?
Use a session variable to test if the previous ID is the same as the current ID:
SELECT num, IF(#lastid = id, '', #lastid := id) AS id, name, prj
FROM table
CROSS JOIN (SELECT #lastid := null) x
ORDER BY table.id, name
DEMO
Note that you need to qualify table.id, because ORDER BY defaults to using the alias from the SELECT list if it's the same as a table column, and that would order the empty fields first.
mysql question... So I would like to auto populate a number, but I would like it to be based on another field. I'm not sure how to explain it other than using this example:
Field 1 | Field 2 (auto populate)
1 | 1
2 | 1
2 | 2
2 | 3
3 | 1
3 | 2
2 | 4
2 | 5
Is there a mysql function expression that can be added to the column?
I know I could do this in my Python script that is feeding the values, but I have to assume it will be faster if the function is baked into mysql.
UPDATE:
Let me add some clarification...
Field 1 will get filled with a value from a script.
There will be multiple entries of Field 1 that are the same number.
I would like Field 2 to auto populate to have a unique number WITHIN that Field 1 number.
You can just store the field1 and generate field2 as increasing integer value within each field1 using user variables like this:
select
t.field1,
#rn := if(field1 = #f1, #rn + 1, if(#fi := field1, 1, 1)) field2
from (
select
t.*
from your_table t
order by field1 --very important
) t cross join (select #rn := 0, #f1 := -1) t2
You could use a trigger which is a function which automatically runs on certain conditions.
Here is the manual http://dev.mysql.com/doc/refman/5.7/en/trigger-syntax.html.
You could for example use a AFTER UPDATE or AFTER INSERT trigger for this purpose.
Setting aside the usual caveats relating to MyISAM tables...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(field1 INT NOT NULL
,field2 INT NOT NULL AUTO_INCREMENT
,PRIMARY KEY(field1,field2)
) ENGINE = MyISAM;
INSERT INTO my_table (field1) VALUES
(1),(2),(2),(2),(3),(3),(2),(2);
SELECT * FROM my_table;
+--------+--------+
| field1 | field2 |
+--------+--------+
| 1 | 1 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 2 | 4 |
| 2 | 5 |
| 3 | 1 |
| 3 | 2 |
+--------+--------+
..but I would question whether there's really any sense storing this information at all...
...hence, a more robust approach...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(field1 INT NOT NULL
,field2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY
) ENGINE = InnoDB;
INSERT INTO my_table (field1) VALUES
(1),(2),(2),(2),(3),(3),(2),(2);
SELECT x.field1
, CASE WHEN #prev = field1 THEN #i:=#i+1 ELSE #i:=1 END i
, #prev:=field1 prev
FROM my_table x
, (SELECT #prev:=null,#i:=0) vars
ORDER
BY field1
, field2;
+--------+------+------+
| field1 | i | prev |
+--------+------+------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 2 | 2 | 2 |
| 2 | 3 | 2 |
| 2 | 4 | 2 |
| 2 | 5 | 2 |
| 3 | 1 | 3 |
| 3 | 2 | 3 |
+--------+------+------+
You probably want to create a trigger. Something like the following:
CREATE TRIGGER `populate_field_2` BEFORE INSERT ON `table_name` FOR EACH ROW BEGIN
DECLARE _new_val INT DEFAULT 0;
SELECT MAX(field_2)+1 INTO _new_val FROM 'table_name' WHERE field_1 = NEW.field_1;
SET NEW.field_2 = _new_val;
END
This would populate field_2 each time you created a new row. You can update the logic to create whatever value for field2 you need.
Table Mytable1
Id | Actual
1 ! 10020
2 | 12203
3 | 12312
4 | 12453
5 | 13211
6 | 12838
7 | 10l29
Using the following syntax:
SELECT AVG(Actual), CEIL((#rank:=#rank+1)/3) AS rank FROM mytable1 Group BY rank;
Produces the following type of result:
| AVG(Actual) | rank |
+-------------+------+
| 12835.5455 | 1 |
| 12523.1818 | 2 |
| 12343.3636 | 3 |
I would like to take AVG(Actual) column and UPDATE a second existing table Mytable2
Id | Predict |
1 | 11133
2 | 12312
3 | 13221
I would like to get the following where the Actual value matches the ID as RANK
Id | Predict | Actual
1 | 11133 | 12835.5455
2 | 12312 | 12523.1818
3 | 13221 | 12343.3636
IMPORTANT REQUIREMENT
I need to set an offset much like the following syntax:
SELECT #rank := #rank + 1 AS Id , Mytable2.Actual FROM Mytable LIMIT 3 OFFSET 4);
PLEASE NOTE THE AVERAGE NUMBER ARE MADE UP IN EXAMPLES
you can join your existing query in the UPDATE statement
UPDATE Table2 T2
JOIN (
SELECT AVG(Actual) as AverageValue,
CEIL((#rank:=#rank+1)/3) AS rank
FROM Table1, (select #rank:=0) t
Group BY rank )T1
on T2.id = T1.rank
SET Actual = T1.AverageValue
Let's say I have a mySQL table whose structure is like this:
mysql> select * from things_with_stuff;
+----+---------+----------+
| id | counter | quantity |
+----+---------+----------+
| 1 | 101 | 1 |
| 2 | 102 | 2 |
| 3 | 103 | 3 |
+----+---------+----------+
My goal is to "expand" the table so I end up with something like:
mysql> select * from stuff;
+----+---------+
| id | counter |
+----+---------+
| 1 | 101 |
| 2 | 102 |
| 3 | 102 |
| 4 | 103 |
| 5 | 103 |
| 6 | 103 |
+----+---------+
And I want to do this "expansion" using only mysql. Note that I end up with a row per quantity and per counter. Any suggestions? A stored procedure is not an option here (I know they offer while loops).
Thanks!
The following will do the trick as long as some_large_table has a length greater than or equal to the largest quantity in things_with_stuff. For example, let some_large_table be a big fact table in a data warehouse.
SELECT #kn:=#kn+1 AS id, counter
FROM (SELECT #kn:=0) k, things_with_stuff ts
INNER JOIN (
SELECT #rn:=#rn+1 AS num
FROM (SELECT #rn:=0) t, some_large_table
) nums ON num <= ts.quantity;
Assuming there is a maximum value for quantity, you could do:
INSERT INTO things SELECT counter FROM things_with_stuff WHERE quantity > 0;
INSERT INTO things SELECT counter FROM things_with_stuff WHERE quantity > 1;
INSERT INTO things SELECT counter FROM things_with_stuff WHERE quantity > 2;
--... and so on until the max.
It's a bit of a hack but it should do the job.
If the ordering is important you could do a clean up afterwards.
I have sometimes in databases a table named num (number) with a single column i, filled with all integers from 1 to 1000000. It's not hard to make such a table and populate it.
Then you could use this if stuff.id is auto incremented:
INSERT INTO stuff
( counter )
SELECT ts.counter
FROM things_with_stuff AS ts
JOIN num
ON num.i <= ts.quantity