MySQL / Mariadb - Select * from Stored Procedure - mysql

I am attempting to use UF Dashbuilder which adds syntax to my SQL query and I don't know how to correct it.
The call that works is similar to:
call database.report ('1234', 'txt');
Dashbuilder turns it into this which does not work:
SELECT * FROM (call database.report ('1234', 'txt');) AS `dbSQL` LIMIT 1
I could also use the code below from the end of the stored procedure to store the results in a table and then SELECT * FROM TABLE in Dashbuilder, but I don't know how to store the results in a table (dynamic number of columns).
Can you please tell me how I can make a stored procedure work with SELECT * added or how I can store the results from this code in a table?
SET #sql = NULL;
SET SESSION GROUP_CONCAT_MAX_LEN = 1000000; -- default is 1024
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(question = ''', REPLACE(question,"'", "\\'"), ''', answer, NULL)) AS ''', REPLACE(question,"'", "\\'"), ''''
)
) INTO #sql
FROM tmp2;
SET #sql = CONCAT('SELECT id, datestamp, ', #sql, ' FROM tmp2 GROUP BY id');
-- SELECT #sql;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
UPDATE 1.
COMMENTS TO ANSWER 1:
Results from original #SQL:
`SET #sql = CONCAT('SELECT id, datestamp, ', #sql, ' FROM tmp2 GROUP BY id');`
SELECT id, datestamp, MAX(IF(question = '1. Our records show that you got care from the provider named below in the last 6 months. {Provider}.  Is that right?', answer, NULL))
AS '1. Our records show that you got care from the provider named below in the last 6 months. {Provider}.  Is that right?',
MAX(IF(question = '2. Is this the provider you usually see if you need a check-up, want advice about a health problem, or get sick or hurt?', answer, NULL))
AS '2. Is this the provider you usually see if you need a check-up, want advice about a health problem, or get sick or hurt?',
MAX(IF(question = 'Area', answer, NULL)) AS 'Area',MAX(IF(question = 'Encounter', answer, NULL)) AS 'Encounter' FROM tmp2 GROUP BY id
Results from #SQL:
ERROR: Error Code: 1166. Incorrect column name '1. Our records show that you got care from the provider named below in the last 6 months. {Provider}'
I guess there is a character that it does not like such as the single quotes?
SET #tableName = 'myreport';
SET #sql = CONCAT('CREATE TABLE ', #tableName, ' AS SELECT id, datestamp, ', #sql, ' FROM tmp2 GROUP BY id');
CREATE TABLE myreport AS SELECT id, datestamp, MAX(IF(question = '1. Our records show that you got care from the provider named below in the last 6 months. {Provider}.  Is that right?', answer, NULL))
AS '1. Our records show that you got care from the provider named below in the last 6 months. {Provider}.  Is that right?',
MAX(IF(question = '2. Is this the provider you usually see if you need a check-up, want advice about a health problem, or get sick or hurt?', answer, NULL))
AS '2. Is this the provider you usually see if you need a check-up, want advice about a health problem, or get sick or hurt?',
MAX(IF(question = 'Area', answer, NULL)) AS 'Area',MAX(IF(question = 'Encounter', answer, NULL)) AS 'Encounter' FROM tmp2 GROUP BY id
UPDATE 2:
THIS WORKS!!! THANKS!
I have to reduce the length of the column name as shown below. Then I can run the stored procedure twice per day and select * from this table in Dashbuilder.
CREATE TABLE myreport AS SELECT id, datestamp, MAX(IF(question = '1. Our records show that you got care from the provider named below in the last 6 months. {Provider}.  Is that right?', answer, NULL))
AS '1.',
MAX(IF(question = '2. Is this the provider you usually see if you need a check-up, want advice about a health problem, or get sick or hurt?', answer, NULL))
AS '2.',
MAX(IF(question = 'Area', answer, NULL)) AS 'Area',MAX(IF(question = 'Encounter', answer, NULL)) AS 'Encounter' FROM tmp2 GROUP BY id
UPDATE 3: This works! Thanks!
SET #t = CONCAT('DROP TABLE IF EXISTS ', survey_report );
PREPARE stmt FROM #t;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET #sql = NULL;
SET SESSION GROUP_CONCAT_MAX_LEN = 1000000; -- default is 1024
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(question = ''', REPLACE(question,"'", "\\'"), ''', answer, NULL)) AS ''', REPLACE(udf_clean_column_name(15, udf_remove_tags(question)),"'", "\\'"), ''''
)
) INTO #sql
FROM tmp2;
SET #sql = CONCAT('CREATE TABLE ', survey_report, ' AS SELECT id, datestamp, ipaddr, ', #sql, ' FROM tmp2 GROUP BY id');
-- SELECT #sql;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
DROP TEMPORARY TABLE IF EXISTS `survey_lookup`;
DROP TEMPORARY TABLE IF EXISTS `tmp`;
DROP TEMPORARY TABLE IF EXISTS `tmp2`;
Mysql Function to remove spaces, etc. from the column names.
CREATE FUNCTION `udf_clean_column_name`(col_name_len INT, str varchar(200)) RETURNS varchar(200)
BEGIN
SET str = SUBSTRING(str,1,col_name_len);
SET str = TRIM(str);
SET str = Replace(str,' ','_');
SET str = Replace(str,'-','');
SET str = Replace(str,'?','');
SET str = Replace(str,',','');
SET str = Replace(str,'.','');
RETURN str;
END;
Mysql function to remove tags (I don't recall where I got this function).
CREATE FUNCTION `udf_remove_tags`(Dirty varchar(4000))
RETURNS varchar(4000)
DETERMINISTIC
BEGIN
DECLARE iStart, iEnd, iLength int;
WHILE Locate( '<', Dirty ) > 0 And Locate( '>', Dirty, Locate( '<', Dirty )) > 0 DO
BEGIN
SET iStart = Locate( '<', Dirty ), iEnd = Locate( '>', Dirty, Locate('<', Dirty ));
SET iLength = ( iEnd - iStart) + 1;
IF iLength > 0 THEN
BEGIN
SET Dirty = Insert( Dirty, iStart, iLength, '');
set Dirty = Replace(Dirty,' ',''); #No space between & and nbsp;
set Dirty = Replace(Dirty,'\r','');
set Dirty = Replace(Dirty,'\n','');
END;
END IF;
END;
END WHILE;
RETURN Dirty;
END;

I don't see any mention of stored procedures in the UF DashBuilder documentation, so it looks like they don't have a way around this.
You can create a table from a SELECT query. If you omit the column specifications, they'll be derived automatically from the select list of the query.
SET #sql = CONCAT('CREATE TABLE ', tableName, ' AS
SELECT id, datestamp, ', #sql, ' FROM tmp2 GROUP BY id');
PREPARE stmt FROM #sql
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

Related

How to call MySQL function for every column that is present in table?

I need to call a mySQL function for all columns in a table.
I know how to do it for a particular column
Like this:
UPDATE `table_name` set `column_name` = function_name(`column_name`)
But i have no clue how to do it for all columns at once.
Thanks in advance.
Little clarification: I dont want to manually mention all columns, as i probably could have 200 columns table.
But i have no clue how to do it for all columns at once.
You just can't - there is no such shortcut in the update syntax.
You can do this with a single update statement, but you need to enumerate each and every column, like:
update table_name set
column_name1 = function_name(column_name1),
column_name2 = function_name(column_name2),
column_name3 = function_name(column_name3)
An alternative would be to use dynamic SQL to programatically generate the proper query string from catalog table information_schema.columns, and then execute it. This seems uterly complicated for what looks like a one-shot task... But here is sample code for that:
-- input variables
set #table_schema = 'myschema';
set #table_name = 'mytable';
set #function_name = 'myfunction';
-- in case "GROUP_CONCAT()" returns more than 1024 characters
set session group_concat_max_len = 100000;
-- build the "set" clause of the query string
select
#sql := group_concat(
'`', column_name, '` = ', #table_schema, '.', #function_name, '(`', column_name, '`)'
separator ', '
)
from information_schema.columns
where table_schema = #table_schema and table_name = #table_name;
-- entire query string
set #sql := concat('update ', #table_schema, '.', #table_name, ' set ', #sql);
-- debug
select #sql mysql;
-- execute for real
prepare stmt from #sql;
execute stmt;
deallocate prepare stmt;

