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

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;

Related

(Beginner) How to exclude null value from Oracle Inner Join Statement Result and How to show ONLY null data in any of the attributes

(Beginner)
Q1) How to exclude null value from Oracle Inner Join Statement Result. i.e. to display only that row which has all the values (i.e. no null data in any of the attribute)
Q2) How to show ONLY those record/s who has missing/null data in any of the attributes in any of the four table
Below is the dummy example in oracle sql
Created 4 Tables i.e.,
name , details , social , address
`create table name
(
id number,
firstname varchar2(20),
lastname varchar2(20)
);
create table details
(
id number,
dob_month varchar2(20),
dob_day varchar2(20),
dob_year varchar2(20)
);
create table social
(
id number,
ssn varchar2(20),
telephone number
);
create table address
(
id number,
address varchar2(20)
);
`
Now insert dummy data into the above tables
`insert into name values (1, 'Will' , 'Smith');
insert into name values (2, 'Barry' , 'White');
insert into name values (3, 'Tom' , 'Jones');
insert into name values (4, 'Rod' , 'Stewart');
insert into name values (5, 'Elvis' , 'Presley');
insert into details values (1,'May',31,null);
insert into details values (2,'August',22,1980);
insert into details values (3,'October',null,1973);
insert into details values (4,'January',30,1980);
insert into details values (5,'March',11,1980);
insert into social values (1,'123-45-6789',null);
insert into social values (2,'222-45-5555',789456123);
insert into social values (3,'333-45-7777',888888888);
insert into social values (4,null,693456741);
insert into social values (5,'999-45-1111',null);
insert into address values (null, null);
insert into address values (2, '12th street');
insert into address values (null, null);
insert into address values (4, '14th Avenue');
insert into address values (5, null);`
Q1 Pictorial Explanation
For Q1,
I tried with below oracle sql query (both returns the same result) but not able to figure out the exact query which will exclude null value from Oracle Inner Join Statement Result and display only that row which has all the values (i.e. not null)
`select name.id, firstname, lastname, dob_month, dob_year, ssn, telephone, address
from name, details, social, address
where name.id=details.id
and details.id=social.id
and social.id=address.id;
select name.id, firstname, lastname, dob_month, dob_year, ssn, telephone, address
from name join details on name.id=details.id
join social on details.id=social.id
join address on social.id=address.id;`
For Q2, I am looking for sample Query
i.e. How to show ONLY those record/s who has missing/null data in any of the attributes in any of the four table
For the first question, I think you are looking for this:
select name.id, firstname, lastname, dob_month, dob_year, ssn, telephone, address
from name
join details on name.id=details.id
join social on details.id=social.id
join address on social.id=address.id
where dob_year is not null
and dob_day is not null
and telephone is not null
and ssn is not null
and address is not null;
Or a bit shorter
...
where COALESCE(dob_year, dob_day, telephone, ssn, address) is not null;
For Q2 it would be this
select *
from address
where id is null
or address is null;
Some more notes:
It is a poor design to store date parts and then even localized strings. You should never do that. In your case it should be
create table details
(
id number,
dob_date DATE
);
Then next question, why do you create four tables? Maybe you learned at school about database normalization but you have taken too literally. Can a person have more than one SSN/telephone? If not, then add these columns to name table (and maybe rename it to person) instead of a separate table. The same question applies to table address.

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;

Selecting from a function that return object type alongside with the another table that gives the parameter

