What is wrong with this stored procedure - database

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.

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.

Why does query execution continue after RETURN?

Consider following script:
IF OBJECT_ID('tempdb.dbo.#INTERMED', 'U') IS NOT NULL
DROP TABLE #INTERMED;
IF OBJECT_ID('tempdb.dbo.#INTERMED1', 'U') IS NOT NULL
DROP TABLE #INTERMED1;
PRINT 'Inserting INTO #INTERMED'
GO
SELECT 11 AS Col1
INTO #INTERMED
RETURN -- Why does execution continue below this line?
PRINT 'Inserting INTO #INTERMED1' -- This doesn't print anything
GO
SELECT 'Testing testing 123' AS Col2
INTO #INTERMED1
SELECT * FROM #INTERMED1 i
When you run it in SSMS you will notice that RETURN is ignored, PRINT statement after RETURN doesn't do anything and then execution continues.
Can someone explain why? I would expect it to exit immediately after RETURN.
I did find that it is somehow related to GO statements because if I commented out all GO statements it behaves as expected (exits after RETURN) but I still don't have an explanation.
GO is not part of the SQL Language. It's a batch separator used by Management Studio, and adopted as a convention by some other tools as well, but it has no special meaning in the language itself. Try to use it in a stored procedure and see what I mean.
Therefore, what happens is you have one batch the looks like this:
IF OBJECT_ID('tempdb.dbo.#INTERMED', 'U') IS NOT NULL
DROP TABLE #INTERMED;
IF OBJECT_ID('tempdb.dbo.#INTERMED1', 'U') IS NOT NULL
DROP TABLE #INTERMED1;
PRINT 'Inserting INTO #INTERMED'
It does it's thing, and then you have a new batch that looks like this:
SELECT 11 AS Col1
INTO #INTERMED
RETURN -- Why does execution continue below this line?
PRINT 'Inserting INTO #INTERMED1' -- This doesn't print anything
It runs to the RETURN statement, at which point the batch, and only that batch, returns/finishes. However, there is still one more batch to run:
SELECT 'Testing testing 123' AS Col2
INTO #INTERMED1
SELECT * FROM #INTERMED1 i
Again, this is a whole new batch. The previous RETURN statement means nothing. It's like you called three methods in sequence.
I also saw this in the comments:
The reason I had GO in it is to actually have PRINT statements output something while script is still executing.
There's a better way. Look into the RAISERROR statement:
RAISERROR('My Progress Message',0,1) WITH NOWAIT

Create user through procedure on another database via database link

