Datatype Mismatch on New Columns - sql

Does anyone know why my code is giving this error even though my return types should match?
The only thing I can think of is that the datatype of reserved_seats and cancelled_seats are not of type INTEGER despite NumSeats column being of type INTEGER (where it gets the data).
If anyone can give any advice I would appreciate it.
The error that occurs is:
ERROR: "return type mismatch in function declared to return record"
My create statement:
CREATE OR REPLACE FUNCTION seatInformation(INTEGER) RETURNS TABLE(flightID INTEGER, reserved_seats
INTEGER, cancelled_seats INTEGER, available_Seats INTEGER, MaxCapacity bigint) AS
$$
SELECT f.flightID, (SELECT reserved_seats FROM (SELECT fb.NumSeats WHERE fb.Status = 'r') AS reserved_seats),
(SELECT cancelled_seats FROM(SELECT fb.NumSeats WHERE fb.Status = 'c') AS cancelled_seats), MaxCapacity - SUM(fb.NumSeats) AS Available_Seats, f.MaxCapacity
FROM Flight f INNER JOIN FlightBooking fb
ON f.flightID = fb.flightID
WHERE f.flightID = $1
GROUP BY f.flightID, fb.NumSeats, fb.Status;
$$
LANGUAGE SQL;
CREATE TABLE Flight
(
FlightID INTEGER NOT NULL,
FlightDate TIMESTAMP NOT NULL,
Origin VARCHAR(30) NOT NULL,
Destination VARCHAR(30) NOT NULL,
MaxCapacity INTEGER NOT NULL,
PricePerSeat DECIMAL NOT NULL,
CONSTRAINT Flight_pk PRIMARY KEY(FlightID)
);
CREATE TABLE FlightBooking
(
BookingID INTEGER NOT NULL,
CustomerID INTEGER NOT NULL CONSTRAINT FlightBooking_fkc REFERENCES LeadCustomer(CustomerID) ON DELETE CASCADE,
FlightID INTEGER NOT NULL CONSTRAINT FlightBooking_fkf REFERENCES Flight(FlightID) ON DELETE CASCADE,
NumSeats INTEGER NOT NULL,
Status CHAR(1) NOT NULL,
BookingTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
TotalCost DECIMAL,
CONSTRAINT FlightBooking_pk PRIMARY KEY(BookingID)
);
Many thanks,
Callum

Consider this:
(SELECT reserved_seats FROM (SELECT fb.NumSeats WHERE fb.Status = 'r') AS reserved_seats)
You may think it is integer, well, it's not. It selects a set.
This is what you may want instead:
(SELECT fb.NumSeats as reserved_seats WHERE fb.Status = 'r' LIMIT 1)
The same for cancelled_seats

Related

Creating stored procedure to insert new rows into a table

