cx_Oracle.DatabaseError: ORA-00947: not enough values - oracle

I have a table:
create table employee (
employee_id NUMBER NOT NULL,
name VARCHAR2(255) NOT NULL,
notes VARCHAR2(4000),
created_by varchar2(255) not null,
created_at date default sysdate not null,
updated_by varchar2(255) not null,
updated_at date default sysdate not null,
PRIMARY KEY(vendor_id)
);
so when I insert from SQL developer:
insert into employee(employee_id, name,notes) values(1,'xyz','test');
it auto populates create_by, created_at, updated_at and updated_by.
row gets inserted successfully.
Whereas if I try to insert using cx_Oracle module in python,
cursor.execute("INSERT INTO employee VALUES (:employee_id,:name,:notes)",
{
'employee_id' : max_value,
'name' : each_vendor,
'notes' : 'test'
}
)
it throws error saying not enough values.
Why do I get this error? How can I solve it?

The answer is very simple, and has nothing to do with python. Your 2 insert statements are very different.
In the 1st, you explicitly name the columns you intend to provide values for: (employee_id, name,notes). However, in the insert statement used from Python, you don't specify the 3 columns by name. As a result, your insert statement expects you to provide the values for all columns in the table.
The fix: explicitly name the 3 columns:
cursor.execute("INSERT INTO employee (employee_id, name, notes) VALUES (:employee_id,:name,:notes)",
{
'employee_id' : max_value,
'name' : each_vendor,
'notes' : 'test'
}
)

Related

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;

table NISHAN.TBL_ADMIN is mutating, trigger/function may not see it

I have a trigger named tr_admin_user_role that automatically insert values into tbl_user_role table when we perform a insert in another table called tbl_admin. There is no error at compile time but whenever I insert a value into tbl_admin table it shows me an error and error is like
This is my tbl_admin table
CREATE TABLE tbl_admin(
admin_id INTEGER,
username VARCHAR2(50) NOT NULL UNIQUE,
passwords VARCHAR2(50) NOT NULL,
email VARCHAR2(100) UNIQUE,
enabled CHAR(1) DEFAULT 1 NOT NULL,
created_at DATE DEFAULT SYSDATE NOT NULL,
CONSTRAINT pk_admin_id PRIMARY KEY(admin_id)
);
tbl_user_role table
CREATE TABLE tbl_user_role(
user_role_id INTEGER,
username VARCHAR2(50) NOT NULL,
user_role VARCHAR2(50) DEFAULT 'ROLE_ADMIN' NOT NULL,
CONSTRAINT pk_user_role_id PRIMARY KEY(user_role_id)
);
Trigger that i have created
CREATE OR REPLACE TRIGGER tr_admin_user_role
AFTER INSERT ON tbl_admin
FOR EACH ROW
DECLARE
new_username TBL_ADMIN.username%TYPE;
BEGIN
SELECT username INTO new_username FROM (
SELECT username FROM tbl_admin ORDER BY username DESC
) WHERE ROWNUM = 1;
INSERT INTO tbl_user_role(username, user_role) VALUES(new_username, 'ROLE_ADMIN');
END;
Insert statement
INSERT INTO tbl_admin(username, passwords) VALUES('nisha', 'nisha');
That's not how you fetch the newly inserted / updated / previous value of a column in a Trigger. You should use the :OLD.column_name and :NEW.column_name to refer the old and new column values.Read the documentation to understand more.
So, your Trigger could be rewritten as
CREATE OR REPLACE TRIGGER tr_admin_user_role AFTER
INSERT ON tbl_admin
FOR EACH ROW
BEGIN
INSERT INTO tbl_user_role (
username,
user_role
) VALUES (
:NEW.username,
'ROLE_ADMIN'
);
END;
/
I assume you are using another trigger to generate
admin_id and user_role_id since they are declared as PRIMARY KEYs
and you are not including them in your inserts.
Db fiddle demo
Here I've used dummy values for those columns.

Oracle - Creating Triggers

