Netezza procedure skip null variable in loop - netezza

We have an SP that generates a list of tables for migration. During processing of the list of tables, there are instances where tables originally in the list are dropped. The SP raises an exception and exits when encountering 'table not found' conditions.
Here is the code:
SQL_STATEMENT1 := 'SELECT * FROM ...';
FOR REC1 IN EXECUTE SQL_STATEMENT1
LOOP
SQL_STATEMENT2 := 'SELECT * FROM ...';
FOR REC2 IN EXECUTE SQL_STATEMENT2
LOOP
...
I'm looking to validate REC1 in SQL_STATEMENT2 to verify it's neither null or an empty string. If it is, I want to skip that instance of REC1 and move to the next. Below does not appear to be the answer:
IF SQL_STATEMENT2 IS NULL THEN
CONTINUE
END IF;
or
IF SQL_STATEMENT2 IS NULL THEN
EXIT FOR
END IF;

Since there is a small possibility that one of the tables from the original list is dropped between the time you generate the outer list and the time the inner statement is executed, I see no immediate way of preventing it from failing. Even if there is only one table on the outer ‘list’...
You need to handle that particular error inside the SP in a benign way.

Related

The temp table is not drop at the end of session

If my understanding is correct, the temp table should be dropped automatically at the end of session. But after my stored procedure ends, the temp table is still there. Is anybody know how to use the tmp table? Thank you in advance.
A temp table exists for the duration of your session. So if you create a temp table in a stored proc and you are still in the same session the temp table will still be there. I would recommend dropping the table in the stored proc. Other RDBMS like SQL Server allow for a table to exist as a variable which sounds like what you are wanting.
Generally The Temp table gets cleaned up when a standalone procedure ends.
However, ff we are calling the procedure within a loop then it behaves as a single session throughout the iterations.
Hence It is the same as creating the same temp table again and again without dropping it.
It's better practice of having "drop table " whenever we are using Temp tables inside a procedure. Rerun will not be impacted.
example :
FOR v_cnt IN SELECT load_dt AS history_date FROM process_dates ORDER BY history_date ASC
LOOP
CALL sp_Logic(v_Record.history_date, 'ALL');
END LOOP;
sp_Logic(v_Record.history_date, 'ALL');
Procedure:
CREATE OR REPLACE PROCEDURE sp_Logic"(DATE, CHARACTER VARYING(ANY))
RETURNS CHARACTER VARYING(ANY) EXECUTE AS CALLER
LANGUAGE NZPLSQL AS
BEGIN_PROC
DECLARE
...
..
..
v_Curr_Step := 'Populate ref_id';
CREATE TEMP TABLE TEMP_REFID_1 AS
(SELECT * FROM ref_id) ;
..
..
EXCEPTION
WHEN OTHERS
THEN
RAISE EXCEPTION 'CAUGHT EXCEPTION WHILE % : %', v_Curr_Step, SQLERRM;
END;
END_PROC;
This code will create issue once the SELECT load_dt AS history_date FROM process_date query returns more than one dates.
I am not sure if the above explanation makes sense. I tried :-)

What are the dangers of BEGIN TRY DROP TABLE?

