MySQL Select columns where the column name contains a substring - mysql

I have a table with a lot of fields about a person and then several recommendations of other people.
They are named:
"recommendation_1_name" "recommendation_1_company" 'recommendation_1_contact"
"recommendation_2_name" "recommendation_2_company" "recommendation_2_contact"
and so on.
I am trying to come up with a statement that allows me to only get the recommendations.
I imported an excel file into the table so it's just one large table.
This is what I have and it is returning an Empty set.
select * from questionnaire where 'COLUMN_NAME' like '%recommendation%';
I've been playing around with it making a table with only the recommendation fields and it still doesn't return anything.

Mysql: select recommendation_1_name, recommendation_2_name etc... from (table) where (USER) = (USERID) or however you can uniquely identify that user.

This Query generates you dynamic a SELECT query with all fields like 'recommendation%'. You only must setup the Databasename, and the Tablename. You can directly query the result of my query or add the WHERE clause.
SELECT
CONCAT( 'SELECT ',
GROUP_CONCAT(COLUMN_NAME SEPARATOR ',\n')
)
FROM information_schema.columns
WHERE TABLE_SCHEMA = 'DBNAME'
AND TABLE_NAME = 'TABLENAME'
AND COLUMN_NAME LIKE 'recommendation%';

You really need to normalize your schema.
But just as an experiment and example for some other cases (maybe somebody really need it). Here is solution to get this case resolved using stored procedure:
CREATE PROCEDURE `get_recommendations`()
BEGIN
DECLARE Q VARCHAR(100);
DECLARE C_NAME VARCHAR(100);
DECLARE cur CURSOR FOR SELECT GROUP_CONCAT(column_name) as `columns`
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'test'
AND TABLE_NAME ='questionnaire'
AND COLUMN_NAME LIKE '%recommendation%'
;
SET Q = 'SELECT ';
OPEN cur;
FETCH cur INTO C_NAME;
SET Q = CONCAT(Q,C_NAME,' ');
CLOSE cur;
SET #Q = CONCAT(Q,'FROM questionnaire;');
PREPARE stmt FROM #Q;
EXECUTE stmt ;
END
Don't forget to replace TABLE_SCHEMA = 'test' with your real database name.

Related

Convert MySQL Variable Query to PostgreSQL

I have migrated MYSQL database to PostgreSQL and I am now converting my queries to match. I have this MySQL query in the Python file:
SET #Drop_Stm = CONCAT('DROP TABLE ', (SELECT GROUP_CONCAT(TABLE_NAME) AS All_Tables
FROM information_schema.tables
WHERE TABLE_NAME LIKE 'temp_%' AND TABLE_SCHEMA = '{client_name}'))
 
