nesting sql statements and selecting (MySQL) - mysql

Given the following MySQL-statement:
SELECT table_name,column_name
FROM information_schema.columns
WHERE table_schema = 'myschema'
AND column_name REGEXP 'ID$'
ORDER BY table_name,ordinal_position
I would like to select from the result all columns that contain a certain ID #.
Since I don't know the exact name of the column or table I should apply my 2nd SELECT, I need to apply my SELECT to some kind of "placeholders", I think.
E.g., if the resultset of the first request is:
('wp_links', 'link_id')
('wp_options', 'option_id')
('wp_postmeta', 'meta_id')
('wp_postmeta', 'post_id')
then the select should comprise all table_name that are in the first column of the result and should take the column_name of the second column of the result as argument to test whether it contains a certain ID #.
In other words I would like to find all columns in a certain database that are named *_ID and contain a certain ID# and know their corresponding table_name the column belongs to.

You can't do what you're wanting to in SQL but you could do it using procedural SQL, either in a procedure or an anonymous block.
Create a cursor which loops through the results of your statement above. When looping through the results, construct the string you want to execute on each table to see if the desired id exists and execute the query. You will create this string by doing something like:
SET sql_statament = CONCAT("SELECT * FROM ", v_table_name," WHERE ", v_column_name," = ", v_id_number);
This tutorial should help: http://www.mysqltutorial.org/mysql-cursor/

You want to do two different things:
Get information on your database (table and column names).
Get data from your database (values from columns).
You cannot do both at the same time. You can use some programming language firing first an SQL query to get table names and columns and then build a query or several queries to get the data.

Related

SQL query - I don't know all identifiers

Is there a way, where I can see every parameter or identifier I can query from my database? Not the contents but the "column names"
Something like
SELECT * FROM myDb AS String
To simply get the column names and types of a table.
You could SHOW them.
SHOW COLUMNS FROM myTable;
But if you want to know the column names of your table, and only a bit of data from it (to see what it looks like).
Then use LIMIT to get only a few records.
SELECT *
FROM myTable
LIMIT 3
It's fast and easy.
But you can also just see the columns without data if you use a criteria that's false.
SELECT *
FROM myTable
WHERE 0=1
You can also use:
show create table table_name;
but as "LukStorms" mentioned, the below statement shows you the data in table format and in a pretier way
show columns from table_name;
You can use INFORMATION_SCHEMA.COLUMNS to retrieve all columns name
select column_name from INFORMATION_SCHEMA.COLUMNS where Table_Name='Your_Table'

How can I run a SQL UPDATE against the results of query that captures table names from INFORMATION_SCHEMA.TABLES?

