function that will only insert Monday-Friday? - sql

I've created a function that will insert a customer into the database, I just wondered if it was possible to make it so it will only except inserts on Monday to Friday days at how this would be done in Oracle SQL?
Here is the code for the function that is running and works
FUNCTION CREATE_CUSTOMER(
Country IN VARCHAR2
,First_Name IN VARCHAR2
,Last_Name IN VARCHAR2
,Birth_Date IN VARCHAR2
,Customer_Type IN VARCHAR2
,Address IN VARCHAR2
) return VARCHAR2 IS
new_customer_id VARCHAR2(8);
BEGIN
SELECT custid_seq.NEXTVAL
INTO new_customer_id
FROM DUAL;
INSERT INTO customer (Customer_id, Country, First_Name, Last_name, Birth_date, Customer_Type, Address)
VALUES (new_customer_id, Country, First_Name, Last_name, Birth_date, Customer_Type, Address);
total_customers := total_customers + 1;
RETURN (new_customer_id);
end;
Anyone got any idea how to develop this or if its possible?
thanks

You want the insert statement to run only on Weekdays? If so, you can check the day of the weeks using
to_char(sysdate,'D')
It returns numbers 1-7(Sunday to Saturday). Based on this you can decide whether to insert or not.

Related

Procedure to insert data if not exist pl sql

I have a table with these columns (id,first_name,birth). I want to create a procedure that insert a new customer only if the id inserted doesn't exist in the table. If it already exist, then don't insert it. This is my code so far, but I got an error 'line 3 sql statement ignored'. Any idea? I need to use procedure and pl sql in oracle. Thanks!
CREATE OR REPLACE PROCEDURE add_emp(v_id IN int,
v_name IN varchar2,
v_bday IN date) IS
BEGIN
INSERT INTO Employees
(Id, First_name, Birth)
SELECT *
FROM (SELECT v_id, v_name, v_bday) AS tmp
WHERE NOT EXISTS (SELECT Id FROM Employees WHERE Id = v_id);
END;
/
DECLARE
m_id int := 3;
m_name varchar2 := 'John';
m_bday date := '16-Dec-1990';
BEGIN
add_cust(m_id, m_name, m_bday);
END;
/
Your procedure has some syntax issue which is fixed in following code:
CREATE OR REPLACE PROCEDURE ADD_EMP (
V_ID IN INT,
V_NAME IN VARCHAR2,
V_BDAY IN DATE
) IS
BEGIN
INSERT INTO EMPLOYEES (
ID,
FIRST_NAME,
BIRTH
)
SELECT V_ID,
V_NAME,
V_BDAY
FROM DUAL -- FROM clause was missing
WHERE NOT EXISTS (
SELECT ID
FROM EMPLOYEES
WHERE ID = V_ID
);
END;
/
Also, Your calling PL/SQL block has some issues which are corrected in the following code:
DECLARE
M_ID INT := 3;
M_NAME VARCHAR2(10) := 'John'; -- varchar2 must be declared with size
M_BDAY DATE := DATE '1990-12-16'; -- added date literal to convert string to date
BEGIN
ADD_CUST(M_ID, M_NAME, M_BDAY);
END;
/
In Oracle SELECT does not work without a FROM clause (other DBMS products are different). So you need to provide a table; you can use DUAL, which is a dummy table provided by Oracle which is guaranteed to return one row.
INSERT INTO Employees(Id,First_name,Birth)
SELECT v_id, v_name, v_bday
from dual
WHERE NOT EXISTS (
SELECT Id FROM Employees WHERE Id = v_id
);
Your INSERT statement would work with a slight change
CREATE OR REPLACE PROCEDURE add_emp(v_id Employees.Id%type,
v_name Employees.First_name%type,
v_bday Employees.Birth%type) IS
BEGIN
INSERT INTO Employees
(Id, First_name, Birth)
SELECT v_id, v_name, v_bday
FROM dual
WHERE NOT EXISTS (SELECT * FROM Employees WHERE Id = v_id);
END;
/
You can replace INSERT statement with MERGE as an alternative DML such as
MERGE INTO Employees e1
USING
(SELECT v_id AS id, v_name AS First_name, v_bday AS birth
FROM dual) e2
ON ( e1.id = e2.id )
WHEN MATCHED THEN UPDATE SET e1.First_name = e2.First_name,
e1.Birth = e2.Birth -- If you need to change for the matching case
WHEN NOT MATCHED THEN INSERT( e1.id, e1.First_name, e1.birth )
VALUES( e2.id, e2.First_name, e2.birth );

