I use db2 9.7.6 full edition for windows. I need develop functions which can be use on sql select expression.
Functions must contain modifying sql data and handling runtime exceptions. There are two variants, but I have problems of implementations all requirments in ever variants.
The first variant is implementation of sql table function, e.g.
CREATE FUNCTION func1 (val CHAR(20))
RETURNS table(result varchar(1000))
LANGUAGE SQL
MODIFIES SQL DATA
BEGIN atomic
insert into a values(val);
return (select result from a);
END
It works, but I can't implement handling exception how in sql procedures. When I tried to use block "declare exit handler", I got a syntax errors.
The second variant is implementation of pl/sql function, e.g.
CREATE OR REPLACE FUNCTION bb
RETURN varchar2
MODIFIES SQL DATA
AS
BEGIN
insert into st values ('a');
return 0;
END bb;
But when I tried to execute this function, I got error "SQLCODE=-740, SQLSTATE=51034 is defined with the MODIFIES SQL DATA option, which is not valid in the context where the routine is invoked". Help me please.
best regards, Turkin Andrew.
There are many differences between inlined SQL compound and compiled compound. I wrote the following script, and it runs ok in db2 10.1 for LUW.
Script.sql
CREATE or replace FUNCTION func1a (val CHAR(20))
RETURNS varchar(20)
LANGUAGE SQL
READS SQL DATA
BEGIN
declare ret varchar(20);
declare exit handler for sqlstate '02000' resignal sqlstate '08888';
select C1 into ret from T1 fetch first 1 row only;
return ret;
END#
CREATE or replace FUNCTION func1b (val CHAR(20))
RETURNS table(result varchar(20))
LANGUAGE SQL
MODIFIES SQL DATA
BEGIN atomic
insert into T1 values(val);
return (select C1 from T1);
END#
The execution (In Windows client, but it does not matter)
db2 CREATE TABLE T1 (C1 CHAR(20))
db2 -td# -vf Script.sql
db2 "values func1a('s')"
db2 "SELECT * FROM TABLE (FUNC1b('A'))"
As you can see there are many difference in DB2 10.1 for inlined and compiled SQL:
- Single value return vs table or row return.
- Reads vs modified data.
- Condition handler vs nothing.
Due to the function definition, the calling method is different, in one case it is a scalar value, in the other is a function table.
It just doesn't work.
List of maximum access levels
Routine type Default SQL access level Maximum allowed SQL access level
SQL procedures MODIFIES SQL DATA MODIFIES SQL DATA
SQL functions (scalar functions) READS SQL DATA READS SQL DATA
SQL functions (table functions) READS SQL DATA MODIFIES SQL DATA
External procedures MODIFIES SQL DATA MODIFIES SQL DATA
External functions (scalar functions) READS SQL DATA READS SQL DATA
External functions (table functions) READS SQL DATA READS SQL DATA
For scalar functions the maximum level is "READS SQL DATA". There is probably now way around that, so you have to reconsider your coding.
Related
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 have a procedure that receives a table as a parameter. I need to fill it on then return it to another procedure that executes the first one. But, the problem is that when y compile I receive this error message:
Mens 352, Nivel 15, Estado 1, Procedimiento Person_InsertCloud, LĂnea 1
The table-valued parameter "#TableServerIds" must be declared with the READONLY option.
Can't a table as a parameter can be modified? What other options do I have?
Thanks
No the contents of the TVP can't be modified. Upvote Relax restriction that table parameters must be readonly when SPs call each other if you want this functionality.
The other option is a local #temp table. Stored procedures have read and write access to #temp tables created in parent scopes. But this does mean that callers of the sub procedure need to be aware of this requirement and create the expected #temp table before calling.
An example below
CREATE PROC P1
AS
CREATE TABLE #Foo
(
X VARCHAR(50)
);
EXEC P2;
SELECT *
FROM #Foo;
GO
CREATE PROC P2
AS
IF OBJECT_ID('tempdb..#Foo') IS NULL
BEGIN
RAISERROR ('This procedure expects table #Foo to already exist',16,1);
RETURN;
END
INSERT INTO #Foo
VALUES ('Inserted by P2')
GO
EXEC P1
Can't a Table as a parameter can be modified?
No. It is one of the restrictions on TVPs.
The restriction is documented:
Table-valued parameters must be passed as input READONLY parameters to Transact-SQL routines. You cannot perform DML operations such as UPDATE, DELETE, or INSERT on a table-valued parameter in the body of a routine.
Instead of filling a table, you can return a table from your second stored procedure using SELECT.
Here's my configuration:
I have a re-runnable batch script that I use to update my database.
Inside of that batch script, I have code that says the following:
If Table 'A' doesn't exist, then create Table 'A' and insert rows into it.
Later on in that batch script, I create an schemabound indexed view on that table.
And if you didn't already know, indexed views require specific client settings.
Sometimes, when I re-run the script, that is after the table has been created, SQL Server Management Studio evaluates the "insert rows" code, which is protected by the 'If this table doesn't exist' code, and yields the following error:
Msg 1934, Level 16, State 1, Line 15
INSERT failed because the following SET options have incorrect settings: 'CONCAT_NULL_YIELDS_NULL, ANSI_WARNINGS, ANSI_PADDING, ARITHABORT'. Verify that SET options are correct for use with indexed views and/or indexes on computed columns and/or filtered indexes and/or query notifications and/or XML data type methods and/or spatial index operations.
Please note: If someone were to try this INSERT statement in a vacuum, I would fully expect SSMS to generate this error.
But not when it's protected by a conditional block.
My Question:
Does the SSMS compiler evaluate all expressions, regardless of whether they will actually be executed?
Yes, it evaluates all of them,take a look at this
declare #i int
select #i =1
if #i = 1
begin
declare #i2 int
set #i2 = 5
end
else
begin
declare #i2 int
set #i2 = 5
end
Msg 134, Level 15, State 1, Line 12
The variable name '#i2' has already been declared. Variable names must be unique within a query batch or stored procedure.
Another example with temp tables is here: What is deferred name resolution and why do you need to care?
your only way out would be to wrap it inside dynamic SQL
Note that most of the settings you mention are connection-level, i.e. in case you set/change them they stay in effect unless you close the connection or explicitly change their value.
Returning to your question. The error you mention looks like runtime error, i.e. the INSERT is actually being executed. It would be better if you could show your script (omitting details, but keeping batches).
Edit: it is not SSMS compiler that evaluates SQL you try to execute - it is SQL Server. What do you meant by 'evaluate'? Is it 'execute'? When you run a batch (which is what actually is being executed by a server), SQL Server first does syntactic analysis and throws error in case it finds any syntactic error, nothing is being executed at this point of time. In case syntax is ok, the server starts executing you batch.
Again, the error you show seems to be runtime - so I guess you'd carefully watch for the conditions and track what happens (or provide us more details about 'sometimes').