what is the proper syntax to display SNowflake DDL for UDF - user-defined-functions

I am logged in as an admin and created the following function
Create or replace function "CONSUMPTION"."UDF_GetCountArea" (
LOCN_CLASS varchar,
LOCN_SIZE_TYPE varchar,
ZONE varchar,
LOCN_AISLE varchar)
Returns varchar(40) as
$$
select CASE
WHEN ZONE in('81','84') THEN 'Pallet Reserve'
WHEN ZONE = '88' AND LOCN_AISLE = '84' THEN 'Pallet Reserve'
WHEN ZONE = '85' THEN 'Case Reserve'
WHEN ZONE in('80','83') THEN 'Pallet Reserve'
WHEN ZONE = '88' AND LOCN_AISLE = '83' THEN 'Pallet Reserve'
WHEN ZONE = '86' THEN 'Case Reserve'
WHEN LOCN_CLASS ='R' AND LOCN_SIZE_TYPE IN ('BFL','AFL','HLF','FUL') THEN 'Pallet Reserve'
WHEN LOCN_CLASS ='R' AND LOCN_SIZE_TYPE IN ('CSR','ACS','X1','BCS') THEN 'Case Reserve'
WHEN LOCN_CLASS ='C' AND ZONE IN ('41', '42', '43') THEN 'Cart Picking'
WHEN LOCN_CLASS ='C' AND ZONE IN ('44', '45', '46') THEN 'Unit Picking'
WHEN LOCN_CLASS ='C' AND ZONE IN ('47', '48', '49') THEN 'Mod Picking'
Else '99-UNK'
End Phys_COUNT_AREA
$$
grant usage on function "CONSUMPTION"."UDF_GetCountArea"(VARCHAR,VARCHAR,VARCHAR,VARCHAR) to APPLICATION_SNOWFLAKE_QA_SC_WMS_NALC_READWRITE;
grant usage on function "CONSUMPTION"."UDF_GetCountArea"(VARCHAR,VARCHAR,VARCHAR,VARCHAR) to APPLICATION_SNOWFLAKE_QA_SC_WMS_NALC_READ;
SHOW USER FUNCTIONS return and shows the function.
I can test the function and it does work properly. but when I try to list out the code with the following command it fails to list
select GET_DDL('FUNCTION', 'UDF_GetCountArea(VARCHAR, VARCHAR, VARCHAR, VARCHAR)');
SQL compilation error: Object 'UDF_GetCountScope(VARCHAR, VARCHAR, VARCHAR)'
does not exist or not authorized.
Also, is there a setting somewhere that allows UDFs and SPs to be listed in the SF objects window?

When you created your function, you wrapped your function name inside double quotes, and therefore the function is named UDF_GetCountArea. However, in your call to the GET_DDL() function, you did NOT wrap the name in double quotes, and in Snowflake, all identifiers are BY DEFAULT upper-cased, so you are asking for a function named UDF_GETCOUNTAREA, but no such function exists. In order to retrieve the DDL for the function that you created, you can use:
select GET_DDL('FUNCTION', '"UDF_GetCountArea"(VARCHAR, VARCHAR, VARCHAR, VARCHAR)');
;
That said, I would strongly urge you to avoid the use of quoted identifiers unless you absolutely need to use them. To do this, you would need to drop the existing function:
DROP FUNCTION "UDF_GetCountArea"(VARCHAR, VARCHAR, VARCHAR, VARCHAR)
and then re-create your function under the new name:
CREATE OR REPLACE FUNCTION CONSUMPTION.UDF_GETCOUNTAREA (
LOCN_CLASS VARCHAR
,LOCN_SIZE_TYPE VARCHAR
,ZONE VARCHAR
,LOCN_AISLE VARCHAR
)
RETURNS VARCHAR AS
$$
...

Related

How to optional update data on oracle?

