I am trying to create a FUNCTION that will insert complete and relevant information into an existing table. I need the FUNCTION to check if certain entities exist in another table and, if not, inserts the data. Example code below:
CREATE FUNCTION insert_payment (customer_uuid uuid, customer_name varchar(63), payment_uuid uuid, total_amount integer
So let's say that I wanted to enter a payment into a table called Payments. I want the FUNCTION to check another existing table called Customers to see if customer_uuid and customer_name already exist within the table. If not, I would like the FUNCTION to insert the customer_uuid and customer_name information into Customers as well as enter the data from all four parameters into Payments.
This is my first question to ask on Stack Overflow so if greater clarification is needed please let me know. I am also a student and still learning how to communicate effectively when talking about coding so, again, if further clarification is needed I will try my best. Thank you!
You can make use of an insert on conflict do nothing to insert the customer and skip inserting if it already exists.
So with this table definition:
create table customer (id uuid primary key, name text);
create table payment (id uuid primary key, customer_id uuid not null references customer, amount integer);
This function would do what you want:
CREATE FUNCTION insert_payment (customer_uuid uuid, customer_name varchar(63),
payment_uuid uuid, total_amount integer)
returns void
as
$$
insert into customer (id, name)
values (customer_uuid, customer_name)
on conflict do nothing;
insert into payment (id, customer_id, amount)
values (payment_uuid, customer_uuid, total_amount);
$$
language sql;
This is just as efficient as first checking the existence (select exists (...)) before inserting, because the INSERT statement will do that check anyway.
Online demo
I can help you more after knowing your table fields name but here I'm sharing a code maybe it'll help you
//set variable
$customer_uuid= $_POST['customer_uuid'];
$customer_name= $_POST['customer_name'];
//WRITE QUERY to the check-in customers table
$duplicate_customer = $this->db->prepare( "SELECT customer_uuid,customer_name FROM customers WHERE customer_uuid = '$customer_uuid' AND customer_name='customer_name'" ); $duplicate_customer->execute();
//now check data if exist in the customer table
if($duplicate_customer->rowCount() > 0){
//write here insert query to insert data in customer table
}
Related
I am trying to build an order system that is able to insert a compound order that consists of multiple items and amounts. My database layout is as follows: I have an order table, containing an autoincrement id, item_id, amount and order_group_id columns. I also have an order_group table containing an autoincrement id and a person_id column. The idea is that when a person orders, one new order_group entry is created, and its id is used as the fk in the orders that the person has done.
I presume that this would normally be done in the code of the application. However, I am using postgrest to provide an API for me, which suggests creating a custom view to insert compound entries via that route. This is described here.
This is what I have so far:
CREATE FUNCTION kzc.new_order()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
DECLARE
group_id int;
BEGIN
INSERT INTO kzc.order_group (person) VALUES (new.person) RETURNING id AS group_id;
INSERT INTO kzc."order" (item, amount, order_group) VALUES (new.item_id, new.amount, group_id);
RETURN new;
END;
$$;
CREATE TRIGGER new_order
INSTEAD OF INSERT ON kzc.new_order
FOR EACH ROW
EXECUTE FUNCTION kzc.new_order()
However, this code makes a new ordergroup for every order that is in the compound insert. How can I make it so that my code only makes one new ordergroup entry and assigns its id to all orders?
Thanks in advance!
I suggest that you add an order_group_id column to the new_order view and create a sequence for it. Then create a DEFAULT value for the column:
ALTER VIEW kzc.new_order
ALTER order_group_id SET DEFAULT currval('order_group_id_seq');
Add a BEFORE INSERT trigger FOR EACH STATEMENT that just calls nextval for the sequence. The currval calls will all pick up the same generated value.
Then you have that number in your trigger and can use it as a primary key for order_group.
To avoid adding the row multiple times, use
INSERT INTO kzc.order_group (id, person)
VALUES (NEW.order_group_id, NEW.person)
ON CONFLICT (id) DO NOTHING;
I'm in trouble with the implementation of a trigger.
Assuming that I have two types:
CREATE TYPE customer_t AS OBJECT(
code INTEGER,
name VARCHAR(20),
surname VARCHAR(20),
age INTEGER);
and the type
CREATE TYPE ticket_t AS OBJECT (
price INTEGER,
cust REF customer_t
)
And then I have the associate tables:
CREATE TABLE customers OF TYPE customer_t
CREATE TABLE tickets OF TYPE ticket_t
I have to do an exercise so I have to create a trigger for ensure that a customer won't buy more than 10 tickets but, if I use command like "select count(*)" I get an error because I can't access to mutating table.
Please can anyone help me with this trigger?
EDIT:
I populated the tables as follows:
INSERT INTO custs (code, name, surname, age) values (123, 'Paolo', 'Past', 32);
and repeating the following operation ten times:
INSERT INTO tickets (price, cust) values
(4, (SELECT * FROM (SELECT REF(T) FROM custs T WHERE name = 'Paolo' AND surname = 'Past') WHERE rownum < 2))
The trigger implemented is:
create or replace
trigger check_num_ticket after insert on tickets
for each row
declare
num_ticket number;
begin
SELECT count(*) INTO num_ticket FROM tickets WHERE :new.cust = cust;
if (num_ticket >= 10) then
raise_application_error('-20099', 'no ticket available');
end if;
end;
And I get this error:
A trigger (or a user defined plsql function that is referenced in
this statement) attempted to look at (or modify) a table that was
in the middle of being modified by the statement which fired it.
You are getting the mutating table error, because you are inserting in the same table where you want to get the row count for. Imagine your insert statement inserts two rows. There is no rule which row to insert first and which last, but your trigger fires on one inserted row and wants to know how many rows are already in the table. The DBMS tells you this is undefined, as the table is currently mutating.
You need an after statement trigger instead of a before row trigger. So when the insert statement's inserts are done, you look at the table to see whether there are suddenly customers with too many rows in it.
(A great alternative is a compound trigger. It combines row and statement triggers. So in the after row section you'd remember the customers in some array/collection and in the after statement section you'd look up the table for only the remembered customers.)
I have created tables T1 with columns( id as Primary key and name) and T2 with columns( id as primary key, name, t_id as foreign key references T1(id)) . I Inserted some values from inputs from a Windows form. After querying SELECT * FROM T2; using isql, all the values in the foreign key column are null instead of duplicating values in T1(id) because of the relationship created. Is they anything I have left out or need to add? The primary key of both tables are autoincremented.
You are confusing auto-incremented keys and relationship uses.
Auto-incremented keys (or generally talking, fields) just help you when you are inserting a new record on the table of the key. But when you are inserting a new record that makes a reference to a record in another table, then you must specify that record, using the foreign key field. Or in your case, the user that is inserting the "name" in T2 must say which one record on T1 that "name" in T2 is making a reference.
Your confusion on the relationship is that you are thinking that an established relationship will enforce the use of that values automatically. But the relationship just enforce the validation of the values. So, the field t_id in T2 will not use the value of the last record of T1 automatically. But if you try to insert a value that do not exist in T1 in the field t_id, the relationship will not let you do.
So, answering your question, what you left out and need to add?
You left out the part of the code that insert the value on the t_id field of T2 table.
Let me try to explain using an example that is more common.
The most common case of this is that the application insert first the T1 record and then when the user is inserting T2, the application provide a way to the user to choose which one T1 record his T2 record is referencing.
Suppose T1 is a publishers table and T2 is a book table. User insert a publisher, and when it is inserting a book it can choose which one publisher publish that book.
Field "ID" of Customers will be AUTOINCREMENT by default in table create using Event BeforeInsert on table CUSTOMERS. LOOK AT
CREATE TRIGGER nametrigger FOR nametable
ACTIVE BEFORE INSERT POSITION 0
AS
BEGIN
IF (NEW.ID IS NULL) THEN BEGIN
NEW.ID = GEN_ID(GEN_PK_ID, 1);
END
END
Now one new record in Customers
INSERT INTO Customers (CustomerName, ContactName, Address, City, PostalCode, Country)
VALUES ('Cardinal','Tom B. Erichsen','Skagen 21','Stavanger','4006','Norway');
Then ID will be automaticaly one sequencial number from 1 up to last integer or smallint or bigint as you defined in your create table (pay attencion that ID field is not include in FIELDS and VALUES) because TRIGGER
now you can use the dataset (obj) options to link the table MATER and DETAIL see in help delphi
or in SQL you can to use PARAMS FIELDS
later insert one new record in table MASTER try...
INSERT INTO xTable2 (IDcustomersField, ..., ..., ...., ....)
VALUES ( :IDcustomersField, ..., ..., ...., ....);
xTable2 may using one field ID (Primary Key) autoincrement too. this help when DELETING or UPDATING fileds in this table
Then you can say the value to :IDcustomersField in table detail using
xQuery.PARAM( 0 ).value or xQuery.PARAMBYNAME( IDcustomersField).value (here im using Query obj as example )
you can to use example with DATASOURCE in code to say the value for IDcustomersField
can to use
Events in SQL
can to use
PROCEDURE IN SQL
DONT FORGOT
you have to create Relationship between two table ( REFERENCIAL INTEGRITY and PRIMARY KEY in mater table ) NOT NULL FOR TWO FIELDS ON TABLES
I believe that understand me about my poor explanation (i dont speak english
You need to insert the values for t_id manually, after you get the ID's value from the main table T1.
Depending on your logic in the database you also can use a trigger or a stored procedure. Give us more information about what values you expect to have in NAME field in T2 after the insert? Are they duplicates from T1 or independent from T1?
If T1.NAME=T2.NAME, you can automate the process with a trigger
CREATE OR ALTER TRIGGER TR_T1_AI0 FOR T1
ACTIVE AFTER INSERT POSITION 0
AS
BEGIN
INSERT INTO T2(NAME, T_ID)
VALUES (NEW.NAME, NEW.ID);
END
If T2.NAME's value is different from T1.NAME you can use a stored procedure with parameters both names:
CREATE ORA ALTER PROCEDURE XXXX(
P_NAME_T1 TYPE OF T1.NAME,
P_NAME_T2 TYPE OF T2.NAME)
AS
DECLARE VARIABLE L_ID TYPE OF T1.ID;
BEGIN
INSERT INTO T1(NAME)
VALUES (:p_NAME_T1)
RETURNING ID INTO:L_ID;
INSERT INTO T2(NAME, T_ID)
VALUES (:P_NAME_T2, :l_ID);
END
You can use both statements from the stored procedure directly in your program if it supports the returning syntax. If not, you need an additional query with SELECT NEXT VALUE FOR GENERATOR_FOR_T1 FROM RDB$DATABASE; and use the value returned from it in both INSERT statements.
Please help me, I have a simple case that makes me little crazy, I have a table called PRODUCTS here is the structure
create table PRODUCT (
ID_PRODUCT CHAR(5) primary key not null,
NAME_PRODUCT CHAR(30),
PRICE_PRODUCT integer
)
then the second table is TRANSACTION, here is the table structure
create table TRANSACTION (
ID_TRANSACTION CHAR(5) primary key not null,
ID_PRODUCT CHAR(30) REFERENCES PRODUCTS(ID_PRODUCT),
PRICE integer,
QUANTITY integer
)
After that I want to create a trigger that works when users fill data into the ID_PRODUCT column in the TRANSACTIONS table, it is automatically trigger will fetch/select data from the column PRICE in the PRODUCTS table according to ID_PRODUCT that has been inserted, then update the column PRICE in table TRANSACTIONS
I'm a starter in SQL Server,... I hope you want to help me in this case, because I have no idea & don't know what I have to do, thanks
Something like this will probably do what you need:
CREATE TRIGGER UpdateTransaction
ON [TRANSACTION]
INSTEAD OF INSERT
AS
BEGIN
INSERT [TRANSACTION] (ID_TRANSACTION, ID_PRODUCT, PRICE, QUANTITY)
SELECT INSERTED.ID_TRANSACTION, INSERTED.ID_PRODUCT, PRODUCT.PRICE_PRODUCT, INSERTED.QUANTITY
FROM INSERTED
JOIN PRODUCT ON INSERTED.ID_PRODUCT = PRODUCT.ID_PRODUCT
END
It'll run INSTEAD of the normal insert and join the row to be INSERTED with the PRODUCT table, allowing it to insert the right PRICE_PRODUCT. You could do something similar for UPDATE's if you need to.
On a side note, TRANSACTION is a terrible name for a table - it's a reserved word in T-SQL (notice that it gets highlighted by your editor, and this website?) - it's part of the language, so I would avoid it in object names.
Perhaps naming your tables like this might be better:
TRANSACTION -> tbl_Transactions
PRODUCT -> tbl_Products
You're having data duplication by having two columns in two tables that are connected (id and price). I suggest removing the TRANSACTION.PRICE column.
You could instead create a VIEW:
CREATE VIEW V_Transaction AS
SELECT
TRANSACTION.ID_TRANSACTION,
TRANSACTION.ID_PRODUCT,
TRANSACTION.QUANTITY * PRODUCT.PRICE_PRODUCT as PRICE,
TRANSACTION.QUANTITY
FROM
TRANSACTION
JOIN ID_PRODUCT ON TRANSACTION.ID_PRODUCT = PRODUCT.ID_PRODUCT
There is just no point (that I see) to store the price per produkt and the price per transaction
I have 6 tables:
Staff ( StaffID, Name )
Product ( ProductID, Name )
Faq ( FaqID, Question, Answer, ProductID* )
Customer (CustomerID, Name, Email)
Ticket ( TicketID, Problem, Status, Priority, LoggedTime, CustomerID* , ProductID* )
TicketUpdate ( TicketUpdateID, Message, UpdateTime, TicketID* , StaffID* )
Question to be answered:
Given a Product ID, remove the record for that Product. When a product is removed all associated FAQ can stay in the database but should have a null reference in the ProductID field. The deletion of a product should, however, also remove any associated tickets and their updates. For completeness deleted tickets and their updates should be copied to an audit table or a set of tables that maintain historical data on products, their tickets and updates. (Hint: you will need to define a additional table or set or tables to maintain this audit information and automatically copy any deleted tickets and ticket updates when a product is deleted). Your audit table/s should record the user which requested the deletion and the timestamp for the deletion operation.
I have created additional maintain_audit table:
CREATE TABLE maintain_audit(
TicketID INTEGER NOT NULL,
TicketUpdateID INTEGER NOT NULL,
Message VARCHAR(1000),
mdate TIMESTAMP NOT NULL,
muser VARCHAR(128),
PRIMARY KEY (TicketID, TicketUpdateID)
);
Addittionally I have created 1 function and trigger:
CREATE OR REPLACE FUNCTION maintain_audit()
RETURNS TRIGGER AS $BODY$
BEGIN
INSERT INTO maintain_audit (TicketID,TicketUpdateID,Message,muser,mdate)
(SELECT Ticket.ID,TicketUpdate.ID,Message,user,now() FROM Ticket, TicketUpdate WHERE Ticket.ID=TicketUpdate.TicketID AND Ticket.ProductID = OLD.ID);
RETURN OLD;
END;
$BODY$
LANGUAGE plpgsql;
CREATE TRIGGER maintain_audit
BEFORE DELETE
ON Product
FOR EACH ROW
EXECUTE PROCEDURE maintain_audit()
DELETE FROM Product WHERE Product.ID=30;
When I run this all I get this :
ERROR: null value in column "productid" violates not-null constraint
CONTEXT: SQL statement "UPDATE ONLY "public"."faq" SET "productid" = NULL WHERE $1 OPERATOR(pg_catalog.=) "productid""
GUYS,Could you help me in sorting out this problem?
What you probably want is triggers. Not sure what RDBMS you are using, but that's where you should start. I started from zero and had triggers up and running in a somewhat similar situation within an hour.
In case you don't already know, triggers do something after a specific type of query happens on a table, such as an insert, update or delete. You can do any type of query.
Another tip I would give you is not to delete anything, since that could break data integrity. You could just add an "active" boolean field, set active to false, then filter those out in most of your system's queries. Alternatively, you could just move the associated records out to a Products_archive table that has the same structure. Easy to do with:
select * into destination from source where 1=0
Still, I would do the work you need done using triggers because they're so automatic.
create a foreign key for Ticket.product_id, and TicketUpdate.Ticket_id which has ON DELETE CASCADE. This will automatically delete all tickets and ticketupdates when you delete the product.
create an audit table for Product deleters with product_id, user and timestamp. audit tables for ticket,ticketUpdate should mirror them exactly.
create a BEFORE DELETE TRIGGER for table Ticket which copies tickets to the audit table.
Do the same for TicketUpdate
Create an AFTER DETETE Trigger on Products to capture who requested a product be deleted in the product audit table.
In table FAQ create Product_id as a foreign key with ON DELETE SET NULL