I just started learning use defined functions in MYSQL and I tried few basic examples, but when I tried a function which returns varchar, which actually returns a portion of a select query, it gives me something unexpected.
There are lot of tables in database with columns as id and name, so whenever people want to fetch id for a particular name or vice versa they go into the table and manually fetch the data. So I was trying to create a function that would smoothen the process.
This is my function
create function getTableDetails(table_name varchar(20), id int(5), name varchar(20))
returns varchar(50)
begin
return " * from " + table_name + " where id = " + id +
" or name like '%" + name + "%'";
end
As you can see I'm trying to generalize the query for all tables and it returns everything other than the select keyword.
Now my query will be like select getTableDetails('classes', 2, 'a')
Assuming that the return of the function will fill the rest of the query and give me the table data, but the result set what I'm getting is
getTableDetails('classes'; 2; 'a')
2
getTableDetails('classes'; 2; 'a') is the header.
Where I'm getting wrong?
You can't dynamically generate and execute query like that, from command line. You need to use preparedstatement and that too, from stored procedure, e.g.:
SET #query = CONCAT('select', getTableDetails('classes', 2, 'a'));
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Related
I have been trying to create a simple loop of SELECT statements in MySQL to reduce code. I have started this using CONCAT() however this causes the procedure to stop/fail. For example (where k is a loop counter):
CONCAT('SELECT (Child_', k, ' INTO #Age_Child_', k, ' FROM lookup_childage WHERE ModYear = ModYear_var LIMIT 1)');
To diagnose the issue, I simply tried to place the SELECT statement (without concatenated loop variables) inside a string to then be executed. While I could get this to work for simple statements it would not work for the following:
SET #queryString = CONCAT('SELECT Child_1 INTO #Age_Child_1 FROM lookup_childage WHERE ModYear = ModYear_var LIMIT 1');
PREPARE stmt FROM #queryString;
EXECUTE stmt;
Does anyone know why the #queryString containing the CONCAT() statement will not be executed/cause the procedure to fail?
tl;dr The statement you're trying to write has the form SELECT(rest of statement) LIMIT 1. It should have the form SELECT rest of statement LIMIT 1.
It looks like you want to create variable column names, ummm, because your lookup_childage table is denormalized. I guess that table has these columns.
Child_1 INT
Child_2 INT
Child_3 INT
Child_4 INT
It looks like you hope to get a #queryString value containing this sort of thing:
SELECT Child_4 INTO #Age_Child_4 FROM lookup_childage WHERE ModYear = ModYear_var LIMIT 1
Only the 4s are variable.
So to get that string you want
SELECT CONCAT('SELECT Child_', k,
' INTO #Age_Child_', k,
' FROM lookup_childage WHERE ModYear = ModYear_var LIMIT 1'
)
INTO #queryString;
I have the following code so far:
delimiter //
CREATE FUNCTION Iteration(InputArray CHAR)
RETURNS DECIMAL
BEGIN
RETURN
SELECT Max(IF(Stock = InputArray, ValueFrom, NULL)) AS Stock FROM DatabaseName.TableName LIMIT 1;
END
delimiter ;
SET #Stocks = (SELECT ColumnName FROM DatabaseName.TableName LIMIT 1);
SELECT Iteration(#Stocks)FROM DatabaseName.TableName;
What I'm interested in doing is passing an array to the Iteration function, which would then be operated on by looking up the corresponding value in the DatabaseName database, spitting back out a corresponding decimal for each value in the input array-- in other words, one array in, another array out.
I think I instantiated everything correctly, but I keep getting the following error:
Error Code: 1305. FUNCTION applications.Iteration does not exist
For example, I have the variables AAA, BBB, CCC, etc, which are stock inventory codes. I want to pass an array of these variables to the procedure/function, and then go onto get back an array as well. In this case, it would be 1.7, 1.3, and 1.8.
There's a few questions I have:
Does this make sense-- that is, can I pass an array to a function like this?
If 1. is yes, am I passing the array to the function properly?
Why am I getting this error?
First, we need to nail down the logic you actually want for the SQL query. You want to take the MAX value of the ValueFrom column, but only for those records where the Stock is contained in the input array. This can be accomplished by doing a simple SELECT, along with a WHERE clause which filters out the non matching records, e.g.
SELECT ValueFrom
FROM DatabaseName.TableName
WHERE Stock IN ('stock1', 'stock2', 'stock3')
Of course, we will need to replace the list of stocks with the input array. I would use a stored procedure here:
CREATE PROCEDURE Iteration(IN inputArray VARCHAR(255))
BEGIN
SET #sql = CONCAT('SELECT ValueFrom FROM DatabaseName.TableName WHERE Stock IN (', inputArray, ')');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
$$
DELIMITER ;
Usage:
SET #inputArray = '\'stock1\',\'stock2\',\'stock3\'';
CALL Iteration(#inputArray);
I have a Users table and I'm getting user details (usual way) with id.
This is how I'm getting user details;
select id,kullaniciadi,FotoGet(K.Foto,K.Gender) from kullanicilar K where id=1;
FotoGet function always returning string value like 'Photos/ssss.jpg'
So far I have not trouble to use this way but I'm wondering how can I do this with a single function?
Like
set #uid=1;
Select UserGet(#uid);
I will put the id inside parentheses and the function will run my select query and return all user detail columns. id, kullaniciadi, FotoGet(K.id,K.Foto,K.Gender)
Can I use this way to get details?
A MySQL stored function cannot return multiple columns, it can only return a single scalar value.
But you could design a stored procedure that returns a result set:
CREATE PROCEDURE UserGet(uid INT)
SELECT id, kullaniciadi, FotoGet(K.Foto,K.Gender) FROM kullanicilar K WHERE id=uid;
Then call it like so:
CALL UserGet(1234);
It returns a result set just like a SELECT query would.
DELIMITER //
CREATE PROCEDURE UserGet(IN uid INT, IN column_name VARCHAR(64))
BEGIN
SET #sql = CONCAT('SELECT id, ', column_name, ' FROM kullanicilar K WHERE id=?');
PREPARE stmt FROM #sql;
SET #uid = uid;
EXECUTE stmt USING #uid;
END//
DELIMITER ;
Call it like so:
CALL UserGet(1234, 'kullaniciadi');
Remember that it's your responsibility to write code to pass a valid column name as the procedure argument. If you allow untrustworthy content to be passed, then it might be an SQL injection vulnerability.
Re your additional comment:
This should work fine:
CALL UserGet(1234, 'FotoGet(Foto,Gender)');
people know that we can use if statement to configure a query in the select statement like this
select if(var=1,amount,amount/2) from mytable;
But what if I want to achieve something like this:
select amount from if(var=1,mytable1,mytable2);
Is there any way to configure the table at run time?
SELECT amount FROM mytable1 WHERE #var = 1
UNION
SELECT amount FROM mytable2 WHERE #var = 0
UPD: Here's what MySQL EXPLAIN looks like for the part of the query which has a condition evaluating to FALSE:
Note the Impossible WHERE part. MySQL recognizes that the expression in WHERE is constantly evaluating to FALSE, so it doesn't even try executing the query. Hence, no performance overhead when using this approach.
(Upgrading to an answer)
Where did var come from?
If it's a variable in another language, you could test it in that other language and then construct different SQL as appropriate:
$sql = "SELECT amount FROM " . ($var = 1 ? "mytable1" : "mytable2");
If it's a user variable in SQL, you could similarly use an IF statement around the two alternative SELECT statements:
DELIMITER ;;
IF #var = 1 THEN
SELECT amount FROM mytable1;
ELSE
SELECT amount FROM mytable2;
END IF;;
DELIMITER ;
If it's anything else (like a field from your tables), then your question doesn't make a great deal of sense.
Based on the mysql manual pages, it appears you cannot do this with the traditional syntax.
"User variables are intended to provide data values. They cannot be used directly in an SQL statement as an identifier or as part of an identifier, such as in contexts where a table or database name is expected, or as a reserved word such as SELECT."
- [http://dev.mysql.com/doc/refman/5.6/en/user-variables.html][1]
The exception to this is that you can assemble a prepared statement, but is probably not a better solution for most programming tasks. It would be better to leave the sql string generation to the language invoking mysql.
But, if you are doing this as part of a "sql only" task, like an import, this seems to be the approach you must take.
SET #s = CONCAT("SELECT * FROM ", if(true, "table1", "table2"), " LIMIT 1");
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET #s = CONCAT("SELECT * FROM ", if(false, "table1", "table2"), " LIMIT 1");
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
I'm doing a SELECT INTO OUTFILE and it's showing a "\N" for every NULL value. Is there any way for me to make it just be blank instead?
I'm on MySQL.
You can use the COALESCE function something like this:
COALESCE(yourfield, '')
Good question, and as relevant today as it was in 2011. I use a stored procedure for this, wich takes the name of a table as argument. Subsequently, it converts all null-values to empty strings (''), using a prepaired statement and no cursors, for cursors are so non-SQL. I've tested it with a table with some 200 columns and a resulting prepaired statement that's about 15,000 characters long:
CREATE DEFINER=`root`#`localhost` PROCEDURE `ExportFixNull`(
in tblname tinytext)
BEGIN
set #string=concat(
"Update ",#tblname," set ",
(
select group_concat(column_name,"=ifnull(",column_name,",'')")
from information_schema.columns
where table_name=#tblname
)
,";"
);
prepare s1 from #string;
execute s1;
drop prepare s1;
In the main SQL-file, there is a statement
SET ##group_concat_max_len = 60000;
that might be crucial.
More details (in Dutch): http://wiki.devliegendebrigade.nl/SELECT_INTO_OUTFILE_%28MySQL%29
Regards,
Jeroen Strompf