ORACLE table valued function - sql

I'm in the process of migrating from MS SQL Server to Oracle and I'm struggling with a simple table-valued function:
CREATE TABLE Department (
Id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY,
Name VARCHAR(64) NOT NULL,
PRIMARY KEY (Id)
);
/
CREATE TABLE Employee (
Id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY,
PRIMARY KEY (Id),
DepartmentId INT NOT NULL REFERENCES Department(Id),
FirstName VARCHAR(64) NOT NULL,
LastName VARCHAR(64) NOT NULL,
Email VARCHAR(64) NULL
);
/
CREATE TYPE AType AS OBJECT (Department VARCHAR(64), Employee VARCHAR(64));
/
CREATE TYPE ATypeCol AS TABLE OF AType;
/
CREATE OR REPLACE FUNCTION fEmployee(dep IN VARCHAR(64))
RETURN ATypeCol PIPELINED IS
BEGIN
FOR i IN (SELECT Department.Name, Employee.FirstName || ' ' || Employee.LastName
FROM Employee JOIN Department ON DepartmentId = Department.Id
WHERE Department.Name = dep) LOOP
PIPE ROW(AType(i.Department, i.Employee));
END LOOP;
RETURN;
END;
/
SELECT * FROM TABLE(fEmployee('IT'));
/
However it fails with
FUNCTION FEMPLOYEE compiled
Errors: check compiler log
Error starting at line 94 in command:
SELECT * FROM TABLE(fEmployee('IT'))
Error at Command Line:94 Column:21
Error report:
SQL Error: ORA-06575: Package or function FEMPLOYEE is in an invalid state
06575. 00000 - "Package or function %s is in an invalid state"
*Cause: A SQL statement references a PL/SQL function that is in an
invalid state. Oracle attempted to compile the function, but
detected errors.
*Action: Check the SQL statement and the PL/SQL function for syntax
errors or incorrectly assigned, or missing, privileges for a
referenced object.
Any help is appreciated.

CREATE OR REPLACE FUNCTION fEmployee (
dep IN VARCHAR
)
RETURN ATypeCol PIPELINED IS
BEGIN
FOR i IN (SELECT Department.Name, Employee.FirstName || ' ' || Employee.LastName Employee
FROM Employee JOIN Department ON DepartmentId = Department.Id
WHERE Department.Name = dep) LOOP
PIPE ROW(AType(i.Name, i.Employee));
END LOOP;
RETURN;
END;
Formal parameter must not have scale/precision. Also, you should specify correct aliases in cursor.
Please use SELECT * FROM USER_ERRORS WHERE NAME = ''
or "show error" SQL*Plus command to get the information about compilliation errors.

Related

SQL Stored Procedure Invalid Identifier