i have this table:
CREATE TABLE "ALMAT"."PRODUCT"
( "ID" NUMBER(*,0) NOT NULL ENABLE,
"NAME" VARCHAR2(50 BYTE),
"PRICE" NUMBER(*,0),
"DESCRIPTION" VARCHAR2(180 BYTE),
"CREATE_DATE" DATE,
"UPDATE_DATE" DATE,
CONSTRAINT "PRODUCT_PK" PRIMARY KEY ("ID"))
i want to update data in this table, this is my stored procedure:
CREATE OR REPLACE PROCEDURE UPDATEPRODUCT(prod_id int, prod_name varchar2 default null, prod_price int default null) AS
BEGIN
update product
set
name = prod_name,
price = prod_price,
update_date = sysdate
where id = prod_id;
commit;
END UPDATEPRODUCT;
im using optional parameters, how can i update only 1 column? for example: only "NAME" or "PRICE".
Use COALESCE (or NVL) to keep the current value when a NULL value is passed in (or the default is used):
CREATE OR REPLACE PROCEDURE UPDATEPRODUCT(
prod_id PRODUCT.ID%TYPE,
prod_name PRODUCT.NAME%TYPE DEFAULT NULL,
prod_price PRODUCT.PRICE%TYPE DEFAULT NULL
)
AS
BEGIN
UPDATE product
SET name = COALESCE(prod_name, name),
price = COALESCE(prod_price, price),
update_date = SYSDATE
WHERE id = prod_id;
END UPDATEPRODUCT;
Also, do not COMMIT in a stored procedure as it prevents you from chaining multiple procedures together in a single transaction and rolling them all back as a block. Instead, COMMIT from the PL/SQL block that calls the procedure.
You can use NVL function here. So your updated procedure would look alike -
CREATE OR REPLACE PROCEDURE UPDATEPRODUCT(prod_id int,
prod_name varchar2 default null,
prod_price int default null) AS
BEGIN
UPDATE product
SET name = NVL(prod_name, name),
price = NVL(prod_price, price),
update_date = sysdate
WHERE id = prod_id;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
RAISE;
END UPDATEPRODUCT;

How to use NVL in PL/SQL for Date columns?

This is my first SP in Oracle.
create or replace PROCEDURE SEARCH(R1 OUT SYS_REFCURSOR,
UserID IN VARCHAR2,
Name IN VARCHAR2,
FromDate IN VARCHAR2,
ToDate IN VARCHAR2
)
IS
BEGIN
OPEN R1 FOR
SELECT * FROM USER WHERE id = NVL( UserID, id )
and ((LASTNAME =NVL(Name,LASTNAME)) OR( FIRSTNAME =NVL(Name, FIRSTNAME)))
and ( to_char(releaseddate, 'mm/dd/rrrr') between FromDate and ToDate)
order by RELEASEDDATE desc
FETCH FIRST 100 ROWS ONLY;
END SEARCH;
In my table, I have columns Id, FirstName, LastName, and ReleasedDate. These columns are not dependent on each other. I used NVL to handle the Table columns, but RleasedDate column depends on FromDate and To date input parameters. I tried to use NVL, but I am facing issues. I am receiving the data in mm/dd/rrrr format. Is there any way that I use NVL for Releaseddate ?
Currently, I am receiving all the data from table where releaseddate is null and I am trying to avoid it.
Thanks for the help in advance.
and ( to_char(releaseddate, 'mm/dd/rrrr') between FromDate and ToDate)
I wouldn't use nvl for this, partly because it would exclude column values that are null. I would test explicitly for null arguments; something like:
OPEN R1 FOR
SELECT * FROM USER
WHERE (UserID IS NULL OR id = UserID)
AND (Name IS NULL OR LASTNAME = Name OR FIRSTNAME = Name)
AND (FromDate IS NULL OR releaseddate >= TO_DATE(FromDate, 'dd/mm/rrrr'))
AND (ToDate IS NULL OR releaseddate <= TO_DATE(ToDate, 'dd/mm/rrrr'))
ORDER BY RELEASEDDATE DESC
FETCH FIRST 100 ROWS ONLY;
This compares the releasedate column values as a date, rather than converting it to a string; instead the string arguments are converted to dates to match the column data type, which is more efficient. It would be better to have the procedure arguments declared as dates, and make the caller provide a valid value.
Don't try to convert dates to character strings in order to compare them - you'll just get unexpected results. Instead, convert the character strings to dates.
As far as RELEASEDATE being NULL - I'd just use a condition to test for this. Assuming that you want to include the rows where RELEASEDATE is NULL I suggest:
create or replace PROCEDURE SEARCH(R1 OUT SYS_REFCURSOR,
UserID IN VARCHAR2,
Name IN VARCHAR2,
FromDate IN VARCHAR2,
ToDate IN VARCHAR2)
IS
BEGIN
OPEN R1 FOR
SELECT *
FROM USER
WHERE id = NVL( UserID, id ) and
( LASTNAME = NVL(Name, LASTNAME) OR
FIRSTNAME = NVL(Name, FIRSTNAME) ) and
( REALEASEDDATE IS NULL OR
REALEASEDDATE between TO_DATE(FromDate, 'mm/dd/rrrr')
and TO_DATE(ToDate, 'mm/dd/rrrr') )
order by REALEASEDDATE desc
FETCH FIRST 100 ROWS ONLY;
END SEARCH;

