I have a stored procedure that looks like below Pseudo:
create procedure composite(IN a varchar(255),IN b varchar(255),IN c datetime,IN d datetime)
begin
DECLARE str VARCHAR(255);
DECLARE count float;
SET str = '';
SET str = CONCAT("aname like '%",a,"%' "," and bname ='",b,"' ");
set #comp = CONCAT("SELECT * from abc where ",str, "GROUP BY qname");
PREPARE stmt FROM #comp;
EXECUTE stmt;
set count = found_rows();
SET STR1 = CONCAT("aname like '%",a,"%' "," and bname ='",b,"' ");
SET #sql = CONCAT("SELECT * from xyz",str,"GROUP BY DATE(FROM_UNIXTIME(abcdate)),qname");
PREPARE stmt FROM #sql;
EXECUTE stmt;
end//
When I execute this i get 2 result-sets as output i.e. from executing 2 select statement. I want the output of only last select. Is there any way to do this?
Do not quite understand what you want to do. I imagine that the stored procedure is not complete.
Is it really necessary to use prepared statements in this case? Do you need to count as float?
Maybe something like this might be useful:
...
DECLARE count BIGINT UNSIGNED;
SELECT COUNT(*) INTO count /* Here you can use a user variable (#count) and avoiding the local variable (count) */
FROM (
SELECT 0
FROM abc
WHERE aname LIKE CONCAT('%', a, '%') AND bname = b
GROUP BY qname
) der;
SELECT aname, bname, qname
FROM xyz
WHERE aname LIKE CONCAT('%', a, '%') AND bname = b
GROUP BY qname;
...
The first statement will store the number of rows in the local variable count and will not return the dataset. Beware of variable names and reserved words, in this case count is allowed, but read 9.3. Reserved Words in the documentation.
Related
When we use a statement like select count(*) from TABLE, the function count() automatically knows which table it is counting. Is it possible to grab the table and use it in a user defined function.
drop function if exists related_count;
create function related_count(parent int(11)) returns int(11) deterministic
begin
declare count int(11) default 0;
set count=(select count(*) from TABLENAME where id=parent);
return count;
end;
So that I can use it like this:
select count(*),related_count(id) from TABLENAME
So that I can use the same function regardless of table instead of defining multiple functions because of multiple tables.
Is there a way to switch between select count(*) from TABLENAME1 where id=parent or select count(*) from TABLENAME2 where id=parent dependent on a variable related_count('TABLE1',id)
The comment above from #RajeevRanjan mentions using dynamic SQL. This won't work, but if it did it would look like this:
create function related_count(tablename varchar(64), parent int) returns int reads sql data
begin
declare count int default 0;
set #sql = concat('select count(*) into count from `', tablename, '` where id = ', parent);
prepare stmt from #sql;
execute stmt;
return count;
end
However, this is not allowed:
ERROR 1336 (0A000): Dynamic SQL is not allowed in stored function or trigger
The reason it doesn't work is that your stored function could be called by an expression in an SQL statement that is itself a dynamic SQL execution. I guess MySQL only allows one level "deep" of prepare/execute. You can't make a prepared query run another prepared query.
To do this, you'd have to hard-code each table name like the following:
create function related_count(tablename varchar(64), parent int) returns int reads sql data
begin
declare count int default null;
if tablename = 'foo' then set count = (select count(*) from foo where id = parent);
elseif tablename = 'bar' then set count = (select count(*) from bar where id = parent);
elseif tablename = 'baz' then set count = (select count(*) from baz where id = parent);
end if;
return count;
end
This also has an advantage that it isn't an SQL injection vulnerability, whereas the PREPARE/EXECUTE solution (if it had worked) would be.
PS: A function that reads from other tables is not deterministic.
Since mysql functions cannot return tables/result sets therefore i created a stored procedure (Get_StudentsWithAllIndicators) that does the stuff i needed. Now, i need to use this stored procedure result inside the actual stored procedure(Find_MapDetails) like this.
select * from students where studentid in (Get_StudentsWithAllIndicators('7,8', 2));
but it does not work! If you are recommending creating a temporary table and insert values into it please tell me the syntax..thanks
Get_StudentsWithAllIndicators
CREATE PROCEDURE `Get_StudentsWithAllIndicators`(IN p_list VARCHAR(255), IN p_length int)
BEGIN
/*make query with the length of indicators in the list*/
DECLARE x INT;
SET x=1;
SET #queryMain = 'SELECT distinct studentid FROM studentindicators WHERE studentid IN ';
SET #queryWhere = '(SELECT studentid FROM studentindicators WHERE indicatorid = substring_index(\'';
SET #query = '';
WHILE x <= p_length DO
SET #query = CONCAT(#query, #queryWhere, p_list, '\', ",", 1)) AND studentId IN ');
SET x=x+1;
SET #lengthWithCommas = Length(p_list);
SET p_list = substr(p_list, instr(p_list, ',') + 1, #lengthWithCommas - instr(p_list, ','));
END WHILE;
/*remove last AND - note: no occurence of A after AND is expected*/
SET #query = CONCAT(#queryMain, LEFT(#query, LENGTH(#query) - LOCATE('A', REVERSE(#query))));
PREPARE stmt FROM #query;
EXECUTE stmt;
END
INSERT INTO #MYTEMPTABLE exec Get_StudentsWithAllIndicators('7,8', 2)
SELECT * FROM students where studentId in (SELECT studentid FROM #MYTEMPTABLE)
Save the results of the first sp into a temp table.
I'm looking for a way to easily check each table of a MySQL database and make sure that a certain field contains one value only. I have tables named Authors, Titles, Places, etc.
Each table contains a field called xuser and it needs to ask "does the field xuser contain the value xy in all records of all tables".
Can someone push me in the right direction how to do this with a SQL query if this is possible?
Thanks for reading, regards
Nico
I've created stored procedure which checks all table for provided db:
DELIMITER $$
DROP PROCEDURE IF EXISTS `UTL_CHECK_BACKUP_FOR_USER` $$
CREATE PROCEDURE `UTL_CHECK_BACKUP_FOR_USER`(
IN i_database_name VARCHAR(255),
IN i_user_column_name VARCHAR(255),
IN i_user_column_value VARCHAR(255),
OUT o_result TINYINT(1)
)
BEGIN
DECLARE v_table_name VARCHAR(255);
DECLARE v_last_row_fetched TINYINT(3) DEFAULT 0;
DECLARE tables_cursor CURSOR FOR
SELECT table_name
FROM information_schema.tables
WHERE table_schema = i_database_name
;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_last_row_fetched = 1;
SET v_last_row_fetched = 0;
OPEN tables_cursor;
SET #query =
CONCAT(
'SELECT SUM(IF(user_column=''',
i_user_column_value,
''', 1, -1)) = 1 INTO #o_result FROM ( SELECT ''test'' AS user_column FROM information_schema.tables WHERE 1<>1 '
)
;
table_loop: LOOP
FETCH tables_cursor INTO v_table_name;
IF (v_last_row_fetched = 1) THEN
LEAVE table_loop;
END IF;
SET #query =
CONCAT(
#query,
' UNION SELECT DISTINCT(',
i_user_column_name,
') AS user_column FROM ',
v_table_name
)
;
END LOOP table_loop;
CLOSE tables_cursor;
SET v_last_row_fetched=0;
SET #query =
CONCAT(
#query,
' ) all_xusers;'
)
;
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET o_result = COALESCE(#o_result, 0);
END $$
DELIMITER ;
Just deploy this stored procedure to database.
And then it could be executed in the following way:
-- db_name, user_column_name, user_column_value, result
call UTL_CHECK_BACKUP_FOR_USER('test', 'xuser', 'xxx', #result);
select #result;
To get the rows from all three tables where xuser has the same value in all three tables you could use:
SELECT *
FROM authors a
JOIN titles t
ON t.xuser = a.xuser
JOIN places p
ON p.xuser = t.xuser
If you want to look at a specific xuser value you could add the following WHERE clause:
WHERE a.xuser = 'xy'
The first thing comes to my mind:
select sum(if(xuser='xxx', 1, -1)) = 1
from (
select distinct(xuser) from authors
union
select distinct(xuser) from titles
union
select distinct(xuser) from places
) all_xusers;
This will return 1 (true) if all tables contains records belonging ONLY to 'xxx' user. Otherwise (if there is no 'xxx' records or there is some other user records) it will return 0 (false).
Example: I have a table with 5 fields, named id, field_1, field_2, field_3, field_4
And I am searching for 'foo' across all fields.
SELECT ID FROM table WHERE field_1 LIKE ('%foo%') OR field_2 LIKE ('%foo%') OR ...
I'd like to return the IDs, as well as which fields the term was found in.
What would be the most efficient way to do this?
Note: I am looking for a solution that could dynamically accommodate adding new DB fields, without having to manually update the SQL.
One possible approach is to map these matches in returned columns:
SELECT ID,
field_1 LIKE '%foo%' AS field_1_match,
field_2 LIKE '%foo%' AS field_2_match
...
... so you can just check each corresponding column_match value to know, well, was it matched or not.
It's easy to extend this into returning a string with columns (separated by ,, for example) with CONCAT_WS:
SELECT ID,
CONCAT_WS(',',
IF(field_1 LIKE '%foo%', 'field_1', NULL),
IF(field_2 LIKE '%foo%', 'field_2', NULL)
...
)
... but, honestly speaking, I doubt it'll be easier to process data formatted this way.
Use unions:
SELECT id, 'field_1' as 'fieldName' FROM table WHERE field1 LIKE '%foo%' UNION
SELECT id, 'field_2' as 'fieldName' FROM table WHERE field2 LIKE '%foo%' UNION
...
This will return the id and column name wherever it is found. If it happens multiple times on the same row, multiple results will be returned for that row.
Updated:
It is possible to dynamically search all of the tables using a stored procedure and a cursor.
DELIMITER //
CREATE PROCEDURE search_all_fields(IN search VARCHAR(100), IN tableName VARCHAR(100), IN idColumnName VARCHAR(100))
BEGIN
DECLARE sqlQuery VARCHAR(200);
DECLARE done INT DEFAULT 0;
DECLARE columnName VARCHAR(30);
DECLARE cur CURSOR FOR SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_NAME`= tableName; # cursor will iterate over the column names
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
CREATE TEMPORARY TABLE temp ( id VARCHAR(100), FieldName VARCHAR(100) ); # procedure returns multiple result sets, so we'll dump them in a temp table and get them at the end
OPEN cur;
read_loop: LOOP # iterates through column names
FETCH cur INTO columnName;
IF done THEN
LEAVE read_loop;
END IF;
# execute search
SET #sqlQuery = CONCAT("INSERT INTO temp SELECT ", idColumnName, ", '", columnName, "' as 'FieldName' FROM ", tableName, " WHERE ", columnName, " LIKE '%", search, "%'");
PREPARE stmt FROM #sqlQuery;
EXECUTE stmt;
END LOOP;
CLOSE cur;
# grab the results
SELECT * FROM temp;
END;//
DELIMITER ;
Is it possible to query a table whose name comes from a sub-query?
For example.,
SELECT * FROM <TABLE_NAME IS <SUB_QUERY>>
select * from (
(select distinct(name) from category where id = 3 limit 1) CAT);
INNER QUERY RESULTS --> DEPARTMENT;
So it has to fetch from department table.
Using Mysql as DB.
You should use Prepared Statements.
In your case it should be:
select #name := name from (
(select distinct(name) from category where id = 3 limit 1) CAT);
set #sqlquery := 'select * from ' . #name ;
prepare qry from #sqlquery ;
execute qry;
deallocate prepare qry;
This might be helpful SQL Syntax for Prepared Statements
In two words: you can execute sql commands specified in varchar variables which can be produced by concatenation and other stuff.