Return an array in oracle stored procedure

I have a Stored procedure which takes 2 input parameter and 6 out. One of the out parameter returns more than one record. Is there any way to declare it as an array hence I don't have to declare a cursor. Here is the code,
CREATE OR REPLACE PROCEDURE SP_GET_USER_DETAILS
(
P_ID IN OUT VARCHAR2
, P_USER_NAME IN OUT VARCHAR2
, P_USER_TYPE OUT VARCHAR2
, P_EMPLOYEE_ID OUT VARCHAR2
, P_LICENSE_NO OUT VARCHAR2
, P_PHONE_NO OUT VARCHAR2
) IS
BEGIN
SELECT ACCT.ID, ACCT.USERNAME, ACCT.EMPLOYEE_ID, ACCT.LICENSE_NO,
ADDRESS.PHONE_NO INTO P_ID, P_USER_NAME, P_EMPLOYEE_ID, P_LICENSE_NO, P_PHONE_NO
FROM PROVIDER_ACCT ACCT
LEFT OUTER JOIN EMP_ADDRESS ADDRESS ON ACCT.ID=ACCT.ID
END SP_GET_PRISON_USER_DETAILS;
The problem is ADDRESS.PHONE_NO alone returns multiple rows. Is there any way to declare it as an array and get this working ? Thanks in advance.
If its not possible, could you please explain how to do it using a ref cursor ?
Use a collection:
CREATE OR REPLACE PROCEDURE SP_GET_USER_DETAILS
(
P_ID IN OUT VARCHAR2
, P_USER_NAME IN OUT VARCHAR2
, P_USER_TYPE OUT VARCHAR2
, P_EMPLOYEE_ID OUT VARCHAR2
, P_LICENSE_NO OUT VARCHAR2
, P_PHONE_NO OUT SYS.ODCIVARCHAR2LIST
)
IS
BEGIN
SELECT ID,
USERNAME,
EMPLOYEE_ID,
LICENSE_NO
INTO P_ID,
P_USER_NAME,
P_EMPLOYEE_ID,
P_LICENSE_NO
FROM PROVIDER_ACCT;
SELECT ADDRESS.PHONE_NO
BULK COLLECT INTO P_PHONE_NO
FROM PROVIDER_ACCT ACCT
LEFT OUTER JOIN EMP_ADDRESS ADDRESS
ON ACCT.ID = ADDRESS.ID;
END SP_GET_PRISON_USER_DETAILS;
/
Note: you haven't specified a where clause in your question - you probably want to add one in.

Check if there is a location_id and manager_id with the same name (they are from different tables)

