Restricción basada en el tiempo de MySQL

I have an auction application with these two tables (this is highly simplified, obviously):

create table auctions (
   auction_id int,
   end_time datetime
);

create table bids (
   bid_id int,
   auction_id int,
   user_id int,
   amount numeric,
   bid_time timestamp,
   constraint foreign key (auction_id) references auctions (auction_id)
);

I don't want bids on an auction after that auction has ended. In other words, rows in the bids table should be allowed only when the bid_time is earlier than the end_time for that auction. What's the simplest way to do this in MySQL?

preguntado el 31 de julio de 12 a las 12:07

3 Respuestas

Ufortunately MySQL does not have a CHECK constraint feature. But You should be able to enforce this using a trigger. However, MySQL trigger support isn't as advanced or well optimized as it is in other RDBMS-es, and you will suffer a considerable performance hit if you do it this way. So if this is a real-time trading system with massive amounts of concurrent users, you should look for another solution.

CREATE TRIGGER bir_bids
BEFORE INSERT ON bids
FOR EACH ROW
BEGIN
    DECLARE v_end_time datetime;
    -- declare a custom error condition. 
    -- SQLSTATE '45000' is reserved for that.
    DECLARE ERR_AUCTION_ALREADY_CLOSED CONDITION FOR SQLSTATE '45000';

    SELECT end_time INTO v_end_time
    FROM   auctions
    WHERE  auction_id = NEW.auction_id;

    -- the condition is written like this so that a signal is raised 
    -- only in case the bid time is greater than the auction end time.
    -- if either bid time or auction end time are NULL, no signal will be raised.
    -- You should avoid complex NULL handling here if bid time or auction end time
    -- must not be NULLable - simply define a NOT NULL column constraint for those cases.
    IF NEW.bid_time > v_end_time THEN
        SIGNAL ERR_AUCTION_ALREADY_CLOSED;
    END IF;
END:

Tenga en cuenta que SIGNAL syntax is available only since MySQL 5.5 (currently GA). Triggers are available since MySQL 5.0. So if you need to implement this in a MySQL version prior to 5.5, you need to hack your way around not being able to raise a signal. You can do that by causing some change in the data that will guarantee the INSERT to fail. For instance you could write:

    IF NEW.bid_time > v_end_time THEN
        SET NEW.auction_id = NULL;
    END IF;

Como acution_id se declara NOT NULL in the table, the state of the data will be such that it cannot be inserted. The drawback is that you will get a NOT NULL constraint violation, and the application will have to guess whether this is due to this trigger firing or due to a "real" NOT NULL constraint violation.

Para más información, ver: http://rpbouman.blogspot.nl/2009/12/validating-mysql-data-entry-with_15.html y http://rpbouman.blogspot.nl/2006/02/dont-you-need-proper-error-handling.html

Respondido 31 Jul 12, 13:07

Insert into bids (auction_id, user_id, amount, bid_time)
Select auction_id, [USER_ID], [AMOUNT], [TIMESTAMP]
From auctions
WHERE UNIX_TIMESTAMP() <= UNIX_TIMESTAMP(end_time)

Of course you have to replace the '[]' values

Respondido 31 Jul 12, 12:07

Can you give some more context? Would this have to be executed with every new bid? I would prefer to set a constraint so that I don't have to remember to enter bids in a specific way every time. - Dan B.

This should be your insert statement, so yes, this will be executed with every new bid. But of you can use UNIX_TIMESTAMP() instead of replacing [TIMESTAMP] by some other input. You can use the trigger like something as given by @Roland Bouman, but I think my answer is much easier. - arnoudhgz

Thanks. Given the limitations outlined by Roland, I will probably use something like this insert statement. - Dan B.

Ok great, maybe you should tag this answer as the accepted answer @Dan-B - arnoudhgz

instead do a simple thing create a column name status. set its type to enum. when you want to block it update its value to 0. default should be 1 means open. 0 means closed

Respondido 31 Jul 12, 12:07

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.