SQL get user input and further use it - sql

I've got this task of inserting a new record into table EMPLOYEES. I know how to do it by asking the user to type in a value, for instance:
INSERT INTO EMPLOYEES (first_name, last_name, email, hire_date, job_id)
VALUES ('&first_name', '&last_name', '&email' ,'&hire_date', 'SA_REP' );
However, I'd like not to ask the user about the email but rather insert it automatically by taking the first letter of the first_name input concatenated with the last_name of the person whose data is being added to the table. In order to do this, I think I have to store the inserted values temporarily or at least get some reference to the first_name and last_name. I tried searching online but really got nowhere. Could you provide me with the simplest solution to this task? I'm using Oracle SQL Developer.

You may wrap it inside a PL/SQL block to use appropriate variables with right datatypes. This will also ensure that values for date variable is entered correctly, in the right format expected.
DECLARE
v_first_name employees.first_name%type := '&first_name';
v_last_name employees.last_name%type := '&last_name';
v_hire_date employees.hire_date%type := TO_DATE('&hire_date_YYYYMMDD','YYYYMMDD');
BEGIN
INSERT INTO EMPLOYEES (first_name, last_name, email, hire_date, job_id)
VALUES (v_first_name, v_last_name,
substr(v_first_name,1,1)||'.'||v_last_name , v_hire_date, 'SA_REP' );
--first letter of the first_name with last name
END;
/
Result
Enter value for first_name: John
Enter value for last_name: Doe
Enter value for hire_date_YYYYMMDD: 20190521
..
..
PL/SQL procedure successfully completed.

You should be able to do this:
INSERT INTO EMPLOYEES (first_name, last_name, email, hire_date, job_id)
VALUES ('&first_name', '&last_name', '&first_name' || '.' || '&last_name' ,'&hire_date', 'SA_REP' );

Related

SQL Developer : Export of the query result don't keep the data_type

I am working on a project where I can't connect directly to the database. I wrote a query that get all the data that I need so I can export them. The result of this SQL query have the great data_type but When I export the result, I choose the insert format and at this point all the data_type are switching to string.
Example of the exported data where the number are in the string format wherease in the original database and in the respons of the query, the type is respected :
Insert into MY_TABLE (POSTCODE, NUMBER, ADRESS, CODE, CITY, VALUE) values ('90000', '303', 'HultonLane', '2845', 'BOLTON', '3');
Do you know if there is any way to export the data with the same data_type from the original DB?
Thank for your help, if you need more information I will provide them.
That behavior (or bug?) exists in e.g. SQL Developer version 19.4, but is fixed in 20.2 so - if you're on a lower version, I suggest you upgrade.
Here's an example, based on Scott's DEPT table where DEPTNO column's datatype is NUMBER. Result - when this:
desc dept;
select /*insert*/ * from dept;
is ran as a script - is:
Name Null? Type
------ -------- ------------
DEPTNO NOT NULL NUMBER
DNAME VARCHAR2(20)
LOC VARCHAR2(20)
REM INSERTING into DEPT
SET DEFINE OFF;
Insert into DEPT (DEPTNO,DNAME,LOC) values (10,'ACCOUNTING','NEW YORK');
Insert into DEPT (DEPTNO,DNAME,LOC) values (20,'RESEARCH','DALLAS');
Insert into DEPT (DEPTNO,DNAME,LOC) values (30,'SALES','CHICAGO');
Insert into DEPT (DEPTNO,DNAME,LOC) values (40,'OPERATIONS','BOSTON');
As you can see, all DEPTNO values are numbers. In 19.4, you'd have e.g.
Insert into DEPT (DEPTNO,DNAME,LOC) values ('10','ACCOUNTING','NEW YORK');
('10', a string).
You don't have to do anything about it; the result is the same regardless you run such a select statement, or right-click table name and "Export" data as insert statements.
BTW, our versions match: Mine is 20.2.0.175, build 175.1842.

Check the uniqueness of department name

