Incorrect syntax near Throw - what am I missing? - sql-server

I am trying to execute a THROW statement like so (value and 12345 being some random values):
use testdb;
go
begin try
if('value' in (select distinct shelf from itemloc))
update itemloc
set shelf = 'value'
where item = '12345';
end try
begin catch
;Throw 160073, 'Failed to update shelf value', 1;
end catch;
Using this reference
I looked at this question and I have the ; before my THROW. I'm also checking the syntax against the reference and fail to see why executing this returns
Msg 102, Level 15, State 1, Line 11
Incorrect syntax near 'Throw'.

You're inside a CATCH, you can't choose your error here, you would just use THROW;. Also, you don't need to start your statement with a semicolon, you already put one at the end of your last statement. It's a terminator (it goes at the end of the line), not at the beginning and end.
If you want to use a custom error, use RAISERROR. For example:
USE TESTDB;
GO
BEGIN TRY
IF('value' IN (SELECT DISTINCT shelf FROM itemloc))
UPDATE itemloc
SET shelf = 'value'
WHERE item = '12345';
END TRY
BEGIN CATCH
DECLARE #ERROR VARCHAR(MAX);
SET #ERROR = 'Failed to update shelf value';
RAISERROR(#ERROR, 11, 160073);
END CATCH

Related

PLSQL: IF EXISTS in stored procedure while using loop

I am new to PLSQL. I am trying to create a procedure which iterates through an array.
My requirement is if one of the value is not found in table, it should add into FAILARRAY, otherwise it should add into PASSARRAY.
I was getting no data found exception even if it is handled, it goes out of the loop and next value in the loop is not getting iterated again.
Is there any way we can use if exists command here. Please help.
CREATE OR REPLACE PROCEDURE SCHEMA.PR_VALIDATE
(
FILEARRAY IN STRARRAY,
PASSARRAY OUT STRARRAY,
FAILARRAY OUT STRARRAY,
)
IS
--DECLARE
fileName VARCHAR2 (50);
fileId NUMBER;
BEGIN
for i in 1 .. FILEARRAY.count
loop
fileName := FILEARRAY(i);
DBMS_OUTPUT.put_line (FILEARRAY (i));
SELECT FILEID into fileId FROM TABLE_NAME WHERE FILENAME=fileName;
end loop
END;
I suspect you haven't realised that you can have a PL/SQL BEGIN ... END block, including an exception handler, within a loop. In fact, anywhere you can have PL/SQL statements you can have a block.
You mention an exception handler, although your code doesn't contain one. As you say your code goes 'out of the loop', I can only assume it's, well, outside of the for loop. But you can easily add a block, with an exception handler, inside the for loop, for example:
BEGIN
for i in 1 .. FILEARRAY.count
loop
fileName := FILEARRAY(i);
DBMS_OUTPUT.put_line (FILEARRAY (i));
-- Inner block starts at the line below:
BEGIN
SELECT FILEID into fileId FROM TABLE_NAME WHERE FILENAME=fileName;
-- TODO add to PASSARRAY
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- TODO add to FAILARRAY
END;
end loop
END;
This way, if there are 8 values in FILEARRAY and no data is found in the table for the third value, the NO_DATA_FOUND exception gets caught without exiting the loop and the loop then progresses to the fourth value in FILEARRAY.
You are handling the exception but you need to avoid the exception. Try:
SELECT NVL(FILEID, "<Put Something here or leave it empty") FROM TABLE_NAME WHERE FILENAME=fileName;
That way if it finds a null value in the select it will just pull "" instead. Then you can check to see if your SELECT returns "" and if so populate your FAILARRAY, otherwise populate PASSARRAY.
CREATE OR REPLACE PROCEDURE SCHEMA.PR_VALIDATE(
FILEARRAY IN STRARRAY,
PASSARRAY OUT STRARRAY,
FAILARRAY OUT STRARRAY )
IS
fileName VARCHAR2 (50);
l_n_count NUMBER;
l_n_file_id NUMBER;
BEGIN
FOR i IN 1 .. FILEARRAY.count
LOOP
fileName := FILEARRAY(i);
DBMS_OUTPUT.put_line (FILEARRAY(i));
SELECT COUNT(FILEID) INTO l_n_count FROM TABLE_NAME WHERE FILENAME=fileName;
IF l_n_count =0 THEN
failarray(i):='No Value Found';
elsif l_n_count=1 THEN
SELECT FILEID INTO l_n_file_id FROM TABLE_NAME WHERE FILENAME=fileName;
Passarray(i):=l_n_file_id;
END IF;
END LOOP;
END;
/

Nested table loops to format data, PL/SQL

I am trying to format data returned from a cursor to JSON by looping through the records and columns without having to explicitly call on each column name. From what I've researched this vary well may not be a simple task or at least as simple as I'm trying to make it. I'm wondering if anyone else has tried a similar approach and if they had any luck.
declare
type type_cur_tab is table of employees%rowtype
index by PLS_integer;
type type_col_tab is table of varchar2(1000)
index by binary_integer;
tbl_rec type_cur_tab;
tbl_col type_col_tab;
begin
select * BULK COLLECT INTO tbl_rec
from employees;
select column_name BULK COLLECT INTO tbl_col
from all_tab_columns
where UPPER(table_name) = 'EMPLOYEES';
for i IN 1..tbl_rec.COUNT Loop
for j IN 1..tbl_col.count Loop
dbms_output.put_line(tbl_rec(i).tbl_col(j));
end loop;
end loop;
end;
It throws an error saying 'tbl_col' must be declared. I'm sure this is bc it's looking for 'tbl_col' listed inside 'tbl_rec'. Any help is greatly appreciated.
NOTE: I'm aware of the built in JSON conversion but I haven't been able to get it to as fast as I'd like so I'm trying to loop through and add the appropriate formatting along the way.
It is impossible to specify field of tbl_rec(i) in this manner.
Try this:
declare
v_cur sys_refcursor;
col_cnt number;
desc_t dbms_sql.desc_tab;
c number;
vVarchar varchar2(32000);
vNumber number;
vDate date;
v_result clob:='';
rn number:=0;
begin
--Any sql query or pass v_cur as input parameter in function on procedure
open v_cur for
select * from dual;
--------
c:=dbms_sql.to_cursor_number(v_cur);
dbms_sql.describe_columns(c => c, col_cnt => col_cnt, desc_t => desc_t);
for i in 1 .. col_cnt
loop
case desc_t(i).col_type
when dbms_types.TYPECODE_DATE then
dbms_sql.define_column(c, i ,vDate);
when dbms_types.TYPECODE_NUMBER then
dbms_sql.define_column(c, i ,vNumber);
else
dbms_sql.define_column(c, i ,vVarchar,32000);
end case;
end loop;
v_result:='{"rows":{ "row": [';
while (dbms_sql.fetch_rows(c)>0)
loop
if rn > 1 then v_result:=v_result||','; end if;
v_result:=v_result||'{';
for i in 1 .. col_cnt
loop
if (i>1) then v_result:=v_result||','; end if;
case desc_t(i).col_type
--Date
when dbms_types.typecode_date then
dbms_sql.column_value(c,i,vDate);
v_result:=v_result||' "'||desc_t(i).col_name||'" :"'||to_char(vDate,'dd.mm.yyyy hh24:mi')||'"';
--Number
when dbms_types.typecode_number then
dbms_sql.column_value(c,i,vNumber);
v_result:=v_result||' "'||desc_t(i).col_name||'" :"'||to_char(vNumber)||'"';
--Varchar - default
else
dbms_sql.column_value(c,i,vVarchar);
v_result:=v_result||' "'||desc_t(i).col_name||'" :"'||vVarchar||'"';
end case;
end loop;
v_result:=v_result||'}';
end loop;
v_result:=v_result||']}}';
dbms_output.put_line (v_result);
end;
Also you can generate XML from ref cursor with DBMS_XMLGEN package and then translate xml into json with xslt transformation.

SQL Server Function/Proc evaluate an expression(s) return an error if not valid

I would like to take the code below and create a common function to pass in one or more expressions and to return back an error of my choosing.
Example Code:
IF #Variable1 IS NULL AND #Variable IS NULL or #Variable3 is not null
BEGIN
-- EITHER DATASET NAME OR ID MUST BE SUPPLIED.
SET #_msg = 'There was an error'
SET #_returnValue = -1
GOTO ERROR_HANDLER
END
ERROR_HANDLER:
-- CREATE THE CLOSING MESSAGE.
IF #_returnValue <> 0
RAISERROR(#_msg, 18, 2) WITH SETERROR
RETURN #_returnValue
From the above, it would be nice to say something like this below where I could reuse the proc/function and make the code less clutered.
exec ValidateMultipleConditions #Variable1 + 'IS NULL AND ' + #Variable + 'IS NULL or ' + #Variable3 + ' is not null'
Anyway, I think with dynamic SQL being passed in this way I could do something where an complete expression could be sent evaluated, validated and then the code continues or stops with an error.
I wanted to see if the community had better ways of doing this or if I'm on the right path.
Thanks.
I'm not quite sure if I get your question right. But you can easily create an procedure (if it's needed).
CREATE PROCEDURE dbo.errorout #message nvarchar(100), #sev int, #state int
AS
BEGIN
RAISERROR(#message,#sev,#state) WITH NOWAIT
END
But I won't use this at all. I would call RAISERROR() in the place where it occurs, as it will give you more accurate line numbers and procedures in the errorlog.

PL/SQL cursor for loop and record not working

I have the following problem. I'm trying to check a number (bsn), if it's in the database or not. If it's not in the database it should give me an error, however now I'm getting always an error even if the number exists in the database. It worked fine with only one number in the database, but with more... That's the problem. Oh and I'm working with APEX, so I use this as a process.
create or replace PROCEDURE CONTROLE_BSN IS
CURSOR c_klanten
IS
SELECT bsn
FROM klant;
v_bsn VARCHAR2(10) := V('P7_BSN');
e_geen_bsn EXCEPTION;
BEGIN
FOR r_record IN c_klanten
LOOP
IF r_record.bsn != v_bsn THEN
RAISE e_geen_bsn;
END IF;
END LOOP;
EXCEPTION
WHEN e_geen_bsn THEN
raise_application_error(-20001, 'This bsn-number does not exists.');
END CONTROLE_BSN;
Your logic is flowed. As soon as you have two different bsn in your table, your test will be true for at least one of them:
FOR r_record IN c_klanten
LOOP
IF r_record.bsn != v_bsn THEN --< when N different records,
-- this is true for at least N-1 of them
RAISE e_geen_bsn;
END IF;
END LOOP;
Maybe you should go for something a little bit simpler than that. Why not write your cursor like this instead:
CURSOR c_klanten
IS
SELECT count(*) n
FROM klant
WHERE nbc = v_bsn;
That way, you will easily get the number of matching bsn. Either 0, 1 or more. And then perform the appropriate action.
Perhaps the following would help:
create or replace PROCEDURE CONTROLE_BSN IS
CURSOR c_klanten(p_bsn) IS
SELECT count(*) as bsn_count
FROM klant
where bsn = p_bsn;
v_bsn VARCHAR2(10) := V('P7_BSN');
e_geen_bsn EXCEPTION;
BEGIN
FOR r_record IN c_klanten(v_bsn)
LOOP
IF r_record.bsn_count = 0 THEN
RAISE e_geen_bsn;
END IF;
END LOOP;
EXCEPTION
WHEN e_geen_bsn THEN
raise_application_error(-20001, 'This bsn-number does not exists.');
END CONTROLE_BSN;
Best of luck.

Validation - If - SQL Server

I am trying to send a return message to front end. If the parameter which is passed, if it is not in format like
'A-1213-465-798-01'
It should reply back 1. But I am missing out the validation. please help.
'A-1213-465-798-01'
IF #OptParam3 not like '[_-_-_-_-_]'
Begin
Set #Return_Message = '1' -- Validation 'Invalid code
Print 'Error: Invalid Code'
Return
End
Assuming this is a stored procedure you probably want something along the lines of this
IF OBJECT_ID('sp_demo') > 0 DROP PROC sp_demo
go
CREATE PROC sp_demo (#OptParam1 varchar(50),#OptParam2 varchar(50),#OptParam3 varchar(50))
AS
IF #OptParam3 not like '_-____-___-___-__'
Begin
Print 'Error: Invalid Code'
Return 1
End
/*
If we got here then the parameter is validated
*/
PRINT '#OptParam3 is valid:'
PRINT #OptParam3
Return 0
You can check this by doing the following
/* Driver to test the stored procedure */
DECLARE #return_Code int
EXEC #return_Code = sp_demo 'parameterA','parameterB','A-1213-465-798-01';
PRINT #return_Code
But really you should probably be raising an error in your code if your input parameters are invalid. This will come through as a trapable error in your front end
IF OBJECT_ID('sp_demo') > 0 DROP PROC sp_demo
go
CREATE PROC sp_demo (#OptParam1 varchar(50),#OptParam2 varchar(50),#OptParam3 varchar(50))
AS
IF #OptParam3 not like '_-____-___-___-__'
Begin
RAISERROR (N'Error: Invalid Code "%s"',16,1 ,#OptParam3)
return ##ERROR
End
/*
If we got here then the parameter is validated
*/
PRINT '#OptParam3 is valid:'
PRINT #OptParam3
Return 0

Resources