In a script used for interactive analysis of subsets of data, it is often useful to store the results of queries into temporary tables for further analysis.
Many of my analysis scripts contain this structure:
CREATE TABLE #Results (
a INT NOT NULL,
b INT NOT NULL,
c INT NOT NULL
);
INSERT INTO #Results (a, b, c)
SELECT a, b, c
FROM ...
SELECT *
FROM #Results;
In SQL Server, temporary tables are connection-scoped, so the query results persist after the initial query execution. When the subset of data I want to analyze is expensive to calculate, I use this method instead of using a table variable because the subset persists across different batches of queries.
The setup part of the script is run once, and following queries (SELECT * FROM #Results is a placeholder here) are run as often as necessary.
Occasionally, I want to refresh the subset of data in the temporary table, so I run the entire script again. One way to do this would be to create a new connection by copying the script to a new query window in Management Studio, I find this difficult to manage.
Instead, my usual workaround is to precede the create statement with a conditional drop statement like this:
IF OBJECT_ID(N'tempdb.dbo.#Results', 'U') IS NOT NULL
BEGIN
DROP TABLE #Results;
END;
This statement correctly handles two situations:
On the first run when the table does not exist: do nothing.
On subsequent runs when the table does exist: drop the table.
Production scripts written by me would always use this method because it raises no errors for in the two expected situations.
Some equivalent scripts written by my fellow developers sometimes handle these two situations using exception handling:
BEGIN TRY DROP TABLE #Results END TRY BEGIN CATCH END CATCH
I believe in the database world it is better always to ask permission than seek forgiveness, so this method makes me uneasy.
The second method swallows an error while taking no action to handle non-exceptional behavior (table does not exist). Also, it is possible that an error would be raised for a reason other than that the table does not exist.
The Wise Owl warns about the same thing:
Of the two methods, the [OBJECT_ID method] is more difficult to understand but
probably better: with the [BEGIN TRY method], you run the risk of trapping
the wrong error!
But it does not explain what the practical risks are.
In practice, the BEGIN TRY method has never caused problems in systems I maintain, so I'm happy for it to stay there.
What possible dangers are there in managing temporary table existence using BEGIN TRY method? What unexpected errors are likely to be concealed by the empty catch block?
What possible dangers? What unexpected errors are likely to be concealed?
If try catch block is inside a transaction, it will cause a failure.
BEGIN
BEGIN TRANSACTION t1;
SELECT 1
BEGIN TRY DROP TABLE #Results END TRY BEGIN CATCH END CATCH
COMMIT TRANSACTION t1;
END
This batch will fail with an error like this:
Msg 3930, Level 16, State 1, Line 7
The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction.
Msg 3998, Level 16, State 1, Line 1
Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.
Books Online documents this behavior:
Uncommittable Transactions and XACT_STATE
If an error generated in a TRY block causes the state of the current transaction to be invalidated, the transaction is classified as an uncommittable transaction. An error that ordinarily ends a transaction outside a TRY block causes a transaction to enter an uncommittable state when the error occurs inside a TRY block. An uncommittable transaction can only perform read operations or a ROLLBACK TRANSACTION. The transaction cannot execute any Transact-SQL statements that would generate a write operation or a COMMIT TRANSACTION.
now replace TRY/Catch with the Test Method
BEGIN
BEGIN TRANSACTION t1;
SELECT 1
IF OBJECT_ID(N'tempdb.dbo.#Results', 'U') IS NOT NULL
BEGIN
DROP TABLE #Results;
END;
COMMIT TRANSACTION t1;
END
and run again.Transaction will commit without any error.
A better solution may be to use a table variable rather than a temporary table
ie:
declare #results table(
a INT NOT NULL,
b INT NOT NULL,
c INT NOT NULL
);
I also think that a try block is dangerous because can hide an unexpected problem. Some programing languages can catch only selected errors and don't catch unexpected ones, if your programing language has this functionality then use it (T-SQL can't catch for an specific error)
For your scenario, I can explain that I codify exactly like you, with this try catch block.
The desirable behavior would be:
begin try
drop table #my_temp_table
end try
begin catch __table_dont_exists_error__
end catch
But this don't exists! Then you can write some think like:
begin try
drop table #my_temp_table
end try
begin catch
declare #err_n int, #err_d varchar(MAX)
SELECT
#err_n = ERROR_NUMBER() ,
#err_d = ERROR_MESSAGE() ;
IF #err_n <> 3701
raiserror( #err_d, 16, 1 )
end catch
This will raise an event when error deleting table is different that 'table don't exists'.
Notice that for your issue all this code not worth it. But can be useful for other approach. For your problem, elegant solution is drop table only if exists or use table variable.
Not in you question but possibly overlooked is the resources used by the temp table. I always drop the table at the end of the script so it does not tie up resources. What if you put a million rows in the table? Then I also test for the table at the start of the script to handle the condition there was an error in the last run and the table was not dropped. If you want to reuse the temp then at least clear out the rows.
A table variable is another option. It is lighter weight and has limitations. Avoid a table variable if you are going to use it in a query join as the query optimizer does not handle a table variable was well as it does a temp.
SQL documentation:
If more than one temporary table is created inside a single stored procedure or batch, they must have different names.
If a local temporary table is created in a stored procedure or application that can be executed at the same time by several users, the Database Engine must be able to distinguish the tables created by the different users. The Database Engine does this by internally appending a numeric suffix to each local temporary table name. The full name of a temporary table as stored in the sysobjects table in tempdb is made up of the table name specified in the CREATE TABLE statement and the system-generated numeric suffix. To allow for the suffix, table_name specified for a local temporary name cannot exceed 116 characters.
Temporary tables are automatically dropped when they go out of scope, unless explicitly dropped by using DROP TABLE:
A local temporary table created in a stored procedure is dropped automatically when the stored procedure is finished. The table can be referenced by any nested stored procedures executed by the stored procedure that created the table. The table cannot be referenced by the process that called the stored procedure that created the table.
All other local temporary tables are dropped automatically at the end of the current session.
Global temporary tables are automatically dropped when the session that created the table ends and all other tasks have stopped referencing them. The association between a task and a table is maintained only for the life of a single Transact-SQL statement. This means that a global temporary table is dropped at the completion of the last Transact-SQL statement that was actively referencing the table when the creating session ended.

Short circuit in IF clause

I have searched and found nothing about it (I believe it is impossible to do it). My problem is that I have to check if a temporary table exists and also if there is some specific data on that temporary table.
Did anyone faced this before? How did you managed to solve it? I would like to avoid creating milions of IF..ELSE blocks.
EDIT:
IF (OBJECT_ID('tempdb..#tempTable') IS NOT NULL
AND EXISTS (SELECT * FROM #tempTable WHERE THIS_COLUMN = 'value'))
BEGIN
PRINT 'Temp table and data exists'
END
ELSE
BEGIN
PRINT 'Temp table or data does not exist'
END
This is what I want to do. The problem comes when the tempTable doesn't exist (that could happen). It throws an error because, although the first stamement returns false, it continues to execute the second statement. And the SELECT statement is not able to find the table and therefore throws the error. The solution I found was to do this:
IF OBJECT_ID('#tempTable') IS NOT NULL
BEGIN
IF EXISTS (SELECT * FROM #tempTable WHERE THIS_COLUMN = 'value'
BEGIN
PRINT 'Temp table and data exists'
END
ELSE
BEGIN
PRINT 'Temp table exists but data does not exist'
END
END
ELSE
BEGIN
PRINT 'Temp table does not exist'
END
My question would be, is there a way of having 2 conditions and if the first condition returns false not check the second one? Kind of using && in a programming language.
What you are trying to do is not possible as this is a compile time failure and the whole statement needs to be compiled together.
It won't evaluate the first part of the statement then compile the second part only if that is true. You need to split the test for existence and the query referencing the table into two separate statements so they are compiled separately.
See here
There is no such thing as XAND logical gate (exclusive AND). In theory XAND would mean, that both operands are true or both are false. So this means XAND is the same as Equals (=), at least for bitwise logical operations.
Please show some sample code to illustrade what you are trying to do.
Regards
I have searched this sometime ago and if I remember correctly Sql Server does indeed short circuit logical conditions but it is it who decides which one it will check first regardless of the order in which they appear in the if clause.
This is probably a sketchy solution, but I sometimes use COALESCE statements to control the sorts of if, else, then structure you are trying to get at. In this case, it is a little more dirty looking because we are looking for the inverse of a coalesce statement.
DECLARE #temp_message AS varchar(100)
SELECT #temp_message = COALESCE(CASE
WHEN OBJECT_ID('tempdb..#tempTable') IS NOT NULL THEN NULL
ELSE 'Temp table does not exist'
END,
CASE
WHEN EXISTS (SELECT * FROM #tempTable WHERE THIS_COLUMN = 'value') THEN NULL
ELSE 'Specified value does not exist in temp table'
END,
'Temp table and data exists')
PRINT #temp_message
The COALESCE runs one statement after another until one does not yield a NULL value. That means you can do cool things like run a series of small queries to check some values before running a large costly query. Let me know if that is really illegitimate! It worked on my machine :)
I see two ways to get close to this in MSSQL:
First. If you use sp_executesql (dynamic sql) your stored procedure will be compiled without errors. Also if #tempTable doesn't exists server will output error but continue batch execution:
exec sp_executesql N'SELECT count(*) FROM #tempTable WHERE THIS_COLUMN = ''value'''
if ##rowcount > 0
print 'ok'
else
print 'error'
end;
Second. Just create User defined function with nested IF and EXISTS (as you do it now) which output 0 and 1. And use dynamic sql to input table name and possible filter value(s) to this UDF. In this case you can use this UDF in IF.

How to stop the execution of a Stored Procedure using SQL Server?

Lets say I have a stored procedure which has a simple IF block. If the performed check meets the criteria, then I want to stop the procedure from further execution.
What is the best way to do this?
Here is the code:
IF EXISTS (<Preform your Check>)
BEGIN
// NEED TO STOP STORED PROCEDURE EXECUTION
END
ELSE
BEGIN
INSERT ()...
END
Thanks for any help with this!
Just make a call to RETURN:
IF EXISTS (<some condition>)
BEGIN
// NEED TO STOP STORED PROCEDURE EXECUTION
RETURN
END
This will return the control back to the caller immediately - it skips everything else in the proc.
You could simply put a jump label at the end of the SP body, and issue a GOTO in the first IF statement. Alternatively, you can extend the first BEGIN ... END block to contain the entire rest of the SP body, and reverse the condition (IF NOT EXISTS...).

Do database cursors pick up changes to the underlying data?

Quick question about cursors (in particular Oracle cursors).
Let's say I have a table called "my_table" which has two columns, an ID and a name. There are millions of rows, but the name column is always the string 'test'.
I then run this PL/SQL script:
declare
cursor cur is
select t.id, t.name
from my_table t
order by 1;
begin
for cur_row in cur loop
if (cur_row.name = 'test') then
dbms_output.put_line('everything is fine!');
else
dbms_output.put_line('error error error!!!!!');
exit;
end if;
end loop;
end;
/
if I, while this is running, run this SQL:
update my_table
set name = 'error'
where id = <max id>;
commit;
will the cursor in the PL/SQL block pick up that change and print out "error error error" and exit? or will it not pick up the change at all ... or will it even allow the update to my_table?
thanks!
A cursor effectively runs a SELECT and then lets you iterate over the result set, which is kept in a snapshot of the DB state. Because your result set has already been fetched, it won't be affected by the UPDATE statement. (Handling things otherwise would require you to re-run the query every time you advanced your cursor!)
See:
http://www.techonthenet.com/oracle/cursors/declare.php

Resources