How to list all columns in table with Sql trigger - mysql

CREATE TRIGGER `table_triger_on_before_delete`
BEFORE DELETE ON `db`.`table`
FOR EACH ROW
INSERT INTO db_bkp.`table`
(col1, col2, col3)
VALUES
(OLD.col1, OLD.col2, OLD.col3)
I want to get this
(col1, col2, col3)
without type them one by one

If you're absolutely sure that the tables are with the same fields you can use simply that
CREATE TRIGGER `table_triger_on_before_delete`
BEFORE DELETE ON `db`.`table`
FOR EACH ROW
INSERT INTO db_bkp.`table`
SELECT * FROM `db`.`table` WHERE `you_primary_key`= OLD.`you_primary_key`
Becouse you row isn't deleted yet :)

It is possible to query the list of columns of a particular table from information_schema.columns table within MySQL.
However, in this case you need to create your insert statement using prepared statement because this allows you to create the sql command as a string and then you can execute it.
...
declare s_fields, s_sql varchar(1000); --length should depend on how many fields you have
select group_concat(column_name) into s_fields
from information_schema.columns
where table_name='yourtable'
group by table_name
limit 1;
set s_sql=concat('insert into tablename (',s_fields, ') values (', variable_holding_comma_separated_list_of_values,')');
prepare stmt from s_sql;
execute stmt;
deallocate prepare stmt;
...

I have modified the code from #Shadow. you must also include the schema name.
and also put it in backquotes to prevent spaces in the fieldnames.
...
DECLARE s_sql VARCHAR(1000); --length should depend ON how many fields you have
SELECT
CONCAT(
'insert into tablename ('
,GROUP_CONCAT(CONCAT('`',COLUMN_NAME,'`'))
,') values ('
,GROUP_CONCAT(CONCAT('`OLD`.`',COLUMN_NAME,'`')),
')'
)
INTO s_sql
FROM information_schema.columns
WHERE
TABLE_NAME='your_table'
AND
TABLE_SCHEMA='your_schema'
GROUP BY TABLE_NAME;
prepare stmt from s_sql;
execute stmt;
deallocate prepare stmt;
...
sample
MariaDB [your_schema]> SELECT
-> CONCAT(
-> 'insert into tablename ('
-> ,GROUP_CONCAT(CONCAT('`',COLUMN_NAME,'`'))
-> ,') values ('
-> ,GROUP_CONCAT(CONCAT('`OLD`.`',COLUMN_NAME,'`')),
-> ')'
-> )
-> INTO #s_sql
-> FROM information_schema.columns
-> WHERE
-> TABLE_NAME='your_table'
-> AND
-> TABLE_SCHEMA='your_schema'
-> GROUP BY TABLE_NAME;
Query OK, 1 row affected (0.00 sec)
MariaDB [your_schema]> select #s_sql;
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| #s_sql |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| insert into tablename (`sr_no`,`scholar_no`,`paid_amount`,`due_amount`,`fee_date`,`section_id_fk`) values (`OLD`.`sr_no`,`OLD`.`scholar_no`,`OLD`.`paid_amount`,`OLD`.`due_amount`,`OLD`.`fee_date`,`OLD`.`section_id_fk`) |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
MariaDB [your_schema]>

Related

How to Create View or Table from Prepared Statement (Select Query)