Insert sysdate using french format(dd/mm/yyyy) to date field in plsql

I have table that contain field 'EVT_START_DATE' with datatype is DATE on Oracle.
I want to insert the current date into this field, with french format dd/mm/yyyy.
I did this command line:
ALTER SESSION SET NLS_LANGUAGE = 'FRENCH';
After that, i tried this block of PL/SQL code:
DECLARE
v_evt_num VARCHAR(200);
BEGIN
v_evt_num := 'PC_' || evt_seq.NEXTVAL;
INSERT INTO event
(EVT_NUM,EVT_CODE_PAY,EVT_CODE_USER,EVT_START_DATE,EVT_MT)
VALUES
(v_evt_num,
'129',
'247',
TO_DATE(SYSDATE, 'DD/MM/YYYY'),
:i_mt);
COMMIT;
dbms_output.put_line('Success.');
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line('Fail.');
END;
But I get fail message.
If EVT_START_DATE is of type DATE, then use just:
INSERT INTO event
(EVT_NUM,EVT_CODE_PAY,EVT_CODE_USER,EVT_START_DATE,EVT_MT)
VALUES
(v_evt_num,
'129',
'247',
SYSDATE,
:i_mt);
whithout usingto_date function.
Oracle's SYSDATE function returns value of type DATE, so there is no need to use to_date in order to convert it to DATE, bacause it is DATE yet.
SYSDATE returns the current date and time set for the operating system
on which the database resides. The datatype of the returned value is
DATE

Oracle Express Edition (Browser Interface) vs. SQL Plus Commd Line

