DB2 error in using the cursor in PL/SQL function - cursor

I am declaring a cursor in DB2 PL/SQL function as below:
create function query1(tbname VARCHAR(32), msisdn VARCHAR(32))
returns VARCHAR(40)
LANGUAGE SQL
READS SQL DATA
NO EXTERNAL ACTION
DETERMINISTIC
begin atomic
DECLARE vsql varchar(2000);
DECLARE dt_UTC date;
DECLARE C1 CURSOR FOR select productid from Subscription_000 where msisdn= 123456;
SET vsql = 'select productid from Subscription_000 where msisdn= 123456';
OPEN C1;
return '123';
end
#
I saved the above contents to k2.sql and
When I try to compile this function using the command: db2 -td# -f k2.sql I am getting the below error:
DB21034E The command was processed as an SQL statement because it was
not a valid Command Line Processor command. During SQL processing it
returned: SQL0104N An unexpected token "FOR" was found following
"DECLARE C1 CURSOR". Expected tokens may include:
"". LINE NUMBER=10.
SQLSTATE=42601
Any suggestions on What is the problem here...?

This was a tricky one, but after some testing I figured out the problem: you can't declare a cursor within an atomic compound statement. Replace BEGIN ATOMIC with just BEGIN.
I wish I could point you to the list of allowed statements for ATOMIC, but it is buried in the manual somewhere and I can't find it at the moment.
(This is a classic case of DB2's terrible error reporting. Shouldn't it be able to recognize a disallowed statement and tell you that?)
Edit: this is one of the problems, but there is also some other problem, which I am not having, so I can't debug it.

Related

Export the result of stored procedure into text file as my computer work as both client/server