I am trying to create a stored procedure which fulfill the following question,
Write a stored procedure called event_registration which handles the participant's entry for an event. When a participant registers for an event, they may also choose to create or join a group.
Your procedure must take four input parameters:
participant number
exhibition date
event type description
group name.
The procedure must:
check if the input exhibition date and event name are valid
check if the participants wants to create/join a group (ie. they provide a group name) and, if provided, whether the group name exists for the input exhibition:
If the group does not exist then the procedure should add a new group and assign this participant as the group leader, or
If the group exists then the procedure should add the participant into the existing group
So the below are my tables:
CREATE TABLE group
(
group_id NUMBER(3) NOT NULL,
group_name VARCHAR2(30) NOT NULL,
exhibition_date DATE NOT NULL,
group_no_members NUMBER(2) NOT NULL,
event_id NUMBER(6) NOT NULL,
entry_no NUMBER(5) NOT NULL,
char_id NUMBER(3)
);
CREATE TABLE event
(
event_id NUMBER(6) NOT NULL,
exhibition_date DATE NOT NULL,
eventtype_code CHAR(3) NOT NULL,
event_starttime DATE NOT NULL
);
CREATE TABLE eventtype
(
eventtype_code CHAR(3) NOT NULL,
eventtype_desc VARCHAR2(50) NOT NULL
);
CREATE TABLE exhibition
(
exhibition_date DATE NOT NULL,
exhibition_name VARCHAR2(50) NOT NULL,
exhibition_director VARCHAR2(50) NOT NULL,
exhibition_location VARCHAR2(50) NOT NULL
);
CREATE TABLE entry
(
event_id NUMBER(6) NOT NULL,
entry_no NUMBER(5) NOT NULL,
entry_starttime DATE,
entry_finishtime DATE,
part_no NUMBER(5) NOT NULL,
group_id NUMBER(3),
char_id NUMBER(3)
);
And this is my procedure:
CREATE OR REPLACE PROCEDURE event_registration
(new_part_no IN NUMBER,
new_exhibition_date IN DATE,
new_eventtype_desc IN VARCHAR2,
new_group_name IN VARCHAR2,
output OUT VARCHAR2)
AS
exhibition_date_and_event_found NUMBER; group_found NUMBER;
BEGIN
SELECT COUNT(*)
INTO exhibition_date_and_event_found
FROM event
NATURAL JOIN eventtype
WHERE exhibition_date = new_exhibition_date
AND eventtype_desc = new_eventtype_desc;
IF (exhibition_date_and_event_found = 0) THEN
output := 'Invalid exhibition date/This exhibition does not have this event type';
ELSE
IF(new_group_name != NULL) THEN
SELECT COUNT(*) INTO group_found FROM group NATURAL JOIN exhibition WHERE group_name = new_group_name AND exhibition_date = new_exhibition_date;
IF(group_found = 0) THEN
INSERT INTO group VALUES ((SELECT COUNT(*)+1 FROM group), new_group_name, TO_DATE(new_exhibition_date, 'DD/MM/YYYY'), 1, (SELECT event_id FROM event NATURAL JOIN eventtype WHERE eventtype_desc = new_eventtype_desc AND exhibition_date = TO_DATE(new_exhibition_date,'DD/MM/YYYY')), (SELECT MAX(entry_no)+1 FROM entry WHERE event_id = (SELECT event_id FROM event NATURAL JOIN eventtype WHERE eventtype_desc = new_eventtype_desc AND exhibition_date = TO_DATE(new_exhibition_date, 'DD/MM/YYYY'))), NULL);
INSERT INTO entry VALUES ((SELECT event_id FROM event NATURAL JOIN eventtype WHERE eventtype_desc = new_eventtype_desc AND exhibition_date = TO_DATE(new_exhibition_date, 'DD/MM/YYYY')), (SELECT MAX(entry_no)+1 FROM entry WHERE event_id = (SELECT event_id FROM event NATURAL JOIN eventtype WHERE eventtype_desc = new_eventtype_desc AND exhibition_date = TO_DATE(new_exhibition_date, 'DD/MM/YYYY'))), NULL, NULL, new_part_no, (SELECT group_id FROM group WHERE group_name = new_group_name), NULL);
ELSE --group already exist
--update the group member number by 1
UPDATE group
SET group_no_members = group_no_members + 1
WHERE group_name = new_group_name;
INSERT INTO entry VALUES ((SELECT event_id FROM event NATURAL JOIN eventtype WHERE eventtype_desc = new_eventtype_desc AND exhibition_date = TO_DATE(new_exhibition_date, 'DD/MM/YYYY')), (SELECT MAX(entry_no)+1 FROM entry WHERE event_id = (SELECT event_id FROM event NATURAL JOIN eventtype WHERE eventtype_desc = new_eventtype_desc AND exhibition_date = TO_DATE(new_exhibition_date, 'DD/MM/YYYY'))), NULL, NULL, new_part_no, (SELECT group_id FROM group WHERE group_name = new_group_name), NULL);
END IF;
ELSE
INSERT INTO entry VALUES ((SELECT event_id FROM event NATURAL JOIN eventtype WHERE eventtype_desc = new_eventtype_desc AND exhibition_date = TO_DATE(new_exhibition_date, 'DD/MM/YYYY')), (SELECT MAX(entry_no)+1 FROM entry WHERE event_id = (SELECT event_id FROM event NATURAL JOIN eventtype WHERE eventtype_desc = new_eventtype_desc AND exhibition_date = TO_DATE(new_exhibition_date, 'DD/MM/YYYY'))), NULL, NULL, new_part_no, NULL, NULL);
END IF;
END IF;
END;
/
The procedure is not done yet but I think it is good enough to be compiled, but when I run it, it gives out errors like
Procedure EVENT_REGISTRATION compiled
LINE/COL ERROR
--------- -------------------------------------------------------------
12/9 PL/SQL: SQL Statement ignored
12/21 PL/SQL: ORA-00947: not enough values
18/9 PL/SQL: SQL Statement ignored
18/21 PL/SQL: ORA-00947: not enough values
21/9 PL/SQL: SQL Statement ignored
21/21 PL/SQL: ORA-00947: not enough values
Errors: check compiler log
Can anybody help me with this? Thank you in advance!
Use constraints on your tables (primary keys, foreign keys, etc.).
Use sequences for primary key values (NEVER use SELECT COUNT(*) + 1 FROM ... to generate ids as you are likely to get duplicates on a parallel system).
If you use constraints then you don't need to check that the exhibition date and event (exhibition?) name are correct as, if they are not, the constraint will raise an exception when you try to against a non-existent relationship.
NEVER use INSERT INTO table_name VALUES (...), you should always explicitly name the columns you are inserting into using:
INSERT INTO table_name (col1, col2, col3) VALUES (value1, value2, value3);
Do not keep using the same query over-and-over to find the event_id. Find it once and store it in a PL/SQL variable and then use the PL/SQL variable the next time.
GROUP is a reserved word; do not use it as an identifier.
You cannot compare a value to NULL using value != NULL as NULL is never equal or not-equal to anything else; you need to use value IS NOT NULL.
Make sure you do not have cyclic referential constraints in your tables (i.e. GROUP has an entry_no which refers to the entry table and the entry table has a group_id column that refers to the GROUP table. You do not need both and when you put constraints in then you will get a cycle.)
Something like:
CREATE TABLE exhibition
(
exhibition_date DATE
PRIMARY KEY
NOT NULL,
exhibition_name VARCHAR2(50)
NOT NULL,
exhibition_director VARCHAR2(50)
NOT NULL,
exhibition_location VARCHAR2(50)
NOT NULL
);
CREATE TABLE eventtype
(
eventtype_code CHAR(3)
PRIMARY KEY
NOT NULL,
eventtype_desc VARCHAR2(50)
NOT NULL
);
CREATE TABLE event
(
event_id NUMBER(6)
NOT NULL
PRIMARY KEY,
exhibition_date REFERENCES exhibition(exhibition_date)
NOT NULL,
eventtype_code REFERENCES eventtype(eventtype_code)
NOT NULL,
event_starttime DATE
NOT NULL
);
CREATE TABLE "GROUP"
(
group_id NUMBER(3)
NOT NULL
PRIMARY KEY,
group_name VARCHAR2(30)
UNIQUE
NOT NULL,
group_no_members NUMBER(2)
NOT NULL,
event_id REFERENCES event(event_id)
NOT NULL,
char_id NUMBER(3)
);
CREATE TABLE entry
(
event_id REFERENCES event(event_id)
NOT NULL,
entry_no NUMBER(5)
PRIMARY KEY
NOT NULL,
entry_starttime DATE,
entry_finishtime DATE,
part_no NUMBER(5)
NOT NULL,
group_id NUMBER(3)
REFERENCES "GROUP"(group_id),
char_id NUMBER(3)
);
Then:
CREATE SEQUENCE group_seq;
CREATE SEQUENCE entry_seq;
Then:
CREATE OR REPLACE PROCEDURE event_registration
(new_part_no IN NUMBER,
new_exhibition_date IN DATE,
new_eventtype_desc IN VARCHAR2,
new_group_name IN VARCHAR2,
output OUT VARCHAR2)
AS
v_event_id EVENT.EVENT_ID%TYPE;
v_group_id "GROUP".GROUP_ID%TYPE;
BEGIN
SELECT e.event_id
INTO v_event_id
FROM event e
INNER JOIN eventtype et
ON (e.eventtype_code = et.eventtype_code)
WHERE exhibition_date = new_exhibition_date
AND eventtype_desc = new_eventtype_desc;
IF new_group_name IS NOT NULL THEN
MERGE INTO "GROUP" g
USING DUAL
ON (g.group_name = new_group_name)
WHEN MATCHED THEN
UPDATE
SET group_no_members = group_no_members + 1
WHEN NOT MATCHED THEN
INSERT (
group_id,
group_name,
group_no_members,
event_id,
char_id
) VALUES (
GROUP_SEQ.NEXTVAL,
new_group_name,
1,
v_event_id,
NULL
);
SELECT group_id
INTO v_group_id
FROM "GROUP"
WHERE group_name = new_group_name;
END IF;
INSERT INTO entry (
event_id,
entry_no,
entry_starttime,
entry_finishtime,
part_no,
group_id,
char_id
)VALUES (
v_event_id,
entry_seq.NEXTVAL,
NULL,
NULL,
new_part_no,
v_group_id,
NULL
);
EXCEPTION
WHEN NO_DATA_FOUND THEN
output := 'Invalid exhibition date/This exhibition does not have this event type';
END;
/
db<>fiddle here