I am using Application Express (OracleXE 11g Express Edition installed on Win7 32-bit) on-line and the below stored procedure compiles and executes successfully with no errors. When compiling in SQL Plus command line, code compiles successfully, but when I execute, it gives me an error. Now I already solved the error (code snippet shown below), my question is why doesn't the database engine in Application Express throw an error ? Hope I am explaining my question correctly.
Table definition
CREATE TABLE DATE_DIMENSION
(
DATE_KEY DATE NOT NULL,
FULL_DATE_DESCRIPTION VARCHAR2(64) NOT NULL,
DAY_OF_WEEK NUMBER(1,0) NOT NULL,
DAY_OF_MONTH NUMBER(2,0) NOT NULL,
DAY_OF_YEAR NUMBER(3,0) NOT NULL,
LAST_DAY_OF_WEEK_INDICATOR CHAR(1) NOT NULL,
LAST_DAY_OF_MONTH_INDICATOR CHAR(1) NOT NULL,
WEEK_ENDING_DATE DATE NOT NULL,
MONTH_NUMBER NUMBER(2,0) NOT NULL,
MONTH_NAME VARCHAR2(32) NOT NULL,
YEAR_MONTH CHAR(32) NOT NULL,
QUARTER_NUMBER NUMBER(1,0) NOT NULL,
YEAR_QUARTER CHAR(32) NOT NULL,
YEAR_NUMBER NUMBER(4,0) NOT NULL,
CONSTRAINT DATE_DIMENSION_PK PRIMARY KEY (DATE_KEY)
)
/
Stored Procedure
create or replace PROCEDURE sp_DATE_DIMENSION(v_START_YEAR IN INT, v_END_YEAR IN INT) AS
--Declare two variables as DATE datatypes
v_CURRENT_DATE DATE;
v_END_DATE DATE;
BEGIN
--Assign the start year and end year to it's respective variables
v_CURRENT_DATE := TO_DATE('0101' || v_START_YEAR, 'MMDDYYYY');
v_END_DATE := TO_DATE('1231' || v_END_YEAR, 'MMDDYYYY');
--Clear/Dump what is currently stored in the table
DELETE FROM DATE_DIMENSION;
--Check the condition to see if the start year is less than the end year (Input Parameters)
WHILE v_CURRENT_DATE <= v_END_DATE
LOOP
--DATE_DIMENSION Table
INSERT INTO DATE_DIMENSION
(
DATE_KEY,
FULL_DATE_DESCRIPTION,
DAY_OF_WEEK,
DAY_OF_MONTH,
DAY_OF_YEAR,
LAST_DAY_OF_WEEK_INDICATOR,
LAST_DAY_OF_MONTH_INDICATOR,
WEEK_ENDING_DATE,
MONTH_NUMBER,
MONTH_NAME,
YEAR_MONTH,
QUARTER_NUMBER,
YEAR_QUARTER,
YEAR_NUMBER
)
VALUES
(
v_CURRENT_DATE, --DATE_KEY
TO_CHAR(v_CURRENT_DATE, 'Day, Month DD, YYYY'), --FULL_DATE_DESCRIPTION
TO_NUMBER(TO_CHAR(v_CURRENT_DATE, 'D')) -1, --DAY_OF_WEEK
TO_CHAR(v_CURRENT_DATE,'DD'), --DAY_OF_MONTH
TO_CHAR(v_CURRENT_DATE,'DDD'), --DAY_OF_YEAR
CASE --LAST_DAY_OF_WEEK_INDICATOR
WHEN TO_CHAR(v_CURRENT_DATE,'FMDay') = 'Saturday' THEN 'Y'
ELSE 'N'
END,
CASE --LAST_DAY_OF_MONTH_INDICATOR
WHEN LAST_DAY(TO_DATE(v_CURRENT_DATE, 'MM/DD/YYYY')) = TO_DATE(v_CURRENT_DATE, 'MM/DD/YYYY') THEN 'Y'
ELSE 'N'
END,
CASE --WEEK_ENDING_DATE OF CURRENT WEEK ENDING ON SATURDAY
WHEN TO_CHAR(v_CURRENT_DATE,'FMDay') = 'Saturday' THEN v_CURRENT_DATE
ELSE NEXT_DAY(v_CURRENT_DATE,'SATURDAY')
END,
TO_CHAR(v_CURRENT_DATE,'MM'), --MONTH_NUMBER
TO_CHAR(v_CURRENT_DATE,'MONTH'), --MONTH_NAME
TO_CHAR(v_CURRENT_DATE,'MONTH YYYY'), --YEAR_MONTH
TO_CHAR(v_CURRENT_DATE,'Q'), --QUARTER_NUMBER
TO_CHAR(v_CURRENT_DATE,'YYYY Q'), --YEAR_QUARTER
TO_CHAR(v_CURRENT_DATE,'YYYY') --YEAR_NUMBER
);
--Increment and assign the current date value to be re-evaluated
v_CURRENT_DATE := v_CURRENT_DATE + 1;
END LOOP;
END;
FYI - This piece of code solved my issue to make it execute using SQL Plus.
CASE --LAST_DAY_OF_MONTH_INDICATOR
WHEN LAST_DAY(v_CURRENT_DATE) = v_CURRENT_DATE THEN 'Y'
ELSE 'N'
END,
You're doing this:
WHEN LAST_DAY(TO_DATE(v_CURRENT_DATE, 'MM/DD/YYYY'))
= TO_DATE(v_CURRENT_DATE, 'MM/DD/YYYY') THEN 'Y'
But v_current_date is already a DATE type, so for both of those calls to to_date, you're really doing to_date(to_char(v_current_date), 'MM/DD/YYYY'), and the to_char is using your session NLS_DATE_FORMAT - which is presumably MM/DD/YYYY in Apex, but something else in SQL*Plus.
You haven't shown your actual error, so I'm speculating a little, but you're effectively doing something like:
to_date(to_char(v_current_date, 'DD/MM/YYYY'), 'MM/DD/YYYY')
That would work sometimes, but get an invalid month error if the day of the month is after the 12th, since it's transposing the month and day numbers. Or your NLS setting mat be using MON, which would get the same error as Oracle is quite forgiving about using names instead of month numbers. Or some other format which gives a different error - there are several you could hit.
Your fix, to just use WHEN LAST_DAY(v_CURRENT_DATE) = v_CURRENT_DATE, avoids both the explicit conversion to a date, and the implicit conversion from a date to string, so there is no impact from your NLS settings.
It sounds like you are saying that neither environment threw a compilation error but one environment threw a runtime error. I would guess in that case that you've written code that depends on environmental settings that are different in the two environments.
Looking at your LAST_DAY_OF_WEEK_INDICATOR, that is in fact what you did by passing a DATE to TO_DATE. Functionally, that doesn't make sense, to_date does not take a DATE as a parameter, it only accepts a VARCHAR2. When you call
TO_DATE(v_CURRENT_DATE, 'MM/DD/YYYY')
therefore, Oracle has to do a few things.
First, it implicitly casts v_current_date to a string. Because it is an implicit cast, it will use your session's NLS_DATE_FORMAT setting. Every session in a database may have a different NLS_DATE_FORMAT and the NLS_DATE_FORMAT for a single session can change over time so the behavior of this implicit cast is not known at compile time. If your NLS_DATE_FORMAT is DD-MON-RR, which is the default setting if you did an Oracle client install on an English language Windows machine, the string that is passed to to_date would be "20-FEB-15" (assuming you called the procedure today). If your NLS_DATE_FORMAT is DD/MM/YYYY, the string passed to to_date would be "20/02/2015".
Next, it calls to_date passing in the string that was just generated and the format mask that you specified. If you happen to be in a session where the NLS_DATE_FORMAT matches the format mask you passed to to_date, you'll get the same date back from to_date. If there is a mismatch, however, you may get an error (a string in the format DD-MON-YYYY will never convert to a valid date in the MM/DD/YYYY format) or you may get an unexpected result (a string in the format DD/MM/YYYY may convert to a valid date using the MM/DD/YYYY format mask but that date won't be the same one that you started with-- March 1 would be converted to January 3 for example).
If you want your code to run correctly regardless of the environment, avoid implicit casts.