How can I pass my in's in a MySQL stored procedure as the column titles?

In this stored procedure let's use these for the example:
varReportYear1 = '2018'
varReportYear2 = '2019'
I want those two years to be the column titles in this pivot table.
In the SELECT you can see I've added AS '2018' and AS '2019', but ideally these would equal the incoming values.
Any help is very appreciated -- thank you!.
CREATE DEFINER=`me`#`%` PROCEDURE `spPivotReport`(
in varReportYear1 char(4),
in varReportYear2 char(4)
)
BEGIN
/*
* I want to have the values for
* varReportYear1 and varReportYear2
* set as column headers
* in this pivot table.
*/
SELECT
`query_for_pivot`.`school` AS varschool,
COUNT(IF((`query_for_pivot`.`year_of_request` = varReportYear1),
`query_for_pivot`.`request_id`,
NULL)) AS '2018',
COUNT(IF((`query_for_pivot`.`year_of_request` = varReportYear2),
`query_for_pivot`.`request_id`,
NULL)) AS '2019'
FROM
`query_for_pivot`
WHERE
`query_for_pivot`.`year_of_request` = varReportYear1
OR `query_for_pivot`.`year_of_request` = varReportYear2
GROUP BY `query_for_pivot`.`school`
ORDER BY `query_for_pivot`.`school`
;
END
OKAY... WITH a little help I was able to answer my own question. Thank you to everyone who answered.
This answer has been tested in MySQL 5.6.12 and 8.0.14. To come up with a working stored procedure, I modified #GMB's answer.
What did I change from GMB's sp? (see below)
For the column headers, I had to concatenate yr_ to the variable value. It did not like the digit-only column headers (varReportYear1='2018', varReportYear2='2019' in my example).
I had to put single quotes around the variables.
Here's my working code:
CREATE DEFINER=`root`#`localhost` PROCEDURE `spPivotReportPrepared`(
in varReportYear1 char(4),
in varReportYear2 char(4)
)
BEGIN
SET #sql = CONCAT(
'SELECT
`query_for_pivot`.`school` AS varschool,
COUNT(IF((`query_for_pivot`.`year_of_request` = ',varReportYear1,'),
`query_for_pivot`.`request_id`,
NULL)) AS yr_',varReportYear1, ',
COUNT(IF((`query_for_pivot`.`year_of_request` = ',varReportYear2,'),
`query_for_pivot`.`request_id`,
NULL)) AS yr_',varReportYear2,'
FROM
`query_for_pivot`
WHERE
`query_for_pivot`.`year_of_request` = ',varReportYear1,'
OR `query_for_pivot`.`year_of_request` = ',varReportYear2,'
GROUP BY `query_for_pivot`.`school`
ORDER BY `query_for_pivot`.`school`');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
you can use prepared statements. see below
CREATE DEFINER=`me`#`%` PROCEDURE `spPivotReport`(
in varReportYear1 char(4),
in varReportYear2 char(4)
)
BEGIN
PREPARE stmt1 FROM 'SELECT
`query_for_pivot`.`school` AS varschool,
COUNT(IF((`query_for_pivot`.`year_of_request` = ?),
`query_for_pivot`.`request_id`,
NULL)) AS ?,
COUNT(IF((`query_for_pivot`.`year_of_request` = ?),
`query_for_pivot`.`request_id`,
NULL)) AS ?
FROM
`query_for_pivot`
WHERE
`query_for_pivot`.`year_of_request` = ?
OR `query_for_pivot`.`year_of_request` = ?
GROUP BY `query_for_pivot`.`school`
ORDER BY `query_for_pivot`.`school`';
EXECUTE stmt1 USING
varReportYear1 ,
varReportYear1,
varReportYear2,
varReportYear2,
varReportYear1,
varReportYear2 ;
DEALLOCATE PREPARE stmt1;
END;

