How to check if a variable in snowflake stored procedure is null or not. I am using snowflake scripting.
My Code:
begin
select id into :id from REQUEST where id = 1;
if(id = null) then
return 0;
The syntax for branch construct:
BEGIN
-- ...
IF (id IS NULL) THEN
RETURN 0;
END IF;
END;
It is also possible to use COALESCE if it is just a return statement:
BEGIN
-- ...
RETURN COALESCE(id, 0);
END;
Tried to cover the bases here, primarily when you have no results from the query as well as when the actual result is a NULL returned value.
--
-- create test table
--
CREATE or replace TABLE REQUEST (id integer, val INTEGER);
INSERT INTO REQUEST VALUES (1,1),(2,null),(3,3);
--
-- create test proc using SQL Script
--
CREATE OR REPLACE PROCEDURE TEST_NULLS(input integer)
returns INTEGER
LANGUAGE SQL
AS
DECLARE
val INTEGER;
rc INTEGER;
input_id INTEGER;
BEGIN
input_id := input;
-- get the row count to determine if the query criteria produces a result
SELECT count(*) into :rc
FROM REQUEST
WHERE id = :input_id;
-- if empty result set, return 0.
IF (rc = 0) then return (rc);
ELSE
-- otherwise, return the value retrieved from the query, unless it is NULL in which case return -1
SELECT nvl(val,-1) into :val
FROM REQUEST where id = :input_id;
return (val);
END IF;
END;
--
-- Test examples
--
call test_nulls(1); -- Returns 1
call test_nulls(2); -- Returns -1, since returned value is NULL
call test_nulls(3); -- Returns 3
call test_nulls(4); -- Returns 0, since there is no result set
Related
The purpose of this stored procedure is to check whether a record exists (by input parameter):
if not, create it with default value 0 and return 0.
if it does, then increment the last value by 1 and return new value
But no matter how I rearrange this, it always returns 1, and not my computed value. Other operations are fine, records are properly created, values are properly incremented.
CREATE OR ALTER PROCEDURE ORD_SCHEMA.ORDER_NUMBER_INCREMENTER
#allocatedDate DATE
AS
BEGIN
DECLARE #resultNumberValue int;
DECLARE #currentNumberValue int;
BEGIN TRANSACTION INCREMENTER_TRANSACTION
BEGIN TRY
SET #currentNumberValue = (SELECT NUMBER_VALUE
FROM ORD_SCHEMA.ORD_ORDER_NUMBER
WHERE ALLOCATED_DATE = #allocatedDate);
IF(#currentNumberValue IS NULL)
BEGIN
INSERT INTO ORD_SCHEMA.ORD_ORDER_NUMBER (ALLOCATED_DATE, NUMBER_VALUE)
VALUES(#allocatedDate, 0);
SET #resultNumberValue = 0;
END
ELSE
BEGIN
UPDATE ORD_SCHEMA.ORD_ORDER_NUMBER
SET NUMBER_VALUE = NUMBER_VALUE + 1
WHERE ALLOCATED_DATE = #allocatedDate;
SET #resultNumberValue = #currentNumberValue + 1;
END
COMMIT TRANSACTION INCREMENTER_TRANSACTION
RETURN #resultNumberValue;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION INCREMENTER_TRANSACTION
END CATCH
END
I am trying to fetch the columns from a previously returned into a table of records. However, when i run the code in the package body I get the following error:
ORA-06504: PL/SQL: Return types of Result Set variables or query do not match
However, when i try to do this in a PL/SQL anonymous block window, I can successfully fetch the records using Bulk Collect instruction.
Here is my successful try as I stated:
DECLARE
--
O_ref SYS_REFCURSOR;
-- Variable and types declaration.
TYPE REC_TYP is record (
column_1 number(8),
column_2 varchar2(13)
);
TYPE TAB_TYP is table of REC_TYP;
L_tab_typ TAB_TYP;
--
BEGIN
--
open O_ref for
select sku,
upc
from upc_ean
where sku = 2004030;
--
LOOP
--
FETCH O_ref BULK COLLECT into L_tab_typ;
EXIT WHEN L_tab_typ.COUNT = 0;
--
FOR indx IN 1 .. L_tab_typ.COUNT
LOOP
--
dbms_output.put_line('SKU: ' || L_tab_typ(indx).column_1);
dbms_output.put_line('UPC: ' || L_tab_typ(indx).column_2);
--
END LOOP;
--
END LOOP;
--
CLOSE O_ref;
--
END;
When I run this code I get the following output:
SKU: 2004030
UPC: 5601126003439
SKU: 2004030
UPC: 5601126039056
In the package body I have the following:
Why doesn't this work in a regular package?
FUNCTION GET_STORE_ITEMS(I_store IN number
----------- output ------------
O_item_data OUT NB_TAB_ITEM_DETAIL, -- i want to return a table type after I get the info from the sys_ref
----------- error -------------
O_error_message OUT VARCHAR2)
RETURN BOOLEAN IS
--
L_tab_type NB_TAB_ITEM_DETAIL;
L_sys_ref SYS_REFCURSOR;
L_test_sku number(8);
--
CURSOR C_GET_ITEMS IS
--
SELECT a.sku
FROM win_store a
WHERE a.store = I_store;
--
BEGIN
--
-- Loop over the fashion skus.
FOR R_items IN C_GET_ITEMS LOOP
--
BEGIN
--
IF GET_ITEM_DETAIL(I_store => I_store,
I_sku => R_items.sku,
O_item_data => L_sys_ref, -- returns a sys_refcursor with the same structure as the type
O_error_message => L_error_message) = FALSE THEN
--
O_error_message := NB_MESSGE40_SQL.EMESSAGE(L_error_message);
RETURN FALSE;
--
END IF;
--
LOOP
--
FETCH L_sys_ref BULK COLLECT into L_tab_type; -- It jumps to when others exception
EXIT WHEN L_tab_type.COUNT = 0;
--
FOR indx IN 1 .. L_tab_type.COUNT
LOOP
--
L_test_sku := L_tab_type(indx).sku;
--
END LOOP;
--
END LOOP;
--
END;
--
END LOOP;
--
RETURN TRUE;
--
EXCEPTION
--
WHEN OTHERS THEN
--
-- ...
RETURN FALSE;
--
END GET_STORE_ITEMS;
Thank you!
I actually happened to solve the problem. It was necessary to declare a package-spec level type before fetching the sys_refcursor, just like it was done in the anonymous block.
At first, the record and table types were created in the database:
CREATE OR REPLACE TYPE NB_REC_ITEM_DETAIL AS OBJECT
(
column_1 number(8),
column_2 varchar2(13)
);
CREATE OR REPLACE TYPE NB_TAB_ITEM_DETAIL AS TABLE OF NB_REC_ITEM_DETAIL;
However, this structure is unknown for the PL/SQL engine and so it is necessary to create these data structures in the package spec as follows:
TYPE REC_TYP is record (
column_1 number(8),
column_2 varchar2(13)
);
TYPE TAB_TYP is table of REC_TYP;
Now it is possible to fetch the data from the sys_refcursor.
FUNCTION GET_STORE_ITEMS(I_store IN NUMBER,
----------- output ------------
O_item_data OUT NB_TAB_ITEM_DETAIL,
----------- error -------------
O_error_message OUT VARCHAR2)
RETURN BOOLEAN IS
--
L_error_message VARCHAR2(255);
L_item_data_rec REC_TYP;
L_item_detail_ref SYS_REFCURSOR;
--
CURSOR C_GET_STORE_ITEMS IS
--
SELECT a.sku
FROM win_store a
WHERE a.store = I_store;
--
BEGIN
--
-- Instantiate output structure object.
--
O_item_data := NB_TAB_ITEM_DETAIL();
--
-- Loop over the items in the store range.
--
FOR R_items IN C_GET_STORE_ITEMS LOOP
--
BEGIN
--
IF NB_ITEM_INFO_SQL.GET_ITEM_DETAIL(I_store => I_store,
I_sku => R_items.sku,
I_ean => NULL,
O_item_data => L_item_detail_ref,
O_error_message => L_error_message) = FALSE THEN
--
O_error_message := NB_MESSGE40_SQL.EMESSAGE(L_error_message);
RETURN FALSE;
--
END IF;
--
-- Fetch info from item_data sys_refcursor to record type.
--
FETCH L_item_detail_ref INTO L_item_data_rec;
EXIT WHEN L_item_detail_ref%NOTFOUND;
--
CLOSE L_item_detail_ref;
--
O_item_data.extend();
O_item_data(O_item_data.last) := nb_rec_item_detail(column_1 => L_item_data_rec.column_1 ,
column_2 => L_item_data_rec.column_2);
--
END;
--
END LOOP;
--
RETURN TRUE;
--
EXCEPTION
--
WHEN OTHERS THEN
-- ...
RETURN FALSE;
--
--
END GET_STORE_ITEMS;
The following Stored Procedure accepts EmployeeId as a parameter. It checks whether an Employee with the supplied EmployeeId exists in the Employees table of the Northwind database.
What will be the default return type and value?
CREATE PROCEDURE CheckEmployeeId
#EmployeeId INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Exists INT
IF EXISTS(SELECT EmployeeId
FROM Employees
WHERE EmployeeId = #EmployeeId)
BEGIN
SET #Exists = 1
END
ELSE
BEGIN
SET #Exists = 0
END
RETURN #Exists
END
Whenever, you execute a procedure, it returns an integer status variable.Usually,zero indicates success and non-zero indicates failure.
Now. its depends on you, what integer value you want to return from procedure to inform your application.
A procedure can return an integer value called a return code to indicate the execution status of a procedure. You specify the return code for a procedure using the RETURN statement.
Suppose you have
Return code value Meaning
0 Employee does not exists.
1 Employee exists.
Example:-
CREATE PROCEDURE CheckEmployeeId
#EmployeeId INT
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS(SELECT EmployeeId
FROM Employees
WHERE EmployeeId = #EmployeeId)
BEGIN
RETURN(1)
END
ELSE
BEGIN
RETURN(0)
END
RETURN(0)
END
RETURN always returns int from a stored procedure.
The default value can vary if
- try to return NULL (get zero and a warning)
- you get a SQL Server error where you get a value "10 minus the severity level of the error"
But it's always int
I do have a challenge here, I have a table called fileTable with the following columns:
FileID FileName
=================
1 | sdk
2 | mdk
3 | jdk
4 | apk
My challenge here is writing a function that gets passed the filename, if it does exist in the table, return a value 1 and its fileID and if not return a value 0 and ID 0
Now here is what I had started with and I thought about bringing it as a table but then got stuck when trying to use declarations or if statements inside the function
ALTER FUNCTION [dbo].[booker](
#filenumber nvarchar(50))
RETURNS TABLE
AS
RETURN
(
--NB
-- note the challenges am facing i thought of passing a value of FileID to #fn then check if its empty or not, that wont work
--- Declare #fid int; // is not accepted inside here
-- IF EXISTS (SELECT #fid=FILENUMBER FROM [dbo].[File] WHERE FILENUMBER = #filenumber) // the if statement is wrong here too
SELECT FileID
FROM [dbo].[File]
WHERE FileID = #filenumber
-- am left with the above statement where i can extract the FileID but don't know how to check if it exists and if so how to
-- return it with a value 1 that shows the record is in and value 0, fileid=0 if there is no record
)
BUT WHEN I CHANGED mhasan's code to stored procedure it worked
ALTER PROCEDURE [dbo].[booker](
#filenumber nvarchar(50))
AS
IF NOT EXISTS (SELECT 1 FROM [dbo].[File] WHERE FileNumber= #filenumber)
BEGIN
SELECT 0 As FileID, 0 As IsExist
END
ELSE
BEGIN
SELECT FileID, 1 As IsExist
FROM [dbo].[File]
WHERE FileNumber = #filenumber
END
I think am going to use this for a while, thank you tho
From your question, getting data as Table seems to be invalid approach.
You can get the data as single value in function using below query
CREATE FUNCITON [dbo].[booker](#filenumber nvarchar(50)) RETURNS nvarchar(10) DETERMINISTIC
BEGIN
DECLARE fileDetail nvarchar(10);
DECLARE #IDValue INT;
SET fileDetail = '';
SET #IDValue = 0;
SELECT FileID INTO #IDValue
FROM [dbo].[File]
WHERE FileID = #filenumber;
IF IDValue = 0 THEN
SET fileDetail := 0 +'|'+0;
ELSE
SET fileDetail := 1 +'|'+#IDValue;
END IF;
RETURN (fileDetail);
END
Hope this should help you out.
Use multistatement table valued function as below for using multiple code blocks as per your logic. You are using inline table valued function which allows only for single SELECT statement.
ALTER FUNCTION [dbo].[booker](#filenumber nvarchar(50))
RETURNS #ResultTableVariable TABLE (FileID INT, IsExist INT)
AS
BEGIN
IF NOT EXISTS (SELECT 1 FROM [dbo].[File] WHERE FileName= #filenumber)
BEGIN
INSERT INTO #ResultTableVariable (FileID , IsExist)
SELECT 0 , 0
END
ELSE
BEGIN
INSERT INTO #ResultTableVariable (FileID , IsExist)
SELECT FileID, 1
FROM [dbo].[File]
WHERE FileID = #filenumber
END
RETURN
END
This returns a table with two columns FileID and IsExist
I am working with Sage Evolution and do a lot of the back end stuff to customize it for our company.
I need to write a query where, when a user enters a negative quantity the system must not allow the transaction, however when the user enters a negative quantity and the product belongs to the "chemicals" group it needs to process the transaction.
Here is my code I have written so far.
DECLARE
#iAfterfQuantity Int;
#iAfteriStockCodeID Int;
#iAfterStockItemGroup VarChar
SELECT
#iAfterfQuantity = fQuantity,
#iAfteriStockCodeID = iStockCodeID
FROM
INSERTED
SELECT
#iAfterStockItemGroup = ItemGroup
FROM
dbo.stkItem
WHERE
StockLink = #iAfteriStockCodeID
BEGIN
IF #iAfterfQuantity < 0 AND #iAfterStockItemGroup <> 'chemicals'
BEGIN
RAISERROR ('',16,1)
ROLLBACK TRANSACTION
END
END
This is a task better suited for a check constraint then for a trigger, especially considering the fact that you are raising an error.
First, create the check function:
CREATE FUNCTION fn_FunctionName
(
#iAfterfQuantity Int,
#iAfteriStockCodeID Int
)
RETURNS bit
AS
BEGIN
DECLARE #iAfterStockItemGroup VarChar(150) -- Must specify length!
SELECT #iAfterStockItemGroup = ItemGroup FROM dbo.stkItem WHERE StockLink=#iAfteriStockCodeID
IF #iAfterfQuantity < 0 AND #iAfterStockItemGroup <> 'chemicals'
RETURN 0
RETURN 1 -- will be executed only if the condition is false...
END
Then, alter your table to add the check constraint:
ALTER TABLE YourTableName
ADD CONSTRAINT ck_ConstraintName
CHECK (dbo.fn_FunctionName(fQuantity, iStockCodeID) = 1)
GO