I'm looking to replace a string in a CMS multi-site database across a common set of tables. Here is the initial query to collect the target tables:
SELECT TABLE_NAME as target_table
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE '%_content'
...against the results of which I'd like to run the following:
UPDATE target_table
SET title = replace(title, 'SEARCH_STRING', 'REPLACE_STRING')
WHERE title LIKE ('%SEARCH_STRING%');
Thanks in advance for the assist!
For a one off, a workable approach is to use SQL to generate a set of SQL statements.
Assuming that the table_schem and table_name don't contain backtick characters, and if your SEARCH_STRING and REPLACE_STRING don't contain single quotes (or are properly escaped), we could do something like this:
SELECT CONCAT('UPDATE `',t.table_schema,'`.`',t.table_name,'` c'
,' SET c.title = REPLACE(c.title, ''SEARCH_STRING'', ''REPLACE_STRING'')'
,' WHERE c.title LIKE (''%SEARCH_STRING%'') ;') AS `-- stmt`
FROM INFORMATION_SCHEMA.TABLES t
WHERE t.table_name LIKE '%_content'
AND t.table_schema NOT IN ('information_schema','mysql','performance_schema')
ORDER BY t.table_schema, t.table_name
we can save the results from the query into a file. and then submit the SQL statements in the file to the MySQL server.
(I think I would be using INFORMATION_SCHEMA.COLUMNS, with tables containing column named 'title' as well as the table_name matching a pattern, but the approach is the same.
Note that this cannot be accomplished in a single SQL statement; the query to get the list of tables is going to have to be a separate statement, separated from the execution of the actual UPDATE statement(s).
EDIT I just took a look at the answer linked to in the question; that is totally unrelated. There's nothing there that would apply to the problem we are trying to solve here.
Because of the way SQL is processed (parse, syntax check, semantic check, determine execution plan, then execute) ... identifiers (e.g. table names) must be supplied as tokens in the SQL text. Identifiers cannot be supplied as values at execution time. That's why we need separate statements.

SQL - Query to find if a string contains part of the value in Column

I am trying to write a Query to find if a string contains part of the value in Column (Not to confuse with the query to find if a column contains part of a string).
Say for example I have a column in a table with values
ABC,XYZ
If I give search string
ABCDEFG
then I want the row with ABC to be displayed.
If my search string is XYZDSDS then the row with value XYZ should be displayed
The answer would be "use LIKE".
See the documentation: https://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html
You can do WHERE 'string' LIKE CONCAT(column , '%')
Thus the query becomes:
select * from t1 where 'ABCDEFG' LIKE CONCAT(column1,'%');
If you need to match anywhere in the string:
select * from t1 where 'ABCDEFG' LIKE CONCAT('%',column1,'%');
Here you can see it working in a fiddle:
http://sqlfiddle.com/#!9/d1596/4
Select * from table where #param like '%' + col + '%'
First, you appear to be storing lists of things in a column. This is the wrong approach to storing values in the database. You should have a junction table, with one row per entity and value -- that is, a separate row for ABC and XYZ in your example. SQL has a great data structure for storing lists. It is called a "table", not a "string".
If you are stuck with such a format and using MySQL, there is a function that can help:
where find_in_set('ABC', col)
MySQL treats a comma delimited string as a "set" and offers this function. However, this function cannot use indexes, so it is not particularly efficient. Did I mention that you should use a junction table instead?

Query a database with results from multiple tables?

There are some similar questions around but they aren't quite what I'm looking for, so forgive me if you think this is answered elsewhere.
I am basically looking for an easy way to do things as I have over 4000 tables to get data from. This kind of follows on from my previous post: mysql search for segment of table name
The general situation is that I have a database filled with tables and I only want about a quarter of this which comes to around 4000 tables. I have a list of the individual table names thanks to my previous post, but I want the data that goes with them.
I know that for an individual one I can do SELECT table1.*, table2.*; or something similar but I don't want to go through all 4000 or so.
They all end with the same thing, e.g. staff_name, manager_name, customer_name so I can use
SHOW TABLES LIKE '%_name'
to see the table names that I want in the database. Someone suggested using dynamic mysql, but I don't even know where to start with that. Any suggestions?
Generic example (in PHP):
Constructing dynamic SQL or building your SQL queries with the aid of a programming language would look like this (in PHP for ex.):
$pdos = $pdo->query("SHOW TABLES LIKE '%_name'");
$tables = $pdos->fetchAll();
$query = 'SELECT * FROM '.implode(' UNION SELECT * FROM ');
$pdo->query($query);
The fetchAll method will return an array containing the names of each table selected.
The implode($glue, $array) function takes an array and concatenates every value in the array using the $glue parameter - usually you take an array of values and implode them using $glue = ',' to create a coma separated list of values.
In our case the implode has a partial query as $glue in order to create one big UNION JOIN query.
Once the final $query is build it should look something like:
SELECT * FROM table_1_name
UNION
SELECT * FROM table_2_name
UNION
SELECT * FROM table_3_name
....
....
UNION
SELECT * FROM table_4000_name
The result should contain all of the DISTINCT rows from all 4000 tables.
Specific example (in SQL-only format):
SELECT GROUP_CONCAT(
CONCAT('select * from ', table_name)
SEPARATOR ' union '
)
INTO #my_variable
FROM information_schema.tables
WHERE table_schema = 'dbname'
AND table_name LIKE '%_name';
PREPARE my_statement FROM #my_variable;
EXECUTE my_statement;
The first statement will get all of the table names from the information_schema database;
The CONCAT function prefixes every table name with a a 'SELECT * FROM ' string;
The GROUP_CONCAT does the job that implode would have done in PHP;
The INTO clause makes sure the values are saved inside a variable named my_variable;
The PREPARE statement takes a string value (such as the one you saved in my_variable) and checks if the value is an SQL query;
The EXECUTE statement takes a "prepared statement" and well... executes it.
#my_variable is a temporary variable but it can only be of a scalar type (varchar, int, date, datetime, binary, float, double etc.) it is not an array.
The GROUP_CONCAT function is an "aggregate function" which means it takes an aggregate value (similar concept to an array - in our case the result set of our query) and outputs a simple string result.
I would suggest generating the SQL statement.
Try doing:
select concat('select * from ', table_name) as query
from Information_Schema.tables
where table_schema = <dbname> and
table_name like <whatever>
You can then run this as a bunch of queries by copying into a query editor window.
If you want everything as one query, then do:
select concat('select * from ', table_name, ' union all ') as query
from Information_Schema.tables
where table_schema = <dbname> and
table_name like <whatever>
And remove the final "union all".
This has the table name matching a like. Leave out the table_name part of the WHERE to get all tables. Or, include specific tables using table_name in ().

Searching for multiple keywords using SQL Server stored procedure

I'm going to search my database (SQL Server 2008) using a stored procedure. My users can enter keyword(s) in a textbox (keywords can be separated using , for instance).
Currently I'm using something like this:
keyword like N"%'+#SearchQuery%'%"
(keyword is a nvarchar column in my table, and #SearchQuery is the input to my stored procedure)
It works fine but what if user types several keywords: apple,orange, banana
Should I limit number of my keywords? How should I write my stored procedure if I have more than one keyword? How should I pass my user input to the stored procedure? I should pass apple, orange, banana as a whole phrase and then I should parse them in my stored procedure, or I should separate my keywords and send 3 keywords? How can I query these 3 keywords? A for loop?
What are best practices for performing such queries?
thanks
Do the parsing of the keywords in your application. SQL is not the best place for string manipulation.
Send the keywords as a table valued parameter (ie : http://www.mssqltips.com/sqlservertip/2112/table-value-parameters-in-sql-server-2008-and-net-c/ ) then you aren't limited to a fixed number of keywords.
Add the wildcards to the parameter in the stored procedure
update #keywords set keyword = '%'+keyword+'%'
filter your results by joining your source data to this table
eg:
SELECT result
FROM source
INNER JOIN #keywords keywords
ON source.keyword LIKE keywords.keyword
It depends on:
* How big it's your database.
* How often users will search for something.
* How precise results users except.
LIKE is not performance daemon, especially starting with %.
Maybe you should try full search text?
If you would like stay with LIKE (it will works only for small tables) I would try something like:
Split intput by , character (insert them into table as podiluska suggested is a good idea).
Build query for each token and UNION all results. Or run it in loop for each token and insert results to temporary table.
If you need some precise results (i.e. only records matches all 3 words) you can select most matching results from temporary results built above.
You could use CTE to split the string of keywords in a temporary table and then use it as you like. The keyword list can even have numbers or any characters, like %$<> or what you want, just remember comma is the string separator
DECLARE #CommaSeparatorString VARCHAR(MAX),
#CommaSeparatorXML XML
DECLARE #handle INT
SELECT #CommaSeparatorString = 'apple,orange,banana'
SELECT #CommaSeparatorString = REPLACE(REPLACE(#CommaSeparatorString,'<','$^%'),'>','%^$')
SELECT #CommaSeparatorXML = CAST('<ROOT><i>' + REPLACE(#CommaSeparatorString, ',', '</i><i>') + '</i></ROOT>' AS XML)
SELECT REPLACE(REPLACE(c.value('.', 'VARCHAR(100)'),'$^%','<'),'%^$','>') AS ID
FROM (SELECT #CommaSeparatorXML AS CommaXML) a
CROSS APPLY CommaXML.nodes('//i') x(c)
Result:
ID
------
apple
orange
banana