Limit the columns displayed in a MYSQL pivot table

The Question:
How do I limit the number of columns displayed/produced on a MYSQL pivot table?
My Setup:
I have a table named "updates" that looks like the following:
I have the following snippet of query (This is only part of the query, the whole thing only adds more columns from other tables but this is the only section that gets pivoted):
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(Date = ''',
Date,
''', Description, NULL)) AS ',
CONCAT("'",Date_Format(Date, '%d/%m/%Y'),"'")
)
)INTO #sql
FROM updates;
SET #sql = CONCAT('SELECT Action, ', #sql, ' FROM updates GROUP BY Action');
PREPARE stmt FROM #sql;
EXECUTE stmt;
The result of this query is as follows:
As you can see, this pivots the table as intended with the dates as columns. However, there is potential for these updates (to actions) to become very long before they are "closed" and not displayed. Therefore, I would like to limit the outcome to the latest 3 updates. BUT..Not per action as this would potentially still give me a lot of updates in the pivot table.
I would like to have the most recent 3 dates from the updates table with all updates for each date keeping this pivot format.
Example: The outcome table above would look the same but with the exception of the columns titled "02/10/2016" and "04/10/2016".
Thanks in advance for any assistance or advise.
For anyone else trying to solve this issue, I managed to use the following query to produce the desired results:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(Date = ''',
Date,
''', Description, NULL)) AS ',
CONCAT("'",Date_Format(Date, '%d/%m/%Y'),"'")
) ORDER BY Date ASC
) INTO #sql
FROM (
SELECT * FROM updates
GROUP BY Date
ORDER BY Date DESC
LIMIT 2)
AS updates;
SET #sql = CONCAT('SELECT Action, ', #sql, ' FROM updates GROUP BY Action');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

