i am getting issue at return #averagesalary1 and from the line as ,error being not valid at this position, expecting an identifier
create function world.function1()
returns double
as
begin
DECLARE averagesalary1 double;
select #averagesalary1=avg(averagesalary) from coampanies;
return #averagesalary1
end;
what seems to be the issue here?
Microsoft SQL Server supports the AS keyword in CREATE FUNCTION syntax as you show. This is different from MySQL syntax.
You must keep in mind that what you learned in one implementation may be different in another, so you have read the documentation for the brand of SQL database you use, and don't assume that they are compatible.
Another difference between Microsoft and MySQL that is shown in your code example is that a declared variable and a variable with the # symbol are in fact different variables. A value in one is not the same value in the other.
Here's how I would write your function in MySQL:
CREATE FUNCTION world.function1()
RETURNS DOUBLE
READS SQL DATA
BEGIN
DECLARE averagesalary1 double;
SELECT AVG(averagesalary) FROM coampanies INTO averagesalary1;
RETURN averagesalary1;
END;
I have two base tables "tutorials_tb2","tutorials_tbl"
and I am trying to create two new tables from these tables as "tutorials_tb2_new","tutorials_tbl_new"
So tutorials_tb2 will be source to tutorials_tb2_new and similarly tutorials_tbl to tutorials_tbl_new
I am trying by creating a MySQL function with new and orig tables as array by using JSON_* methods
DELIMITER //
CREATE FUNCTION example()
BEGIN
DECLARE _counter INT DEFAULT 0;
DECLARE _value varchar(50);
SET #origTables = '["tutorials_tb2","tutorials_tbl"]';
SET #newTables = '["tutorials_tb2_new","tutorials_tbl_new"]';
WHILE _counter < JSON_LENGTH(#origTables) DO
CREATE TABLE JSON_VALUE(#newTables, CONCAT('$[',_counter,']')) LIKE JSON_VALUE(#origTables, CONCAT('$[',_counter,']'))
SET _counter = _counter + 1;
END WHILE;
END //
DELIMITER ;
SELECT example();
And while executing I am getting below 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 'BEGIN
DECLARE _counter INT DEFAULT 0;
DECLARE _value varchar(50);
SET #origT' at line 2
MySQL>
The reason for your syntax error is that a stored function requires a RETURNS <type> clause before the BEGIN. Review the syntax documentation: https://dev.mysql.com/doc/refman/8.0/en/create-procedure.html
The next problem you will encounter, as Akina commented above, is that the JSON expression returns a string, not an identifier. You can't CREATE TABLE <string-expression> and have the result of the expression be the table name.
You would have to interpolate the string expression into a string, and execute that string as a dynamic SQL statement. But you can't do that in a stored function.
https://dev.mysql.com/doc/refman/8.0/en/stored-program-restrictions.html#stored-routine-sql-restrictions says:
SQL prepared statements (PREPARE, EXECUTE, DEALLOCATE PREPARE) can be used in stored procedures, but not stored functions or triggers. Thus, stored functions and triggers cannot use dynamic SQL (where you construct statements as strings and then execute them).
Really, you are making this task much harder than it should be by using stored functions and JSON.
Just write an app or a script that loops over the table names you want to create, format the DDL statement as a string including the table name, then execute the DDL statement.
Here's an example in Python:
import mysql.connector
cnx = mysql.connector.connect(...)
cursor = cnx.cursor()
for tablename in ["tutorials_tb2","tutorials_tbl"]:
sql = f"CREATE TABLE `{tablename}_new` LIKE `{tablename}`"
cursor.execute(sql)
cnx.close()
I hardly ever use stored routines or JSON in MySQL.
I want to create a function that basically returns a random string. I don't know what characteristics to assign in this situation. I'm also in an environment that uses binary logging.
Here's a simplified version of my function:
CREATE FUNCTION `MYRAND`() RETURNS char(10) NOT DETERMINISTIC
RETURN CONCAT('rand_', FLOOR(RAND() * 10000));
I get this error when creating the function in my environment.
This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in
its declaration and binary logging is enabled (you might want to use
the less safe log_bin_trust_function_creators variable)
Possible characteristics:
NOT DETERMINISTIC - used because this function returns random values
READS/MODIFIES SQL DATA - Function does not read data from tables
NO SQL - I am calling other SQL functions (RAND) so I'm not sure if I should be specifying this or not...
Any advice on how to properly define this function when binary logging is enabled would be appreciated.
MySQL wants you to declare the function as DETERMINISTIC, NO SQL, or READS SQL DATA.
Is it DETERMINISTIC? No - Since it is random.
Does id read SQL DATA? No - Since you have no SELECT statement.
Does it modify SQL DATA? No - Since you have no INSERT, UPDATE or DELETE statement.
Since your function does not touch any data in the DB it's NO SQL.
So you should declare it as NOT DETERMINISTIC and NO SQL
CREATE FUNCTION `MYRAND`() RETURNS char(10) NOT DETERMINISTIC NO SQL
RETURN CONCAT('rand_', FLOOR(RAND() * 10000));
Use somthing like this code:
CREATE FUNCTION get_string(in_strlen int) RETURNS VARCHAR(500) DETERMINISTIC
BEGIN
set #var:='';
while(in_strlen>0) do
set #var:=concat(#var,IFNULL(ELT(1+FLOOR(RAND() * 1000),1,2,3,4,5,6,7,8,9));
set in_strlen:=in_strlen-1;
end while;
RETURN #var;
END
I have a simple custom type for use it as table valued parameter.
Like this:
CREATE TYPE dbo.PeriodsList AS TABLE
(
PeriodDate NVARCHAR(8) NOT NULL
)
I also have very simple table valued function:
alter FUNCTION GetPeriodsInYear (#periods dbo.PeriodsList READONLY) returns #PeriodsSet Table(period NVARCHAR(8))
BEGIN
insert #PeriodsSet
select
'0' as period
Return
end
But when I try to execute this function in this way
DECLARE #periods1 dbo.PeriodsList
INSERT INTO #periods1
VALUES ('20130916')
select * from GetPurchasesInYear(#periods1)
I receive error message - "Must declare the scalar variable "#periods1".
I have found many examples about stored procedures but not about functions. Is it possible to pass table valued parameters into functions?
And where the mistake in my code?
Check the "compatibility level" of your SQL Server 2008 database. If it's set to 80 (SQL Server 2000), you will get the error you are describing when you try to call a function (but not a stored procedure) that takes a table-valued parameter.
It should work if the compatibility level value is 90 (SQL Server 2005) or higher.
To view the compatibility level of your database, do:
SELECT compatibility_level
FROM sys.databases
WHERE name = 'YOUR_DB_NAME_HERE';
Source: http://msgroups.net/microsoft.public.sqlserver.programming/pass-table-valued-para/97879
Changing the compatibility level (SQL Server 2008): http://technet.microsoft.com/en-us/library/bb510680%28v=sql.100%29.aspx
I want to pass a table name as a parameter in a Postgres function. I tried this code:
CREATE OR REPLACE FUNCTION some_f(param character varying) RETURNS integer
AS $$
BEGIN
IF EXISTS (select * from quote_ident($1) where quote_ident($1).id=1) THEN
return 1;
END IF;
return 0;
END;
$$ LANGUAGE plpgsql;
select some_f('table_name');
And I got this:
ERROR: syntax error at or near "."
LINE 4: ...elect * from quote_ident($1) where quote_ident($1).id=1)...
^
********** Error **********
ERROR: syntax error at or near "."
And here is the error I got when changed to this select * from quote_ident($1) tab where tab.id=1:
ERROR: column tab.id does not exist
LINE 1: ...T EXISTS (select * from quote_ident($1) tab where tab.id...
Probably, quote_ident($1) works, because without the where quote_ident($1).id=1 part I get 1, which means something is selected. Why may the first quote_ident($1) work and the second one not at the same time? And how could this be solved?
Before you go there: for only few, known tables names, it's typically simpler to avoid dynamic SQL and spell out the few code variants in separate functions or in a CASE construct.
That said, what you are trying to achieve can be simplified and improved:
CREATE OR REPLACE FUNCTION some_f(_tbl regclass, OUT result integer)
LANGUAGE plpgsql AS
$func$
BEGIN
EXECUTE format('SELECT (EXISTS (SELECT FROM %s WHERE id = 1))::int', _tbl)
INTO result;
END
$func$;
Call with schema-qualified name (see below):
SELECT some_f('myschema.mytable'); -- would fail with quote_ident()
Or:
SELECT some_f('"my very uncommon table name"');
Major points
Use an OUT parameter to simplify the function. You can directly select the result of the dynamic SQL into it and be done. No need for additional variables and code.
EXISTS does exactly what you want. You get true if the row exists or false otherwise. There are various ways to do this, EXISTS is typically most efficient.
You seem to want an integer back, so I cast the boolean result from EXISTS to integer, which yields exactly what you had. I would return boolean instead.
I use the object identifier type regclass as input type for _tbl. That does everything quote_ident(_tbl) or format('%I', _tbl) would do, but better, because:
.. it prevents SQL injection just as well.
.. it fails immediately and more gracefully if the table name is invalid / does not exist / is invisible to the current user. (A regclass parameter is only applicable for existing tables.)
.. it works with schema-qualified table names, where a plain quote_ident(_tbl) or format(%I) would fail because they cannot resolve the ambiguity. You would have to pass and escape schema and table names separately.
It only works for existing tables, obviously.
I still use format(), because it simplifies the syntax (and to demonstrate how it's used), but with %s instead of %I. Typically, queries are more complex so format() helps more. For the simple example we could as well just concatenate:
EXECUTE 'SELECT (EXISTS (SELECT FROM ' || _tbl || ' WHERE id = 1))::int'
No need to table-qualify the id column while there is only a single table in the FROM list. No ambiguity possible in this example. (Dynamic) SQL commands inside EXECUTE have a separate scope, function variables or parameters are not visible there - as opposed to plain SQL commands in the function body.
Here's why you always escape user input for dynamic SQL properly:
db<>fiddle here demonstrating SQL injection
Old sqlfiddle
If at all possible, don't do this.
That's the answer—it's an anti-pattern. If the client knows the table it wants data from, then SELECT FROM ThatTable. If a database is designed in a way that this is required, it seems to be designed sub-optimally. If a data access layer needs to know whether a value exists in a table, it is easy to compose SQL in that code, and pushing this code into the database is not good.
To me this seems like installing a device inside an elevator where one can type in the number of the desired floor. After the Go button is pressed, it moves a mechanical hand over to the correct button for the desired floor and presses it. This introduces many potential issues.
Please note: there is no intention of mockery, here. My silly elevator example was *the very best device I could imagine* for succinctly pointing out issues with this technique. It adds a useless layer of indirection, moving table name choice from a caller space (using a robust and well-understood DSL, SQL) into a hybrid using obscure/bizarre server-side SQL code.
Such responsibility-splitting through movement of query construction logic into dynamic SQL makes the code harder to understand. It violates a standard and reliable convention (how a SQL query chooses what to select) in the name of custom code fraught with potential for error.
Here are detailed points on some of the potential problems with this approach:
Dynamic SQL offers the possibility of SQL injection that is hard to recognize in the front end code or the back end code alone (one must inspect them together to see this).
Stored procedures and functions can access resources that the SP/function owner has rights to but the caller doesn't. As far as I understand, without special care, then by default when you use code that produces dynamic SQL and runs it, the database executes the dynamic SQL under the rights of the caller. This means you either won't be able to use privileged objects at all, or you have to open them up to all clients, increasing the surface area of potential attack to privileged data. Setting the SP/function at creation time to always run as a particular user (in SQL Server, EXECUTE AS) may solve that problem, but makes things more complicated. This exacerbates the risk of SQL injection mentioned in the previous point, by making the dynamic SQL a very enticing attack vector.
When a developer must understand what the application code is doing in order to modify it or fix a bug, he'll find it very difficult to get the exact SQL query being executed. SQL profiler can be used, but this takes special privileges and can have negative performance effects on production systems. The executed query can be logged by the SP but this increases complexity for questionable benefit (requiring accommodating new tables, purging old data, etc.) and is quite non-obvious. In fact, some applications are architected such that the developer does not have database credentials, so it becomes almost impossible for him to actually see the query being submitted.
When an error occurs, such as when you try to select a table that doesn't exist, you'll get a message along the lines of "invalid object name" from the database. That will happen exactly the same whether you're composing the SQL in the back end or the database, but the difference is, some poor developer who's trying to troubleshoot the system has to spelunk one level deeper into yet another cave below the one where the problem exists, to dig into the wonder-procedure that Does It All to try to figure out what the problem is. Logs won't show "Error in GetWidget", it will show "Error in OneProcedureToRuleThemAllRunner". This abstraction will generally make a system worse.
An example in pseudo-C# of switching table names based on a parameter:
string sql = $"SELECT * FROM {EscapeSqlIdentifier(tableName)};"
results = connection.Execute(sql);
While this does not eliminate every possible issue imaginable, the flaws I outlined with the other technique are absent from this example.
Inside plpgsql code, The EXECUTE statement must be used for queries in which table names or columns come from variables. Also the IF EXISTS (<query>) construct is not allowed when query is dynamically generated.
Here's your function with both problems fixed:
CREATE OR REPLACE FUNCTION some_f(param character varying) RETURNS integer
AS $$
DECLARE
v int;
BEGIN
EXECUTE 'select 1 FROM ' || quote_ident(param) || ' WHERE '
|| quote_ident(param) || '.id = 1' INTO v;
IF v THEN return 1; ELSE return 0; END IF;
END;
$$ LANGUAGE plpgsql;
I know this is an old thread, but I ran across it recently when trying to solve the same problem - in my case, for some fairly complex scripts.
Turning the entire script into dynamic SQL is not ideal. It's tedious and error-prone work, and you lose the ability to parameterize: parameters must be interpolated into constants in the SQL, with bad consequences for performance and security.
Here's a simple trick that lets you keep the SQL intact if you only need to select from your table - use dynamic SQL to create a temporary view:
CREATE OR REPLACE FUNCTION some_f(_tbl varchar) returns integer
AS $$
BEGIN
drop view if exists myview;
execute format('create temporary view myview as select * from %s', _tbl);
-- now you can reference myview in the SQL
IF EXISTS (select * from myview where myview.id=1) THEN
return 1;
END IF;
return 0;
END;
$$ language plpgsql;
The first doesn't actually "work" in the sense that you mean, it works only in so far as it does not generate an error.
Try SELECT * FROM quote_ident('table_that_does_not_exist');, and you will see why your function returns 1: the select is returning a table with one column (named quote_ident) with one row (the variable $1 or in this particular case table_that_does_not_exist).
What you want to do will require dynamic SQL, which is actually the place that the quote_* functions are meant to be used.
If the question was to test if the table is empty or not (id=1), here is a simplified version of Erwin's stored proc :
CREATE OR REPLACE FUNCTION isEmpty(tableName text, OUT zeroIfEmpty integer) AS
$func$
BEGIN
EXECUTE format('SELECT COALESCE ((SELECT 1 FROM %s LIMIT 1),0)', tableName)
INTO zeroIfEmpty;
END
$func$ LANGUAGE plpgsql;
If you want table name, column name and value to be dynamically passed to function as parameter
use this code
create or replace function total_rows(tbl_name text, column_name text, value int)
returns integer as $total$
declare
total integer;
begin
EXECUTE format('select count(*) from %s WHERE %s = %s', tbl_name, column_name, value) INTO total;
return total;
end;
$total$ language plpgsql;
postgres=# select total_rows('tbl_name','column_name',2); --2 is the value
I have 9.4 version of PostgreSQL and I always use this code:
CREATE FUNCTION add_new_table(text) RETURNS void AS
$BODY$
begin
execute
'CREATE TABLE ' || $1 || '(
item_1 type,
item_2 type
)';
end;
$BODY$
LANGUAGE plpgsql
And then:
SELECT add_new_table('my_table_name');
It works good for me.
Attention! Above example is one of those which shows "How do not if we want to keep safety during querying the database" :P