Not a valid month when executing an IN parameter procedure with date value

CREATE OR REPLACE PROCEDURE PROC1(
V_STARTTIME IN TIMESTAMP ,
V_ENDTIME IN TIMESTAMP )
BEGIN
INSERT INTO TAB1
SELECT COINS FROM TAB2
WHERE DATE BETWEEN TO_DATE(V_STARTTIME,'mm/dd/yyyy hh:mi:ss aM') AND TO_DATE(V_ENDTIME ,'mm/dd/yyyy hh:mi:ss aM');
END;
SAMPLE DATE in Tab2 5/5/2014 9:46:38.000000 AM
My script runs between a range of dates. The two dates are IN parameters.
When I execute the procedure
Execute proc1('5/05/2014 11:25:00 AM','5/05/2014 12:25:00 PM')
I am getting not a valid month error.
Any idea how to fix this?
Thanks
Your procedure takes parameters of type timestamp. You're actually passing parameters of type varchar2 in your call. That forces Oracle to perform implicit conversion of the varchar2 parameters to timestamp using your session's NLS_TIMESTAMP_FORMAT. That will likely be different for different sessions so it is likely that at least some sessions will get an error because the string doesn't match the format of that session's NLS_TIMESTAMP_FORMAT. You'd be much better served passing in an actual timestamp either by explicitly calling to_timestamp or by passing a timestamp literal.
Your procedure then takes the timestamp parameters and pass them to the to_date function. The to_date function does not take parameters of type timestamp, it only takes parameters of type varchar2. That forces Oracle to do another implicit conversion of the timestamp parameters to varchar2, again using the session's NLS_TIMESTAMP_FORMAT. If the session's NLS_TIMESTAMP_FORMAT doesn't match the explicit format mask in your to_date call, you'll get an error or the conversion will return a result that you don't expect.
If the column in your table is actually of type date, you can directly compare a date to a timestamp. So there doesn't appear to be any reason to call to_date here. Based on your sample data, though, it appears that the column in your table is actually of type timestamp rather than date as your code implies, since a date does not have fractional seconds of precision. If that's the case, it makes even less sense to call to_date in your SELECT statement since your parameters are actually of type timestamp and your column is of type timestamp. Just compare the timestamp values.
My guess, therefore, is that you want something like
CREATE OR REPLACE PROCEDURE PROC1(
V_STARTTIME IN TIMESTAMP ,
V_ENDTIME IN TIMESTAMP )
BEGIN
INSERT INTO TAB1( <<column name>> )
SELECT COINS
FROM TAB2
WHERE <<timestamp column name>> BETWEEN v_starttime AND v_endtime;
END;
and that you want to cal the procedure by passing actual timestamps. Using timestamp literals
Execute proc1(timestamp '2014-05-05 11:25:00', timestamp '2014-05-05 12:25:00' )
or by explicitly calling to_timestamp
execute proc1( to_timestamp( '5/05/2014 11:25:00 AM', 'MM/DD/YYYY HH:MI:SS AM' ),
to_timestamp( '5/05/2014 12:25:00 PM', 'MM/DD/YYYY HH:MI:SS AM' ) );
That should get rid of all the implicit type conversions that are currently taking place.

Resources