I have a case that confused and annoyed me for a long time and currently I am struggling whether I should use trigger.
Here I have two table in MYSQL
Create table type(
id int primary key not null auto_increment,
class int not null default 0,
name string not null
)
Create table buddy (
id int primary key not null auto_increment,
value string not null)
Create table value (
id int primary key not null auto_increment,
type_id int not null,
buddy_id int default null,
value string not null,
relied_id int default null
foreign key type_id references type(id)
)
in this case, there are several rules,
Inside value table, some value may refer to the buddy table, while some are not.
if (the buddy_id inside value is not null){
row with this buddy_id should be unique
} else{
could have several instances with the nil buddy_id
}
If the buddy_id is nil, which means it does not refer to anything, it could be duplicate.
In my module, I will receive the message from my colleague's queue and then do the following things:
1), if I detect inside the message, there is a buddy_id, I need to use sql to check whether the value with this buddy_id is already in the database, if yes, just ignore, else should create one.
2), else, directly create a new one
in the first case, my colleague is keep sending me the duplicate message, as after I get "whether the instance with this buddy_id is already inside database" as result as false, it still needs to take sometime to do some logic and create a new instance, then insert into database. There maybe a gap between I got the result and insert.
During this period, another message may come with the same buddy_id and it will trigger another go routine or thread, but the first insert is still not done, in this case, it will also find "instance with this buddy_id is not inside databases, I should create one".
As a result, more than one instance with same buddy_id will be insert into database.
I think this pic could better illustrate what I am talking, two messages with same buddy_id come one by one, but when message 1 with buddy_id = 10 is processing, before inserting, message 2 comes and find there is no buddy_id = 10, so message 2 also creates an instance in database.
Previously, I tried to lock the table, but the project manager asked me to remove the lock as this value table is a main table and he does not want me to lock the database.
I also had a look of the unique constraint but the case for buddy_id is "if it is nil, no unique, if it is not nil, unique". I think this cannot be done just using the unique key constraint.
So anyone has idea whether I should use trigger to do the check before insert to make sure the instance is not duplicate? Or any other recommended methods?
Thanks.
Related
I have decided to use mysql sequence table, since I am using spring jdbc batch insert (can't get primary key with this feature), where I will be pass generated key while inserting each row, I have googled long time now, didnt get proper way of creating sequence table.
I have created a sequence table
create table table_sequence (value int not null) ENGINE = MYISAM;
but I feel it seems to be very basic, since I need to have max value, and cache limit for each instance.
I have many tables, do I need to have one sequence table for each table?
I have very less idea about db sequence, so suggestion are helpful to me. thanks
this may help you:
http://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html
CREATE TABLE animals (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
name CHAR(30) NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM;
INSERT INTO animals (name) VALUES
('dog'),('cat'),('penguin'),
('lax'),('whale'),('ostrich');
SELECT * FROM animals;
RELATIONSHIP : students (1 can have N) addresses
SCENARIO: Students can have many records but only one associated record must have 'current' field set as 'Yes' (other value is NULL) so the query below should always return only one record per student.
SELECT * FROM address WHERE student_id = 5 AND current = 'Yes'
PROBLEM:
People sometimes mark more than one record as 'Yes' after INSERT or UPDATE for same student so I need to avoid it. What is the best way of doing it by using triggers or stored procedures within MySQL?
If UPDATE happens on 'address' table then this should run somewhere to mark other records as NULL: UPDATE addresses SET current = NULL WHERE student_id = IN_student_id
If INSERT happens on 'address' table then this should run somewhere to mark other records as NULL: UPDATE addresses SET current = NULL WHERE student_id = IN_student_id AND id <> IN_inserted_id
Thanks in advance
If you need something updated automatically after data is modified, the right approach is a trigger. Notice a trigger may call a stored procedure.
However you will not be able to implement the described behaviour in a trigger because:
A stored function or trigger cannot modify a table that is already being used (for reading or writing) by the statement that invoked the function or trigger.
In fact, the information "Address X is the current address" should be stored in a column in the students table, as a foreign key to the address table. Therefore, unicity is guaranteed.
Something like this (fiddle with it here):
CREATE TABLE student (
id INT NOT NULL PRIMARY KEY,
current_address INT,
name VARCHAR(20)
);
CREATE TABLE address (
id INT NOT NULL PRIMARY KEY,
student_id INT NOT NULL,
contents VARCHAR(50) NOT NULL,
CONSTRAINT student_fk FOREIGN KEY student_fk_idx (student_id)
REFERENCES student(id)
);
ALTER TABLE student
ADD CONSTRAINT curraddr_fk_idx
FOREIGN KEY curraddr_fk_idx (id, current_address)
REFERENCES address(student_id, id);
Notice this structure allows insertion of students with no "current address". This is because at least one for the two tables must allow a NULL value for their foreign key (or else we cannot add a single row in either table). If it makes more sense, let address.student_id be NULL instead, and allow an address to be nobody's address until you create the corresponding student.
How can I create a Message table such as this in mySQL where it stores messages and their responses?
You can try this:
create table messages (
message_id int primary key,
response_to int null references messages(message_id), -- self relationship
user_from int not null references users(user_id),
user_to int not null references users(user_id),
content varchar(200) not null
);
The first message will have a null value un response_to field.
As a side note, if you are planning to store "conversations" instead of "messages", consider a plain table with a CLOB (character large object) to store a JSON or XML representation of the conversation. It will speed up your queries (if you are always planning to read the entire conversation at once instead of individual messages).
You can create a foreign_key which references the original messageId, but do not forget to allow null values since original messages will not have this key set.
But is'nt it a better approach to have a thread table, and then in the messages table to save the threadId so you can match which messages belong to which thread, the posting time could be a good indicator to identify response messages.
I am developing a HTML5 multiplayer game, where I have a m:n relation between tables "keyword" and "model", like the following image shows:
keyword.id and model.id are auto_increment unsigned int and keyword.keyword is an unique index.
For the sake of efficiency, I am searching for a way to manage the relation. The trivial way would be:
Check if keyword already exist
If yes: update timesCount and roundCount from model_has_keyword
If no: insert into keyword and insert into model_has_keyword
But with a growing number of users playing simultaneously, I'm afraid that the trivial way will become too slow. So what is the most efficient way to do this?
While searching on StackOverflow, I've stumpled upon two ideas, but I think they both don't fit my needs.
INSERT INTO table ON DUPLICATE KEY UPDATE col=val
If INSERT INTO would be processed, I would need to trigger another INSERT INTO statement to insert into both tables keyword and model_has_keyword
REPLACE statement: if I replace the record in table keyword, id is assigned the next auto-increment value, so that the reference for table model_has_keyword is lost.
As I'm not an expert, please correct me if I misunderstood something here.
You need to check whether the relation exists, and then insert/update.
A.- Enclose the an INSERT query in a try block, and, in case of error, make an update query. That would save all the checks when the relation doesn't exists...
B.- Make all INSERT ON DUPLICATE UPDATE. This is gonna do exactly the same as in "A", but you don't need to worry about exceptions.
Definitely your second idea is absolutely wrong.
I would not create a table keywords, since you only need the keyword itself... then I would define my model_has_keyword like this:
CREATE TABLE model_has_keyword (
model_id INT NOT NULL,
keyword VARCHAR(50) NOT NULL,
timesCount INT NOT NULL,
roundCount INT NOT NULL,
PRIMARY KEY (model_id,keyword)
);
and update it like this:
INSERT INTO model_has_keyword
(model_id,keyword,timesCount,$myIntValue)
VALUES
($model_id,$keyword,0,0)
ON DUPLICATE KEY UPDATE
timesCount=timesCount+1,roundCount=roundCount + $myIntValue
I want a table with an integer column, that may or may not be filled (it is a social security number). But if it is filled, I want it to be UNIQUE : there cannot be two entries of the same number.
Using a unique constraint won't work cause integer won't accept NULL values, and MySQL detects multiple 0 values.
How can I set a unique constraint on an integer with a default value ? Or how can I set the integer column to accept NULL values ? (this question takes it for granted : MySQL Foreign Key Constraint - Integer Column but I can't)
create table test (
myint INT NULL, UNIQUE INDEX (myint)
);
This will allow a unique constraint on any integers added but will allow multiple NULL values to be entered.
MySQL treats NULL as 'unknown' value so cant possibly do a comparison to see if a like value is already there 'unknown' !== 'unknown'.
This also depends on which database engine you are using, the above holds true for MyISAM and InnoDB
Please see the code below:
ALTER TABLE test MODIFY myint INT NULL
ALTER TABLE test ADD UNIQUE INDEX (myint)
It works when data inputs are directly from MySQL (PHPMyadmin), saved as NULL, but a php script saves it as zeroes, and so it does not allow multiple entries.