I am trying to create a trigger in Oracle whereby we move deleted records to another table. So when deleted column is set to 1, it should move the records from the patient_table to the deleted_patient_table.
Can you please help :)
CREATE TABLE Patient_Table(
PatientID NUMBER(6) Primary Key,
Title char(4) NOT NULL,
Forename varchar2(20) NOT NULL,
Surname varchar2(20) NOT NULL,
Gender char(1) NOT NULL CHECK (Gender in ('M','F')),
DOB date NOT NULL,
TelNo varchar(12) NOT NULL,
Conditions varchar(200) NOT NULL,
Deleted Number(1) NOT NULL CHECK (Deleted in ('0','1'));
-- Table that should contain deleted records --
Create Table Deleted_Patient_table(
PatientID NUMBER(6) Primary Key,
Title char(4) NOT NULL,
Forename varchar2(20) NOT NULL,
Surname varchar2(20) NOT NULL,
Gender char(1) NOT NULL CHECK (Gender in ('M','F')),
DOB date NOT NULL,
TelNo varchar(12) NOT NULL,
Conditions varchar(200) NOT NULL,
Deleted Number(1));
Create or replace trigger trg_del
Before delete on Patient_Table
for each row
Begin
Insert into Deleted_Patient_table value (:old.PatientID,...)
End;
It seems you're soft-deleting your rows. i.e. not deleting but updating related column(Deleted) from 0 to 1. The values of column can be one of two and not null, so you only need to compare only two values of them without nvl operator. During this update operation you can use below trigger to produce log records :
Create or Replace Trigger Trg_Del_Patient
After Update on Patient_Table
For Each Row
Begin
If ( :old.Deleted = 0 and :new.Deleted = 1 ) Then
Insert into Deleted_Patient_table
values(:old.PatientID,:old.Title,:old.Forename,:old.Surname,
:old.Gender,:old.DOB,:old.TelNo,:old.Conditions);
Delete Patient_Table where PatientID = :old.PatientID;
-- Include this Delete statement, if you want to remove after the row has been inserted to the Deleted_Patient_Table
End If;
End;

Oracle trigger insert to other table then modify the original table

I have theses two tables:
TABLE ASSET_ENTRY_NOTE (
ID NUMBER NOT NULL, --PK
ASSETMDL_ID NUMBER NOT NULL, --FK
DEPT_ID NUMBER NOT NULL, --FK
LOCATION NVARCHAR2(100) NOT NULL,
ASSET_ID NUMBER, --FK TO ASSETS
ACCOUNT_ID NUMBER NOT NULL, --FK
TOTAL_DPRC_DURATION FLOAT(126) NOT NULL,
TOTAL_PROD_HRS FLOAT(126),
AMORTIZATION_PRCNTG FLOAT(126),
ACQUIRE_DATE DATE NOT NULL,
DESCRIPTION NVARCHAR2(200) NOT NULL,
APPRFLAG NUMBER DEFAULT 0 NOT NULL,
WRK_HRS FLOAT(126),
)
TABLE ASSETS (
ID NUMBER NOT NULL, --PK
ASSETMDL_ID NUMBER NOT NULL, --FK
DEPT_ID NUMBER NOT NULL,
LOCATION NVARCHAR2(100) NOT NULL, --FK
ACCOUNT_ID NUMBER NOT NULL,
ACQUIRE_DATE DATE NOT NULL,
TOTAL_DPRC_DURATION FLOAT(126),
BALANCE_CLOSING_DATE DATE,
SELL_VAL FLOAT(126),
RPLCMNT_DISCOUNT FLOAT(126),
DESCRIPTION NVARCHAR2(200) NOT NULL,
)
Note that there's a one to one relationship between the two tables (i.e. ASSET_ENTRY_NOTE.ASSET_ID is Unique.
When the ASSETS_ENTRY_NOTE.APPRFLAG is updated to 1 I have this trigger that:
gets a new primary key sequence for the ASSETS table.
insert data from ASSETS_ENTRY_NOTE to ASSETS.
updates the column ASSETS_ENTRY_NOTE.ASSET_ID to the same value as the primary key value on the sequence.
This is the latest try for my trigger:
CREATE OR REPLACE TRIGGER ENTRYNT_ASSET_TRIG
after UPDATE OF APPRFLAG ON ASSET_ENTRY_NOTE
for each row
when (new.apprflag = 1)
declare
v_asset_id number;
BEGIN
SELECT assets_PK_SEQ.NEXTVAL INTO v_asset_id
FROM DUAL d;
insert into assets (ID,
assets.assetmdl_id,
assets.dept_id,
assets.location,
assets.account_id,
assets.acquire_date,
assets.total_dprc_duration,
assets.description
)
values (v_asset_id,
assetmdl_id,
dept_id,
location,
account_id,
acquire_date,
total_dprc_duration,
description
);
update ASSET_ENTRY_NOTE set asset_id = v_asset_id where ;
END;
The thing is, I know that ASSET_ENTRY_NOTE is a mutating table and the last UPDATE statement is not allowed here, But nothing else is working for me.
What I've already tried:
creating a statement-level trigger to update one value only.
using before instead of after but that's incorrect because I need the values just to insert into the ASSETS.
using a cursor to go through each value changed but I had exact fetch error.
creating a procedure that handles inserting and updating.
Any help would be appreciated.
The design seems quite strange to me, but to answer the question about the trigger:
To change the asset_entry_note row in the trigger, you need a before update trigger. In there you can just assign the value to the asset_id column.
Your insert statement is also wrong. You can table-qualify column names in the column list of an insert statement. And the values clause needs to use the values from the inserted row. You are referencing the target table's columns which is not allowed).
You also don't need a select statement to obtain the sequence value.
Putting all that together, your trigger should look something like this:
CREATE OR REPLACE TRIGGER ENTRYNT_ASSET_TRIG
BEFORE UPDATE OF APPRFLAG ON ASSET_ENTRY_NOTE
for each row
when (new.apprflag = 1)
declare
v_asset_id number;
BEGIN
v_asset_id := assets_PK_SEQ.NEXTVAL;
insert into assets
(ID,
assetmdl_id,
dept_id,
location,
account_id,
acquire_date,
total_dprc_duration,
description)
values
(v_asset_id,
new.assetmdl_id, -- reference the inserted row here!
new.dept_id,
new.location,
new.account_id,
new.acquire_date,
new.total_dprc_duration,
new.description);
new.asset_id := v_asset_id;
END;
/
You have to change the design of the application to have only one table with sign to indicate the membership of a particular entity.
Another way is to create 'after statement' trigger to update all affected rows in ASSET_ENTRY_NOTE with proper values. These rows is to be collected in, for example, package collection in row-level trigger.
I fixed it and it worked:
changed to before.
edited the update statement to an assignment of new so that the last line would become :new.asset_id := v_asset_id ;

Oracle fetch returns more than requested

So I've got another simple procedure with an error. This time, the fetch is returning more records than requested. I initially checked my query and found an error. My query returns the right results, but the fetch still won't work.
PROCEDURE:
--View Customer Service History
create or replace procedure SERVICEHISTORY(name in customer.name%type) is
carModel char(11);
serviceCharge decimal(7,2);
serviceName char(20);
serviceDate date;
cusName char(15);
cusID number;
--Query tested, it works
cursor nextService is
select name, workOrder.serviceDate, workOrderServices.actualCharge
from workOrder join workOrderServices on workOrder.workOrderID=workOrderServices.workOrderID join services on workOrderServices.serviceID=services.serviceID
where workOrderServices.customerID=cusID;
begin
--Get customer ID from name, another janky work around
select customerID
into cusID
from customer
where customer.name=name;
--Gets the car model
select model
into carModel
from vehicle
where vehicle.customerID=cusID;
open nextService;
fetch nextService into serviceName, serviceDate, serviceCharge;
if nextService%notfound then
dbms_output.put_line('No service history found.');
else
dbms_output.put_line('Service-------Date-------Cost');
loop
dbms_output.put_line(serviceName||' '||serviceDate||' '||serviceCharge);
fetch nextService into serviceName, serviceDate, serviceCharge;
if nextService%notfound then
dbms_output.put_line('Report finished.');
end if;
exit when nextService%notfound;
end loop;
end if;
close nextService;
end serviceHistory;
/
But it throws this error:
ERROR:
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at line 20
ORA-06512: at line 1
Here are the tables:
create table customer
(
customerID number(8) not null primary key constraint lengthCHK13 check(LENGTH(customerID)=8),
name varchar2(20) not null,
address varchar2(20) not null,
insurance varchar2(20) not null,
contactInfo number(10) not null,
customerType varchar2(15) not null,
licenseNumber varchar2(13) not null,
amountOwed decimal(7,2) not null constraint notNeg6 check(amountOwed >=0)
);
create table vehicle
(
--some error about no matching unique or primary key for this column on cusomterID
VIN varchar2(17) not null primary key constraint lengthCHK113 check(length(VIN)=17),
customerID number(8) not null references customer(customerID) constraint lengthCHK12 check(length(customerID)=8),
make varchar2(10) not null,
model varchar2(10) not null,
carYear number(4) not null,
color varchar2(10) not null,
notes varchar2(20),
currentMileage number(6) not null,
engineType varchar2(10) not null,
licenseNumber varchar(12) not null,
amountOwed decimal(7,2) constraint notNeg7 check(amountOwed >=0)
--primary key(VIN, customerID) DOESNT WORK BUT NEEDS TO
);
create table workOrder
(
workOrderID number(8) not null constraint lengthCHK10 check(length(workOrderID)=8),
VIN varchar2(17) not null references vehicle(VIN) constraint lengthCHK14 check(length(VIN)=17),
serviceDate date not null,
--Removing b/c it's a pain serviceTime TIME not null,
serviceBay number(2),
description varchar2(20) not null,
results varchar2(20) not null,
primary key(workOrderID)
);
create table services
(
serviceID number(8) not null primary key constraint lengthCHK17 check(length(serviceID)=8),
name varchar2(20) not null,
price decimal(7,2) not null constraint notNeg8 check(price >=0),
estimatedHours number(2) not null
);
create table workOrderServices
(
serviceID number(8) not null references services(serviceID) constraint lengthCHK20 check(length(serviceID)=8),
workOrderID number(8) not null references workOrder(workOrderID) constraint lengthCHK22 check(length(workOrderID)=8),
customerID number(8) not null references customer(customerID) constraint lengthCHK87 check(length(customerID)=8),
actualHours number(2) not null constraint notNeg11 check(actualHours >=0),
actualCharge decimal(7,2) not null constraint notNeg10 check(actualCharge >=0),
primary key(serviceID, workOrderID)
);
Test data:
INSERT INTO services(serviceID, name, price, estimatedHours)
VALUES(48937322, 'Tire Rotate', 19.99, 1);
INSERT INTO services(serviceID, name, price, estimatedHours)
VALUES(47873231, 'Xmission Flush', 63.99, 1);
INSERT INTO customer (customerID, name, address, insurance, contactInfo, customerType, licenseNumber, amountOwed)
VALUES (45124512, 'Bob Jones', '232 Sycamore Ln.', 'Pekin', 3095555145, 'New', 'SSSSFFFYYDDD', 220.00);
INSERT INTO customer (customerID, name, address, insurance, contactinfo, customertype, licensenumber, amountOwed)
VALUES (12892222, 'Mike Tyson','100 Haters Rd.', 'Progressive', 2175555555, 'Regular', 'FGHJHHHHTYYY', 42.00);
INSERT INTO vehicle(VIN, customerID, make, model, carYear, color, notes, currentMileage, engineType, licenseNumber, amountOwed)
VALUES('KNDKG3A31A7568300', 45124512, 'Ford', 'Focus', 2009, 'Red', 'side door damage', 10346, 'V4', 'h5303h87dk23', 0);
INSERT INTO vehicle(VIN, customerID, make, model, carYear, color, notes, currentMileage, engineType, licenseNumber, amountOwed)
VALUES('SALTW16413A376838', 12892222, 'Chrysler', 'Sebring', 2004, 'Green', 'Basically a Go-Kart', 105098, 'V4', 'r2345h23tx31', 0);
INSERT INTO workOrder( workOrderID, VIN, serviceDate, serviceBay, description, results)
VALUES(12312312, 'KNDKG3A31A7568300', '07-FEB-12', 2, 'Oil Change', 'Changed oil');
INSERT INTO workOrder( workOrderID, VIN, serviceDate, serviceBay, description, results)
VALUES(32132132, 'SALTW16413A376838', '07-FEB-12', 3,'Tire Rotation', 'Rotated the tires');
INSERT INTO workOrderServices(serviceID, workOrderID, customerID, actualHours, actualCharge)
VALUES(17278722, 12312312, 45124512, 5, 45.00);
INSERT INTO workOrderServices(serviceID, workOrderID, customerID,actualHours, actualCharge)
VALUES(48937322, 32132132, 12892222,10, 90.00);
If you had put some extra messaging into your code, as I did, you would have quickly discovered the source of the error.
create or replace procedure SERVICEHISTORY(name in customer.name%type) is
carModel char(11);
serviceCharge decimal(7,2);
serviceName char(20);
serviceDate date;
cusName char(15);
cusID number;
--Query tested, it works
cursor nextService is
select name, workOrder.serviceDate, workOrderServices.actualCharge
from workOrder
join workOrderServices
on workOrder.workOrderID=workOrderServices.workOrderID
join services on workOrderServices.serviceID=services.serviceID
where workOrderServices.customerID=cusID;
begin
dbms_output.put_line('Get customer ID from name, another janky work around');
select customerID
into cusID
from customer
where customer.name=name;
dbms_output.put_line('cusID='||cusID);
--Gets the car model
select model
into carModel
from vehicle
where vehicle.customerID=cusID;
dbms_output.put_line('carModel='||carModel);
open nextService;
fetch nextService into serviceName, serviceDate, serviceCharge;
if nextService%notfound then
dbms_output.put_line('No service history found.');
else
dbms_output.put_line('Service-------Date-------Cost');
loop
dbms_output.put_line(serviceName||' '||serviceDate||' '||serviceCharge);
fetch nextService into serviceName, serviceDate, serviceCharge;
if nextService%notfound then
dbms_output.put_line('Report finished.');
end if;
exit when nextService%notfound;
end loop;
end if;
close nextService;
end serviceHistory;
/
Here's my output:
SQL> exec SERVICEHISTORY(name=>'Bob Jones')
Get customer ID from name, another janky work around
BEGIN SERVICEHISTORY(name=>'Bob Jones'); END;
*
ERROR at line 1:
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at "APC.SERVICEHISTORY", line 23
ORA-06512: at line 1
It's the very first query which hurls. Why does it do that? Because you've given the parameter the same name as the column. In PL/SQL variable naming has scope, with the nearest declaration trumping further ones.
So in your query ...
select customerID
into cusID
from customer
where customer.name=name;
... Oracle interprets name on the right-hand side as being the column of the table, because that's the narrowest scope. Effectively your filter is where 1=1, which is no filter at all.
All you need to do to fix this is change the parameter to something like p_name and use that in your query instead...
SQL> exec SERVICEHISTORY(p_name=>'Mike Tyson')
Get customer ID from name, another janky work around
cusID=12892222
carModel=Sebring
Service-------Date-------Cost
Tire Rotate 07-FEB-12 90
Report finished.
PL/SQL procedure successfully completed.
SQL>
Of course, you will get the same error from the query on VEHICLE should you customer have more than one car, which the data model supports.
At the moment you don't use the result from that query, so the easiest fix would be to remove it. But probably you want to include it in the output, so the best solution might be to include VEHICLE in the main query.

Resources