I have a procedure which takes as argument the function name and then finds out the full body of that function and stores it in a local variable V_FullString.
I need this output not to show in screen rather to put it in text file.
Is it possible ?
ALTER PROCEDURE [dbo].[SP_DictionaryFunction]
#P_FunctionName VARCHAR(100)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #V_ObjectName varchar(100) = #P_FunctionName;
DECLARE #V_FULLSTRING VARCHAR(5000) = ''
IF EXISTS (SELECT object_id FROM SYS.objects
WHERE object_id = OBJECT_ID(#V_ObjectName))
BEGIN
DECLARE #V_TABLE TABLE (Line VARCHAR(500))
INSERT INTO #V_TABLE
EXECUTE SP_HELPTEXT #V_ObjectName
SELECT #V_FULLSTRING = #V_FULLSTRING + Line
FROM #V_TABLE
PRINT #V_FULLSTRING
END
END
Writing a file from the server would be a very bad idea. Ideally you'd be writing it at the client. So... how are you executing the SQL? If this is SSMS, just press ctrl+shift+f. If you are using sqlcmd, then the -o {filename} switch should work. If you're executing it via ADO.NET, then File.WriteAllText. Otherwise, you'll have to tell us what client tool you are using.
Note: I expect that the main problem you're seeing is that print is often ignored. You might prefer select, which all tools will respect.

How to create "dynamic" stored proc without using dynamic SQL

I have an existing SP.
I want to start using database snapshots in some cases.
Users can save a snapshot at any time.
If they do, the SP should use it.
Otherwise, use the primary database.
I want to adapt the SPs to handle this, by making the database/table names dynamic instead of hard-coded in the SP.
I can imagine something like this working, with fully qualified table names, but it gives the error 'Must declare the table variable "#db1"':
declare #table1 varchar(25);
set #table1 = Snapshot.schema.tablename;
select * from #table1;
This gives "Incorrect syntax near '#db'."
declare #db varchar(25);
set #db = "Snapshot";
use #db;
This sorta works, but the "use" is only in effect during the execute. By the time the 'select' is executed, you are back to using the original database.
declare #db varchar(25);
set #db = 'use Snapshot';
EXECUTE(#db);
select * from Schema.Tablename;
I know I could generate the entire SP on the fly, but that seems guaranteed to get rejected by the DBAs. I'd rather a less radical solution.
Any better ideas?

What is wrong with this stored procedure

What is wrong with this stored procedure.
CREATE PROCEDURE PROC_RESULT_SET ()
LANGUAGE SQL
RESULT SETS 1
BEGIN
DECLARE MYCUR CURSOR WITH RETURN FOR
SELECT USERNAME , NAME FROM SLPG.USER ORDER BY ID;
OPEN MYCUR;
END;
I get error like
DB2 for Linux, UNIX, and Windows: "END" was expected to form a complete scope. -- near BEGIN statement
DB2 for Linux, UNIX, and Windows: "" was expected to form a complete scope. -- near DECLARE statement
DB2 for Linux, UNIX, and Windows: " JOIN " was expected to form a complete scope. -- near OPEN statement
DB2 for Linux, UNIX, and Windows: Unexpected text "END" encountered. -- near END statement
You need to use a different terminator for the CREATE PROCEDURE statement itself, as the semicolon is used to terminate statements within the procedure body.

SQL Server parameters without "#" / string literals

Today, a bug that was really hard to track down manifested itself in our project.
We had a trigger that performed certain actions when data was inserted or updated, including calling several stored procedures, and the program seemed to work correctly. Except when it didn't.
After hours of hair-tairing, we finally found the culprit: a missing "#" in front of a parameter name in an EXEC statement. The following minimal example shows the issue:
CREATE PROCEDURE EchoString #TheString nvarchar(30)
AS
SELECT #TheString
GO
declare #MyString char(10) = 'FooBar!'
exec EchoString #MyString
exec EchoString MyString -- Why does this work?
Now, this made me wonder: what is the purpose of allowing this? Is it only for backward compatibility, or are there legitimate use cases for this? Is it documented somewhere (my feeble googling turned up blank, but "#" is not all that google-able.)
In some cases SQL Server will interpret such a parameter as a string. The most well-known example:
EXEC sp_who2 active;
Is the same as:
EXEC sp_who2 'active';
I don't know if this behavior is documented but writing code this way is certainly fragile IMHO.

Is there a way to prevent SQL Server silently truncating data in local variables and stored procedure parameters?

I recently encountered an issue while porting an app to SQL Server. It turned out that this issue was caused by a stored procedure parameter being declared too short for the data being passed to it: the parameter was declared as VARCHAR(100) but in one case was being passed more than 100 characters of data. What surprised me was that SQL Server didn't report any errors or warnings -- it just silently truncated the data to 100 characters.
The following SQLCMD session demonstrates this:
1> create procedure WhereHasMyDataGone (#data varchar(5)) as
2> begin
3> print 'Your data is ''' + #data + '''.';
4> end;
5> go
1> exec WhereHasMyDataGone '123456789';
2> go
Your data is '12345'.
Local variables also exhibit the same behaviour:
1> declare #s varchar(5) = '123456789';
2> print #s;
3> go
12345
Is there an option I can enable to have SQL Server report errors (or at least warnings) in such situations? Or should I just declare all local variables and stored procedure parameters as VARCHAR(MAX) or NVARCHAR(MAX)?
SQL Server has no such option. You will either have to manually check the length of strings in your stored procedure and somehow handle the longer strings or use the nvarchar(max) option. If disk space isn't an issue then the nvarchar(max) option is certainly the easiest and quickest solution.
You don't have to use nvarchar(max) just use nvarchar(length+1) [e.g. if your column length is 50 then you would set the parameter to be nvarchar(51)]. See the answer from DavidHyogo - SQL Server silently truncates varchar's in stored procedures.
I don't know of a way to make the server do it, but I've been using the SQL Server Projects feature of Visual Studio Team System Developer Edition. It includes code analysis which caught a truncation problem of mine: using an int parameter to insert into a smallint column.
Though awkward, you can, however, dynamically check for parameter length before a call, e.g.
CREATE FUNCTION MyFunction(#MyParameter varchar(10))
RETURNS int
AS
BEGIN
RETURN LEN(#MyParameter)
END
GO
DECLARE #MyValue varchar(15) = '123456789012345'
DECLARE #ParameterMaxLength int
SELECT #ParameterMaxLength = CHARACTER_MAXIMUM_LENGTH
FROM INFORMATION_SCHEMA.PARAMETERS
WHERE SPECIFIC_SCHEMA = 'dbo' AND
SPECIFIC_name = 'MyFunction' AND
PARAMETER_NAME = '#MyParameter'
IF #ParameterMaxLength <> -1 AND
LEN(#MyValue) > #ParameterMaxLength
PRINT 'It''s too looooooooooooooooooong'
I omitted the called function's database name in the query and in the reference to INFORMATION_SCHEMA.PARAMETERS to ensure that my sample would run without edits.
I don't necessarily advocate this, but I wanted to point out that the information may be available to detect imminent truncation dynamically, if in some critical situation this is needed.
You can use LEFT in SQL and specified the length that you want to insert.
for example.
CREATE TABLE Table1
(
test varchar(10)
)
insert into Table1 values (LEFT('abcdefghijklmnopqrstuvwxyz',10))
This will insert only
abcdefghij on table

Resources