I need one help on splitting multiple values from multiple columns into another column. Below is an example
CREATE TABLE split
(
`Col_1` VARCHAR(120),
`Col_2` VARCHAR(50),
`Col_3` VARCHAR(20),
`Col_4` VARCHAR(50)
);
Insert into split (Col_1,Col_2,Col_3,Col_4) values ('ABC','1','10',null);
Insert into split (Col_1,Col_2,Col_3,Col_4) values ('DEF','2,3','30,40',null);
Insert into split (Col_1,Col_2,Col_3,Col_4) values ('GHI','4,5','50','500,600,700');
select * from split;
+-------+-------+-------+-------------+
| Col_1 | Col_2 | Col_3 | Col_4 |
+-------+-------+-------+-------------+
| ABC | 1 | 10 | NULL |
| DEF | 2,3 | 30,40 | NULL |
| GHI | 4,5 | 50 | 500,600,700 |
+-------+-------+-------+-------------+
I am no expert in this, but have been playing around and have managed to split only col_2 into multiple rows as below:
SELECT
Col_1,Col_2,Col_3,Col_4,
SUBSTRING_INDEX(SUBSTRING_INDEX(split.Col_2, ',', numbers.n), ',', -1) Col_2_NEW,
SUBSTRING_INDEX(SUBSTRING_INDEX(split.Col_3, ',', numbers.n), ',', -1) Col_3_NEW,
SUBSTRING_INDEX(SUBSTRING_INDEX(split.Col_4, ',', numbers.n), ',', -1) Col_4_NEW
FROM
(SELECT 1 n UNION ALL SELECT 2
UNION ALL SELECT 3 UNION ALL SELECT 4) numbers INNER JOIN split
ON CHAR_LENGTH(split.Col_2) - CHAR_LENGTH(REPLACE(split.Col_2, ',', ''))>=numbers.n-1
ORDER BY Col_2, n;
+-------+-------+-------+-------------+-----------+-----------+-----------+
| Col_1 | Col_2 | Col_3 | Col_4 | Col_2_NEW | Col_3_NEW | Col_4_NEW |
+-------+-------+-------+-------------+-----------+-----------+-----------+
| ABC | 1 | 10 | NULL | 1 | 10 | NULL |
| DEF | 2,3 | 30,40 | NULL | 2 | 30 | NULL |
| DEF | 2,3 | 30,40 | NULL | 3 | 40 | NULL |
| GHI | 4,5 | 50 | 500,600,700 | 4 | 50 | 500 |
| GHI | 4,5 | 50 | 500,600,700 | 5 | 50 | 600 |
+-------+-------+-------+-------------+-----------+-----------+-----------+
However, I would like to split, col_3 and col_4 into new as well, so it gives me below output.
+-------+-------+-------+-------------+-----------+-----------+-----------+
| Col_1 | Col_2 | Col_3 | Col_4 | Col_2_NEW | Col_3_NEW | Col_4_NEW |
+-------+-------+-------+-------------+-----------+-----------+-----------+
| ABC | 1 | 10 | NULL | 1 | 10 | NULL |
| DEF | 2,3 | 30,40 | NULL | 2 | 30 | NULL |
| DEF | 2,3 | 30,40 | NULL | 2 | 40 | NULL |
| DEF | 2,3 | 30,40 | NULL | 3 | 30 | NULL |
| DEF | 2,3 | 30,40 | NULL | 3 | 40 | NULL |
| GHI | 4,5 | 50 | 500,600,700 | 4 | 50 | 500 |
| GHI | 4,5 | 50 | 500,600,700 | 4 | 50 | 600 |
| GHI | 4,5 | 50 | 500,600,700 | 4 | 50 | 700 |
| GHI | 4,5 | 50 | 500,600,700 | 5 | 50 | 500 |
| GHI | 4,5 | 50 | 500,600,700 | 5 | 50 | 600 |
| GHI | 4,5 | 50 | 500,600,700 | 5 | 50 | 700 |
+-------+-------+-------+-------------+-----------+-----------+-----------+
I have searched all over and so far, they are splitting the row into one column only and have not been able to find problem something similar to mine.
Maybe some join is missing or some union, I don't know as I am not good at queries.
Can anyone help me here? without asking me to read guide or handbooks :-)
Thanks in advance
You can try one of the the recommendation in this thread here.
Something along the lines of
SELECT s.[Col_1], Split.a.value('.', 'VARCHAR(100)') AS String
FROM (SELECT [Col_1],
CAST ('<M>' + REPLACE([Col_2], ',', '</M><M>') + '</M>' AS XML) AS String
FROM split) AS s
CROSS APPLY String.nodes ('/M') AS Split(a);
iterated over your columns should work fine.
Edit: Didn't see that this is MySQL, sorry. See below for a working solution.
The following code should work for the first two columns.
1.) Create table:
CREATE TABLE split(
`Col_1` VARCHAR(120),
`Col_2` VARCHAR(50),
`Col_3` VARCHAR(20),
`Col_4` VARCHAR(50)
);
INSERT INTO split (Col_1,Col_2,Col_3,Col_4) values ('ABC','1','10',null);
INSERT INTO split (Col_1,Col_2,Col_3,Col_4) values ('DEF','2,3','30,40',null);
INSERT INTO split (Col_1,Col_2,Col_3,Col_4) values ('GHI','4,5','50','500,600,700');
which leads to
SELECT * FROM split;
Col_1 Col_2 Col_3 Col_4
ABC 1 10 (null)
DEF 2,3 30,40 (null)
GHI 4,5 50 500,600,700
2.) Split the strings in Col_2:
SELECT
split.Col_1,
SUBSTRING_INDEX(SUBSTRING_INDEX(split.Col_2, ',', numbers.n), ',', -1) Col_2,
Col_3,
Col_4
FROM
(select 1 n UNION ALL
select 2 UNION ALL select 3 UNION ALL
select 4 UNION ALL select 5) numbers INNER JOIN split
ON CHAR_LENGTH(split.Col_2)
-CHAR_LENGTH(REPLACE(split.Col_2, ',', ''))>=numbers.n-1
ORDER BY Col_1, Col_2;
3.) Result:
Col_1 Col_2 Col_3 Col_4
ABC 1 10 (null)
DEF 2 30,40 (null)
DEF 3 30,40 (null)
GHI 4 50 500,600,700
GHI 5 50 500,600,700
Here is a SQL fiddle with the above code: http://sqlfiddle.com/#!9/948fcb/4.
You should be able to iterate from there. Just comment this post if you need more guidance.
Important caveat: This works with up to 5 comma-separated strings in one column.
Solution is inspired by fthiella's answer to SQL split values to multiple rows.
Just wanted to answer with final query, which is giving me desired output as required.
Posting it here, as maybe it will help others.
SELECT distinct Col_1,Col_2,Col_3,Col_4,
SUBSTRING_INDEX(SUBSTRING_INDEX(t.Col_2, ',', n.n), ',', -1) Col_2_New,
SUBSTRING_INDEX(SUBSTRING_INDEX(t.Col_3, ',', n1.n), ',', -1) Col_3_New,
SUBSTRING_INDEX(SUBSTRING_INDEX(t.Col_4, ',', n2.n), ',', -1) Col_4_New
FROM split t CROSS JOIN
(
SELECT a.N + b.N * 10 + 1 n
FROM
(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 ) a
,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 ) b
,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 ) c
ORDER BY n
) n,
(
SELECT a.N + b.N * 10 + 1 n
FROM
(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 ) a
,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 ) b
,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 ) c
ORDER BY n
) n1,
(
SELECT a.N + b.N * 10 + 1 n
FROM
(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 ) a
,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 ) b
,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 ) c
ORDER BY n
) n2
WHERE coalesce(n.n,'0') <= 1 + (LENGTH(coalesce(t.Col_2,'0')) - LENGTH(REPLACE(coalesce(t.Col_2,'0'), ',', '')))
AND coalesce(n1.n,'0') <= 1 + (LENGTH(coalesce(t.Col_3,'0')) - LENGTH(REPLACE(coalesce(t.Col_3,'0'), ',', '')))
AND coalesce(n2.n,'0') <= 1 + (LENGTH(coalesce(t.Col_4,'0')) - LENGTH(REPLACE(coalesce(t.Col_4,'0'), ',', '')))
ORDER BY 1,2,3,4,5,6,7 ;
Related
New to mysql,
I have a table like this.
___|____
Id | name
1 | a
2 | b
3 | c
4 | A
5 | B
6 | C
What will be the query to get the result like this in sql
___|____
Id | name
4 | A
1 | a
5 | B
2 | b
6 | C
3 | c
Editted
This seams to be the most easy way changed DDS idea into a more general working one for all MySQL versions..
Query
SELECT
id
, name
FROM
Table1
ORDER BY
CASE
WHEN name COLLATE latin1_bin BETWEEN 'A' AND 'Z'
THEN ASCII(name) + 31
ELSE ASCII(name)
END
Result
| id | name |
| --- | ---- |
| 4 | A |
| 1 | a |
| 5 | B |
| 2 | b |
| 6 | C |
| 3 | c |
View on DB Fiddle
Explaining View so you can see what happens on DB Fiddle
Or the more stabile one because of the unique generated values for the calculated_ascii_value column.
Explaining View so you can see what happens on DB Fiddle
The other query is pretty complex..
Query
SELECT
Table1.id
, Table1.name
FROM (
SELECT
DISTINCT
(
SUBSTRING_INDEX(SUBSTRING_INDEX(#orderList, ',', number_generator.number), ',', -1)
COLLATE utf8mb4_bin
) AS letter
FROM (
SELECT
(#number := #number + 1) AS number
FROM (
SELECT 1 AS number UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10
) AS row_1
CROSS JOIN (
SELECT 1 AS number UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6
) AS row_2
CROSS JOIN (SELECT #number := 0) AS init_user_param
) AS number_generator
CROSS JOIN (SELECT #orderList := 'A,a,B,b,C,c' /* add all to Z,z */) AS init_user_param
) AS letters
INNER JOIN
Table1
ON
letters.letter = Table1.name
;
Results
| id | name |
| --- | ---- |
| 4 | A |
| 1 | a |
| 5 | B |
| 2 | b |
| 6 | C |
| 3 | c |
View on DB Fiddle
Note there are some things you should know
COLLATE utf8mb4_bin might be changed to COLLATE utf8_bin instead when your MySQL uses utf8 charset.
And SELECT #orderList := 'A,a,B,b,C,c' /* add all to Z,z */ you need to might add more to Z,z
this will make a new column 'newid' with the order you need
select newid = row_number() over (order by case when val between 'A' and 'Z'
then ascii(val) -31
else ascii(val) end), *
from yourtable
Try like this. I Just used database from w3schools, need to check if works in MySql.
SELECT * FROM myTable GROUP BY name ORDER BY UPPER(name), LOWER(name);
Problem 1
How can I get lowest value from table (not null), for ID_CAR? For example, for ID_CAR 1 lowest value is 50, for ID_CAR 2 lowest value is 50 and for ID_CAR 3 lowest value is 300. I don't need duplicates, I need only one value for one car.
ID_CAR | col_1 | col_2 | col_3 | col_4 | col_5 | col_6
1 | null | 250 | 300 | null | 900 | null
2 | 100 | null | 300 | 600 | 200 | 100
1 | 300 | 100 | 800 | 100 | 50 | 900
3 | 300 | 4000 | null | null | null | null
2 | null | null | null | 50 | null | 100
4 | 400 | 900 | 500 | 700 | 800 | 500
Problem 2
In this example, values in col_* are days. I need to add days to col_date and get lowest. For example lowest date for ID_CAR 1 is 2018-01-03 (col_2) and for ID_CAR 2 is 2018-01-15 (col_4).
ID_CAR | col_1 | col_2 | col_3 | col_4 | col_5 | col_6 | col_date
1 | null | 2 | 3 | null | 5 | null | 2018-01-01
2 | 1 | null | 3 | 6 | 10 | 10 | 2018-01-13
1 | 3 | 20 | 80 | 10 | 50 | 90 | 2018-01-02
3 | 30 | 40 | null | null | null | null | 2018-01-03
2 | null | null | null | 5 | null | 10 | 2018-01-10
4 | 10 | 9 | 5 | 70 | 8 | 50 | 2018-01-07
Without union you can simply combine least and min function :
select
ID_CAR,min(least(col_1,col_2,col_3,col_4,col_5,col_6)) lowest_value
from
table
group by
ID_CAR
or if you have nullvalues you need ifnull or coalesce function
select
ID_CAR,
min(least(
ifnull(col_1,~0),
ifnull(col_2,~0),
ifnull(col_3,~0),
ifnull(col_4,~0),
ifnull(col_5,~0),
ifnull(col_6,~0)
)) as lowest_value
from
table
group by
ID_CAR
~0 is the max bigint in mysql
The opposite function of least is greatest
The opposite function of min is max ;-)
Works with Mysql, Oracle, Postgres, Hive ...
Problem 2, something like this :
select
ID_CAR,
min(least(
DATE_ADD(col_date, INTERVAL ifnull(col_1,0) DAY),
DATE_ADD(col_date, INTERVAL ifnull(col_2,0) DAY),
DATE_ADD(col_date, INTERVAL ifnull(col_3,0) DAY),
DATE_ADD(col_date, INTERVAL ifnull(col_4,0) DAY),
DATE_ADD(col_date, INTERVAL ifnull(col_5,0) DAY),
DATE_ADD(col_date, INTERVAL ifnull(col_6,0) DAY)
)) as lowest_date
from
table
group by
ID_CAR
or like this (except if all columns can be null):
select
ID_CAR,
DATE_ADD(col_date, INTERVAL min(least(
ifnull(col_1,~0),
ifnull(col_2,~0),
ifnull(col_3,~0),
ifnull(col_4,~0),
ifnull(col_5,~0),
ifnull(col_6,~0)
)) DAY) as lowest_date
from
table
group by
ID_CAR
You need UNION :
select id_car, min(val) as lowest_value
from (select id_car, col_1 as Val
from table union
select id_car, col_2
from table
. . .
) t
group by id_car;
The following query will give you the required result
select tab.ID_CAR, min(tab.val) as lowest_value from
(
(select ID_CAR,min(col_1) val
from table
group by ID_CAR)
union
(select ID_CAR,min(col_2) val
from table
group by ID_CAR)
union
(select ID_CAR,min(col_3) val
from table
group by ID_CAR)
union
(select ID_CAR,min(col_4) val
from table
group by ID_CAR)
union
(select ID_CAR,min(col_5) val
from table
group by ID_CAR)
union
(select ID_CAR,min(col_6) val
from table
group by ID_CAR)
) tab
group by tab.ID_CAR
Try this
If you are expecting values greater than 9999999999999999999, then use a higher values
select id_car,
min(least(coalesce(col_1,9999999999999999999),coalesce(col_2,9999999999999999999),coalesce(col_3,9999999999999999999),
coalesce(col_4,9999999999999999999),coalesce(col_5,9999999999999999999),coalesce(col_6,9999999999999999999)
)
) as min_val
from your_table
group by id_car
The naive approach would be using least:
SELECT ID_CAR, least(t.col_1, t.col_2, t.col_3, t.col_4, t.col_5, t.col_6)
FROM
(SELECT ID_CAR, min(col_1) as col_1, min(col_2) as col_2, min(col_3) as col_3, min(col_4) as col_4, min(col_5) as col_5, min(col_6) as col_6
FROM YOUR_TABLE GROUP BY ID_CAR) t;
However: If ANY argument to LEAST is NULL, it'll return NULL. You'll either need to convert the NULLs to a high value (which is a hack but will work in practice, see other answers for this).
Which means doing something like this:
SELECT ID_CAR, LEAST(col_1, col_2, col_3,
col_4, col_5, col_6) as l
FROM
(SELECT ID_CAR,
IFNULL(min(col_1), 9999) as col_1,
IFNULL(min(col_2), 9999) as col_2,
IFNULL(min(col_3), 9999) as col_3,
IFNULL(min(col_4), 9999) as col_4,
IFNULL(min(col_5), 9999) as col_5,
IFNULL(min(col_6), 9999) as col_6
FROM YOUR_TABLE GROUP BY ID_CAR) t;
However, it might be good to use a trick to convert your table
into a three row table of the form:
car_id | attr | value
1 1 NULL ; or use strings such as "size"
1 2 250
I have a query which I want to insert a variable into a WHERE statement.
WHERE
#Variable
I've tried the following (simplified) but it doesn't seem to work.
NOTE: I haven't included the concatenation element here trying to figure that part out myself before asking the question.
SET #id := x;
SET #n := (SELECT COUNT(*) FROM Table2 WHERE id=#id);
SET #Variable := (
(
Table1.Column1=(SELECT Column1 FROM Table2 WHERE id=#id LIMIT 1 OFFSET 0)
AND Table1.Column2=(SELECT Column2 FROM Table2 WHERE id=#id LIMIT 1 OFFSET 0)
AND Table1.Column3=(SELECT Column3 FROM Table2 WHERE id=#id LIMIT 1 OFFSET 0)
)
.........
OR
(
Table1.Column1=(SELECT Column1 FROM Table 2 WHERE id=#id LIMIT 1 OFFSET #n)
AND Table1.Column2=(SELECT Column2 FROM Table2 WHERE id=#id LIMIT 1 OFFSET #n)
AND Table1.Column3=(SELECT Column3 FROM Table2 WHERE id=#id LIMIT 1 OFFSET #n)
)
)
;
SELECT Table1.Column1, Table1.Column2, Table1.Column3, Table2.Column1, Table2.Column2, Table2.Column3
FROM Table1, Table2
WHERE
#Variable
;
So This:
SET #IDNumber := 21;
SELECT Players.PlayerID, COUNT(*) AS Games, SUM(Games.Points)
FROM Teams, Players, Games
WHERE
Teams.PlayerID=Players.PlayerID
AND
Games.Game=Teams.Game
AND
Games.Team=Teams.Team
AND
Games.GameDate=Teams.GameDate
AND
(
Games.Game= ANY (SELECT Game FROM Teams WHERE PlayerID=#IDNumber)
AND
Games.Team= ANY (SELECT Team FROM Teams WHERE PlayerID=#IDNumber)
AND
Games.GameDate= ANY (SELECT GameDate FROM Teams WHERE PlayerID=#IDNumber)
)
GROUP BY Teams.PlayerID
ORDER BY Games DESC
;
Creates This:
+----------+-------+-------------------+
| PlayerID | Games | SUM(Games.Points) |
+----------+-------+-------------------+
| 15 | 8 | 10 |
| 21 | 8 | 10 |
| 8 | 8 | 10 |
| 14 | 6 | 7 |
| 5 | 6 | 7 |
| 19 | 5 | 6 |
| 11 | 5 | 7 |
| 12 | 3 | 4 |
| 10 | 3 | 3 |
| 4 | 2 | 3 |
+----------+-------+-------------------+
10 rows in set (0.01 sec)
Which is exactly what I was looking for.
If the table has the following data
num | date_ | s | i_id
-------+-------------+------------------------+---------
1 | 2013-12-12 | (2,1,2013-12-12,80.56) | 2
1 | 2013-12-12 | (3,1,2013-12-12,70.56) | 3
1 | 2013-12-10 | (4,1,2013-12-10,90.76) | 4
2 | 2013-12-10 | (5,2,2013-12-10,90.76) | 5
2 | 2013-12-06 | (6,2,2013-12-06,90.76) | 6
3 | 2013-12-06 | (7,3,2013-12-06,90.76) | 7
3 | 2013-12-06 | (8,3,2013-12-06,90.76) | 8
i want a query which will give num,i_id for the records which have different dates for same num.
It should return num-1,2 and the corresponding i_id.
How should i proceed?
This should work in PostgreSQL with an version number >= 9.0
SELECT
num
, array_to_string(array_agg(i_id ORDER BY num), ',')
FROM a
GROUP BY num
see demo http://sqlfiddle.com/#!15/25502/5
And it look like in your comment that you want to have the MAX(date_)
SELECT
num
, MAX(date_)
, array_to_string(array_agg(i_id ORDER BY num), ',')
FROM a
GROUP BY num
see demo http://sqlfiddle.com/#!15/25502/6
select num, group_concat(i_id) as iids
from your_table
group by num
having count(distinct date_) > 1
I've this table : extended
+--------+-----+-----+-----+-----+- -+-----+-----+
| Name | T1 | T2 | T1 | T3 | .. | T19 | T20 |
+--------+-----+-----+-----+-----+- -+-----+-----+
| john | 5 | 10 | 50 | 10 | .. | 20 | 8 |
| bill | 2 | 8 | 11 | 5 | .. | 9 | 55 |
| james | 30 | 15 | 12 | 40 | .. | 13 | 10 |
| elsie | 28 | 35 | 20 | 32 | .. | 18 | 1 |
| .... | .. | .. | .. | .. | .. | .. | .. |
+--------+-----+-----+-----+-----+- -+-----+-----+
And i want to return this one :
+--------+-------+-----+-----+-----+-----+- -+-----+-----+
| Name | TOTAL | T1 | T2 | T1 | T3 | .. | T19 | T20 |
+--------+-------+-----+-----+-----+-----+- -+-----+-----+
| bill | 250 | 2 | 8 | 11 | 5 | .. | 9 | 55 |
| john | 230 | 5 | 10 | 50 | 10 | .. | 20 | 8 |
| elsie | 158 | 28 | 35 | 20 | 32 | .. | 18 | 1 |
| james | 129 | 30 | 15 | 12 | 40 | .. | 13 | 10 |
| .... | .... | .. | .. | .. | .. | .. | .. | .. |
+--------+-------+-----+-----+-----+-----+----+-----+-----+
Order by TOTAL. This total is the sum of best of 15 Tx ...
I don't now how to do this.
The table come from a request ( CREATE VIEW ) from another table with a lot of data.
Can you help me ?
At this point, i do the sum of ALL Tx, but it's not what i want ...
SELECT `Name`, (T1+T2+ T3+T4+T5+T6+T7+T8+T9+T10+T11+T12+T13+T14+T15+T16+T17+T18+T19+T20) AS TOTAL, T1,T2, T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,T17,T18,T19,T20
FROM `extended`
ORDER BY TOTAL DESC
If you wanted to remove the lowest value, that is easy:
select name,
(t1 + . . . + t20) -
least(t1, . . . , t20)
from table;
Unfortunately, MySQL does not have the nth function, so getting the second least and others is quite difficult.
If you had the values in separate rows, you could do:
select name, sum(t)
from (select en.*,
if(#name = name, #rn := #rn + 1, #rn := 1) as rn,
#name := name
from extended_norm en cross join
(select #name := '', #rn := 0) const
order by name, t desc
) en
where rn <= 15
group by name;
With your data structure, you'll probably need to write a user-defined function to do what you want.
EDIT:
If you want the list of t's, you can do it two ways. You can modify the above to include the pivot (this assumes that you have a column called something like tnumber to identify which t-value):
select name, sum(case when rn <= 15 then t end) as Total,
max(case when en.tnumber = 1 then t end) as T1,
max(case when en.tnumber = 2 then t end) as T2,
. . .
max(case when en.tnumber = 1 then t end) as T20
from (select en.*,
if(#name = name, #rn := #rn + 1, #rn := 1) as rn,
#name := name
from extended_norm en cross join
(select #name := '', #rn := 0) const
order by name, t desc
) en
group by name;
Otherwise, take the above query and join it to the denormalized table:
select e.*, tt.total
from extended e join
(the above query) tt
on e.name = tt.name;
It's awkward but this can also work!
select name, sum(t) from
(select name, t from (
select name, t1 as t from extended
union
select name, t2 as t from extended
union
select name, t3 as t from extended
union
select name, t4 as t from extended
union
select name, t5 as t from extended
union
select name, t6 as t from extended
union
select name, t7 as t from extended
union
select name, t8 as t from extended
union
select name, t9 as t from extended
union
select name, t10 as t from extended
union
select name, t11 as t from extended
union
select name, t12 as t from extended
union
select name, t13 as t from extended
union
select name, t14 as t from extended
union
select name, t15 as t from extended
union
select name, t16 as t from extended
union
select name, t17 as t from extended
union
select name, t18 as t from extended
union
select name, t19 as t from extended
union
select name, t20 as t from extended
) z
where name='bill' order by name, t desc limit 0,15);
But you have to run the query for each user by replacing 'bill' by other name