Argument of AND must not return a set when used with BETWEEN in a check constraint

I am encountering the error "argument of AND must not return a set" from the AND in the check constraint of the below table.
CREATE TABLE loan (
id SERIAL PRIMARY KEY,
copy_id INTEGER REFERENCES media_copies (copy_id),
account_id INT REFERENCES account (id),
loan_date DATE NOT NULL,
expiry_date DATE NOT NULL,
return_date DATE,
CONSTRAINT max_student_concurrent_loans CHECK(
CurrentStudentLoansCount() BETWEEN 1 AND 7
)
);
The implementation of CurrentStudentLoansCount() is shown below.
CREATE OR REPLACE FUNCTION CurrentStudentLoansCount()
RETURNS TABLE(accid BIGINT) AS $$
BEGIN
RETURN QUERY
SELECT COUNT(*)
FROM loan
WHERE account_id IN (SELECT id FROM student)
AND return_date IS NULL
GROUP BY account_id;
END
$$ LANGUAGE PLPGSQL;
Why am I running into this error and how can I work around it?
For context, the below figure displays my database schema.
Your function returns a table with multiple rows so you can't use that for a BETWEEN condition. Presumably you just want that value for the account_id of that row of the table (not for all account_ids).
So change the function to return a single value by passing it the account_id. And you also don't need PL/pgSQL for this:
CREATE OR REPLACE FUNCTION currentstudentloanscount(p_account_id integer)
RETURNS bigint
as
$$
SELECT COUNT(*)
FROM loan
WHERE account_id = p_account_id
AND return_date IS NULL;
$$ LANGUAGE sql;
And change your table definition to:
CREATE TABLE loan (
id SERIAL PRIMARY KEY,
copy_id INTEGER REFERENCES media_copies (copy_id),
account_id INT REFERENCES account (id),
loan_date DATE NOT NULL,
expiry_date DATE NOT NULL,
return_date DATE,
CONSTRAINT max_student_concurrent_loans
CHECK(currentstudentloanscount(account_id) BETWEEN 1 AND 7)
);

