I wonder how to replace such a table
(the table is the result of 3x LEFT JOIN)
SELECT *
FROM users
LEFT JOIN items on users.id = items.id
LEFT JOIN items_additional on users.id = items_additional.items_id
LEFT JOIN items_ask_user on users.id = items_ask_user.items_id';
ID
item_id
name
surname
addition
question
amount
1
1
Gladys
Warner
hot-dog
mayo
14
2
1
Gladys
Warner
pizza
chilli
11
3
2
Harrison
Croft
pizza
4
2
Harrison
Croft
burger
chilli
11
5
2
Harrison
Croft
hod-dog
mayo
14
to somthing like
ID
item_id
name
surname
addition
addition2
addition3
question1
question2
question3
amount
1
1
Gladys
Warner
hot-dog
pizza
-
mayo
chilli
-
25
2
2
Harrison
Croft
pizza
burger
hod-dog
chilli
mayo
-
25
the number of additions or questions may increase or decrease, depending on person.
Edit
SET #sql = NULL;
WITH cte AS(
SELECT DISTINCT ROW_NUMBER() OVER(PARTITION BY user_id) AS idx
FROM users
LEFT JOIN items on users.id = items.id
LEFT JOIN items_additional on users.id = items_additional.items_id
LEFT JOIN items_ask_user on users.id = items_ask_user.items_id
)
SELECT GROUP_CONCAT(
CONCAT('MAX(IF(rn_add = ', cte.idx, ', additional_option_name, NULL)) AS additional_option_name', cte.idx, ','
'MAX(IF(rn_qst = ', cte.idx, ', ask_user, NULL)) AS ask_user', cte.idx
)) INTO #sql
FROM cte;
SET #cte = 'WITH cte AS(SELECT *, ROW_NUMBER() OVER(PARTITION BY name, surname ORDER BY IF(additional_option_name IS NULL, 1, 0), `event_items`.`id`) AS rn_add, ROW_NUMBER() OVER(PARTITION BY name, surname ORDER BY IF(ask_user IS NULL, 1, 0), `event_items`.`id`) AS rn_qst
FROM users
LEFT JOIN event_items on users.id = event_items.id
LEFT JOIN event_items_additional on users.id = event_items_additional.items_id
LEFT JOIN event_items_ask_user on users.id = event_items_ask_user.items_id';
SET #sql = CONCAT(#cte,
'SELECT `event_items`.`id`, user_id, name, surname,',
#sql,
',SUM(additional_option_price) AS additional_option_price FROM cte GROUP BY user_id, name, surname'
);
SELECT #sql;
PREPARE stmt1 FROM #sql;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
Edit2
Schema demo here
Will just throw this here as a possibility - it won't give you dynamic columns but may be of use depending on how you intend to consume the data.
It's certaintly less faff and more performant if you can.
select
item_Id, name, surname,
group_concat(addition separator ', ') Additions,
group_concat(question separator ', ') Questions,
Sum(amount) amount
from t
group by item_Id, name, surname;
As long as the dynamic solution is based on the static one, for reasons of clarity I'll first explain the static one by assuming that, as in the example your provided, there are exactly 3 fields at max, for addition and question fields.
Let's look at the static solution first, by assuming that we have specifically 3 fields. In this case what you can do is compute a row number for each addition and question, which will be used to match the specific value at the required index for each of the three fields addition1, addition2 and addition3 (same goes for question), using an IF statement. In order to remove the NULL values that are generated by this statement, we can select the maximum value and aggregate over item_id, name and surname
WITH cte AS(
SELECT *,
ROW_NUMBER() OVER(
PARTITION BY name, surname
ORDER BY IF(addition IS NULL, 1, 0),
ID ) AS rn_add,
ROW_NUMBER() OVER(
PARTITION BY name, surname
ORDER BY IF(question IS NULL, 1, 0),
ID ) AS rn_qst
FROM tab
)
SELECT item_id AS ID,
item_id,
name,
surname,
MAX(IF(rn_add = 1, addition, NULL)) AS addition1,
MAX(IF(rn_add = 2, addition, NULL)) AS addition2,
MAX(IF(rn_add = 3, addition, NULL)) AS addition3,
MAX(IF(rn_qst = 1, question, NULL)) AS question1,
MAX(IF(rn_qst = 2, question, NULL)) AS question2,
MAX(IF(rn_qst = 3, question, NULL)) AS question3,
SUM(amount) AS amount
FROM cte
GROUP BY item_id,
name,
surname
Check the demo here.
The dynamic solution aims at reproducing that exact same query as a prepared statement (which is essentially a string that you first build and then ask MySQL to execute over the database), with the only difference that it needs to generalize on the amount of fields to extract:
MAX(IF(rn_add = 1, addition, NULL)) AS addition1,
MAX(IF(rn_qst = 1, addition, NULL)) AS question1,
...
...
MAX(IF(rn_add = <n>, addition, NULL)) AS addition<n>,
MAX(IF(rn_qst = <n>, addition, NULL)) AS question<n>,
And we need to reproduce these instructions n times with n equals to the item_id's highest amount of both addition and question values. In order to generate this piece of query, we get the longest list of indices:
WITH cte AS(
SELECT DISTINCT ROW_NUMBER() OVER(PARTITION BY item_id) AS idx
FROM tab
)
and cycle over it to generate all MAX rows as a string where, in place of the specific number (as in the static query), we will use all the numbers stored inside cte.idx:
SELECT GROUP_CONCAT(
CONCAT('MAX(IF(rn_add = ', cte.idx, ', addition, NULL)) AS addition', cte.idx, ','
'MAX(IF(rn_qst = ', cte.idx, ', question, NULL)) AS question', cte.idx
)) INTO #sql
FROM cte;
Once we have the generalized amonut of MAX rows, we can just use this together with the rest of the static query, which does not depend on the number of addition or question values.
SET #cte = 'WITH cte AS(SELECT *, ROW_NUMBER() OVER(PARTITION BY name, surname ORDER BY IF(addition IS NULL, 1, 0), ID) AS rn_add, ROW_NUMBER() OVER(PARTITION BY name, surname ORDER BY IF(question IS NULL, 1, 0), ID) AS rn_qst FROM tab)';
SET #sql = CONCAT(#cte,
'SELECT item_id AS ID, item_id, name, surname,',
#sql,
',SUM(amount) AS amount FROM cte GROUP BY item_id, name, surname'
);
Once we have the static query generated as a string in a dynamic way, we can ask MySQL to prepare, execute and deallocate it.
PREPARE stmt1 FROM #sql;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
The execution will show you your desired output.
Here's the full code for the dynamic query:
SET #sql = NULL;
WITH cte AS(
SELECT DISTINCT ROW_NUMBER() OVER(PARTITION BY item_id) AS idx
FROM tab
)
SELECT GROUP_CONCAT(
CONCAT('MAX(IF(rn_add = ', cte.idx, ', addition, NULL)) AS addition', cte.idx, ','
'MAX(IF(rn_qst = ', cte.idx, ', question, NULL)) AS question', cte.idx
)) INTO #sql
FROM cte;
SET #cte = 'WITH cte AS(SELECT *, ROW_NUMBER() OVER(PARTITION BY name, surname ORDER BY IF(addition IS NULL, 1, 0), ID) AS rn_add, ROW_NUMBER() OVER(PARTITION BY name, surname ORDER BY IF(question IS NULL, 1, 0), ID) AS rn_qst FROM tab)';
SET #sql = CONCAT(#cte,
'SELECT item_id AS ID, item_id, name, surname,',
#sql,
',SUM(amount) AS amount FROM cte GROUP BY item_id, name, surname'
);
SELECT #sql;
PREPARE stmt1 FROM #sql;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
Check the demo here.
Side Note: if you want to store the output of this query, you may require to create a view inside the prepared statement. In that case you should change the #sql assignment to:
SET #sql = CONCAT('CREATE VIEW my_view AS ',
#cte,
'SELECT item_id AS ID, item_id, name, surname,',
#sql,
',SUM(amount) AS amount FROM cte GROUP BY item_id, name, surname'
);
hence select the content of the view whenever you need it, for example to export it to Excel.
I want to transform row of mysql table to column, through mysql pivot table
My input table.
My input table which has data in below format.
Area Status User
-----------------------
1 Active user1
1 Failed user2
1 Success user4
2 Active user2
2 Failed user3
2 Success user4
My Desired Output Format is below
Status user1 user2 user3 user4
-----------------------------------------
Active 1 1 0 0
Failed 0 1 1 0
Success 0 0 0 2
Since i do not know the exact number of users i want to pivot it through dynamic column only.
in your case, if you have a separate user table, you can easily make a Cartesian Product between your user table and status table, and make pivoting.. if you need further helps let me know..
have look on one of my following blog post about a pivoting schenerio for sales report, I am using a dynamic calendar table to produce Cartesian Product with Order Category table..
Sample Pivoting and Cartesian Product
I have another runnable example for you, from
product_id supplier_id number price
p1 s1 2 2.12
p1 s2 3 3.12
p2 s1 4 4.12
to
product_id supplier_id1 supplier_id2 number1 number2 price1 price2
p1 s1 s2 2 3 2.12 3.12
p2 s1 NULL 4 NULL 4.12 NULL
here the "supplier_id" is dynamic, it could be one little data set from a 1 million big set.so there could be supplier1,or supplier99,or supplier999,depends on whats in the source table.
first, lets create the source table:
CREATE TABLE test
(
product_id varchar(10),
supplier_id varchar(10),
number int(11),
price decimal(10,2)
);
INSERT INTO test (product_id, supplier_id, number, price) VALUES ('p1', 's1', 2, 2.12);
INSERT INTO test (product_id, supplier_id, number, price) VALUES ('p1', 's2', 3, 3.12);
INSERT INTO test (product_id, supplier_id, number, price) VALUES ('p2', 's1', 4, 4.12);
I don't think one select will do it, so the temp table and column are needed, this code is what you need:
DROP TABLE IF EXISTS final_data;
DROP TABLE IF EXISTS temp_data;
CREATE TEMPORARY TABLE temp_data
SELECT
product_id,
IF(#productid = product_id, #rownum := #rownum + 1, #rownum := 1) seller_number,
#productid := product_id,
supplier_id,
number,
price
FROM
test
CROSS JOIN (SELECT #rownum := 0) r
CROSS JOIN (SELECT #productid:="") n
ORDER BY
product_id ASC;
ALTER TABLE temp_data ADD PRIMARY KEY(product_id, seller_number);
ALTER TABLE temp_data ADD INDEX (seller_number);
#Dynamic Pivot starts via prepared statements
#Step 1: Dynamily create colum names for sql
#Field supplier_id
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
' MAX(IF( seller_number= ''',
seller_number,
''', supplier_id, NULL)) AS ',
CONCAT("supplier_id", seller_number)
)
) INTO #sql
FROM temp_data;
#Field number
SELECT
CONCAT(#sql, ', ',
GROUP_CONCAT(DISTINCT
CONCAT(
' MAX(IF( seller_number= ''',
seller_number,
''', number, NULL)) AS ',
CONCAT("number", seller_number)
)
) )INTO #sql
FROM temp_data;
#Field price
SELECT
CONCAT(#sql, ', ',
GROUP_CONCAT(DISTINCT
CONCAT(
' MAX(IF( seller_number= ''',
seller_number,
''', price, NULL)) AS ',
CONCAT("price", seller_number)
)
) )INTO #sql
FROM temp_data;
#Step 2: Add fields to group by query
SET #sql = CONCAT(' create table final_data as (SELECT
product_id,
', #sql, '
FROM
temp_data
GROUP BY
product_id) ');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DROP TABLE IF EXISTS temp_data;
I am trying to make a cross tab pivot table. I need to get all teams from the database table dynamically on separate columns instead of specifying names of the teams in the query which I am currently doing. I have looked at another example i.e. MySQL pivot table query with dynamic columns.
At the moment I am receiving the results fine from the database but only by manually typing what column I need in the query. See the example below:
SELECT IFNULL(DATE( date_posted ), 'Total') AS DATE,
SUM(CASE WHEN added_by LIKE '%Team One%' THEN 1 ELSE 0 END) AS Team1,
SUM(CASE WHEN added_by LIKE '%Team Two%' THEN 1 ELSE 0 END ) AS Team2,
COUNT( added_by ) AS Daily_Total FROM teamdata WHERE status LIKE
'%completed%' GROUP BY IFNULL(DATE( date_posted ), 'Total') DESC WITH ROLLUP;
Which displays 4 columns of DATE, Team1, Team2 and Daily_Total. But at a later stage there will be more columns to add in but to be done dynamically. I am trying to execute this prepared statement but to no success:
SET #sql = NULL;
SELECT GROUP_CONCAT( DISTINCT
CONCAT(
'sum(CASE WHEN added_by = ''',
added_by,
''' THEN 1 else ''-'' END) AS `', added_by,
'`'
)
) into #sql
FROM teamdata;
SET #sql
= CONCAT('SELECT IFNULL(DATE( date_posted ), \'Total\') AS DATE, ', #sql, '
from teamdata
WHERE status = \'completed\'
GROUP BY IFNULL(DATE( date_posted ), \'Total\') DESC WITH ROLLUP');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Please could I get further help on this.
I have the following DB structure:
cars:
id
make
features:
id
name
cars_feature:
id
car_id
feature_id
value
I want to be able to select all cars with all features and return results with feature.name as the column name and cars_feature.value as the value. Right now I am able to get all feature and all values but I only figured out how to do that with group_concat.
What I am looking for is the following output:
car_id car_make color wheels doors
1 Ford blue alloy
2 Audi alloy 3
Data example: SQLFiddle
The following is a modified version of code that I found at Combine multiple rows into one row MySQL.
SET #sql = NULL;
SELECT GROUP_CONCAT(DISTINCT CONCAT(
'MAX(CASE WHEN `name` = ''',
`name`,
''' THEN value END) AS `',
`name`, '`'
)
) INTO #sql
FROM (SELECT name,
value
FROM features, car_feature
WHERE features.id = car_feature.feature_id) AS car_features_feature;
SET #sql = CONCAT('SELECT car_feature.car_id AS car_id,
cars.make AS car_make,
',
#sql,
'
FROM car_feature
LEFT JOIN cars ON car_feature.car_id = cars.id
LEFT JOIN features ON car_feature.feature_id = features.id
GROUP BY car_id;');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Thank you for the learning experience.
If I have these data in MySQL database:
PID LANG TRANS
1 EN Apple
1 DE Apfle
I want to get results in sql:
PID EN_TRANSLATION DE_TRANSLATION
1 Apple Apfle
How to do this? I already have tables and everything but need help writing query that will give me columns per language per result for each product.
NOTE: I don't know set of language(s) ! It can be EN, DE or whatever else..
Thx!
If you have known set of LANG then you can use the following technique to generate the pivot view
select
PID,
max(case when LANG = 'EN' then TRANS end) as EN_TRANSLATION,
max(case when LANG = 'DE' then TRANS end) as DE_TRANSLATION
from table_bame
group by PID
If the LANG values are unknown then you need to use dynamic sql for this as
set #sql = null;
select
group_concat(distinct
concat(
'max(case when LANG = ''',
LANG,
''' then TRANS end) AS ',
concat(LANG,'_TRANSLATION')
)
) into #sql
from table_name ;
set #sql = concat('select PID, ', #sql, ' from table_name
group by PID
');
prepare stmt from #sql;
execute stmt;
deallocate prepare stmt;
http://www.sqlfiddle.com/#!9/95d29/1