I'm getting ORA-00904 Invalid Identifier for several variables in this stored procedure when trying to compile it in oracle but cannot figure out why; All working storage events are declared at the DECLARE block of the code, and the variables from the schema are all properly defined too.
Has anyone had this error and/or knows how to fix it?
This is the DDL for the schema im working with
CREATE TABLE history
("history_id" number(4) primary key,
"history_dt" date not null,
"history_time" timestamp not null,
"booking_cost" varchar2(50) not null,
"booking_status" varchar2(50) not null
);
CREATE TABLE attendees
("attendee_id" number(8) primary key,
"attendee_name" varchar2(50) not null,
"attendee_class" number(4) not null,
"attendee_school" varchar2(50) not null,
"attendee_status" varchar2(50) not null
);
CREATE TABLE event
("event_id" number(10) primary key,
"event_name" varchar2(100) not null,
"event_location" varchar2(100) not null,
"event_size" number(4) not null,
"start_dt" date not null,
"end_dt" date not null,
"class_restriction" number(4) not null,
"school_restriction" varchar2(100) not null
);
CREATE TABLE reservation
("reservation_id" number(3) primary key,
"event" number(10) references event("event_id"),
"attendee" number(8) references attendees("attendee_id"),
"booking" number(4) references history("history_id"),
"reservation_status" varchar2(50) not null
);
These are the error messages I'm getting
Compilation failed,line 19 (15:38:10)
PL/SQL: ORA-00904: "END_DT": invalid identifierCompilation failed,line 18 (15:38:10)
PL/SQL: SQL Statement ignoredCompilation failed,line 26 (15:38:10)
PLS-00302: component 'EVENT_ID' must be declaredCompilation failed,line 26 (15:38:10)
PL/SQL: ORA-00904: "RESERVATION"."EVENT_ID": invalid identifierCompilation failed,line 26 (15:38:10)
PL/SQL: SQL Statement ignoredCompilation failed,line 36 (15:38:10)
PL/SQL: ORA-00904: "EVENT_ID": invalid identifierCompilation failed,line 35 (15:38:10)
PL/SQL: Statement ignoredCompilation failed,line 51 (15:38:10)
PL/SQL: ORA-00947: not enough valuesCompilation failed,line 51 (15:38:10)
create or replace procedure Event_Planning
(arg_event_id in number, arg_student_id in number)
IS
ws_event_name varchar(100);
ws_capacity number;
ws_event_school varchar2(4);
ws_event_class number;
past_event exception;
capacity exception;
school exception;
class exception;
BEGIN
--Test for active event
select max(event_name) into ws_event_name from event
where event_id = arg_event_id and end_dt > SYSDATE;
if ws_event_name is null
then raise past_event;
end if;
--Test for capacity
select max(event_capacity) into ws_capacity from event JOIN reservation ON event.event_id = reservation.event_id
where event_id = arg_event
and event_capacity > reservation_size;
if ws_capacity is null
then raise capacity;
end if;
--Test for restricted school
select max(school_restriction) into ws_event_school from event
where event_id = arg_event_id;
if ws_event_school = arg_school
then raise school;
end if;
--Test for restricted class
select max(class_restriction) into ws_event_class from event
where event.id = arg_event;
if ws_event_class = arg_class
then raise class;
end if;
--Update reservation table
insert into reservation values
(Seq.nextval, arg_event, arg_student);
update reservation
set reservation_size = reservation_size + 1;
--Exceptions
Exception
when past_event
then raise_application_error(-20001, 'Event has passed');
when capacity
then raise_application_error(-20002, 'Event at capacity');
when school
then raise_application_error(-20003, 'Invalid school');
when class
then raise_application_error(-20004, 'Invalid class');
END;
To make it more likely to have your questions answered, share everything.
That includes, the actual error messages. And if you REALLY want to be nice, include the TABLE DDL (and even some data) for your EVENT and RESERVATION tables.
I was too 'lazy' to guess what yours looked like, and just changed your queries to dummy tables to replicate the issue.
You haven't declared
ws_school
arg_school
ws_class
arg_class
When you compile, the compiler will return the line number and curpos of your issue. You're referring to things the db doesn't know about.
Recommendations
Don't hard code the data type definitions for your variables. Because, tables CAN and WILL change.
Instead, make them dynamic.
So, instead of saying
WS_SCHOOL VARCHAR2(4);
do something like
WS_SCHOOL TABLE.COLUMN%TYPE;
Then when your tables change, your code won't necessarily break.
By default, Oracle identifiers such as table and column names are case-insensitive, so for example you can
select dummy, DUMMY, Dummy
from dual;
However, double-quoting is also available to allow you to override the standard rules if you really need to:
create table demo("123/Wow!" number);
SQL> desc demo
Name Null? Type
----------------------------------------- -------- ----------------------------
123/Wow! NUMBER
Your tables history, attendees, event etc have case-sensitive, lowercase column names due to the double quoting, so you have to follow the same convention whenever you use them:
SQL> select count(event_id) from event;
select count(event_id) from event
*
ERROR at line 1:
ORA-00904: "EVENT_ID": invalid identifier
SQL> select count("event_id") from event;
COUNT("EVENT_ID")
-----------------
0
1 row selected.
Unless there is some important reason to do this, it would be simplest to recreate the tables without double-quoted column names.
(Also, reservation has an "event" column, not "event_id". There may be other typos like this - I haven't checked the whole thing.)
That doesn't need to use end_date on filters, because event_id is a primary key.
select "event_name" into ws_event_name
from event
where "event_id" = arg_event_id;
That looks so confuse to know, where the following columns come from: ---=^^^=---
-- event_capacity
-- reservation_size
select max("event_size") into ws_capacity
from event
join reservation
on event."event_id" = reservation."event"
where "event_id" = arg_event_id
and "event_size" > count("reservation_id");
Syntax error for event.id, it must event_id
select "class_restriction" into ws_event_class
from event
where "event_id" = arg_event_id;
Insert into reservation table:
select count(*) into reserve_count
from reservation
where "event" = arg_event_id
and "attendee" = arg_studen_id;
if reserve_count = 0
then
insert into reservation values
(Seq.nextval, arg_event_id, arg_student_id, null, "R");
end if;
--- note: that needs to declare reserve_count
Use count() to populate attendees, then no need to update reservation table.
-- update reservation
-- set reservation_size = reservation_size + 1;
reservation table:
CREATE TABLE reservation
("reservation_id" number(13) primary key,
"event" number(10) references event("event_id"),
"attendee" number(8) references attendees("attendee_id"),
"booking" number(10) references history("history_id"),
"reservation_status" varchar(1) not null
);
To merge as a single query:
select count(*), "event_name", "class_restriction"
into event_count, ws_event_name, ws_event_class
from event
where "event_id" = arg_event_id;
-- note: that needs to declare event_count

SQL: Can't create function returning table. "Table is not valid at this position, expecting: bit, bool, boolean, ...."

I can't figure out what's wrong with this SQL function... in "returns table" it complains that "Table is not valid at this position, expecting: bit, bool, boolean, ....". Does anyone know?
create table department(
dept_name varchar(20),
n_prof int,
budget numeric(10,2),
primary key (dept_name)
);
create table instructor(
ID char(5),
name varchar(20),
dept_name varchar(20),
salary numeric(8,2),
primary key (ID),
foreign key (dept_name) references department (dept_name)
);
create function instructors (dept_name char(20))
returns TABLE
as
return
select ID, name, dept_name, salary
from instructor
where instructor.dept_name=dept_name;
Based on error, I guess you are using MYSQL database.
As per documentation on user defined functions in MySQL
you can only return values of type {STRING|INTEGER|REAL|DECIMAL}
CREATE [AGGREGATE] FUNCTION function_name RETURNS {STRING|INTEGER|REAL|DECIMAL}
SONAME shared_library_name
If you want to read a select resultset you have to define a procedure but not function.
DELIMITER //
DROP PROCEDURE IF EXISTS myProcedure //
CREATE PROCEDURE
myProcedure( dept_name char(20) )
BEGIN
select ID, name, dept_name, salary
from instructor
where instructor.dept_name=dept_name;
;
END
//
DELIMITER ;
And you can call procedure like
call myProcedure( 'CSE' )
That returns implicit objects based on the statements used in the procedure.
Please use below query.
USE Your_Database
create table dbo.department(
dept_name varchar(20),
n_prof int,
budget numeric(10,2),
primary key (dept_name)
);
GO
create table dbo.instructor(
ID char(5),
name varchar(20),
dept_name varchar(20),
salary numeric(8,2),
primary key (ID),
foreign key (dept_name) references department (dept_name)
);
GO
create function dbo.instructors (#dept_name char(20))
returns TABLE
as
return
select ID, name, dept_name, salary
from dbo.instructor
where instructor.dept_name=#dept_name;
You have used dept_name which is already existing in your table column so use #dept_name instead of that.
Please use GO statement to separate the each transaction otherwise you will get error.
Msg 111, Level 15, State 1, Line 19 'CREATE FUNCTION' must be the
first statement in a query batch. Msg 137, Level 15, State 2, Line 25
Must declare the scalar variable "#dept_name".

Is it possible to create a cross relationship constraint in postgresql? [duplicate]

I would like to add a constraint that will check values from related table.
I have 3 tables:
CREATE TABLE somethink_usr_rel (
user_id BIGINT NOT NULL,
stomethink_id BIGINT NOT NULL
);
CREATE TABLE usr (
id BIGINT NOT NULL,
role_id BIGINT NOT NULL
);
CREATE TABLE role (
id BIGINT NOT NULL,
type BIGINT NOT NULL
);
(If you want me to put constraint with FK let me know.)
I want to add a constraint to somethink_usr_rel that checks type in role ("two tables away"), e.g.:
ALTER TABLE somethink_usr_rel
ADD CONSTRAINT CH_sm_usr_type_check
CHECK (usr.role.type = 'SOME_ENUM');
I tried to do this with JOINs but didn't succeed. Any idea how to achieve it?
CHECK constraints cannot currently reference other tables. The manual:
Currently, CHECK expressions cannot contain subqueries nor refer to
variables other than columns of the current row.
One way is to use a trigger like demonstrated by #Wolph.
A clean solution without triggers: add redundant columns and include them in FOREIGN KEY constraints, which are the first choice to enforce referential integrity. Related answer on dba.SE with detailed instructions:
Enforcing constraints “two tables away”
Another option would be to "fake" an IMMUTABLE function doing the check and use that in a CHECK constraint. Postgres will allow this, but be aware of possible caveats. Best make that a NOT VALID constraint. See:
Disable all constraints and table checks while restoring a dump
A CHECK constraint is not an option if you need joins. You can create a trigger which raises an error instead.
Have a look at this example: http://www.postgresql.org/docs/9.1/static/plpgsql-trigger.html#PLPGSQL-TRIGGER-EXAMPLE
CREATE TABLE emp (
empname text,
salary integer,
last_date timestamp,
last_user text
);
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Check that empname and salary are given
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname cannot be null';
END IF;
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% cannot have null salary', NEW.empname;
END IF;
-- Who works for us when she must pay for it?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
END IF;
-- Remember who changed the payroll when
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;
CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
FOR EACH ROW EXECUTE PROCEDURE emp_stamp();
...i did it so (nazwa=user name, firma = company name) :
CREATE TABLE users
(
id bigserial CONSTRAINT firstkey PRIMARY KEY,
nazwa character varying(20),
firma character varying(50)
);
CREATE TABLE test
(
id bigserial CONSTRAINT firstkey PRIMARY KEY,
firma character varying(50),
towar character varying(20),
nazwisko character varying(20)
);
ALTER TABLE public.test ENABLE ROW LEVEL SECURITY;
CREATE OR REPLACE FUNCTION whoIAM3() RETURNS varchar(50) as $$
declare
result varchar(50);
BEGIN
select into result users.firma from users where users.nazwa = current_user;
return result;
END;
$$ LANGUAGE plpgsql;
CREATE POLICY user_policy ON public.test
USING (firma = whoIAM3());
CREATE FUNCTION test_trigger_function()
RETURNS trigger AS $$
BEGIN
NEW.firma:=whoIam3();
return NEW;
END
$$ LANGUAGE 'plpgsql'
CREATE TRIGGER test_trigger_insert BEFORE INSERT ON test FOR EACH ROW EXECUTE PROCEDURE test_trigger_function();

Why is my PL/SQL trigger raising an "Error(2,2): PL/SQL: Statement ignored" and an "Error(2,5): PLS-00204: function or pseudo-column 'EXISTS' " error?

I am attempting to write a trigger which prevents the insertion into my TA (teaching assistant) table if the student is already taking the class for which they're attempting to become a TA. From what I understand the best way to do this (other than a unique constraint, this has to be a trigger) is through the use of rollback, and I'm also using raiserror.
create or replace trigger CheckTA
after insert
on TA for each row
begin
if exists (select * from Enrolls where Student_ID = :new.Student_ID and Course_ID = :new.Course_ID) then
rollback;
raiserror('TA enrolled in course as student');
end if;
end;
Upon creating the trigger, I'm met with the following errors:
Error(2,2): PL/SQL: Statement ignored
Error(2,5): PLS-00204: function or pseudo-column 'EXISTS' may be used inside a SQL statement only
The trigger still gets created, but it doesn't trigger when improper values are inserted into TA.
Tables:
TA
Create table TA
(
Student_ID int references Student(Student_ID),
Course_ID int references Course(Course_ID),
Courses_taught varchar2(250),
Office_hours varchar2(25)
);
Student
create table Student
(
Student_ID int primary key,
Name varchar2(50),
Start_began int,
Semester_began varchar2(50),
GPA int,
Degree_status varchar2(25),
Degree_type varchar2(50),
Courses_Taken varchar2(250),
JI_list varchar2(250),
Profile_status varchar2(50)
);
Course
create table Course
(
Course_ID int primary key,
Course_Name varchar2(25)
);
Enrolls
create table Enrolls
(
Student_ID int references Student(Student_ID),
Course_ID int references Course(Course_ID)
);
Stored Procedure
This is the SP I use to insert TA values that should trigger the trigger. It doesn't raise errors but I'll put it here in case it's relevant.
create or replace procedure addTA (s_id int, c_id int, course varchar2)
as
begin
insert into TA (Student_ID,Course_ID,Courses_taught)
values (s_id,c_id,course);
end;
Honestly I'm not quite sure what these errors mean, let alone what's causing them, but still I've tried messing with the syntax of raiserror as well as replacing it with raise_application_error with no luck. If anyone could help me resolve or at least explain these errors it would be greatly appreciated. And of course if there is anything obviously wrong with my trigger please let me know.
The error says "function or pseudo-column 'EXISTS' may be used inside a SQL statement only", meaning that you only can use EXISTS inside a SQL statment, while you are using in in a PL/SQL statement.
If you need to check for the existence of some records, you can try something like this:
vNum number;
...
select count(1)
into vNumber
from Enrolls
where Student_ID = ...
if vNumber > 0 then
...
else
...
end if;

Postgres error updating column data

Trying to run a update script on a table, but getting an error:
ERROR: column "ok" does not exist
LINE 2: SET first_name="ok", last_name="pk", email="ooo", phone="...
CREATE TABLE employee (
employee_id SERIAL PRIMARY KEY,
first_name varchar(255) NOT NULL,
last_name varchar(255) NOT NULL,
email varchar(255) NOT NULL,
phone varchar(255)
);
INSERT INTO employee(
first_name, last_name, email, phone)
VALUES ('Kyle', 'Belanger', 'kbelanger#ok.com', '(240) 298-4664');
UPDATE "employee"
SET first_name="ok", last_name="pk", email="ooo", phone="000"
WHERE employee_id = 1;
There is no need to wrap table name in double quote "employee", and use single quotes for column values
UPDATE employee
SET first_name='ok', last_name='pk', email='ooo', phone='000'
WHERE employee_id = 1;
See Working Example
Try below sql:
UPDATE employee
SET first_name='ok', last_name='pk', email='ooo', phone='000'
WHERE employee_id = 1;
Table name was wrapped in double quotes which is not allowed.