PostgreSQL trigger error: 'column "t" of relation "inventory_product" does not exist'

I am trying to create a trigger that decrements the qty column on the inventory_product table based on the qty that was inserted into account_sale. The trigger works fine with MySQL (with different syntax), but I'm not sure whats wrong with the PostgreSQL version.
When I run an insert on inventory_sale I get:
error: column "t" of relation "inventory_product" does not exist
the trigger:
CREATE OR REPLACE FUNCTION update_inventory()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.product_id IS NOT NULL THEN
UPDATE inventory_product AS t
SET t.qty = t.qty - NEW.qty #error is thrown here
WHERE t.id = NEW.product_id;
END IF;
END; $$ LANGUAGE 'plpgsql';
CREATE TRIGGER after_insert_account_sale
AFTER INSERT ON account_sale
FOR EACH ROW
EXECUTE PROCEDURE update_inventory();
inventory_product:
CREATE TABLE public.inventory_product
(
id integer NOT NULL DEFAULT nextval('inventory_product_id_seq'::regclass),
upc character varying(45) COLLATE pg_catalog."default",
sku character varying(45) COLLATE pg_catalog."default",
asin character varying(45) COLLATE pg_catalog."default",
ebay_sku character varying(45) COLLATE pg_catalog."default",
tcgplayer_sku integer,
qty integer NOT NULL,
opt_qty integer NOT NULL,
reserve integer NOT NULL,
sell_price numeric(10,2) NOT NULL,
buy_price numeric(10,2) NOT NULL,
product_weight_g numeric(12,5) NOT NULL,
dim_x_cm numeric(12,5) NOT NULL,
dim_y_cm numeric(12,5) NOT NULL,
dim_z_cm numeric(12,5) NOT NULL,
stock_image_path character varying(75) COLLATE pg_catalog."default",
CONSTRAINT inventory_product_pkey PRIMARY KEY (id),
CONSTRAINT inventory_product_asin_key UNIQUE (asin)
,
CONSTRAINT inventory_product_ebay_sku_key UNIQUE (ebay_sku)
,
CONSTRAINT inventory_product_stock_image_path_key UNIQUE (stock_image_path)
,
CONSTRAINT inventory_product_tcgplayer_sku_key UNIQUE (tcgplayer_sku)
)
account_sale:
CREATE TABLE public.account_sale
(
id integer NOT NULL DEFAULT nextval('account_sale_id_seq'::regclass),
unit_price numeric(10,2) NOT NULL,
qty integer NOT NULL,
order_id integer NOT NULL,
product_id integer,
CONSTRAINT account_sale_pkey PRIMARY KEY (id),
CONSTRAINT account_sale_order_id_product_id_8c7f2e6a_uniq UNIQUE (order_id, product_id)
,
CONSTRAINT account_sale_order_id_7724b965_fk_account_order_id FOREIGN KEY (order_id)
REFERENCES public.account_order (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
DEFERRABLE INITIALLY DEFERRED,
CONSTRAINT account_sale_product_id_716f2cb2_fk_inventory_product_id FOREIGN KEY (product_id)
REFERENCES public.inventory_product (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
DEFERRABLE INITIALLY DEFERRED
)
insert:
INSERT INTO account_sale (qty, unit_price, order_id, product_id)
SELECT $1::integer,$2::float,$3::integer,t.id FROM inventory_product
AS t WHERE t.ebay_sku=$4
UNION
SELECT $5::integer,$6::float,$7::integer,t.id FROM inventory_product
AS t WHERE t.ebay_sku=$8
insert params:
[
2, 79.98, 167, '1',
2, 19.98, 167, '2',
2, 79.98, 168, '1',
2, 79.98, 169, '3',
2, 79.98, 170, '4'
]
Note that the insert works fine when I remove the trigger.
Also I am running the insert from a Node.js server (but I don't think that's relevant).
What am I missing here?
Don't use the target's table alias on the left hand side of the SET assignment. It's always clear which table is meant there. Btw: the function language is an identifier and should not be quoted:
CREATE OR REPLACE FUNCTION update_inventory()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.product_id IS NOT NULL THEN
UPDATE inventory_product AS t
SET qty = t.qty - NEW.qty
-- ^ here
WHERE t.id = NEW.product_id;
END IF;
END; $$ LANGUAGE plpgsql;
In fact, you don't need any alias at all in the UPDATE statement:
CREATE OR REPLACE FUNCTION update_inventory()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.product_id IS NOT NULL THEN
UPDATE inventory_product
SET qty = qty - NEW.qty
WHERE id = NEW.product_id;
END IF;
END; $$ LANGUAGE plpgsql;

How can this postgresql query return as table?

I am trying to call modules which in specific course but it returns as error: more than one row returned by a subquery used as an expression
Query:
CREATE OR REPLACE FUNCTION course_modules(_courseID integer)
RETURNS SETOF modules AS
$$
BEGIN
RETURN QUERY SELECT * FROM modules WHERE mod_id =(SELECT module_id from coursemodules WHERE course_id = _courseID);
END
$$
LANGUAGE 'plpgsql';
coursemodule table
CREATE TABLE coursemodules(
course_id integer references courses (id) ON DELETE CASCADE,
module_id integer references modules (mod_id) ON DELETE CASCADE
);
Modules Table
CREATE TABLE modules(
documents text NOT NULL,
mod_id serial primary key,
content text NOT NULL,
title varchar(50) NOT NULL
);
Course Table
CREATE TABLE courses(
finishDate Date,
description text NOT NULL,
duration varchar(50) NOT NULL,
startDate Date,
id serial primary key,
courseName varchar(50) NOT NULL
);
There is no restriction in the coursemodule table that a course could only have one module. Because of that the SELECT module_id from coursemodules WHERE course_id = _courseID subquery could return multiple lines.
If you change mod_id = (SELECT module_id from coursemodules WHERE course_id = _courseID)
to
mod_id IN (SELECT module_id from coursemodules WHERE course_id = _courseID).
It should work. Otherwise you have to add constraints to the coursemodule table.
This is a simple SQL syntax error. You're using
WHERE mod_id =
But you may have more than one row returning from the subquery. User IN:
WHERE mod_id IN

Delete rows from several tables

I use these tables to store data.
I would like to delete all data older than 1 month (for example) using one SQL query from all tables.
-- TABLE AGENT_HISTORY
CREATE TABLE AGENT_HISTORY(
EVENT_ID INTEGER NOT NULL,
AGENTID INTEGER NOT NULL,
EVENT_DATE DATE NOT NULL
)
/
CREATE TABLE CPU_HISTORY(
CPU_HISTORY_ID INTEGER NOT NULL,
EVENT_ID INTEGER,
CPU_NAME VARCHAR2(50 ) NOT NULL,
CPU_VALUE NUMBER NOT NULL
)
/
CREATE TABLE CPU_TEMP_HISTORY(
CPU_TEMP_HISTORY_ID INTEGER NOT NULL,
EVENT_ID INTEGER,
CPU_TEMP_NAME VARCHAR2(50 ) NOT NULL,
CPU_TEMP_VALUE NUMBER NOT NULL
)
/
CREATE TABLE MEMORY_HISTORY(
MEMORY_HISTORY_ID INTEGER NOT NULL,
EVENT_ID INTEGER,
MEMORY_NAME VARCHAR2(50 ) NOT NULL,
MEMORY_VALUE NUMBER NOT NULL
)
/
CREATE TABLE DISK_HISTORY(
DISK_HISTORY_ID INTEGER NOT NULL,
EVENT_ID INTEGER,
DISK_NAME VARCHAR2(50 ) NOT NULL,
DISK_VALUE NUMBER NOT NULL
)
/
CREATE TABLE NETWORK_HISTORY(
NETWORK_HISTORY_ID INTEGER NOT NULL,
EVENT_ID INTEGER,
ADAPTER_NAME VARCHAR2(50 ) NOT NULL,
TRANSMITBYTES NUMBER NOT NULL,
TRANSMITSPEED NUMBER,
RECEIVESPEED NUMBER,
RECEIVEBYTES NUMBER
)
/
CREATE TABLE SWAP_HISTORY(
SWAP_HISTORY_ID INTEGER NOT NULL,
EVENT_ID INTEGER,
SWAP_NAME VARCHAR2(50 ) NOT NULL,
SWAP_VALUE NUMBER NOT NULL
)
/
CREATE TABLE CONNECTIONS_HISTORY(
CONNECTIONS_HISTORY_ID INTEGER NOT NULL,
EVENT_ID INTEGER,
CONNECTIONS_NAME VARCHAR2(50 ) NOT NULL,
CONNECTIONS_VALUE NUMBER NOT NULL
)
/
CREATE TABLE PARTITIONS_HISTORY(
PARTITIONS_HISTORY_ID INTEGER NOT NULL,
EVENT_ID INTEGER,
PARTITIONS_NAME VARCHAR2(50 ) NOT NULL,
PARTITIONS_VALUE NUMBER NOT NULL
)
/
Is this possible in Oracle?
I use EVENT_ID as unique key ID in all tables.
As suggested by "a_horse_with_no_name", you can use ON DELETE CASCADE by creating the required relationships.
For example,
ALTER TABLE table_name
ADD CONSTRAINT constraint_name
FOREIGN KEY (column1, column2, ... column_n)
REFERENCES parent_table (column1, column2, ... column_n)
ON DELETE CASCADE;
Bookmark the Database SQL Language Reference and start reading the concepts for better understanding.