So on that code I checked the uniqueness of department_id and department_name. Now I would like to do the same with manager_id and locations_id. Please keep in mind that manager_id is from table EMPLOYEES and locations_id is from table LOCATIONS.
I wonder if I can just continue that statement:
upper(s.department_id) = upper(d.department_id)
OR upper(s.department_name) = upper(d.department_name)
But I think that will not be enough, because it will check only in the DEPARTMENTS table. Not from LOCATIONS and EMPLOYEES like I want. Please advise.
CREATE OR REPLACE PROCEDURE add_dep(p_id NUMBER,
p_name VARCHAR2,
p_mgr NUMBER,
p_loc NUMBER) IS
BEGIN
MERGE INTO departments d
USING (
SELECT
p_id department_id,
p_name department_name,
p_mgr manager_id,
p_loc location_id
FROM dual) s
ON ( upper(s.department_id) = upper(d.department_id)
OR upper(s.department_name) = upper(d.department_name))
WHEN NOT MATCHED THEN
INSERT VALUES (s.department_id, s.department_name, s.manager_id, s.location_id);
IF SQL%ROWCOUNT = 0 THEN
INSERT INTO error_depa VALUES (p_id, p_name, p_mgr, p_loc);
END IF;
END;
EDIT - additional information
I got an error that there are not enough values for LOCATIONS and EMPLOYEES table. But I also tried to make this with exception and no_data_found. Can you modify this code and help me with that please? I would like to check the same for locations_id and manager_id.
create or replace procedure add_de(
p_id NUMBER,
p_name VARCHAR2,
p_mgr NUMBER,
p_loc NUMBER
)
is
v_dummy number;
begin
select 1
into v_dummy
from departments
where department_name = p_name OR DEPARTMENT_ID = p_id ;
insert
into error_depa
values(
p_id,
p_name,
p_mgr,
p_loc
);
exception
when no_data_found
then
insert
into departments
values(
p_id,
upper(p_name),
p_mgr,
p_loc
);
end;
I think that you want something like this:
CREATE OR REPLACE PROCEDURE add_dep(p_id NUMBER,
p_name VARCHAR2,
p_mgr NUMBER,
p_loc NUMBER) IS
BEGIN
MERGE INTO departments d
USING (
SELECT
p_id department_id,
p_name department_name,
p_mgr manager_id,
p_loc location_id
FROM dual) s
ON ( upper(s.department_id) = upper(d.department_id)
OR upper(s.department_name) = upper(d.department_name))
WHEN NOT MATCHED THEN
***INSERT(department_id, department_name,manager_id,location_id)*** VALUES (s.department_id, s.department_name, s.manager_id, s.location_id);
COMMIT;
MERGE INTO employees trg
USING (
SELECT
*
FROM employees
where
manager_id = p_mgr) src
ON ( upper(trg.manager_id) = upper(src.manager_id))
WHEN NOT MATCHED THEN
***INSERT(department_id, ...)*** VALUES (src.department_id, ....);
COMMIT;
MERGE INTO locations trg
USING (
SELECT
*
FROM locations
where
locations_id = p_loc) src
ON ( upper(trg.locations_id) = upper(src.locations_id))
WHEN NOT MATCHED THEN
***INSERT(locations_id, ...)*** VALUES (src.locations_id, ....);
COMMIT;
IF SQL%ROWCOUNT = 0 THEN
INSERT INTO error_depa VALUES (p_id, p_name, p_mgr, p_loc);
END IF;
END;
/

PL/SQL help. How to write a anonymous block that inserts 100 new rows