I would like to make a procedure that will help me add a new row in the Departments table. I have to insert department_id, department_name, location_id and manager_id. I made this successfully with that code:
create or replace PROCEDURE add_depar(
p_name VARCHAR2, p_mgr NUMBER, p_loc NUMBER) IS
BEGIN
INSERT INTO DEPARTMENTS (department_id,
department_name, manager_id, location_id)
VALUES (DEPARTMENTS_SEQ.NEXTVAL, p_name, p_mgr, p_loc);
DBMS_OUTPUT.PUT_LINE('Added Dept: '|| p_name);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Err: adding dept: '|| p_name);
END;
Now I'd like to check the uniqueness of the department_name, And if the requirements are not done, I would like to make an exception for that. May I ask for your help! I have tried to do this many times, but still not resolved. Please help!
Thanks
It you want the department name to be unique, then add a constraint/unique index and let the database do the work:
create unique index unq_department_name on department(name);
You are much better off letting the database do the validation, because the database will ensure consistency for both updates and inserts, as well as multiple concurrent transactions.
Simplest most effective way would be to add a unique index on "department.name" (Like mentioned by #Gordon) and then add a DUP_VAL_ON_INDEX exception. Would look like this -
EXCEPTION
WHEN dup_val_on_index THEN
[exception handling];
WHEN others THEN
DBMS_OUTPUT.PUT_LINE('Err: adding dept: '|| p_name);
END;

Using PL/SQL to develop relationship among two tables?

I am new to the PL/SQL language and I am working on a problem and was looking for advice to the right direction to go. Any help would be greatly appreciated!
I have an Employee table and a Building table.
The Employee table consists of Employee_ID, Last_Name, First_Name, and Job(F/P) full or part time.
The Building table consists of Employee_ID and Building_location.(This is the location where the employee works from) either a home or office.
I want the user to type in the last_name from user input. As a result I am looking for the information from the employee: Employee Name, Job (full or part employee) and the building_location. For example, I want my output to look something like this if the user typed in Johnson:
Employee Name Job Position Building Location
====================================================
Andre Johnson Part Time Home Office
Nick Johnson Full Time Downtown Office
If a user types in any string I want to list all Employees who work for the company. But if the name is on the Employee table like 'Johnson' is then it only displays Johnson. Would I need to add in another SELECT clause?
At this point I put together some code that checks if the employee works for the company or not. I am looking to build from this.
ACCEPT p_1 PROMPT 'Please enter the last name of the employee:'
DECLARE
v_name employee.last_name%TYPE := '&p_1';
v_lname employee.last_name%TYPE;
v_fname employee.first_name%TYPE;
v_jobpos employee.job%TYPE;
v_buildtype building.building_type%TYPE;
v_count NUMBER;
BEGIN
SELECT COUNT(*)
INTO v_count
FROM employee
WHERE UPPER(last_name) = UPPER(v_name);
IF v_count = 0 THEN
DBMS_OUTPUT.PUT_LINE('EMPLOYEE NAME Job Position Building Location');
DBMS_OUTPUT.PUT_LINE('----------------------------------------------------' );
for i in (select * from employee order by last_name) loop
DBMS_OUTPUT.PUT_LINE(i.last_name || ', ' || i.first_name)
end loop;
ELSE
for i in (select * from employee order by last_name) loop
DBMS_OUTPUT.PUT_LINE(i.last_name || ', ' || i.first_name);
end loop;
END IF;
END;
You can do this. The simplest way might be like this:
...
IF v_count = 0 THEN
DBMS_OUTPUT.PUT_LINE(v_name || ' Does not work here.');
/* New code starts here! */
DBMS_OUTPUT.PUT_LINE('Employees are:');
for r in (select * from employees) loop
dbms_output.put_line(r.last_name||', '||r.first_name);
end loop;
/* New code ends here! */
ELSE
...
end if;
end;

PL/SQL error question

I am trying to write a stored procedure that inserts a row into an employee table. If the department does not exist, that department needs to be inserted into the departments table. I have the following code:
drop table employees;
drop table departments;
create table departments(
dept varchar2(30),
dept_number number,
dept_city varchar2(30),
CONSTRAINT pk_dept PRIMARY KEY(dept)
);
create table employees(
dept varchar2(30),
employee_name varchar2(40),
employee_id number,
CONSTRAINT pk_id PRIMARY KEY(employee_id),
CONSTRAINT fk_dept FOREIGN KEY (dept) REFERENCES departments(dept)
);
CREATE OR REPLACE PROCEDURE employeeadd(
a_dept IN VARCHAR2,
a_employee_name IN VARCHAR2,
a_employee_id IN NUMBER)
as
li_count NUMBER;
BEGIN
sp_check_dept(a_dept, li_count);
if li_count = 0 then
INSERT INTO departments (dept) values (a_dept);
return;
end if;
INSERT INTO employee values (a_dept, a_employee_name, a_employee_id);
end;
/
create or replace procedure sp_check_dept(a_dept IN NUMBER,
a_count OUT NUMBER)
as
begin
select count(*)
into a_count
from departments
where dept_number = a_dept;
end;
/
When I run my execute statement as execute employeeadd('marketing', 'john', 10); I get the following errors. I can't seem to figure out how to get past the errors and/or write this correctly:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at "employeeadd", line 8
ORA-06512: at line 1
Why is li_count declared outside the BEGIN...END block? Do you need to assign it before sending it as an argument to sp_check_dept()?
Edit: Just saw your followup comment: sp_check_dept is expecting a number as its first parameter; you have declared a_dept as VARCHAR.
sp_check_dept takes a department number an input parameter (a NUMBER) and returns a count as an ouput parameter. employeeadd is passing a department name (a VARCHAR2) as the first parameter to sp_check_dept. There are a couple of ways to fix this. In general, you'll want a more consistent method of naming parameters to make it easier to identify these problems.
Option 1: Use the department name for both functions
create or replace procedure sp_check_dept(p_dept_name IN departments.dept%type,
p_count OUT NUMBER)
as
begin
select count(*)
into p_count
from departments
where dept = p_dept_name;
end;
/
CREATE OR REPLACE PROCEDURE employeeadd(
p_dept_name IN departments.dept%type,
p_employee_name IN employees.employee_name%type,
p_employee_id IN employees.employee_id%type)
as
li_count NUMBER;
BEGIN
sp_check_dept(p_dept_name, li_count);
if li_count = 0 then
INSERT INTO departments (dept)
VALUES (p_dept_name);
end if;
INSERT INTO employee(dept, employee_name, employee_id)
VALUES (p_dept, p_employee_name, p_employee_id);
end;
/
Option 2: Convert the department name in employeeAdd to the department number before passing it to sp_check_dept
create or replace procedure sp_check_dept(p_dept_number IN departments.dept_number%type,
p_count OUT NUMBER)
as
begin
select count(*)
into p_count
from departments
where dept_number = p_dept_number;
end;
/
CREATE OR REPLACE FUNCTION get_dept_number( p_dept_name IN departments.dept%tyep )
RETURN departments.dept_number%type
IS
l_dept_number departments.dept_number%type;
BEGIN
SELECT dept_number
INTO l_dept_number
FROM departments
WHERE dept = p_dept_name;
RETURN l_dept_number
END;
/
CREATE OR REPLACE PROCEDURE employeeadd(
p_dept_name IN departments.dept%type,
p_employee_name IN employees.employee_name%type,
p_employee_id IN employees.employee_id%type)
as
li_count NUMBER;
BEGIN
sp_check_dept( get_dept_number(p_dept_name), li_count);
if li_count = 0 then
INSERT INTO departments (dept)
VALUES (p_dept_name);
end if;
INSERT INTO employee(dept, employee_name, employee_id)
VALUES (p_dept, p_employee_name, p_employee_id);
end;
/
A couple of other observations
I removed the RETURN statement from your IF statement in employeeAdd. You almost certainly do not want to exit the procedure after inserting a row into the DEPARTMENTS table before inserting the row into the EMPLOYEE table.
Your table definition used the plural EMPLOYEES. Your procedure used the singular EMPLOYEE. I did not correct that because I wasn't sure whether the DDL you posted was incorrect or whether the procedure you posted was incorrect.
It would, in general, make far more sense for sp_check_dept to be implemented as a function that returned the count rather than as a procedure with an OUT parameter. If a piece of code simply exists to return data to the caller, it should be declared as a function.
From a data model standpoint, the column name DEPT isn't particularly good. It would be far more appropriate to use something like DEPARTMENT_NAME that conveys what the column actually represents.
From a data model standpoint, having the VARCHAR2 column DEPT (even if it is renamed to DEPARTMENT_NAME) as the primary key of DEPARTMENTS and the foreign key in EMPLOYEES does not make much sense. The primary key should be immutable. However the name of the department will change over time. It would make far more sense for the DEPARTMENT_NUMBER to be the primary key and for the DEPARTMENT_NAME to simply be marked as unique. That will make it far easier when the Marketing department gets renamed Advertising in the future because you won't have to chase down all the child tables to update them.
You should pick a naming convention for procedures and stick with that. I would prefer check_dept and add_employee (verb followed by subject, underscores separating words, no prefix). But if you wanted sp_check_dept and sp_add_employee or checkDept and addEmployee or even sp_dept_check and sp_employee_add that would be fine. But you'll drive yourself, and the other developers, crazy if there is no pattern to your procedure naming conventions.
2 possibilities I can see:
1. the employee table has columns in a different order than your insert statement and it's trying to convert dept or name to the id
2. the value set into li_count isn't a number so it's trying to convert the return value to a number and giving you the error

ORA-00984 column not allowed here

I am getting error
"Execute-984 ORA-00984: column not allowed here"
while I am inserting values in my table Registred_Customer using Pro*C
Registred_Customer is defined as
CREATE TABLE Registred_Customer (
Cust_id NUMBER(6) PRIMARY KEY,
Name VARCHAR2(20) NOT NULL,
Age NUMBER,
Sex CHAR,
Addr VARCHAR2(50),
Contact NUMBER(10)
);
Inserting values using a pro*c method
addCustomer(i, name,age, gender, address,contectNo);
in Pro*C method I use following code to insert
EXEC SQL INSERT INTO REGISTRED_CUSTOMER VALUES
(cust_id, cust_name, age, sex, addr, contact);
here cust_name and addr are char *; and sex is char rest as int;
It reports error while using variable but works fine using direct values
like EXEC SQL INSERT INTO REGISTRED_CUSTOMER VALUES (10, 'Pankaj', 23, 'M', 'asdfs', 45875);
I tried changing few lines but in vain.
Thanks in advance.
Your Pro*C code is basically missing the colons (assuming that your formal parameters are called cust_id, cust_name, age etc.):
EXEC SQL INSERT INTO REGISTRED_CUSTOMER VALUES
(:cust_id, :cust_name, :age, :sex, :addr, :contact);
And it would be more robust to explicitly specify the columns name. Otherwise a change to the table schema can result in difficult to find bugs:
EXEC SQL INSERT INTO REGISTRED_CUSTOMER (Cust_Id, Name, Ag, Sex, Addr, Contact)
VALUES (:cust_id, :cust_name, :age, :sex, :addr, :contact);
If im seeing correct you are trying to insert into the columns, the columns??
"EXEC SQL INSERT INTO REGISTRED_CUSTOMER VALUES (cust_id, cust_name, age, sex, addr, contact);"??
it would be more helpful if you post your procedure complete.
Regards
As Mr. mentioned, you are trying to use the columns as input values. When you provide actual values it works. Are you perhaps meaning to use PL/SQL variables or the procedure arguments? In this case, whatever your procedure parameters are called is what you should put in the values section.
i.e if addCustomer looks like
PROCEDURE addCustomer (pId NUMBER, pName VARCHAR2, pAge NUMBER, pGender CHAR, pAddress VARCHAR2, pContact NUMBER)
Then you'd do something like
INSERT INTO registered_customer (cust_id, name, age, sex, addr, contact) VALUES (pId, pName, pAge, pGender, pAddress, pContact);
But if you are inserting into all columns you can leave out the column definition and just provide values
I also got this error message in a stored procedure doing an insert. I misspelled a parameter name in the values clause and the oracle interpreter saw the misspelled name as a column name and issued the 00984.