I'd like to create users through procedure on another database via database link.I am getting error while executing procedure.
Here are the code which i used.
create or REPLACE PROCEDURE hostname
(host_name in varchar2,user_name in VARCHAR2, pass_word in VARCHAR2,
table_space in varchar2,pro_file in varchar2)
as
db_link_name varchar2(30);
begin
select db_link into db_link_name from all_db_links where host=host_name;
EXECUTE IMMEDIATE 'dbms_utility.exec_ddl_statement#'||db_link_name||('CREATE USER '||user_name||' IDENTIFIED BY '||pass_word||'
DEFAULT TABLESPACE '||table_space||' PROFILE '|| pro_file||' ACCOUNT UNLOCK');
EXECUTE IMMEDIATE 'dbms_utility.exec_ddl_statement#'||db_link_name||
('GRANT CONNECT,RESOURCE,EXECUTE_CATALOG_ROLE,Create table,create session,create view,create sequence,create procedure,create job,create synonym TO '||user_name);
end;
/
execute hostname('orcl1','rahul1','rahul','users','default');
Error: ORA-00900: invalid SQL statement ORA-06512: at
"SYS.HOSTNAME", line 6 ORA-06512: at line 1
00900. 00000 - "invalid SQL statement"
*Cause:
*Action:
I think you may have got a little confused by some of the advice provided by the commenter mustaccio. What he meant to say is that the SQL strings in your EXECUTE IMMEDIATE statements need to use PL/SQL blocks in order to call stored procedures.
In other words, instead of writing
EXECUTE IMMEDIATE 'dbms_utility.exec_ddl_statement ... ';
you should write
EXECUTE IMMEDIATE 'BEGIN dbms_utility.exec_ddl_statement ... ; END;';
For your procedure, I would recommend doing introducing a local variable for the strings you are passing to EXECUTE IMMEDIATE. These can be notoriously tricky to get right, and if you've assigned them to a local variable you can easily output them using dbms_output.put_line. In fact, I did exactly this while fixing up the problems in your procedure. I would recommend continuing to do this if you wish to modify or extend your procedure.
Anyway, here's what I ended up with, which appeared to work:
create or REPLACE PROCEDURE hostname
(host_name in varchar2,user_name in VARCHAR2, pass_word in VARCHAR2,
table_space in varchar2,pro_file in varchar2)
as
db_link_name varchar2(30);
l_ddl_sql varchar2(4000);
begin
select db_link into db_link_name from all_db_links where host=host_name;
l_ddl_sql := 'begin dbms_utility.exec_ddl_statement#'||db_link_name||'(''CREATE USER '||user_name||' IDENTIFIED BY '||pass_word||'
DEFAULT TABLESPACE '||table_space||' PROFILE '|| pro_file||' ACCOUNT UNLOCK''); END;';
EXECUTE IMMEDIATE l_ddl_sql;
l_ddl_sql := 'begin dbms_utility.exec_ddl_statement#'||db_link_name||
'(''GRANT CONNECT,RESOURCE,EXECUTE_CATALOG_ROLE,Create table,create session,create view,create sequence,create procedure,create job,create synonym TO '||user_name || '''); END;';
EXECUTE IMMEDIATE l_ddl_sql;
end;
/

DB2 error in using the cursor in PL/SQL function

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.

How do I flush the PRINT buffer in TSQL?

I have a very long-running stored procedure in SQL Server 2005 that I'm trying to debug, and I'm using the 'print' command to do it. The problem is, I'm only getting the messages back from SQL Server at the very end of my sproc - I'd like to be able to flush the message buffer and see these messages immediately during the sproc's runtime, rather than at the very end.
Use the RAISERROR function:
RAISERROR( 'This message will show up right away...',0,1) WITH NOWAIT
You shouldn't completely replace all your prints with raiserror. If you have a loop or large cursor somewhere just do it once or twice per iteration or even just every several iterations.
Also: I first learned about RAISERROR at this link, which I now consider the definitive source on SQL Server Error handling and definitely worth a read:
http://www.sommarskog.se/error-handling-I.html
Building on the answer by #JoelCoehoorn, my approach is to leave all my PRINT statements in place, and simply follow them with the RAISERROR statement to cause the flush.
For example:
PRINT 'MyVariableName: ' + #MyVariableName
RAISERROR(N'', 0, 1) WITH NOWAIT
The advantage of this approach is that the PRINT statements can concatenate strings, whereas the RAISERROR cannot. (So either way you have the same number of lines of code, as you'd have to declare and set a variable to use in RAISERROR).
If, like me, you use AutoHotKey or SSMSBoost or an equivalent tool, you can easily set up a shortcut such as "]flush" to enter the RAISERROR line for you. This saves you time if it is the same line of code every time, i.e. does not need to be customised to hold specific text or a variable.
Yes... The first parameter of the RAISERROR function needs an NVARCHAR variable. So try the following;
-- Replace PRINT function
DECLARE #strMsg NVARCHAR(100)
SELECT #strMsg = 'Here''s your message...'
RAISERROR (#strMsg, 0, 1) WITH NOWAIT
OR
RAISERROR (n'Here''s your message...', 0, 1) WITH NOWAIT
Another better option is to not depend on PRINT or RAISERROR and just load your "print" statements into a ##Temp table in TempDB or a permanent table in your database which will give you visibility to the data immediately via a SELECT statement from another window. This works the best for me. Using a permanent table then also serves as a log to what happened in the past. The print statements are handy for errors, but using the log table you can also determine the exact point of failure based on the last logged value for that particular execution (assuming you track the overall execution start time in your log table.)
Just for the reference, if you work in scripts (batch processing), not in stored procedure, flushing output is triggered by the GO command, e.g.
print 'test'
print 'test'
go
In general, my conclusion is following: output of mssql script execution, executing in SMS GUI or with sqlcmd.exe, is flushed to file, stdoutput, gui window on first GO statement or until the end of the script.
Flushing inside of stored procedure functions differently, since you can not place GO inside.
Reference: tsql Go statement
To extend Eric Isaac's answer, here is how to use the table approach correctly:
Firstly, if your sp uses a transaction, you won't be able monitor the contents of the table live, unless you use the READ UNCOMMITTED option:
SELECT *
FROM table_log WITH (READUNCOMMITTED);
or
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT *
FROM table_log;
To solve rollback issues, put an increasing ID on the log table, and use this code:
SET XACT_ABORT OFF;
BEGIN TRY
BEGIN TRANSACTION mytran;
-- already committed logs are not affected by a potential rollback
-- so only save logs created in this transaction
DECLARE #max_log_id = (SELECT MAX(ID) FROM table_log);
/*
* do stuff, log the stuff
*/
COMMIT TRANSACTION mytran;
END TRY
BEGIN CATCH
DECLARE #log_table_saverollback TABLE
(
ID INT,
Msg NVARCHAR(1024),
LogTime DATETIME
);
INSERT INTO #log_table_saverollback(ID, Msg, LogTime)
SELECT ID, Msg, LogTime
FROM table_log
WHERE ID > #max_log_id;
ROLLBACK TRANSACTION mytran; -- this deletes new log entries from the log table
SET IDENTITY_INSERT table_log ON;
INSERT INTO table_log(ID, Msg, LogTime)
SELECT ID, Msg, LogTime
FROM #log_table_saverollback;
SET IDENTITY_INSERT table_log OFF;
END CATCH
Notice these important details:
SET XACT_ABORT OFF; prevents SQL Server from just shutting down the entire transaction instead of running your catch block, always include it if you use this technique.
Use a #table_variable, not a #temp_table. Temp tables are also affected by rollbacks.

Resources