I would like to make it works in Postgres, I tried the following but returns error:
WITH Drop_Stm AS (CONCAT('DROP TABLE ', (SELECT STRING_AGG(TABLE_NAME, ',') AS All_Tables
FROM information_schema.tables
WHERE TABLE_NAME LIKE 'temp_%' AND TABLE_SCHEMA = '{client_name}')))
LINE 1: WITH Drop_Stm AS (CONCAT('DROP TABLE ', (SELECT STRING_AGG(T...
^
I also tried DECLARE, SET, and DO $$ .. END $$ with no luck
The query itself should be changed to this:
select concat('drop table ', string_agg(format('%I.%I', table_schema, table_name), ','), ' cascade;')
FROM information_schema.tables
WHERE TABLE_NAME LIKE 'temp_%'
AND TABLE_SCHEMA = '{client_name}'
Note that I used the format() function to properly deal with identifiers needing quoting. I also generated a fully qualified table name (including the schema) so that you don't accidentally drop a table in the current schema, rather than {client_name}.
If you want to run the generated script, I see two options. One is to put this into a PL/pgSQL block and use execute:
do
$$
declare
l_stmt text;
begin
select concat('drop table ', string_agg(format('%I.%I', table_schema, table_name), ','), ' cascade;')
into l_stmt
FROM information_schema.tables
WHERE TABLE_NAME LIKE 'temp_%'
AND TABLE_SCHEMA = '{client_name}';
execute l_stmt;
end;
$$
;
I don't know Python, so I am not entirely sure if {client_name} gets replaced correctly with that approach.
Another option is to run the SELECT query in Python, and store the result into a Python variable then run that SQL through Python.

Search a db for all tables that have a certain value in a column

Is it possible to search all tables in a DB for a certain value in a column? I have 30 tables in my DB. Not all of them are using the FK employee_no. Out of all the tables that do contain an employee_no column, not all tables will have a record entered for every employee.
I would like to get a list of all the tables that contain the value 6172817 for the employee_no column.
I know
SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME like '%employee_no'
will return all the tables with the column name employee_no, but now I want all the tables with the value 6172817 for employee_No
I have tried
SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE employee_no like '%6172817'
Is this possible?
So this is what i got so far (made in postegresql though so you'll need to convert to mysql):
DO $$
DECLARE
rowt text; -- cursor retun
rowf text; -- name of the current table that meets our requirement
rowfz text; -- formated rout
cr CURSOR FOR (SELECT t.table_name::text
FROM information_schema.tables t
INNER JOIN information_schema.columns c
ON c.table_name = t.table_name
AND c.table_schema = t.table_schema
WHERE c.column_name = 'employee_no' -- The column you're looking for here
AND t.table_schema NOT IN ('information_schema', 'my_db') -- Add any unwanted DB's
-- separated by comas
AND t.table_type = 'BASE TABLE'
ORDER BY t.table_name);
BEGIN
FOR rowt IN cr LOOP
rowfz := REPLACE (rowfz::text,'(','');
rowfz := REPLACE (rowfz::text,')','');
EXECUTE (concat(' SELECT ''',rowfz, ''' FROM ', rowfz,' WHERE ', rowfz,
'.employee_no LIKE '''%6172817''' ')) INTO rowf;
IF rowf IS NOT NULL -- prints out only the valid(not null) table names
RAISE NOTICE '%',rowf;
END IF;
END LOOP;
END $$;
This will tell you exactly what tables have what you want, however it won't be shown in a neat looking table(you might need to scroll through the result text).

How to loop through all the tables on a database to update columns

I'm trying to update a column (in this case, a date) that is present on most of the tables on my database. Sadly, my database has more than 100 tables already created and full of information. Is there any way to loop through them and just use:
UPDATE SET date = '2016-04-20' WHERE name = 'Example'
on the loop?
One painless option would be to create a query which generates the UPDATE statements you want to run on all the tables:
SELECT CONCAT('UPDATE ', a.table_name, ' SET date = "2016-04-20" WHERE name = "Example";')
FROM information_schema.tables a
WHERE a.table_schema = 'YourDBNameHere'
You can copy the output from this query, paste it in the query editor, and run it.
Update:
As #PaulSpiegel pointed out, the above solution might be inconvenient if one be using an editor such as HeidiSQL, because it would require manually copying each record in the result set. Employing a trick using GROUP_CONCAT() would give a single string containing every desired UPDATE query in it:
SELECT GROUP_CONCAT(t.query SEPARATOR '; ')
FROM
(
SELECT CONCAT('UPDATE ', a.table_name,
' SET date = "2016-04-20" WHERE name = "Example";') AS query,
'1' AS id
FROM information_schema.tables a
WHERE a.table_schema = 'YourDBNameHere'
) t
GROUP BY t.id
You can use SHOW TABLES command to list all tables in database. Next you can check if column presented in table with SHOW COLUMNS command. It can be used this way:
SHOW COLUMNS FROM `table_name` LIKE `column_name`
If this query returns result, then column exists and you can perform UPDATE query on it.
Update
You can check this procedure on sqlfiddle.
CREATE PROCEDURE UpdateTables (IN WhereColumn VARCHAR(10),
IN WhereValue VARCHAR(10),
IN UpdateColumn VARCHAR(10),
IN UpdateValue VARCHAR(10))
BEGIN
DECLARE Finished BOOL DEFAULT FALSE;
DECLARE TableName VARCHAR(10);
DECLARE TablesCursor CURSOR FOR
SELECT c1.TABLE_NAME
FROM INFORMATION_SCHEMA.COLUMNS c1
JOIN INFORMATION_SCHEMA.COLUMNS c2 ON (c1.TABLE_SCHEMA = c2.TABLE_SCHEMA AND c1.TABLE_NAME = c2.TABLE_NAME)
WHERE c1.TABLE_SCHEMA = DATABASE()
AND c1.COLUMN_NAME = WhereColumn
AND c2.COLUMN_NAME = UpdateColumn;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET Finished = TRUE;
OPEN TablesCursor;
MainLoop: LOOP
FETCH TablesCursor INTO TableName;
IF Finished THEN
LEAVE MainLoop;
END IF;
SET #queryText = CONCAT('UPDATE ', TableName, ' SET ', UpdateColumn, '=', QUOTE(UpdateValue), ' WHERE ', WhereColumn, '=', QUOTE(WhereValue));
PREPARE updateQuery FROM #queryText;
EXECUTE updateQuery;
DEALLOCATE PREPARE updateQuery;
END LOOP;
CLOSE TablesCursor;
END
This is just an example how to iterate through all tables in database and perform some action with them. Procedure can be changed according to your needs.
Assuming you are using MySQL, You can use Stored Procedure.
This post is a very helpful.
Mysql-loop-through-tables

Mysql UNION with dynamic query

i have the following problem. I inherited a software that uses a database prefix for every customer.
All tables have the same structure and columns. For a data migration to new version i want to union all these tables
and set a customer foreign key instead and get rid of all the subtables. i'm looking for a way to create
a view for this task because i also want to stay backwards compatible for now.
I found this dynamic query which seems to do what i want
but i can't execute on my mysql server. I assume it was written for another sql server.
The table name structure is (about 80 customer tables):
customer1_faxe
customer2_faxe
customer3_faxe
customer4_faxe
...
How would you approach this problem?
DECLARE #SelectClause VARCHAR(100) = 'SELECT *'
,#Query VARCHAR(1000) = ''
SELECT #Query = #Query + #SelectClause + ' FROM ' + TABLE_NAME + ' UNION ALL '
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE '%_faxe'
SELECT #Query = LEFT(#Query, LEN(#Query) - LEN(' UNION ALL '))
EXEC (#Query)
This query is using SQL Server syntax. You need something like this:
declare #SelectClause varchar(8000);
declare #Query varchar(8000);
set #SelectClause = 'SELECT *';
SELECT #Query := group_concat(#SelectClause, ' FROM ', TABLE_NAME SEPARATOR ' UNION ALL ')
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE '%_faxe';
prepare stmt from #Query;
execute stmt;
Note that the group_concat() with separator simplifies the logic.

MySQL sorting table by column names

I have already built a table with field names in arbitrary order. I want those field names to be in alphabetical order so that I can use them in my dropdown list. Is it possible with a query?
Select columns from a specific table using INFORMATION_SCHEMA.COLUMNS and sort alphabetically with ORDER BY:
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_schema = '[schemaname]'
AND table_name = '[tablename]'
ORDER BY column_name
Note: The following code will alter the specified table and reorder the columns in alphabetical order
This should do the trick. It's a bit messy and lengthy, and you'll have to change the database name and table name, but for this one, the only requirement is that there is a database named "test" and that you are running these commands in it:
Let's create the tables we need:
-- CREATE TESTING TABLE IN A DATABASE NAMED "test"
DROP TABLE IF EXISTS alphabet;
CREATE TABLE alphabet (
d varchar(10) default 'dee' not null
, f varchar(21)
, e tinyint
, b int NOT NULL
, a varchar(1)
, c int default '3'
);
-- USE A COMMAND STORAGE TABLE
DROP TABLE IF EXISTS loadcommands;
CREATE TABLE loadcommands (
id INT NOT NULL AUTO_INCREMENT
, sqlcmd VARCHAR(1000)
, PRIMARY KEY (id)
);
Now let's create the two stored procedures required for this to work:
Separating them since one will be responsible for loading the commands, and including a cursor to immediately work with it isn't plausible (at least for me and my mysql version):
-- PROCEDURE TO LOAD COMMANDS FOR REORDERING
DELIMITER //
CREATE PROCEDURE reorder_loadcommands ()
BEGIN
DECLARE limitoffset INT;
SET #rank = 0;
SET #rankmain = 0;
SET #rankalter = 0;
SELECT COUNT(column_name) INTO limitoffset
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_schema = 'test'
AND table_name = 'alphabet';
INSERT INTO loadcommands (sqlcmd)
SELECT CONCAT(t1.cmd, t2.position) AS commander FROM (
SELECT #rankalter:=#rankalter+1 AS rankalter, CONCAT('ALTER TABLE '
, table_name, ' '
, 'MODIFY COLUMN ', column_name, ' '
, column_type, ' '
, CASE
WHEN character_set_name IS NOT NULL
THEN CONCAT('CHARACTER SET ', character_set_name, ' COLLATE ', collation_name, ' ')
ELSE ' '
END
, CASE
WHEN is_nullable = 'NO' AND column_default IS NULL
THEN 'NOT NULL '
WHEN is_nullable = 'NO' AND column_default IS NOT NULL
THEN CONCAT('DEFAULT \'', column_default, '\' NOT NULL ')
WHEN is_nullable = 'YES' THEN 'DEFAULT NULL '
END
) AS cmd
, column_name AS columnname
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_schema = 'test'
AND table_name = 'alphabet'
ORDER BY columnname
) t1
INNER JOIN (
SELECT #rankmain:=#rankmain+1 AS rownum, position FROM (
SELECT 0 AS rownum, 'FIRST' AS position
, '' AS columnname
UNION
SELECT #rank:=#rank+1 AS rownum, CONCAT('AFTER ', column_name) AS position
, column_name AS columnname
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_schema = 'test'
AND table_name = 'alphabet'
ORDER BY columnname
LIMIT limitoffset
) inner_table
) t2 ON t1.rankalter = t2.rownum
;
END//
DELIMITER ;
If anyone thinks/sees that I'm missing to include any important column attributes in the ALTER command, please hesitate not and mention it! Now to the next procedure. This one just executes the commands following the order of column id from the loadcommands table. :
-- PROCEDURE TO RUN EACH REORDERING COMMAND
DELIMITER //
CREATE PROCEDURE reorder_executecommands ()
BEGIN
DECLARE sqlcommand VARCHAR(1000);
DECLARE isdone INT DEFAULT FALSE;
DECLARE reorderCursor CURSOR FOR
SELECT sqlcmd FROM loadcommands ORDER BY id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET isdone = TRUE;
OPEN reorderCursor;
read_loop:LOOP
FETCH reorderCursor INTO sqlcommand;
IF isdone THEN
LEAVE read_loop;
END IF;
SET #sqlcmd = sqlcommand;
PREPARE stmt FROM #sqlcmd;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP read_loop;
CLOSE reorderCursor;
END//
DELIMITER ;
The SQL is long, so if someone can point out ways (and has tested them) to make this shorter I'd gladly do it, but for now, this at least works on my end. I also didn't need to put dummy data in the alphabet table. Checking the results can be done using the SHOW... command.
The last part:
-- TO TEST; AFTER RUNNING DDL COMMANDS:
SHOW CREATE TABLE alphabet; -- SEE ORIGINAL ORDER
CALL reorder_loadcommands(); -- PREPARE COMMANDS
CALL reorder_executecommands(); -- RUN COMMANDS
SHOW CREATE TABLE alphabet; -- SEE NEW ORDER
Perhaps later on I could make reorder_loadcommands dynamic and accept table and schema parameters, but I guess this is all for now..