I want to loop through an associative array that represents a table's columns and values, and call a stored procedure on each iteration that will insert each value into its respective column. The assoc. array and loop looks like this:
public static function update(
int $auctionId,
string $title,
string $description
) : void
{
$new = [
'auction_title' => $title,
'description' => $description
];
foreach ($new as $columnName => $value) {
Database::conn()->callSP('sp_auctions_update', [$auctionId, $columnName, $value]);
}
}
The stored procedure looks like this:
DELIMITER $$
DROP PROCEDURE IF EXISTS sp_auctions_update $$
CREATE PROCEDURE sp_auctions_update(
IN auctionId INT UNSIGNED,
IN columnName,
IN value,
)
SQL SECURITY INVOKER
MODIFIES SQL DATA
BEGIN
UPDATE auctions SET #columnName=#value, WHERE id=#auctionId;
END $$
DELIMITER ;
Is this possible to do? Or is there a better alternative? Many thanks
do you know what the prepared statement would look like in this instance?
CREATE PROCEDURE sp_auctions_update(
IN auctionId INT UNSIGNED,
IN columnName VARCHAR(64),
IN `value` INT UNSIGNED
)
SQL SECURITY INVOKER
MODIFIES SQL DATA
BEGIN
-- Build SQL query text, insert column name from variable into it
SET #sql := CONCAT('UPDATE auctions SET ', columnName, '=? WHERE id=?;');
-- Reassign parameters values from local variables to user-defined ones
-- because local variables cannot be used in USING
SET #value := `value`;
SET #auctionId := auctionId;
-- Prepare and execute the query
PREPARE stmt FROM #sql;
EXECUTE stmt USING #value, #auctionId;
DEALLOCATE PREPARE stmt;
END
Alternatively you may concat ALL parameters values into the query text:
CREATE PROCEDURE sp_auctions_update(
IN auctionId INT UNSIGNED,
IN columnName VARCHAR(64),
IN `value` INT UNSIGNED
)
SQL SECURITY INVOKER
MODIFIES SQL DATA
BEGIN
-- Build SQL query text, insert parameters from variables into it
SET #sql := CONCAT('UPDATE auctions SET ', columnName, '=\'', `value`, '\' WHERE id=', auctionId, ';');
-- Prepare and execute the query
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
Caution.
The columnName parameter value is inserted into SQL query text as-is - so injection is possible. You must add the code which controls this parameter value. For example you may check that the column with provided name exists in table structure.
Another 2 parameters cannot be a source of injection due to their numeric datatype.
BEGIN
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(questions= ''',
questions,
''', yes_no, NULL)) AS ''',
questions,''''
)
) INTO #sql
FROM tbl_main where task=in_task;
SET #sql = CONCAT('SELECT case_id,audited_by,resolved_by, ', #sql, ' FROM tbl_main WHERE task IN (''',in_task,''') and audited_date between(''',from_date,''') and (''',to_date,''') GROUP BY case_id,audited_by,resolved_by');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
Hi, I'm using the stored procedure and I'm converting row values to column headers dynamically , the stored procedure works fine if the row value is within 50 words or so and it fails when the row value is longer than 50 words . I don't know how to fix it. Can any one help me with this .
set session group_concat_max_len = 5000;
I have been trying to change existing table and insert the result into temporary table with memoery engine
1) Step - I swaped rows and columns from existing table
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(`parameterId` = ', `parameterId`, ',`valueId`,NULL)) AS parameter', `parameterId`)
) INTO #sql
FROM product_parameter;
SET #sql = CONCAT('SELECT productId , ', #sql , ' FROM product_parameter GROUP BY productId');
PREPARE stmt FROM #sql;
When I execute the stmt statement it will show me a the result
2) Step - I would like to insert the result to temporary table or for testing into physical table
CREATE TEMPORARY TABLE IF NOT EXISTS temporaryTable ENGINE=MEMORY AS
Is it possible to acomplish my goal by one query? Or should i create table then somehow add the result data?
You should create the temporary table and the populate it.
I've written this code for mysql pivot table:
SET #SQL = NULL;
SET ##group_concat_max_len = 6000;
SELECT GROUP_CONCAT( DISTINCT CONCAT( 'MAX(IF(questiondetails = \'', questiondetails, '\', answer, null)) AS \'', questiondetails, '\' ' )) INTO #SQL FROM wtfeedback;
SET #SQL = CONCAT( 'SELECT trialid, productsku, userkey, category, ', #SQL, ' FROM wtfeedback GROUP BY trialid' );
PREPARE stmt FROM #SQL;
EXECUTE stmt;
This works fine in Sequel Pro (mysql gui editor)
But when I paste into my php page to run this code it is showing a syntax 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 'SET ##group_concat_max_len = 6000;
SELECT GROUP_CONCAT( DISTINCT CONCAT( 'MAX(I' at line 3
I'm struggling to see what the error might be.
Any ideas ? Thanks in advance.
MySQL doesn't use '' to escape single quotes. If you want to embed single quotes in your in-sql strings, then use \':
CONCAT('MAX(IF(questiondetails = \'', questiondetails, '\', answer, null)) AS "', questiondetails, '" ')
^^---------------------^^
From PHP, issue only one statement at a time. I deduce that this is the problem since the error is pointing at the beginning of the second SET.
Here is a stored proc to generate a pivot SELECT for you.
I was struggling with the same issue as Guy Murray, but fought my way out.
Basically I made a stored procedure that lets you run a pivot table on selectable rows and columns, with optional filtering. It does so by first storing the result of a "group by" select query in a temp table, and then fiddling that to a pivot table with the "group_concat" function. Same trick as Guy does.
The advantage is that it goes through the main table only once, which may save time if there are zillions of records in it.
Here is a sample table:
CREATE TABLE `Data` (
`Period` INT(2) NOT NULL,
`Product` VARCHAR(20) NOT NULL DEFAULT '',
`Amount` DOUBLE NOT NULL
) ENGINE=INNODB DEFAULT CHARSET=latin1;
INSERT INTO `Data` (`Period`, `Product`, `Amount`)
VALUES
(1,'PrdA',15484),
(1,'PrdA',45454),
(1,'PrdB',478),
(2,'PrdB',985),
(2,'PrdB',741),
(2,'PrdB',985),
(3,'PrdA',7515),
(3,'PrdA',454),
(3,'PrdB',4584),
(2,'PrdB',445),
(1,'PrdB',669);
And this is the stored procedure. Additional comment in the code.
DELIMITER ;;
CREATE DEFINER=`root`#`localhost` PROCEDURE `pivot`(
source VARCHAR(1000),
val VARCHAR(40),
rws VARCHAR(40),
cls VARCHAR(40),
filter VARCHAR(1000))
BEGIN
/*
Creates a pivot table from any table, view or SQL statement.
Mandatory: source, value, rows, and columns to be pivoted.
Optional filtering.
Sample call strings:
CALL pivot('data', 'amount', 'period', 'product', '');
CALL pivot('(select * from data)', 'amount', 'product', 'period', 'WHERE amount>1000');
*/
/*just to be sure*/
DROP TEMPORARY TABLE IF EXISTS temp1;
/*increase the value of group concat, otherwise the number of columns is very limited*/
SET SESSION group_concat_max_len = 100000;
/*perform a "select...group by" on the source and store it in a temp table1*/
SET #a=CONCAT(
'CREATE TEMPORARY TABLE temp1 (
SELECT ',
rws,' AS rows, ',
cls,' AS cols,
SUM(',val,') AS val
FROM ',source,' S ',
filter, '
GROUP BY '
,rws,', ',
cls,');'
);
PREPARE stmt FROM #a;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
/*use "distinct columns" from temp1 to make a text string #coltext, that contains the column statements, to be used in the final step
Produced text string looks like this: sum(CASE WHEN cols='PrdA' THEN val END) AS 'PrdA', sum(CASE WHEN cols='PrdB' THEN val END) AS 'PrdB' */
SELECT GROUP_CONCAT(
' SUM(CASE WHEN cols=\'',cols,'\' THEN val END) AS \'',cols,'\'')
INTO #coltext
FROM (SELECT DISTINCT(cols) AS cols FROM temp1) A;
/*build the final statement in #b*/
SET #b=CONCAT(
'SELECT
IFNULL(rows, \'Total\') AS ',rws,', '
,#coltext,',
SUM(val) AS Total
FROM temp1
GROUP BY
rows
WITH ROLLUP;');
/*and launch it*/
PREPARE stmt FROM #b;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
/*clean up*/
DROP TEMPORARY TABLE IF EXISTS temp1;
SET #a=NULL;
SET #b=NULL;
SET #coltext=NULL;
END;;
DELIMITER ;
The result looks like this:
period PrdA PrdB total
1 60938 1147 62085
2 NULL 3156 3156
3 7969 4584 12553
total 68907 8887 77794
Hope this shows up correctly on stack overflow. It's my first post here.
edit 2015-10-19: when reading others solutions here, I realised that the code could be cleaned up and improved: it's now free of any hardcoded references. Just plug it in any database and it will work.
I have a table in which i want to insert multiple rows having values from a php array , now i can't figure out how to pass an array in stored procedure .
Example-: i have a php array having names ['sqlite','mysql','sql']
now what i want is to send this array to stored procedure and loop through the array taking one value at a time and inserting into the database table.
You can pass a string with your list and use a prepared statements to run a query, e.g. -
DELIMITER $$
CREATE PROCEDURE GetFruits(IN fruitArray VARCHAR(255))
BEGIN
SET #sql = CONCAT('SELECT * FROM Fruits WHERE Name IN (', fruitArray, ')');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
$$
DELIMITER ;
How to use:
SET #fruitArray = '\'apple\',\'banana\'';
CALL GetFruits(#fruitArray);
SELECT *
FROM fruits
WHERE FIND_IN_SET ( name, fruit_array );
Hope this will help you..
Another Solution:
Use foreach loop:
foreach(condition)
{
//Create insert query string
}
//insert query
//Execute Query
For more help paste code