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

huangapple go评论81阅读模式
英文:

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

问题

我是一个初学者,正在使用MySQL Workbench 8.0,似乎无法使其正常工作。我有三个表格。

这是我的表格:

  1. 书籍 (id=pk)
id title author
1 我的第一本书 莎拉
2 我的第二本书 马曼
3 我的第三本书 巴巴
  1. 用户 (id=pk)
id full_name last_login
1 阿拉 1973-07-03
2 巴迪 1972-07-03
3 萨莉 2015-10-18
  1. 借阅记录 (id=pk, user_id=fk, book_id=fk)
id 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 是 users 表格的子表。

  • book_id=fk 是 books 表格的子表。

我的解决方案是:当book_id列有重复时,return_date列不应该有重复。

所以在插入第4行时,它不应该允许我;因为用户(3)不能借阅没有return_date的书籍。

我尝试过在这两列 (book_id, return_date) 上建立唯一索引,以便拥有唯一组合,但由于MySQL允许NULL值重复,我没有取得任何进展。

而且我也不懂PHP或Python。

还有哪些其他解决方案可以尝试?

你的帮助对我来说意义重大

对于我的英文,我很抱歉。

英文:

im a beginner and using mysql workbench8.0 ,and it seems like i cant get this to work. ive got three tables.

these are my tables:

1.books(id=pk)

id title author
1 my first book sara
2 my second book maman
3 my third book baba

2.users (id=pk)

id full_name last_login
1 ala 1973-07-03
2 badi 1972-07-03
3 saly 2015-10-18

3.checkouts (id=pk, user_id=fk , book_id=fk)

id user_id book_id checkout_date return_date
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
4 3 2 2016-10-18 it should give me an error
  • user_id=fk is the child table of userstable.

  • book_id=fk is the child table of the books table.

My solution is : when there is a duplicate for the book_id column, there cant be duplicates for the return_date column.

so when inserting row number 4, its not supposed to let me ; because user (3) cant checkout sth that doesnt have a return_date.

ive tried unique index for those two columns(book_id,return_date ) so that i can have unique combos , but since MySQL allows duplicates for NULL values, i didnt get anywhere with that.

and i also still dont know php or python.

what are the other solutions that ill be able to try?

Your help would mean a lot

and sorry for my english

答案1

得分: 0

你可以使用触发器来阻止这种情况。

对于触发器,最终需要添加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)
error
INSERTTriggerError: Trying to checkout book, that isn't returned.
SELECT * FROM checkouts
id user_id book_id checkout_date return_date
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

fiddle

英文:

you can use a trigger to prevent that

for the trigger you need eventually to add DELIMITER, depend on gui and method

In bulk insert will 1 error prevent the insertion of all rows ind that session

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)

> error
> INSERTTriggerError: Trying to checkout book, that isn't returned.
>

SELECT * FROM checkouts
id user_id book_id checkout_date return_date
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

fiddle

答案2

得分: 0

你可以创建一个存储过程来执行结帐逻辑。它会锁定表,以便检查书是否已被借阅,然后紧接着的插入将被视为一个"关键部分",只允许一次调用该存储过程运行。除非借书率非常高,否则这不应对性能产生不利影响,并确保两个人不能借阅同一本书。

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;

    /* 书是否已经被借出? */
    SELECT COUNT(*) INTO row_count FROM checkouts
    WHERE book_id = checkout_book_id AND return_date IS NULL
    FOR UPDATE; /* 这实际上锁定了表 */

    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
        /* 书已经被借出。 */
        ROLLBACK;
        SET return_code = 1;
    END IF;

END$$
DELIMITER ;

创建了存储过程后,你可以这样做:

SET @return_code = 0;
/* 用户ID 21想借阅书ID 37: */
CALL checkout(21, 37, @return_code);
/* 0表示成功,否则表示失败: */
SELECT @return_code;
英文:

You could create a stored procedure that does the checkout logic for you. It locks the table so that the check for whether the book has been checked out or not followed by the subsequent insert will be treated as a "critical section" only allowing a single call of the procedure to run at a time. Unless the checkout rate is very high, this should not adversely affect performance and ensures that two individuals cannot check out the same book.

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 ;

Having created the stored procedure, you could do:

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;

huangapple
  • 本文由 发表于 2023年6月18日 22:28:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/76501036.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定