I am new to PL/SQL (and programming in general) and been set a few tasks. I am working my way through them and getting on OKish but am stumped with this particular task. I using the Oracle Application Express and have created the table that I am working with which is called emp2
Here is the task in it's entirety:
Write a PL/SQL anonymous block that inserts 100 (new) employee IDs,
starting at number 2000. Use a FOR loop, and declaration block for
defining the lower/upper limits of the loop. In addition to
the employee IDs, also add code that inserts placeholders in
the first_name and last_name columns (avoid NULLs!), eg
"FName_2000" and "LName_2000" for employee ID 2000.
(Hint: Use the concatenation operator).
This is the code that I have written so far (which I thought was spot on) minus the VALUES, as I just could not get this part to work?
DECLARE
lower constant pls_integer := 2000;
upper constant pls_integer := 2099;
BEGIN
FOR i in lower..upper
LOOP
INSERT INTO emp2 ( EMPLOYEE_ID, FIRST_NAME, LAST_NAME, HIRE_DATE, SALARY,
DEPARTMENT_ID )
VALUES ( //UNSURE WHAT GOES HERE! );
DBMS_OUTPUT.PUT_LINE('Inserted' || SQL%ROWCOUNT || ' row');
END LOOP;
END;
Your insert statement should look like this:
INSERT INTO emp2
( EMPLOYEE_ID, FIRST_NAME, LAST_NAME, HIRE_DATE, SALARY, DEPARTMENT_ID )
VALUES
( i, 'Fname', 'Lname', sysdate, 100, 10 );
You need to add an IF statement for the part "also add code that inserts placeholders in the first_name and last_name columns for employee ID 2000". Like this:
IF i = 2000
THEN
INSERT INTO emp2
( EMPLOYEE_ID, FIRST_NAME, LAST_NAME, HIRE_DATE, SALARY, DEPARTMENT_ID )
VALUES
( i, 'Fname ' || i, 'Lname ' || i, sysdate, 100, 10 );
ELSE
INSERT INTO emp2
( EMPLOYEE_ID, FIRST_NAME, LAST_NAME, HIRE_DATE, SALARY, DEPARTMENT_ID )
VALUES
( i, 'Fname', 'Lname', sysdate, 100, 10 );
END IF;

SQL trigger for tracking history of changes - ORACLE DB

I have a table cust_info and need to track the update and delete on it. I created 2 new tables ( archive_customers is the same as cust_info but customers_hist contains two additional instances (changed_by and change_date)).
CREATE TABLE CUST_INFO
(
CUST_ID NUMBER(15),
CUST_F_NAME VARCHAR(20),
CUST_L_NAME VARCHAR(20),
CUST_ADDRESS VARCHAR(40),
CITY VARCHAR(30),
STATE VARCHAR(30),
ZIP NUMBER,
PHONE VARCHAR(12),
PRIMARY KEY (CUST_ID)
);
I was searching for similar problems and this is what I got so far:
CREATE OR REPLACE TRIGGER TRIGGER_UPDATE
BEFORE UPDATE ON CUST_INFO
FOR EACH ROW
BEGIN
INSERT INTO CUSTOMERS_HIST
(
CUST_ID,
CUST_F_NAME,
CUST_L_NAME,
CUST_ADDRESS,
CITY,
STATE,
ZIP,
PHONE,
SYSDATE
)
SELECT
OLD.CUST_ID,
OLD.CUST_F_NAME,
OLD.CUST_L_NAME,
OLD.CUST_ADDRESS,
OLD.CITY,
OLD.STATE,
OLD.ZIP,
OLD.PHONE
FROM CUST_INFO
END;
CREATE OR REPLACE TRIGGER TRIGGER_DELETE
BEFORE DELETE ON CUST_INFO
FOR EACH ROW
BEGIN
INSERT INTO ARCHIVE_CUSTOMERS
(
CUST_ID,
CUST_F_NAME,
CUST_L_NAME,
CUST_ADDRESS,
CITY,
STATE,
ZIP,
PHONE
)
SELECT
OLD.CUST_ID,
OLD.CUST_F_NAME,
OLD.CUST_L_NAME,
OLD.CUST_ADDRESS,
OLD.CITY,
OLD.STATE,
OLD.ZIP,
OLD.PHONE
FROM CUST_INFO
END;
In addition, I am not sure how to get the change_by and change_date populated??
If you need more info, please make a comment.
Thank you
Perhaps you could use the user and sysdate built-in functions for chnage_by and change_date respectively. The code I would write is completely different if I want to store the change history in a table. Yet, for your example, the code is as follows:
CREATE OR REPLACE TRIGGER TRIGGER_UPDATE
BEFORE UPDATE
ON CUST_INFO
FOR EACH ROW
BEGIN
INSERT INTO CUSTOMERS_HIST (CUST_ID,
CUST_F_NAME,
CUST_L_NAME,
CUST_ADDRESS,
CITY,
STATE,
ZIP,
PHONE,
SYSDATE,
change_by,
change_date)
SELECT OLD.CUST_ID,
OLD.CUST_F_NAME,
OLD.CUST_L_NAME,
OLD.CUST_ADDRESS,
OLD.CITY,
OLD.STATE,
OLD.ZIP,
OLD.PHONE,
user,
sysdate
FROM CUST_INFO;
END;
/
CREATE OR REPLACE TRIGGER TRIGGER_DELETE
BEFORE DELETE
ON CUST_INFO
FOR EACH ROW
BEGIN
INSERT INTO ARCHIVE_CUSTOMERS (CUST_ID,
CUST_F_NAME,
CUST_L_NAME,
CUST_ADDRESS,
CITY,
STATE,
ZIP,
PHONE,
change_by,
change_date)
SELECT OLD.CUST_ID,
OLD.CUST_F_NAME,
OLD.CUST_L_NAME,
OLD.CUST_ADDRESS,
OLD.CITY,
OLD.STATE,
OLD.ZIP,
OLD.PHONE,
user,
sysdate
FROM CUST_INFO;
END;
/
That was the answer for your question.
Now, if I were you, I would build an audit table as follows:
TABLE audit_entry
(
audit_Entry_Id INTEGER,
change_By VARCHAR2 (30), -- User who made the change
change_TS TIMESTAMP, -- TS at which the change was made
table_Name VARCHAR2 (30), -- Name of table on which change was made
column_Name VARCHAR2 (30), -- Name of column in that table
primary_key_id INTEGER, -- The PK or ID of the row which was changed
old_clob CLOB,
new_clob CLOB,
old_TS TIMESTAMP,
new_TS TIMESTAMP,
old_number NUMBER (36, 2),
new_number NUMBER (36, 2)
)
The last old and new columns would store the old and new values of appropriate data type. You could also improve the table as per your needs. Then, build a trigger on your tables to track changes and insert values into audit_entry table accordingly.