I am trying to create View from the following prepared statement:
CREATE VIEW myview AS -- this line is not working
SELECT CONCAT(GROUP_CONCAT('SELECT ''', COLUMN_NAME,''' MyColumns, SUM(`', COLUMN_NAME,'`) Total FROM mydb.source_table' SEPARATOR '\n UNION ALL \n'),'\nORDER BY Total ASC')
INTO #sql
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'mydb'
AND TABLE_NAME = 'source_table'
AND COLUMN_NAME NOT IN ('ID', 'Name');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DROP PREPARE stmt;
I am still confused with handling group_concat syntax.
I just need help on how to create View with the above prepared statement.
UPDATE 1: Stored procedure
As suggested by #nbk, I have to create into 5 stored procedures in single column to be able the data and use it to CREATE TABLE query. Here's the stored procedure version of the above codes.
CREATE DEFINER=`admin`#`%` PROCEDURE `sp_result`()
BEGIN
SELECT CONCAT(GROUP_CONCAT('SELECT ''', COLUMN_NAME,''' MyColumns FROM mydb.source_table' SEPARATOR '\n UNION ALL \n'), '\n LIMIT 0, 1 \n')
INTO #sql
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'mydb'
AND TABLE_NAME = 'source_table'
AND COLUMN_NAME NOT IN ('ID', 'Name');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DROP PREPARE stmt;
END
Here's the result of this stored procedure:
+-----------+
| MyColumns |
+-----------+
| Abc |
+-----------+
In order this approach to be useful to me, I want the above result to be the one of the column of my CREATE TABLE query:
Here are my attempts but no luck:
First attempt: Separate Query
CREATE TABLE my_table AS SELECT id, name, mydb.sp_result() FROM source_table; -- Error Code: 1305. FUNCTION project_x.best_vendor1_name does not exist
Second attempt: Inserted at the last part of the stored procedure
CREATE TABLE my_table AS SELECT #sql FROM source_table; -- no effect
Here, I really do not know how CREATE TABLE using the stored procedure returned data.
UPDATE 2: Illustrate Encapsulation
CREATE DEFINER=`root`#`%` PROCEDURE `proc_column_sum`()
BEGIN
DROP TABLE IF EXISTS table2;
SELECT CONCAT('
CREATE TABLE table2 AS (',GROUP_CONCAT('
SELECT ''', COLUMN_NAME,''' MyColumns, SUM(`', COLUMN_NAME,'`) Total
FROM testdb.products '
SEPARATOR '\n UNION ALL \n'), -- runtime syntax error somewhere here
'\n ORDER BY Total ASC)') -- missing closing single quote right after ASC
INTO #sql
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'testdb'
AND TABLE_NAME = 'products'
AND COLUMN_NAME NOT IN ('ID', 'Name');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DROP PREPARE stmt;
END
You must put the CREATE TABLE in your prepared statement.
this example uses a temporary table only for demonstration purposes
CREATE DEFINER=`root`#`%` PROCEDURE `proc_column_sum`()
BEGIN
SELECT
CONCAT('CREATE TEMPORARY TABLE IF NOT EXISTS table2 AS (',GROUP_CONCAT('SELECT ''', COLUMN_NAME,''' MyColumns, SUM(`', COLUMN_NAME,'`) Total FROM testdb.products ' SEPARATOR '\n UNION ALL \n'),'\nORDER BY Total ASC)')
INTO #sql
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'testdb'
AND TABLE_NAME = 'products'
AND COLUMN_NAME NOT IN ('ID', 'Name');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DROP PREPARE stmt;
END
you call it then
call proc_column_sum();
SELECT * FROM table2
But with this approach you have call every time the procedure to get the newest data.
I can' figure out, what went wrong in your query
CREATE DEFINER=`root`#`%` PROCEDURE `proc_column_sum`()
BEGIN
DROP TABLE IF EXISTS table2;
SELECT CONCAT('
CREATE TABLE IF NOT EXISTS table2 AS (',GROUP_CONCAT('
SELECT ''', COLUMN_NAME,''' MyColumns, SUM(`', COLUMN_NAME,'`) Total
FROM testdb.products '
SEPARATOR '\n UNION ALL \n')
,'\nORDER BY Total ASC)')
INTO #sql
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'testdb'
AND TABLE_NAME = 'products'
AND COLUMN_NAME NOT IN ('ID', 'Name');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DROP PREPARE stmt;
END

Why doesn't my mysql code work

I wrote this code and when I execute it it says I have a problem with mysql syntax near the update statement
set #s1 = (select if ((select count(*) from information_schema.columns where table_name='foo' and column_name='bar_id') > 0,
'select 1',
'alter table foo add column bar_id bigint; update foo set bar_id = baz_id;'));
prepare stmt from #s1;
execute stmt;
deallocate prepare stmt;
If I change my code to
set #s1 = (select if ((select count(*) from information_schema.columns where table_name='foo' and column_name='bar_id') > 0,
'select 1',
'alter table foo add column bar_id bigint;'));
prepare stmt from #s1;
execute stmt;
deallocate prepare stmt;
update foo set bar_id = baz_id;
then it works. but I want the update statement inside the if condition.
I cannot make this into a SP.
Error:
ERROR 1064 (42000): 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 'update foo set bar_id = baz_id' at line 1
In your first code block, you attempt to prepare a string that contains two SQL statements. Unfortunately, MySQL prepare / execute cannot have multiple statements.
If you can't use SP, I think I'd suggest doing this like so:
set #s1 = (select if ((select count(*) from information_schema.columns where table_name='foo' and column_name='bar_id') > 0,
'select 1',
concat('alter table foo add column bar_id bigint default ', baz_id)));
prepare stmt from #s1;
execute stmt;
deallocate prepare stmt;
alter table foo alter column bar_id drop default;
But, honestly, I'd suggest you minimize DDL changes as those can have unpredictable run-time behavior. In this case, that means adding the foo.bar_id out-of-band and just perform an update as needed.
The problem is that MySQL's prepared statements do not support multi-statements.
If you want to script the database structure updates, easiest way is to use a procedure without dynamic SQL (you might want to check the table_schema as well when you are doing the changes).
create procedure sp_fix_structure()
begin
declare v_cnt int;
select count(*) into v_cnt
from information_schema.columns
where table_schema=database() and table_name='foo' and column_name='bar_id';
if (v_cnt=0) then
alter table foo add column bar_id bigint;
update foo set bar_id = baz_id;
end if;
end

How to convert an SQL select query into a formatted HTML table within a MySQL function

I'm looking for a way to generate valid HTML code within MySQL (without PHP) by converting any query output into an HTML table.
Here's my progress so far and evidently, I'm stuck. I hope I can get some help, thanks.
1. "dynSQL" - A procedure to take any Select query and create a named table out of it
Since MySQL doesn't allow dynamic queries in functions, I'm calling a procedure that creates a named table, tmp. I can't use a temporary table because info about temporary tables is not available in information_schema (in mysql 5.6)
CREATE DEFINER=`root`#`%` PROCEDURE `dynSQL`(SQL_QUERY TEXT)
BEGIN
set #SQLQ := 'Drop table if exists tmp;';
PREPARE stmt from #SQLQ;
Execute stmt;
SET #SQLQ := concat('create table tmp as ',SQL_QUERY);
PREPARE stmt from #SQLQ;
Execute stmt;
-- I'm adding a auto increment ID column to be able to loop through the rows later
SET #SQLQ := "ALTER TABLE tmp add column CustColHTML_ID INT NOT NULL AUTO_INCREMENT FIRST, ADD primary KEY Id(CustColHTML_ID)";
PREPARE stmt from #SQLQ;
Execute stmt;
DEALLOCATE PREPARE stmt;
END
2. "MakeHTML" - Function to read from the table tmp and return a formatted HTML table
CREATE DEFINER=`root`#`%` FUNCTION `MakeHTML`() RETURNS text CHARSET utf8
DETERMINISTIC
BEGIN
DECLARE HTML text default "<TABLE><TR>";
DECLARE rowCount int default 0;
DECLARE i int default 0;
select concat('<TR>',group_concat('<TD>',column_name,'</TD>' separator ''),'</TR>') into html from information_Schema.`columns` where table_name='tmp';
Select max(CustColHTML_ID) into rowCount from `tmp`; -- Set the row counter
WHILE i<=rowCount DO
-- What do I do here? How do I loop through the columns of table tmp?
set i:=i+1;
END WHILE;
RETURN HTML;
END
As you can see, I'm stuck at looping through the unknown and dynamic columns of table tmp. I read about how a cursor can be used here, but all the examples I saw make use of known columns and assign those into named variables. However, since the query itself is dynamic, I wouldn't know the names of the columns.
I'd really appreciate your time and assistance, thanks!
p.s. I've posted this as a new question because my earlier question was marked as closed as being too broad. I subsequently edited my question but it was still showing as Closed. I've therefore deleted the older question and replaced it with this one.
With a sample table as such:
CREATE TABLE tmp (ID INT, Col1 INT, Col2 INT);
The SQL you would need to generate your HTML is:
SELECT CONCAT('<table>', GROUP_CONCAT(CONCAT('<tr><td>',ID,'</td><td>',Col1,'</td><td>',Col2,'</td><tr>')), '</table>')
FROM tmp;
You can generate this using the INFORMATION_SCHEMA:
SELECT CONCAT
(
'SELECT CONCAT(''<table>'', GROUP_CONCAT(CONCAT(''<tr>'', ',
GROUP_CONCAT(CONCAT('''<td>'',', COLUMN_NAME, ',''</td>''')),
', ''</tr>'')), ''</table>'') FROM tmp'
)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'tmp';
It is then just a case of executing this:
SET #SQL = (
SELECT CONCAT
(
'SELECT CONCAT(''<table>'', GROUP_CONCAT(CONCAT(''<tr>'', ',
GROUP_CONCAT(CONCAT('''<td>'',', COLUMN_NAME, ',''</td>''')),
', ''</tr>'')), ''</table>'') FROM tmp'
)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'tmp'
);
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Example on SQL Fiddle
ADDENDEUM
Forgot to include table headers:
SET #SQL = (
SELECT CONCAT
(
'SELECT CONCAT(''<table><tr>'',',
GROUP_CONCAT(CONCAT('''<th>'',''', COLUMN_NAME, ''',''</th>''')),
', ''</tr>'', GROUP_CONCAT(CONCAT(''<tr>'', ',
GROUP_CONCAT(CONCAT('''<td>'',', COLUMN_NAME, ',''</td>''')),
', ''</tr>'')), ''</table>'') FROM tmp'
)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'tmp'
);
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Example on SQL Fiddle

How to drop all columns with the same name in MySQL [duplicate]

How I can run a command in phpMyAdmin which will drop all columns in a database that have the prefix test_.
To drop a column from a table, use the syntax:
alter table <tablename> drop column <columnname>
To find all the columns in a table in a database that start with test_, do the following:
select column_name
from INFORMATION_SCHEMA.columns
where table_name = <table_name> and
table_schema = <schema_name> and
left(column_name, 5) = 'test_' -- not using "like" because '_' is a wildcard char
If you were doing this manually, I would recommend running the following query and then pasting the results in to a mysql query interface:
select concat('alter table ', table_name, ' drop column ', column_name)
from INFORMATION_SCHEMA.columns
where table_name = <table_name> and
schema_name = <schema_name> and
left(column_name, 5) = 'test_'
You can do something similar in code, by running the query, returning the results and then running each row as a query.
If you actually want to drop the columns from your schema, you will need to generate the necessary SQL commands dynamically from MySQL's information schema tables. Whilst it is possible to do that within a MySQL stored procedure using SQL prepared statements, which I demonstrate below, you may well find it easier to implement/understand in your preferred development language (which you do not mention in your question):
DELIMITER ;;
CREATE PROCEDURE dropMatchingColumns(IN pattern VARCHAR(64))
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE cur CURSOR FOR
SELECT CONCAT(
'ALTER TABLE `', REPLACE( TABLE_NAME, '`', '``'), '` ',
GROUP_CONCAT(
'DROP COLUMN `', REPLACE(COLUMN_NAME, '`', '``'), '`'
)
)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME LIKE pattern AND TABLE_SCHEMA = DATABASE()
GROUP BY TABLE_NAME
;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO #sql;
IF done THEN
LEAVE read_loop;
END IF;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur;
END;;
DELIMITER ;
With this procedure defined, one need only CALL dropMatchingColumns('test\_%'); in order to drop all the columns prefixed with test_ from the current database.
If you have MySQl Workbench then you can delete multiple columns by simply do a mass selection and telling workbench to do a mass deletion of the selected columns
I would like to explain or simplify this answer for those like me who were having trouble with this.
I was having trouble dropping a column with the name 'seq' in all tables in my database 'demo'.
You can create a selection with the commands formatted for each table using something like this:
Select concat('alter table ', table_name, ' drop column ', 'seq;')
from (select table_name
from INFORMATION_SCHEMA.tables
where table_name = table_name and
table_schema = 'demo') as t
This creates the alter table command for each table in the 'demo' database.
You have to select the result, copy it, and paste it back into the query editor.
It's a two step process, but if you have to do this several times, save the commands in a text file to run again later.

can we run code in mysql without creating a procedure

I want to create a sql file for my dba that will check whether index exists on a table. IF doesn't exist-create it.
I found many examples that use stored procedure, but I only want to run it once.
Something like this:
-- Creates an index if it does not already exist in MySQL.
START TRANSACTION;
SET IndexIsThere = 0;
SET given_table = 'IDR_CHGS';
SET given_index = 'FK_IDR_PATIENT_PT_ID_idx1';
SELECT COUNT(1) INTO IndexIsThere
FROM INFORMATION_SCHEMA.STATISTICS
WHERE table_name = given_table
AND index_name = given_index;
IF IndexIsThere = 0 THEN
SET #sqlstmt = CONCAT('CREATE INDEX ',given_index,' ON ', given_database,'.',given_table,' (',given_columns,')');
PREPARE st FROM #sqlstmt;
EXECUTE st;
DEALLOCATE PREPARE st;
SELECT CONCAT('Created index ', given_table,'.', given_index, ' on columns ', given_columns) AS 'CreateIndex status';
ELSE
SELECT CONCAT('Index ', given_index,' Already Exists on Table ', given_database,'.',given_table) AS 'CreateIndex status';
END IF;
COMMIT;
Is this doable?
MySQL doesn't support IF/THEN/ELSE constructs outside of stored routines or triggers.
If you use PREPARE and EXECUTE, you could simply form a string that has the CREATE INDEX statement if there is no index, and a no-op statement (e.g. a comment) if not.
SELECT COALESCE(CONCAT('SELECT \'index ', S.INDEX_NAME, ' exists already\''),
'CREATE INDEX `idx_x` ON test.foo (`x`)') INTO #sql
FROM (SELECT NULL) AS d
LEFT OUTER JOIN INFORMATION_SCHEMA.STATISTICS AS S
ON (S.TABLE_SCHEMA, S.TABLE_NAME, S.INDEX_NAME) = ('test', 'foo', 'idx_x');
PREPARE s from #sql;
EXECUTE s;
If the index exists, #sql will be:
mysql> select #sql;
+-------------------------------------+
| #sql |
+-------------------------------------+
| SELECT 'index idx_x exists already' |
+-------------------------------------+
If the index does not exist, #sql will be:
mysql> select #sql;
+----------------------------------------+
| #sql |
+----------------------------------------+
| CREATE INDEX `idx_x` ON test.foo (`x`) |
+----------------------------------------+
By the way, all CREATE and ALTER statements implicitly commit before and after the statement, so there's no purpose in using start transaction and commit in the way you're doing. See http://dev.mysql.com/doc/refman/5.6/en/implicit-commit.html