I hope to this work.
When UserAccount table duplicated, Out Parameter put -1.
When UserInfo table duplicated, Out Parameter put -2.
When all work is succeed, Out Parameter put LAST_INSERT_ID.
UserAccount Table
CREATE TABLE UserAccount (
SocialType ENUM('Google', 'Facebook'),
SocialID BINARY(16),
UserID INT UNIQUE NOT NULL AUTO_INCREMENT,
PRIMARY KEY(SocialType, SocialID)
);
UserInfo Table
CREATE TABLE UserInfo (
UserID INT,
Nickname VARCHAR(20) UNIQUE,
PRIMARY KEY(UserID),
FOREIGN KEY(UserID) REFERENCES UserAccount(UserID)
ON DELETE CASCADE,
);
I want to insert tables with duplicate checking in procedure.
But it failed to work and seems inefficient.
DELIMITER $$
CREATE PROCEDURE usp_CreateNewUser(
socialType ENUM('Google','Facebook'),
socialID VARCHAR(100),
nickname VARCHAR(20),
OUT result INT)
Work:BEGIN
IF EXISTS(SELECT 1 FROM UserAccount WHERE SocialType = socialType AND SocialID = UNHEX(MD5(socialID)))
THEN
SET result = -1;
Leave Work;
END IF;
IF EXISTS(SELECT 1 FROM UserInfo WHERE Nickname = nickname)
THEN
SET result = -2;
Leave Work;
END IF;
INSERT INTO UserAccount() VALUE(socialType, UNHEX(MD5(socialID)), 0);
INSERT INTO UserInfo(UserID, Nickname) VALUE(LAST_INSERT_ID(), nickname);
SET result = LAST_INSERT_ID();
END $$
DELIMITER ;
What is the best way I can do in this situation?
Thanks.
Judging from your trigger, you don't want to have duplicate keys in your database, as you are bypassing the insert when you find one.
So all you have to do is have your unique indexes set up correctly in your first table, which will do that work for you, and produce a display in your application.
Do the below:
ALTER TABLE `UserAccount` ADD UNIQUE `UserAccount`(`socialType`, `socialID`);
Since your data appears to be 1 to 1, id put all of this in the same table, and then you wouldn't need the trigger.
Related
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();
I have a MySQL table created using the following syntax:
CREATE TABLE `name_to_id` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(128),
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
);
And a common query this table would like to answer is name to id look-up, but if the <name, id> pair does not exist in the DB, then also insert a new entry and return the newly inserted id.
Can I know should I do that in MySQL?
As commented by Strawberry, this cannot be performed in a single query.
However, here is a stored procedure that should do what you expect. First, it uses the INSERT ... ON DUPLICATE KEYS UPDATE syntax to insert new names ; this actually relies on the UNIQUE key that you correctly set up on the name column.
DELIMITER //
CREATE PROCEDURE get_id_by_name(IN p_name VARCHAR(128))
BEGIN
INSERT INTO name_to_id(name) VALUE(p_name) ON DUPLICATE KEY UPDATE name = p_name;
SELECT id FROM name_to_id WHERE name = p_name;
END //
DELIMITER ;
Demo on DB Fiddle.
This approach is efficient, but the downside of ON DUPLICATE KEYS is that it wastes id sequences : everytime the query is called, the sequence is autoincremented (even if a record already exists). This can be seen in the fiddle.
Here is another approach, that won't burn sequence numbers :
DELIMITER //
CREATE PROCEDURE get_id_by_name(IN p_name VARCHAR(128))
BEGIN
DECLARE p_id bigint(20) unsigned;
SELECT id INTO p_id FROM name_to_id WHERE name = p_name;
IF (p_id IS NULL) THEN
INSERT INTO name_to_id(name) VALUE(p_name);
SELECT LAST_INSERT_ID();
ELSE
SELECT p_id;
END IF;
END //
DELIMITER ;
Demo on DB Fiddle.
you can do this on stored proc, if the select statement did not return a result, then you can execute the insert statement
Given this table:
CREATE TABLE object
(
object_id int NOT NULL AUTO_INCREMENT,
root_id int NOT NULL,
name varchar(255),
PRIMARY KEY(object_id)
)
The column root_id is a self referential foreign key.
Basically what I want is root_id to default to the same value as object_id when a new record is inserted, if root_id is NULL.
I thought a trigger would solve this.
CREATE TRIGGER add_default_original_id AFTER INSERT ON object
FOR EACH ROW BEGIN
IF (NEW.original_id IS NULL) THEN
SET NEW.original_id = NEW.object_id;
END IF;
END;
So when I run this:
INSERT INTO object(name) values('test')
I want both object_id and original_id to have the same auto-generated value
With trigger AFTER INSERT I get the
Error Code: 1362. Updating of NEW row is not allowed in after trigger
With trigger BEFORE INSERT I run get the
Error Code: 1452. Cannot add or update a child row: a foreign key constraint fails (...)
I guess because object_id haven't gotten the generated value yet.
Of course I could just do this "manually" in my code after the insert, but I'm trying to do as few code managed statements as possible. Is there any way to easily achieve this during the insert statement?
You must use two statement:
INSERT INTO object(object_id, root_id, name) values(NULL, NULL, 'test')
UPDATE object set root_id = (select max(object_id) from object)
where id = (select max(object_id) from object)
There is another possible solution although a bit quirk:
CREATE TRIGGER add_default_original_id AFTER INSERT ON object
FOR EACH ROW BEGIN
BEGIN
SET NEW.root_id = (
SELECT `AUTO_INCREMENT`
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'XXX'
AND TABLE_NAME = 'object');
END
Hope this helps.
Whats wrong with this code?
CREATE TRIGGER User_trigger AFTER DELETE ON users
FOR EACH ROW
BEGIN
INSERT INTO del_users ('fullname') VALUES ('fullname');
END;
Please help.
There are several issues:
If you use BEGIN ... END block you have to change DELIMITER. On the other hand if your trigger contains only one statement just don't use BEGIN ... END. Take a closer look at Defining Stored Programs
In MySQL to be able to refer to columns of a row being deleted you have to use OLD keyword. Take a closer look at Trigger Syntax and Examples
That being said and assuming that your simplified table schema look something like this
CREATE TABLE users
(
id int not null auto_increment primary key,
fullname varchar(8)
);
CREATE TABLE del_users
(
id int not null auto_increment primary key,
user_id int, fullname varchar(32),
deleted datetime
);
Your trigger would look
CREATE TRIGGER tg_ad_users
AFTER DELETE ON users
FOR EACH ROW
INSERT INTO del_users (user_id, fullname, deleted)
VALUES (OLD.id, OLD.fullname, NOW());
Here is SQLFiddle demo
or with BEGIN ... END block
DELIMITER //
CREATE TRIGGER tg_ad_users
AFTER DELETE ON users
FOR EACH ROW
BEGIN
INSERT INTO del_users (user_id, fullname, deleted)
VALUES (OLD.id, OLD.fullname, NOW());
END//
DELIMITER ;
Here is SQLFiddle demo
Try the following:
CREATE TRIGGER User_trigger AFTER DELETE
ON users
AS
INSERT INTO del_users (fullname)
SELECT d.fullname from Deleted d
Here is information on using the DELETED and INSERTED Tables:
http://technet.microsoft.com/en-us/library/ms191300.aspx
I have the following table.
CREATE TABLE people(
first_name VARCHAR(128) NOT NULL,
nick_name VARCHAR(128) NULL
)
I would like to prevent people from having their nickname be the same as their firstname if they attempt that insertion. I do not want to create an index on either of the columns just a rule to prevent the insertion of records where the first_name and nick_name are the same.
Is there a way to create a rule to prevent insertion of records where the first_name would equal the nick_name?
CREATE TRIGGER `nicknameCheck` BEFORE INSERT ON `people` FOR EACH ROW begin
IF (new.first_name = new.nick_name) THEN
SET new.nick_name = null;
END IF;
END
Or you can set first_name to NULL which will cause SQL error and you can handle it and show some warning.
You only need triggers for BEFORE INSERT and BEFORE UPDATE. Let these check the values and abort the operation, if they are equal.
Caveat: On older but still widely used versions of MySQL (before 5.5 IIRC) you need to do something bad, such as read from the written table or easier read from an inexistant table/column (in order to abort).
AFTER INSERT trigger to test and remove if same ...
CREATE TABLE ek_test (
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
one INT NOT NULL,
two INT NOT NULL
);
delimiter //
CREATE TRIGGER ek_test_one_two_differ AFTER INSERT ON ek_test
FOR EACH ROW
BEGIN
IF (new.one = new.two) THEN
DELETE FROM ek_test WHERE id = new.id;
END IF;
END//
delimiter ;
INSERT INTO ek_test (one, two) VALUES (1, 1);
SELECT * FROM ek_test;
NOTE you will also need AFTER UPDATE trigger.