I am having trouble selecting from a function that takes one parameter to calculate some amounts and then returns one object type that contains the data
Here's what i have done.
CREATE OR REPLACE TYPE AMOUNTS
AS
OBJECT
(
AMOUNT1 NUMBER, --1
AMOUNT2 NUMBER, --2
AMOUNT3 NUMBER --3
)
/
CREATE OR REPLACE FUNCTION CALCULATEAMOUNTS(
ID NUMBER)
RETURN AMOUNTS
IS
BEGIN
DECLARE
AMOUNT1 NUMBER;
AMOUNT2 NUMBER;
AMOUNT3 NUMBER;
BEGIN
--calculation of amount1, amount2 and amount3
RETURN AMOUNTS(AMOUNT1, AMOUNT2, AMOUNT3) ;
END;
END CALCULATEAMOUNTS;
What I want after this is selecting from the function result and from an other table PRODUCT that i will also need it's ID to calculate the amounts but I couldn't
here's my tries:
-try1
SELECT PR.ID, FUNC_DATA.AMOUNT1, FUNC_DATA.AMOUNT2, FUNC_DATA.AMOUNT3 FROM
(
SELECT CALCULATEAMOUNTS(PR.ID) FROM DUAL
) FUNC_DATA, PRODUCT PR;
result:
ORA-00904: "PR"."ID": invalid identifier
-try2
SELECT PR.ID, TREAT(
(
SELECT CALCULATEAMOUNTS(PR.ID) AS FUNC_DATA FROM DUAL
) AS AMOUNTS) FROM PRODUCT PR;
result:
I just want to return the field ID, AMOUNT1, AMOUNT2 and AMOUNT3 together without calling the function many time and each time to get one amount because it will cause a performance issue
I've created a minimal product table:
CREATE TABLE product (
id NUMBER
);
INSERT INTO product VALUES (1);
Now this query works:
SELECT pr.id,
CALCULATEAMOUNTS(pr.id).amount1 AS amount1,
CALCULATEAMOUNTS(pr.id).amount2 AS amount2,
CALCULATEAMOUNTS(pr.id).amount3 AS amount3
FROM product pr;
ID AMOUNT1 AMOUNT2 AMOUNT3
1 null null null
EDIT:
You could also nest the query:
SELECT q.id, q.f.amount1, q.f.amount2, q.f.amount3
FROM (
SELECT pr.id, calculateamounts(pr.id) as f
FROM product pr
) q;
However, the function is still called three times. I checked with a modified function CALCULATEAMOUNTS that writes to a log table.
EDIT:
I found a way to avoid that the function is called three times: declare your function as being DETERMINISTIC. (Which it is, isn't it? It returns the same results for a given ID?)
CREATE OR REPLACE FUNCTION CALCULATEAMOUNTS(ID NUMBER)
RETURN AMOUNTS DETERMINISTIC
IS
BEGIN ...

How to get the value of clob data which has date field and compare with timestamp in oracle

I have a table named masterregistry and it contains all the info and business logic in it and the data type of the colum is clob
desc master_registy:
id number not null,
name varchar2(100),
value clob
select value from master_registry where name='REG_DATE';
o/p
11-10-17
This date is common across all the business logic, I need to query my tables which has ,
desc get_employee
====================
id number not null,
first_name varchar2(100),
last_name varchar2(100),
last_mod_dt timestamp
Now I need to get all the values from the get_employee whose last_mod_dt should be greater than the value of master_registry where name='REG_DATE'.The value in the latter table is clob data, how to fetch and compare the date of a clob data against the timestamp from another table. Please help.
Maybe you need something like this.
SELECT *
FROM get_employee e
WHERE last_mod_dt > (SELECT TO_TIMESTAMP (TO_CHAR (VALUE), 'DD-MM-YY')
FROM master_registy m
WHERE m.id = e.id);
DEMO
Note that i have used the column value directly in TO_CHAR. You may have to use TRIM,SUBSTR or whatever required to get ONLY the date component.

Oracle Inserting or Updating a row through a procedure

I have a table
CREATE TABLE STUDENT
(
ID INTEGER PRIMARY KEY,
FIRSTNAME VARCHAR2(1024 CHAR),
LASTNAME VARCHAR2(1024 CHAR),
MODIFIEDDATE DATE DEFAULT sysdate
)
I am inserting a row of data
insert into STUDENT (ID, FIRSTNAME, LASTNAME, MODIFIEDDATE) values (1,'Scott', 'Tiger', sysdate);
When I have to insert a record of data, I need to write a procedure or function which does the following:
if there is no record for the same id insert the row.
if there is a record for the same id and data matches then do nothing.
if there is a record for the same id but data does not match then update the data.
I am new to oracle. From the java end, It is possible to select the record by id and then update that record, but that would make 2 database calls. just to avoid that I am trying update the table using a procedure. If the same can be done in a single database call please mention.
For a single SQL statement solution, you can try to use the MERGE statement, as described in this answer https://stackoverflow.com/a/237328/176569
e.g.
create or replace procedure insert_or_update_student(
p_id number, p_firstname varchar2, p_lastname varchar2
) as
begin
merge into student st using dual on (id = p_id)
when not matched then insert (id, firstname, lastname)
values (p_id, p_firstname, p_lastname)
when matched then update set
firstname = p_firstname, lastname = p_lastname, modifiedate = SYSDATE
end insert_or_update_student;
instead of procedure try using merge in oracle .
If Values is matched it will update the table and if values is not found it will insert the values
MERGE INTO bonuses b
USING (
SELECT employee_id, salary, dept_no
FROM employee
WHERE dept_no =20) e
ON (b.employee_id = e.employee_id)
WHEN MATCHED THEN
UPDATE SET b.bonus = e.salary * 0.1
DELETE WHERE (e.salary < 40000)
WHEN NOT MATCHED THEN
INSERT (b.employee_id, b.bonus)
VALUES (e.employee_id, e.salary * 0.05)
WHERE (e.salary > 40000)
Try this
To solve the second task - "if there is a record for the same id and data matches then do nothing." - starting with 10g we have additional "where" clause in update and insert sections of merge operator.
To do the task we can add some checks for data changes:
when matched then update
set student.last_name = query.last_name
where student.last_name <> query.last_name
This will update only matched rows, and only for rows where data were changed

Resources