SQL Reverse entire column - mysql

is there a way to reverse an entire column?
Example:
ID ColX ColY ColZ
0 001 010 100
1 002 020 200
2 003 030 300
shall be:
ID ColX ColY ColZ
0 003 030 300
1 002 020 200
2 001 010 100
So the Column ID shall be reversed, the record with the last ID shall be the first, the second last the second first and so far.
The newest value has ID = 0 and the oldest ID = n, and this must be exactly reversed, else I cannot insert new records.

You can do it using variables:
SELECT t2.ID, ColX, ColY, ColZ
FROM (SELECT ID, ColX, ColY, ColZ,
#row_number := #row_number + 1 AS rn
FROM mytable
CROSS JOIN (SELECT #row_number := 0) AS var
ORDER BY ID) AS t1
INNER JOIN (
SELECT ID, #rn := #rn + 1 AS rn
FROM mytable
CROSS JOIN (SELECT #rn := 0) AS var
ORDER BY ID DESC) AS t2
ON t1.rn = t2.rn
ORDER BY t2.ID
Demo here
If you want to UPDATE then you can use the above query in an UPDATE statement like this:
UPDATE mytable AS t
INNER JOIN(
SELECT ID, ColX, ColY, ColZ,
#row_number := #row_number + 1 AS rn
FROM mytable
CROSS JOIN (SELECT #row_number := 0) AS var
ORDER BY ID) AS t1 ON t.ID = t1.ID
INNER JOIN (
SELECT ID, #rn := #rn + 1 AS rn
FROM mytable
CROSS JOIN (SELECT #rn := 0) AS var
ORDER BY ID DESC) AS t2 ON t1.rn = t2.rn
SET t.ID = t2.ID
Demo here
The above will work irrespective of the values of ID column.

If you don't have gaps in your ID, then you can use this select query:
SELECT
max_id-ID AS ID,
ColX,
ColY,
ColZ
FROM
mytable CROSS JOIN (SELECT MAX(ID) AS max_id FROM mytable) m
ORDER BY
ID
or this update query (but it will work only if ID is not a primary key):
UPDATE
mytable m1 CROSS JOIN (SELECT MAX(ID) as max_id FROM mytable) m
SET
m1.ID = m.max_id - m1.ID
if it's a primary key you could use this:
UPDATE
mytable m1 CROSS JOIN (SELECT MAX(ID) as max_id FROM mytable) m
INNER JOIN mytable m2 ON m1.ID = m.max_id - m2.ID
SET
m1.ColX = m2.ColX,
m1.ColY = m2.ColY,
m1.ColZ = m2.ColZ
(please see it here)
but if your ID column has gaps (e.g. 0, 1, 2, 5, 6) you need a different approach.

SELECT
#x - t.id AS new_id,
t.*
FROM tab t,
( SELECT #x:= max(id+1) FROM tab ) m;
MariaDB [tmp]> SELECT
-> #x - t.id AS new_id,
-> t.*
-> FROM tab t,
-> ( SELECT #x:= max(id+1) FROM tab ) m;
+--------+----+--------+---------------------+
| new_id | id | action | date |
+--------+----+--------+---------------------+
| 7 | 1 | 2 | 2015-09-24 15:28:30 |
| 6 | 2 | 4 | 2015-09-24 15:29:26 |
| 5 | 3 | 2 | 2015-09-24 15:30:01 |
| 4 | 4 | 3 | 2015-09-24 15:30:55 |
| 3 | 5 | 1 | 2015-09-24 16:07:25 |
| 2 | 6 | 5 | 2015-09-24 16:10:25 |
| 1 | 7 | 4 | 2015-09-24 16:29:26 |
+--------+----+--------+---------------------+
7 rows in set (0.00 sec)

Related

MySQL - group by certain value in the list of rows

I have a list of rows and want to group them by ID and a certain start-value. (Say value=1 in the below-given example)
Group-by value
Set of rows - MySQL & Presto
-----------
ID | value
-----------
A | 1
A | 2
B | 1
B | 2
B | 5
B | 1
B | 2
C | 1
C | 3
C | 4
C | 1
D | 1
D | 8
D | 1
-----------
Expected Output :
-----------
ID | Value
-----------
A | 1,2
B | 1,2,5
B | 1,2
C | 1,3,4
C | 1
D | 1,8
D | 1
-----------
Actual Output :
-----------
ID | Value
-----------
A | 1,2
B | 1,2,5,1,2
C | 1,3,4,1
D | 1,8,1
-----------
Without a real id is really hard. So I introduce rowNumber:
SELECT (#row := #row + 1) AS rowNumber, ID,value
FROM myTable
CROSS JOIN (SELECT #row := 0) AS dummy
Then i add 2 columns with max Value:
SELECT a.*
FROM customTable as a
LEFT OUTER JOIN myTable b ON a.id = b.id AND a.value < b.value
WHERE b.id IS NULL
After this i need to use group_concat(value) and group by.
Group by has 2 condition, id and another custom boolean field:
CASE
WHEN l1.rowNumber <= l2.rowNumber THEN 0
ELSE 1
END
FINAL QUERY:
SELECT ct1.id, group_concat(ct1.value) as Value
FROM (
SELECT (#cnt := #cnt + 1) AS rowNumber, ID, value
FROM myTable
CROSS JOIN (SELECT #cnt := 0) AS dummy
) AS ct1
JOIN (
SELECT a.*
FROM (
SELECT (#row := #row + 1) AS rowNumber, ID, value
FROM myTable
CROSS JOIN (SELECT #row := 0) AS dummy
) AS a
LEFT OUTER JOIN myTable b ON a.id = b.id AND a.value < b.value
WHERE b.id IS NULL
) AS ct2 ON ct2.ID = ct1.id
GROUP BY ct1.id,
CASE
WHEN ct1.rowNumber <= ct2.rowNumber THEN 0
ELSE 1
END
You can test Here.
This only works with MySQL 5.6 or above

SQL statement: how can I pick a value only once from a table to update another table?

I wish to update table1 with any distinct value of table2 matching the same code, no matter which one or the order (value from table2 can't be picked more than once)
+-------------------+ +--------------+
| table1 | | table2 |
+-------------------+ +--------------+
| id | code | value | | code | value |
+----+------+-------+ +------+-------+
| 1 | A | | <-- | A | v1 |
| 2 | A | | | B | v2 |
| 3 | B | | | A | v3 |
+----+------+-------+ | A | v5 |
| A | v6 |
+------+-------+
+-------------------+
| table1 |
+-------------------+
| id | code | value |
+----+------+-------+
| 1 | A | v6 |
| 2 | A | v3 |
| 3 | B | v2 |
+----+------+-------+
How can I write the SQL update statement ?
(MySQL 5.7)
This requires Row_Number() Window function's magic! Unfortunately, your MySQL version is 5.7; so a more verbose solution using User-defined variables follows:
UPDATE
table1 AS t1
JOIN
(SELECT
dt1.id,
IF(#cd1 = dt1.code, #rn1 := #rn1 + 1, 1) AS row_num1,
#cd1 := dt1.code AS code
FROM (SELECT id, code FROM table1 ORDER BY code, id) AS dt1
CROSS JOIN (SELECT #rn1 := 0, #cd1 := '') AS init1
) AS t2
ON t2.id = t1.id
JOIN
(SELECT
IF(#cd2 = dt2.code, #rn2 := #rn2 + 1, 1) AS row_num2,
#cd2 := dt2.code AS code,
dt2.value
FROM (SELECT code, value FROM table2 ORDER BY code) AS dt2
CROSS JOIN (SELECT #rn2 := 0, #cd2 := '') AS init2
) AS t3
ON t3.row_num2 = t2.row_num1 AND
t3.code = t2.code
SET t1.value = t3.value
You can check the explanation of a similar technique in this answer.
The statement provided by Madhur Bhaiya does work if
#rn* are initialized to 1 instead of 0 (otherwise row_num* are numbered 1 twice)
the SELECT from table2 is DISTINCT (because pairs of (code,value) are repeated in table2)
The statement should be
UPDATE
table1 AS t1
JOIN
(SELECT
dt1.id,
IF(#cd1 = dt1.code, #rn1 := #rn1 + 1, 1) AS row_num1,
#cd1 := dt1.code AS code,
FROM (SELECT id, code FROM table1 ORDER BY code, id) AS dt1
CROSS JOIN (SELECT #rn1 := 1, #cd1 := '') AS init1
) AS t2
ON t2.id = t1.id
JOIN
(SELECT
IF(#cd2 = dt2.code, #rn2 := #rn2 + 1, 1) AS row_num2,
#cd2 := dt2.code AS code,
dt2.value
FROM (SELECT DISTINCT code, value FROM table2 ORDER BY code) AS dt2
CROSS JOIN (SELECT #rn2 := 1, #cd2 := '') AS init2
) AS t3
ON t3.row_num2 = t2.row_num1 AND
t3.code = t2.code
SET t1.value = t3.value
use join ,As order does not matter so i think your sample output could be changed
UPDATE table1 a
JOIN table2 b ON a.code= b.code
set a.value = b.value
Using OVER functions should work:
Example queries:
select id,code,value,rank() over (partition by code order by id asc) rank_
from dbo.table1;
select code,value,dense_rank() over (partition by code order by code,value asc) rank_ from dbo.table2;
Update statment:
UPDATE t
SET t1.value = t2.value
FROM (select id,code,value,rank() over (partition by code order by id asc) rank_ from dbo.table1) t1
inner join ( select code,value,dense_rank() over (partition by code order by code,value asc) rank_ from dbo.table2 ) t2
on t1.code = t2.code and t1.rank_ = t2.rank_

Mysql how to select base on priority

How to achieve this by joins and group by or any other alternative
Tab 1:
id | data
1 | aaa
2 | bbb
3 | ccc
tab 2:
id | tab1ID | status
101 | 1 | Y
102 | 2 | Y
103 | 1 | X
104 | 2 | X
105 | 3 | X
106 | 1 | Z
107 | 2 | Z
required output:
id | data | status
1 | aaa | Z
2 | bbb | Z
3 | ccc | X
Record with the highest priority status has to come up in the result Z > Y > X
I want to avoid creating a separate table to store the priority order
Edit 1: change in sample data
First, give a row number to the second table based on the columns tablID and the priority of status. Then join it with the first table to get the the columns id and data and select only the rows having row number is 1.
Query
select t1.`id`, t1.`data`, t2.`status`
from `tab1` t1
left join(
select `id`, `tab1ID`, `status`,
(
case `tab1ID` when #curA
then #curRow := #curRow + 1
else #curRow := 1 and #curA := `tab1ID` end
) as rn
from `tab2`,
(select #curRow := 0, #curA := '') r
order by `tab1ID`, case `status` when 'Z' then 1
when 'Y' then 2 when 'X' then 3 else 4 end
)t2
on t1.`id` = t2.`tab1ID`
where t2.rn = 1;
SQL Fiddle Demo
If you want the most recent status, then one method is a correlated subquery:
select t1.*,
(select t2.status
from tab2 t2
where t2.tab1id = t1.id
order by t2.id desc
limit 1
) as status
from tab1 t1;
EDIT:
If you just want the highest status, use JOIN and GROUP BY:
select t1.*, max(t2.status)
from tab1 t1 left join
tab2 t2
on t2.tab1id = t1.id
group by t1.id;
Note: The use of select t1.* is permitted and even supported by the ANSI standard, assuming that t1.id is unique (a reasonable assumption).

SQL Switch even/odd values of the selection

Currently I have this selection from 2 tables:
(SELECT e.id, e.num, '1' as TBL
FROM events e
)
UNION ALL
(SELECT p.id, p.num, '2' as TBL
FROM places p
)
ORDER BY 2 DESC
It returns values ordered by num like this:
id | num | TBL
3 | 9 | 2
1 | 8 | 2
4 | 7 | 1
1 | 4 | 1
7 | 1 | 2
But my goal is to mix tables in selection not losing ORDER within a specific table. Like this:
id | num | TBL
3 | 9 | 2
4 | 7 | 1
1 | 8 | 2
1 | 4 | 1
7 | 1 | 2
Thanks in advance! I appreciate ANY help!
If you want to interleave the tables, you will need additional information. If you enumerate each row, then you can use that for sorting. Something like this:
(SELECT e.id, e.num, '1' as TBL, (#rn1 := #rn1 + 1) as rn
FROM events e CROSS JOIN
(SELECT #rn1 := 0) vars
ORDER BY e.num desc
)
UNION ALL
(SELECT p.id, p.num, '2' as TBL, (#rn2 := #rn2 + 1) as rn
FROM places p CROSS JOIN
(SELECT #rn2 := 0) vars
ORDER BY p.num desc
)
ORDER BY rn, tbl desc;
Try this:
DECLARE #t TABLE (rn INT IDENTITY,id INT,TBL INT)
INSERT INTO #t (id, TBL)
SELECT id,1 FROM events
INSERT INTO #t
SELECT id,2 FROM places
DECLARE #t2 TABLE (rn INT IDENTITY,num numeric(18,2))
INSERT INTO #t2 (num)
(
SELECT num
FROM events
UNION ALL
SELECT num
FROM places)
ORDER BY num DESC
SELECT id, num, TBL
FROM #t a
JOIN #t2 b ON a.rn = b.rn

filter by row number id of specific item id

From MySQL - Get row number on select
I know how to get the row number / rank using this mysql query:
SELECT #rn:=#rn+1 AS rank, itemID
FROM (
SELECT itemID
FROM orders
ORDER BY somecriteria DESC
) t1, (SELECT #rn:=0) t2;
The result returns something like this:
+--------+------+
| rank | itemID |
+--------+------+
| 1 | 265 |
| 2 | 135 |
| 3 | 36 |
| 4 | 145 |
| 5 | 123 |
| 6 | 342 |
| 7 | 111 |
+--------+------+
My question is: How can I get the result in 1 simple SINGLE QUERY that returns items having lower rank than itemID of 145, i.e.:
+--------+------+
| rank | itemID |
+--------+------+
| 5 | 123 |
| 6 | 345 |
| 7 | 111 |
+--------+------+
Oracle sql query is also welcomed. Thanks.
An Oracle solution (not sure if it meets your criteria of "one simple single query"):
WITH t AS
(SELECT item_id, row_number() OVER (ORDER BY some_criteria DESC) rn
FROM orders)
SELECT t2.rn, t2.item_id
FROM t t1 JOIN t t2 ON (t2.rn > t1.rn)
WHERE t1.item_id = 145;
My assumption is no repeating values of item_id.
Attempting to put this in MySQL terms, perhaps something like this might work:
SELECT t2.rank, t2.itemID
FROM (SELECT #rn:=#rn+1 AS rank, itemID
FROM (SELECT itemID
FROM orders
ORDER BY somecriteria DESC), (SELECT #rn:=0)) t1 INNER JOIN
(SELECT #rn:=#rn+1 AS rank, itemID
FROM (SELECT itemID
FROM orders
ORDER BY somecriteria DESC), (SELECT #rn:=0)) t2 ON t2.rank > t1.rank
WHERE t1.itemID = 145;
Disclaimer: I don't work with MySQL much, and it's untested. The Oracle piece works.
SELECT #rn:=#rn+1 AS rank, itemID
FROM (
SELECT itemID
FROM orders
ORDER BY somecriteria DESC
) t1, (SELECT #rn:=0) t2
where rank >
(
select rank from
(
SELECT #rn:=#rn+1 AS rank, itemID
FROM
(
SELECT itemID
FROM orders
ORDER BY somecriteria DESC
) t1, (SELECT #rn:=0) t2
) x where itemID = 145
) y