Mysql with parameters not working

I have a mysql stored procedure like this:
CREATE DEFINER=`root`#`localhost` PROCEDURE `accounts_summary_by`(
IN **created_by** int(10)
)
BEGIN
SET group_concat_max_len=2048;
SET #sql = NULL;
SELECT
GROUP_CONCAT(
DISTINCT CONCAT(
'MAX(IF(fiscal_year = ''',
fiscal_year,
''', amount, 0)) AS ',
CONCAT("'",fiscal_year,"'")
)
) INTO #sql
FROM
net_savings;
SET #sql = CONCAT('SELECT accounts.id,
accounts.account,
accounts.region,
accounts.cluster,
accounts.industry, ,'
,#sql,
'FROM net_savings join accounts
on accounts.id = net_savings.account_id
Where accounts.user_id = **created_by**
GROUP BY accounts.account,net_savings.account_id
ORDER BY accounts.id');
PREPARE statement FROM #sql;
EXECUTE statement;
DEALLOCATE PREPARE statement;
END
But upon calling the procedure like these:
CALL accounts_summary_by(2)
2 is a user_id reference to another table called users.
It gave me an error. Please help as I can't find any fixed to my problem.
0 72 23:41:12 CALL `buckets`.`accounts_summary_by`(1) Error Code: 1054. Unknown column 'created_by' in 'where clause' 0.000 sec
MySQL's internal programming language is not php, it is not going to resolve variables within a text, so you need to concat it properly to the middle of the prepared statement:
SET #sql = CONCAT('SELECT accounts.id,
accounts.account,
accounts.region,
accounts.cluster,
accounts.industry,'
,#sql,
'FROM net_savings join accounts
on accounts.id = net_savings.account_id
Where accounts.user_id ='
,created_by
,'GROUP BY accounts.account,net_savings.account_id
ORDER BY accounts.id');
Since created_by is a parameter of the procedure, you do not need to preposition it with #.

How can I assign a variable with a prepared statement in a stored procedure?

I've put together a simple stored procedure in which two parameters are passed through to make it more dynamic. I've done this with a prepared statement in the "First Two Digits and Count of Records" section.
What I'm not sure of is if I can make the SET vTotalFT section dynamic with a prepared statement as well.
At the moment I have to hard-code the table names and fields. I want my vTotalFT variable to be assigned based on a prepared dynamic SQL statement, but I'm not sure of the syntax. The idea is that when I call my procedure, I could tell it which table and which field to use for the analysis.
CREATE PROCEDURE `sp_benfords_ft_digits_analysis`(vTable varchar(255), vField varchar(255))
SQL SECURITY INVOKER
BEGIN
-- Variables
DECLARE vTotalFT int(11);
-- Removes existing table
DROP TABLE IF EXISTS analysis_benfords_ft_digits;
-- Builds base analysis table
CREATE TABLE analysis_benfords_ft_digits
(
ID int(11) NOT NULL AUTO_INCREMENT,
FT_Digits int(11),
Count_of_Records int(11),
Actual decimal(18,3),
Benfords decimal(18,3),
Difference Decimal(18,3),
AbsDiff decimal(18,3),
Zstat decimal(18,3),
PRIMARY KEY (ID),
KEY id_id (ID)
);
-- First Two Digits and Count of Records
SET #s = concat('INSERT INTO analysis_benfords_ft_digits
(FT_Digits,Count_of_Records)
select substring(cast(',vField,' as char(50)),1,2) as FT_Digits, count(*) as Count_of_Records
from ',vTable,'
where ',vField,' >= 10
group by 1');
prepare stmt from #s;
execute stmt;
deallocate prepare stmt;
SET vTotalFT = (select sum(Count_of_Records) from
(select substring(cast(Gross_Amount as char(50)),1,2) as FT_Digits, count(*) as Count_of_Records
from supplier_invoice_headers
where Gross_Amount >= 10
group by 1) a);
-- Actual
UPDATE analysis_benfords_ft_digits
SET Actual = Count_of_Records / vTotalFT;
-- Benfords
UPDATE analysis_benfords_ft_digits
SET Benfords = Log(1 + (1 / FT_Digits)) / Log(10);
-- Difference
UPDATE analysis_benfords_ft_digits
SET Difference = Actual - Benfords;
-- AbsDiff
UPDATE analysis_benfords_ft_digits
SET AbsDiff = abs(Difference);
-- ZStat
UPDATE analysis_benfords_ft_digits
SET ZStat = cast((ABS(Actual-Benfords)-IF((1/(2*vTotalFT))<ABS(Actual-Benfords),(1/(2*vTotalFT)),0))/(SQRT(Benfords*(1-Benfords)/vTotalFT)) as decimal(18,3));
First, to use dynamic table/column names, you'll need to use a string/Prepared Statement like your first query for #s. Next, to get the return-value from COUNT() inside of the query you'll need to use SELECT .. INTO #vTotalFT.
The following should be all you need:
SET #vTotalFTquery = CONCAT('(select sum(Count_of_Records) INTO #vTotalFT from
(select substring(cast(', vField, ' as char(50)),1,2) as FT_Digits, count(*) as Count_of_Records
from ', vTable, '
where ', vField, ' >= 10
group by 1) a);');
PREPARE stmt FROM #vTotalFTquery;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Please note: the variable name has changed from vTotalFT to #vTotalFT. It doesn't seem to work without the #. And also, the variable #vTotalFT won't work when declared outside of/before the query, so if you encounter an error or empty results that could be a cause.
SELECT CONCAT (
'SELECT DATE(PunchDateTime) as day , '
,GROUP_CONCAT('GROUP_CONCAT(IF(PunchEvent=', QUOTE(PunchEvent), ',PunchDateTime,NULL))
AS `', REPLACE(PunchEvent, '`', '``'), '`')
,'
FROM tbl_punch
GROUP BY DATE(PunchDateTime)
ORDER BY PunchDateTime ASC
'
)
INTO #sql
FROM (
SELECT DISTINCT PunchEvent
FROM tbl_punch
) t;
PREPARE stmt
FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;