一个库数据库,目的是防止多个用户尝试同时借出同一本书 Mysql Workbench 8.0

a library db , the goal is to prevent more than one user trying to check out the same book at the same time Mysql workbench 8.0

提问人:Setayesh 提问时间:6/18/2023 更新时间:6/20/2023 访问量:31

问:

我是初学者并使用mysql workbench8.0,似乎我无法让它工作。我有三张桌子。

这些是我的表格:

1.书籍(id=pk)

编号 标题 作者
1 我的第一本书 萨拉
2 我的第二本书 马曼
3 我的第三本书 巴 巴

2.用户(id=pk)

编号 full_name last_login
1 1973-07-03
2 巴迪 1972-07-03
3 萨利 2015-10-18

3.结账(id=pk, user_id=fk , book_id=fk)

编号 user_id book_id checkout_date return_date
1 1 1 2000-10-18 2017-10-18
2 1 2 2015-11-18
3 2 3 2015-10-28 2025-10-18
4 3 2 2016-10-18 它应该给我一个错误
  • user_id=fk 是 table 的子表。users

  • book_id=fk 是表的子表。books

我的解决方案是:当该列有重复项时,该列不能有重复项。book_idreturn_date

所以在插入第 4 行时,它不应该让我;因为用户 (3) 无法签出没有return_date的 STH。

我尝试了这两列(, )的唯一索引,以便我可以拥有唯一的组合,但是由于MySQL允许重复的值,因此我没有得到任何好处。book_idreturn_dateNULL

而且我仍然不懂PHP或Python。

还有哪些其他解决方案是我无法尝试的?

您的帮助意义重大

对不起我的英语

sql mysql null 外键

评论


答:

0赞 nbk 6/18/2023 #1

您可以使用触发器来防止这种情况发生

对于触发器,您最终需要添加 DELIMITER,具体取决于 GUI 和方法

批量插入将 1 个错误阻止插入该会话中的所有行

CREATE TABLE books
    (`id` int, `title` varchar(14), `author` varchar(5))
;
    
INSERT INTO books
    (`id`, `title`, `author`)
VALUES
    (1, 'my first book', 'sara'),
    (2, 'my second book', 'maman'),
    (3, 'my third book', 'baba')
;

CREATE TABLE users
    (`id` int, `full_name` varchar(4), `last_login` varchar(10))
;
    
INSERT INTO users
    (`id`, `full_name`, `last_login`)
VALUES
    (1, 'ala', '1973-07-03'),
    (2, 'badi', '1972-07-03'),
    (3, 'saly', '2015-10-18')
;

CREATE TABLE checkouts
    (`id` int, `user_id` int, `book_id` int, `checkout_date` Date, `return_date` date
  )
;
    

CREATE TRIGGER before_wcheckouts_insert
BEFORE INSERT
ON checkouts FOR EACH ROW
BEGIN
 DECLARE msg VARCHAR(100);
    IF EXISTS( SELECT 1 FROM checkouts 
  WHERE  `book_id` = NEW.`book_id` AND (`return_date` > NEW.`checkout_date` OR `return_date` IS NULL))   THEN
        set msg = "INSERTTriggerError: Trying to checkout book, that isn't returned.";
        signal sqlstate '45000' set message_text = msg;
    END IF; 

END 
INSERT INTO checkouts
    (`id`, `user_id`, `book_id`, `checkout_date`, `return_date`)
VALUES
    (1, 1, 1, '2000-10-18', '2017-10-18'),
    (2, 1, 2, '2015-11-18', NULL),
    (3, 2, 3, '2015-10-28', '2025-10-18')
;
INSERT INTO checkouts
    (`id`, `user_id`, `book_id`, `checkout_date`, `return_date`)
VALUES

  (4,   3 , 2   , '2016-10-18', NULL)
INSERTTriggerError: Trying to checkout book, that isn't returned.
SELECT * FROM checkouts
编号 user_id book_id checkout_date return_date
1 1 1 2000-10-18 2017-10-18
2 1 2 2015-11-18
3 2 3 2015-10-28 2025-10-18

小提琴

0赞 Booboo 6/18/2023 #2

您可以创建一个存储过程来执行签出逻辑。它锁定了表,以便检查书籍是否已签出,然后进行后续插入将被视为“关键部分”,一次只允许运行一次调用该过程。除非结账率非常高,否则这不会对性能产生不利影响,并确保两个人无法借出同一本书。

DROP PROCEDURE IF EXISTS checkout;

DELIMITER $$
CREATE PROCEDURE checkout(IN checkout_user_id, IN checkout_book_id INT, OUT return_code INT)
NOT DETERMINISTIC
SQL SECURITY INVOKER
BEGIN
    DECLARE row_count INT;

    START TRANSACTION;

    /* Was the book already checked out? */
    SELECT COUNT(*) INTO row_count FROM checkouts
    WHERE book_id = checkout_book_id AND return_date IS NULL
    FOR UPDATE; /* This essentially locks the table */
    
    IF row_count = 0 THEN
        INSERT INTO checkouts(user_id, book_id, checkout_date, return_date)
        VALUES(checkout_user_id, checkout_book_id, CURRENT_DATE(), NULL);
        COMMIT;
        SET return_code = 0;
    ELSE
        /* Book was already checked out. */
        ROLLBACK;
        set return_code = 1;
    END IF;
    
END$$
DELIMITER ;

创建存储过程后,可以执行以下操作:

SET @return_code = 0;
/* user_id 21 wants to checkout book_id 37: */
CALL checkout(21, 37, @return_code);
/* 0 for success otherwise failure: */
SELECT @return_code;

评论

0赞 nbk 6/19/2023
你想怎么找共产党人,如果找不到谁是首都,那好玩,但说真的,你必须在一些国家做记录