I want to implement a Viewed system for my website. Here is the structure of my tables:
// table1
id | user_or_ip | post_id | date_time // inserting new row for each viewed
// table2
id | post_id | total_viewed // getting the number of total viewed for each post
Now I need to a trigger for insert/update table2 after insert in table1.
I
think I have to use on duplicate key.
You can do this fairly easily.
With the following 2 example table:-
CREATE TABLE table1
(
id INT NOT NULL AUTO_INCREMENT,
user_or_ip VARCHAR(255),
post_id INT,
date_time DATETIME,
PRIMARY KEY (id)
);
CREATE TABLE table2
(
id INT NOT NULL AUTO_INCREMENT,
post_id INT,
total_viewed INT,
PRIMARY KEY (id),
UNIQUE KEY post_id (post_id)
);
you can use the following trigger on table 1 to calculate the count and insert it to table 2:-
CREATE TRIGGER `trig_1` AFTER INSERT ON `table1`
FOR EACH ROW BEGIN
INSERT INTO table2(post_id, total_viewed)
SELECT post_id, COUNT(*)
FROM table1
WHERE post_id = NEW.post_id
GROUP BY post_id
ON DUPLICATE KEY UPDATE total_viewed = VALUES(total_viewed);
END
Note that if you are certain that there will never be an error you could just insert a count of 1 and set it to total_count + 1 in the ON DUPLICATE KEY clause. But if anything fails that prevents the trigger the counts will be forever wrong for that post_id:-
CREATE TRIGGER `trig_1` AFTER INSERT ON `table1`
FOR EACH ROW BEGIN
INSERT INTO table2(post_id, total_viewed)
VALUES(NEW.post_id, 1)
ON DUPLICATE KEY UPDATE total_viewed = total_viewed + 1;
END
Note also that the sub query to get the count will be more efficient with an index on post_id in table1
Related
Table Structure
CREATE TABLE `goods` (
`id` int NOT NULL ,
`name` varchar(25) ,
`updateat` datetime
)
Now there is a piece of data like this in the table
When I want to insert a new piece of data like (1,'new','2021-12-18 12:00:00').First determine whether there is the same data in the table as the data to be inserted (except for the update time),Then compare the update time, keep the latest piece of data.
I want to know how to use sql to achieve this function.
First add primary key to your table:
CREATE TABLE goods (
id int NOT NULL ,
name varchar(25) ,
updateat datetime,
PRIMARY KEY (id, name)
)
Then use ON DUPLICATE KEY UPDATE :
insert into goods values (1, 'john', '2021-01-02');
insert into goods (id, name, updateat)
values (1, 'john', '2021-01-03')
ON DUPLICATE KEY UPDATE
updateat = greatest('2021-01-03',
(select updateat from (select * from goods as g) as g where id = 1))
Fiddle
Mysql uses INSERT ON...DUPLICATE instead of merge. This statement allows you to make modifications in your case an update based on a check when duplicate entries are identified. Duplicate entries may be identified using primary keys or unique indexes. The demo below and working db fiddle gives an example of this based on your criteria.
Using a unique index to identify duplicate entries
Using INSERT ON...DUPLICATE with the VALUES (used to identify currently inserted values) and a case expression to determine which updatedat date is more recent.
CREATE TABLE `goods` (
`id` int NOT NULL ,
`name` varchar(25) ,
`updateat` datetime
);
✓
-- use a unique index if you are interested in ensuring a subset of columns are unique and
-- these columns do not meet the criteria to be a primary/composite key based on your database design
create unique index uk_id_name on goods(id,name);
✓
insert into goods values (1,'new','2021-12-18 12:00:00');
✓
-- this should fail because of the duplicate unique index
insert into goods values (1,'new','2021-12-18 12:00:00');
Duplicate entry '1-new' for key 'uk_id_name'
select * from goods;
id | name | updateat
-: | :--- | :------------------
1 | new | 2021-12-18 12:00:00
insert into goods values (1,'new','2021-12-18 12:00:01')
on duplicate key update updateat= CASE
WHEN updateat > VALUES(updateat) THEN updateat
ELSE VALUES(updateat)
END;
✓
select * from goods;
id | name | updateat
-: | :--- | :------------------
1 | new | 2021-12-18 12:00:01
db<>fiddle here
So I have the following table:
CREATE TABLE Hospital_MedicalRecord(
recNo CHAR(5),
patient CHAR(9),
doctor CHAR(9),
enteredOn DATE NOT NULL,
diagnosis VARCHAR(50) NOT NULL,
treatment VARCHAR(50),
PRIMARY KEY (recNo, patient),
FOREIGN KEY (patient) REFERENCES Hospital_Patient(NINumber),
FOREIGN KEY (doctor) REFERENCES Hospital_Doctor(NINumber)
);
I want to make it so there are never more that 65,535 medical records for a single patient. Am I supposed to make a new statement or should I implement it in the table above. I can post the patient table if needed.
You would typically use a before insert trigger for this, that raises an error if the number of records for a patient reached the limit and a new insert is attempted:
delimiter //
create trigger Trg_Hospital_MedicalRecord
before insert on Hospital_MedicalRecord
for each row
begin
if (
select count(*) from Hospital_MedicalRecord where patient = new.patient
) = 65535 then
set msg = concat('Patient ', new.patient, ' cannot have more than 65535 records');
signal state '45000' set message_text = msg;
end if;
end
//
delimiter ;
I would assume that you should not allow a patient to be updated on an existing record. But if this may happen, then you also need a before update trigger (with the very same code).
Consider the following...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table(id SERIAL PRIMARY KEY, user_id INT NOT NULL);
INSERT INTO my_table (user_id)
SELECT 1
FROM (SELECT 1) x
LEFT
JOIN (SELECT user_id FROM my_table GROUP BY user_id HAVING COUNT(*) >=3) y
ON y.user_id = 1
WHERE y.user_id IS NULL
LIMIT 1;
This limits INSERTS to 3 per user_id.
I have a users table with autoincrement id as primary key, and a unique key on email. The email and name fields are from Facebook.
+----+-------+------+
| id | email | name |
+----+-------+------+
And a facebook_users table,
+--------+----------+
| userId | fbUserId |
+--------+----------+
The id is a foreign key to userId on the Facebook table. The fbUserId is guaranteed to be unique by Facebook. The reason I split these tables up is I plan to have more social logins in the future, and I'm trying to make my schema future proof.
Right now my insert update query is this
BEGIN;
INSERT IGNORE INTO users
(id,email,name)
VALUES (0,?,?);
INSERT IGNORE INTO users_facebook
(userId, fbUserId)
VALUES (LAST_INSERT_ID(), ?);
COMMIT
From what I understand, ON DUPLICATE KEY UPDATE only applies to a single row in a single table.
What I'd like to do is if one of my existing users (meaning I have their fbUserId in my database) changes their email or name on Facebook, I'd like to update the users table.
The way I'm doing it now seems.. "backward"? Because fbUserId is the "real" primary key, but I'm trying to insert into users first.
Also, any thoughts on my current query? I'm quite new to MySQL.
EDIT: I ended up moving email to facebook table and using a stored procedure.
users table is id, name ;
facebook_users table is userId, email, fbUserId;
Procedure:
CREATE PROCEDURE 'facebook_user_login'(
firstName varchar(50),
lastName varchar(50),
email varchar(50),
fbUserId bigint(8)
)
BEGIN
IF NOT EXISTS (select * from users_facebook a where a.fbUserId = fbUserId) THEN
BEGIN
INSERT INTO users values (0,'eric','guan');
INSERT INTO users_facebook values (LAST_INSERT_ID(),email, fbUserId);
END;
ELSEIF EXISTS (select * from users_facebook a where a.fbUserId = fbUserId and a.email != email ) THEN
UPDATE users_facebook set email = email;
END IF;
END
I have 6 columns in my table:
Id | Name | Mail id | Gender | Contact Number | father name
while inserting a data into table i wanted to check condition like if Name,mailid,contact number already exists then insert should not happen else record should be inserted.
Can any one suggest how to check the condition while inserting a record.
IF NOT EXISTS (SELECT * FROM Table_Name WHERE Condition you are checking)
BEGIN
INSERT INTO ............. ---<----- Your Insert Statement.....
END
You can define an index on multiple columns, e.g.:
CREATE UNIQUE INDEX arbitrary_index_name ON table_name (Name, mailid, contactnumber);
I also faced similar situation, you can do this by adding unique constraint to your table and using 'insert ignore' statement to add data.
Create table statement:
CREATE TABLE Student (
Id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(50),
Mailid VARCHAR(50),
Gender CHAR,
contactnumber BIGINT,
fathername VARCHAR(50),
UNIQUE(NAME,Mailid,contactnumber));
Insert Ignore statement:
INSERT IGNORE INTO student(NAME, Mailid,Gender,contactnumber,fathername) VALUES('Shekhar', 's#s.com', 'M', 987654321, 'Joshi');
Mysql table
create table table1(
id int(3) zerofill auto_increment primary key,
username varchar(10)
)
engine=innodb;
Mysql insert query
insert into table1 (username)
select id from (select id from table1) as a where
a.id=last_insert_id();
I am trying to insert into a table by selecting the last id from the same table and the same row,the above queries give the explanation of what i want to do.The insert query gives null value in both the id and username.
The expected results is below.
id username
001 001
002 002
003 003
A possible approach
INSERT INTO table1 (username)
SELECT LPAD(COALESCE(MAX(id), 0) + 1, 3, '0')
FROM table1
Here is SQLFiddle demo
A drawback of this approach is that under heavy load different concurrent users may get the same MAX(id) and you'll end up with rows that have different ids but the same username.
Now, the more precise way to do it involves a separate sequencing table and a BEFORE INSERT triger
Proposed changed table schema
CREATE TABLE table1_seq
(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
);
CREATE TABLE table1
(
id INT(3) ZEROFILL PRIMARY KEY DEFAULT 0,
username VARCHAR(10)
);
The trigger
DELIMITER $$
CREATE TRIGGER tg_table1_before_insert
BEFORE INSERT ON table1
FOR EACH ROW
BEGIN
INSERT INTO table1_seq VALUES(NULL);
SET NEW.id = LAST_INSERT_ID(), NEW.username = LPAD(NEW.id, 3, '0');
END$$
DELIMITER ;
Now you just insert new rows into table1 like this
INSERT INTO table1 (username)
VALUES (NULL), (NULL)
Outcome:
| ID | USERNAME |
-----------------
| 1 | 001 |
| 2 | 002 |
Here is SQLFiddle demo
Why store the value at all?
CREATE TABLE table1 (
id int(3) zerofill auto_increment PRIMARY KEY
);
CREATE VIEW oh_look_username
AS
SELECT id
, LPad(Cast(id As varchar(10)), 3, '0') As username
FROM table1