I have below database table : Table name - dim_module
id Creation_Date Goals Alternative Value
-----------------------------------------------------------
1 2014-04-17 10:09:30 G1 A 0.86
2 2014-04-17 10:09:30 G1 B 0.87
3 2014-04-17 10:09:30 G2 A 0.5
4 2014-04-17 10:09:30 G2 B 0
I am using below procedure for getting desired output
CREATE DEFINER=`root`#`localhost` PROCEDURE `stmt`()
BEGIN
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(goals = ''',
goals,
''', round(value, 2), NULL)) AS ',
goals
)
) INTO #sql
FROM sgwebdb.dim_module;
SET #sql = CONCAT('SELECT alternative, ', #sql, ' FROM sgwebdb.dim_module GROUP BY alternative');
prepare stmt from #sql;
But if Dim_Module don't have any row then procedure is not take care and I am getting error.
Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'NULL' at line 1
Please help to take care of empty table.
Maybe because #sql is null.
Try setting it to some value with IFNULL
SET #sql = CONCAT('SELECT alternative, ', IFNULL(#sql,'somecolumnor*here'), ' FROM sgwebdb.dim_module GROUP BY alternative');
Or detect that #sql is null and do something else.
You may check #sql before using it within your second SELECT:
IF #sql
THEN
SET #sql = CONCAT('SELECT alternative, ', #sql, ' FROM sgwebdb.dim_module GROUP BY alternative');
prepare stmt from #sql;
END IF;
Related
I have following tables
demographic_categories
demographic_id demographic_name
1 color
2 age_group
project_tests
test_id group_id project_id
1 1 1
2 1 1
test_demographic_requirements
test_id project_id demgraphic_id demographic_value
1 1 1 blue
1 1 2 young
2 1 1 green
2 1 2 middle
And I need a query which would give me following result :
test_id group_id color age_group
1 1 blue young
2 1 green middle
I guess we need to use concept of pivot table to get the result, but I am unable to. And I demographic categories can we might be same they tend to change so I need something dynamic so what would be the best way to do it ?
I have tried doing the following based of previous similar questions but it doesn't seems to work for me, here is what I have tried:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'max(case when dc.demographic_name = ''',
demographic_name,
''' then trd.demographic_value end) AS ',
replace(demographic_name, ' ', '')
)
) INTO #sql
from demographic_categories;
SET #sql = CONCAT('SELECT pt.test_id, pt.group_id,
', #sql,'
from test_requirement_demographic trd
LEFT JOIN demographic_categories dc ON trd.demographic_id = dc.demographic_id
LEFT JOIN project_tests pt ON pt.test_id = trd.test_id and project_id =1
group by pt.test_id;');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Your dynamic SQL is just fine except for one thing. It has an ambiguous column project_id on your second left join, Just replace it with pt.project_id or trd.project_id and it will give desired result.
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'max(case when dc.demographic_name = ''',
demographic_name,
''' then trd.demographic_value end) AS ',
replace(demographic_name, ' ', '')
)
) INTO #sql
from demographic_categories;
SET #sql = CONCAT('SELECT pt.test_id, pt.group_id,
', #sql,'
from test_demographic_requirements trd
LEFT JOIN demographic_categories dc ON trd.demographic_id = dc.demographic_id
LEFT JOIN project_tests pt ON pt.test_id = trd.test_id and pt.project_id =1
group by pt.test_id;');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
I ran it on a rextester. Here is the link : dynamic_pivot_test
So I am making a stored procedure in MySQL that accepts an integer _prof_id. This code is for changing my rows to columns dynamically.
Here is my code
DELIMITER $$
CREATE PROCEDURE Get_Attendance(IN _profID INT)
BEGIN
SET SESSION group_concat_max_len = 18446744073709547520;
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(date = ''',
date,
''', status_description, NULL)) AS ',
date
)
) INTO #sql
FROM current_att_view;
SET #sql = CONCAT(
'SELECT a.professor_fname, a.professor_lname, ', #sql,
' FROM professor_tbl m JOIN current_att_view a
ON m.professor_id = a.professor_id
WHERE m.professor_id = ', _profID,' Group BY entity_ID');
PREPARE stmt FROM #sql;
EXECUTE stmt;
END$$
DELIMITER ;
so I have researched and found out that 18446744073709547520 is the maximum value for the group_concat_max_len and it cannot be unlimited. but when I call the procedure, it gives me an error
Mysql Error Image, which came from the 24th and 25th row of my table 24th and 25th row of table image. There are still plenty of rows to concatenate. What should I do? Or are there any other way in order for me to change the rows to columns dynamically?
so my current_att_view (Which is a created view) structure is current_att_view and the professor table structure is like this Professor Table and I want to generate a query that would make a table structure like this
Table Structure
When I tried my code for group concat, the output is shown as output
Here's a guess how a simplified version of your data looks
drop table if exists current_att_view,professor_tbl;
create table current_att_view
(attendanceid int auto_increment primary key,
entity_id int,
Professor_id int,
dte date,
status_description varchar(8));
create table professor_tbl
(professor_id int, professor_fname varchar(20), professor_lname varchar(20));
insert into current_att_view (entity_id ,
Professor_id ,
dte ,
status_description) values
(1,1,'2018-01-01','Present'),(1,1,'2018-01-02','Absent'),(1,1,'2018-01-03','Present'),
(2,1,'2018-01-01','Absent'),(2,1,'2018-01-02','Absent'),(2,1,'2018-01-03','Present');
insert into professor_tbl values
(1,'bob','smith'),(2,'fred','jones');
select p.professor_fname,p.professor_lname, dte,status_description
from professor_tbl p
join current_att_view a on a.professor_id = p.professor_id
This procedure
drop procedure if exists p;
DELIMITER $$
CREATE PROCEDURE p(IN _profID INT)
BEGIN
SET SESSION group_concat_max_len = 18446744073709547520;
#SET SESSION group_concat_max_len = 1024;
SET #sql = NULL;
set #sql = (
SELECT
GROUP_CONCAT(DISTINCT
CONCAT('MAX(IF(dte = ', char(39),dte,char(39), ', status_description , null)) AS ', char(39),dte,char(39))
)
from current_att_view
);
select length(#sql);
SET #sql = CONCAT(
'SELECT m.professor_lname,m.professor_fname,entity_id ,', #sql,
' FROM professor_tbl m JOIN current_att_view a
ON m.professor_id = a.professor_id
WHERE m.professor_id = ', _profID,' Group BY m.professor_lname,m.professor_fname,entity_id ');
#set #sql = concat('select ',#sql,' from attendance;');
PREPARE stmt FROM #sql;
EXECUTE stmt;
deallocate prepare stmt;
END$$
DELIMITER ;
Generates this query
SELECT m.name,entity_id ,MAX(IF(dte = '2018-01-01', status_description , null)) AS '2018-01-01',
MAX(IF(dte = '2018-01-02', status_description , null)) AS '2018-01-02',
MAX(IF(dte = '2018-01-03', status_description , null)) AS '2018-01-03'
FROM professor_tbl m
JOIN current_att_view a ON m.id = a.professor_id
WHERE m.id = 1 Group BY m.name, a.entity_ID;
Which produces this result (as expected)
-----------------+-----------------+-----------+------------+------------+------------+
| professor_lname | professor_fname | entity_id | 2018-01-01 | 2018-01-02 | 2018-01-03 |
+-----------------+-----------------+-----------+------------+------------+------------+
| smith | bob | 1 | Present | Absent | Present |
| smith | bob | 2 | Absent | Absent | Present |
+-----------------+-----------------+-----------+------------+------------+------------+
2 rows in set (0.01 sec)
I don't think I can help you any further.
Not an answer more an extended comment.This code works fine for every day in 2016-2017 (albeit very slowly) ie 730 days which seems to be more than you would need. I think this proves there is no group_concat truncation on my 64bit box where max_allowed_packed is default 4194304 at least.
drop procedure if exists p;
DELIMITER $$
CREATE PROCEDURE p(IN _profID INT)
BEGIN
SET SESSION group_concat_max_len = 18446744073709547520;
#SET SESSION group_concat_max_len = 1024;
SET #sql = NULL;
set #sql = (
SELECT
GROUP_CONCAT(DISTINCT
CONCAT('MAX(IF(dte = ', char(39),dte,char(39), ', 1, 0)) AS ', char(39),dte,char(39))
)
from dates
where year(dte) in(2016,2017)
);
/*
SET #sql = CONCAT(
'SELECT a.professor_fname, a.professor_lname, ', #sql,
' FROM professor_tbl m JOIN current_att_view a
ON m.professor_id = a.professor_id
WHERE m.professor_id = ', _profID,' Group BY entity_ID');
*/
set #sql = concat('select ',#sql,' from dates;');
PREPARE stmt FROM #sql;
EXECUTE stmt;
deallocate prepare stmt;
END$$
DELIMITER ;
call p(1);
At MySQL pivot efficiency
I was wondering how to pivot a table of stock prices in MySQL. The consensus seems to be that this is not a good thing to do. I originally wanted each column to be an ID number and each row to be a date. I got much more speed by making each column a date and each row an ID number:
SET ##group_concat_max_len = 1000000;
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(ddate = ',
'ddate',
',closing_price, NULL)) AS ''',
DATE_FORMAT(ddate,'%Y%m%d'),''''
)
) INTO #sql
FROM rawdatatable;
SET #sql = CONCAT('SELECT idnum , ', #sql, ' FROM rawdatatable GROUP BY idnum');
SELECT #sql;
PREPARE stmt FROM #sql;
EXECUTE stmt;
Is there any way to put the results of the EXECUTE statement into a table, where each column name is a different ddate?
Change this line
SET #sql = CONCAT('SELECT idnum , ', #sql, ' FROM rawdatatable GROUP BY idnum');
to
SET #sql = CONCAT('CREATE TABLE blah AS SELECT idnum , ', #sql, ' FROM rawdatatable GROUP BY idnum');
Try to put the result into a temp table like #Desolator say. Something like this in MySQL console
mysql>CREATE TABLE temp_result AS
SELECT * FROM rawdatatable
WHERE ...;
my query :
set #sql = NULL;
select
group_concat(distinct
concat(
'max(if(first.study = ''',
study, ''', first.avg_scores, NULL)) as ',
study
)
) into #sql
from `table`;
SET #sql = concat('select first.name,
', #sql, '
from `table` first
join `table` second
on first.name = second.name
group by first.name');
prepare stmt from #sql;
execute stmt;
deallocate prepare stmt;
when data in column "study"
=================================
name | study | avg_scores
=================================
alfa c 75
beta c 70
alfa php 85
beta php 90
and result : its true.
===========================
name | c | php
===========================
alfa 75 85
beta 70 90
the problem when i have data in table like this :
===================================
name | study | avg_scores
===================================
alfa junior c 75
beta junior c 70
alfa junior php 85
beta junior php 90
error :
"You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version
for the right syntax to use near 'c,max(if(first.study = 'junior php',
first.avg_scores, NULL)) as junior php ' at line 2: prepare stmt from #sql"
so : how to fix my query ?
The problem is that you need to escape the identifiers, because a column name cannot have a space. The following uses double quotes for the name (you can also use backticks):
set #sql = NULL;
select group_concat(distinct concat('max(if(first.study = ''', study,
''', first.avg_scores, NULL)) as "', study, '"'
---------------------------------------------------------------------^ ----------^
)
) into #sql
from `table`;
SET #sql = concat('select first.name, ', #sql, '
from `table` first join `table` second on first.name = second.name
group by first.name');
just playing around with some sql statements on MYSQL Server 5.1
I already asked a question how to make a specific count on two tables (see here)
and I also found an answer how to transpose my result (see here) but I cant use it on my local MYSQL Server 5.1.
This is table one: test
id|name|test_type
-------------
1|FirstUnit|1
2|FirstWeb|2
3|SecondUnit|1
The second table: test_type
id|type
--------
1|UnitTest
2|WebTest
The following result will be written into "yourtable" (a temporary table)
type|amount
-----------
UnitTest|2
WebTest|1
What I want to have at the end is:
UnitTest|WebTest
-----------------
2|1
(The problem is I think, the the last part is from an MS-SQL example, so it will not work on MYSQL)
This are my sql statements:
--create a temporary table
create temporary table IF NOT EXISTS yourtable
(
test_type varchar(255),
amount varchar(255)
);
--make a selecten into the temporary table
INSERT INTO yourtable
SELECT
t.test_type ,
COUNT(*) AS amount
FROM test_types AS t
JOIN test AS te ON t.id= te.test_type
GROUP BY test_type
ORDER BY t.test_type;
--just for debugging
select * from yourtable;
-- transpose result
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(Type)
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ' + #cols + ' from
(
select Type, Amount,
row_number() over(partition by Type order by Type, Amount) rn
from yourtable
) x
pivot
(
max(Amount)
for Type in (' + #cols + ')
) p '
execute(#query)
--drop temporary table
drop table yourtable;
I can't run the last part where I want to transpose my temporary result. I get an error for "DECLARE"
/* SQL Error (1064): You have an error in your SQL syntax; check the
manual that corresponds to your MySQL server version for the right
syntax to use near 'DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = ' at line 2 / / 2 rows affected, 2 rows found.
Duration for 3 of 4 queries: 0,000 sec. */
Can anybody help?
MySQL Does not have a pivot function so you will have to transpose the data from rows into columns using an aggregate function with a CASE expression:
select
sum(case when tt.type = 'UnitTest' then 1 else 0 end) UnitTest,
sum(case when tt.type = 'WebTest' then 1 else 0 end) WebTest
from test t
inner join test_type tt
on t.test_type = tt.id
See SQL Fiddle with Demo.
If you are going to have an unknown number of types that you want to convert to columns, you can use a prepared statement to generate dynamic SQL:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'sum(CASE WHEN tt.type = ''',
type,
''' THEN 1 else 0 END) AS `',
type, '`'
)
) INTO #sql
FROM test_type;
SET #sql
= CONCAT('SELECT ', #sql, '
from test t
inner join test_type tt
on t.test_type = tt.id');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
See SQL Fiddle with Demo