Как я могу использовать два курсора в той же стандартной программе? Если я удаляю второе объявление курсора и выбираю цикл, все хорошо работает. Стандартная программа используется для добавления друга в моем веб-приложении. Это берет идентификатор текущего пользователя и электронное письмо друга, которого мы хотим добавить как друг, затем это проверяет, имеет ли электронная почта соответствующий идентификатор пользователя и если никакой друг, отношение существует, оно создаст тот. Любое другое стандартное решение, чем этот было бы отличным также.
DROP PROCEDURE IF EXISTS addNewFriend;
DELIMITER //
CREATE PROCEDURE addNewFriend(IN inUserId INT UNSIGNED, IN inFriendEmail VARCHAR(80))
BEGIN
DECLARE tempFriendId INT UNSIGNED DEFAULT 0;
DECLARE tempId INT UNSIGNED DEFAULT 0;
DECLARE done INT DEFAULT 0;
DECLARE cur CURSOR FOR
SELECT id FROM users WHERE email = inFriendEmail;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur;
REPEAT
FETCH cur INTO tempFriendId;
UNTIL done = 1 END REPEAT;
CLOSE cur;
DECLARE cur CURSOR FOR
SELECT user_id FROM users_friends WHERE user_id = tempFriendId OR friend_id = tempFriendId;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur;
REPEAT
FETCH cur INTO tempId;
UNTIL done = 1 END REPEAT;
CLOSE cur;
IF tempFriendId != 0 AND tempId != 0 THEN
INSERT INTO users_friends (user_id, friend_id) VALUES(inUserId, tempFriendId);
END IF;
SELECT tempFriendId as friendId;
END //
DELIMITER ;
] Я наконец-то написал другую функцию, которая делает то же самое:[
] [DROP PROCEDURE IF EXISTS addNewFriend;
DELIMITER //
CREATE PROCEDURE addNewFriend(IN inUserId INT UNSIGNED, IN inFriendEmail VARCHAR(80))
BEGIN
SET @tempFriendId = (SELECT id FROM users WHERE email = inFriendEmail);
SET @tempUsersFriendsUserId = (SELECT user_id FROM users_friends WHERE user_id = inUserId AND friend_id = @tempFriendId);
IF @tempFriendId IS NOT NULL AND @tempUsersFriendsUserId IS NULL THEN
INSERT INTO users_friends (user_id, friend_id) VALUES(inUserId, @tempFriendId);
END IF;
SELECT @tempFriendId as friendId;
END //
DELIMITER ;
]
[] Надеюсь, это лучшее решение, оно все равно работает хорошо. Спасибо, что сказали мне не использовать курсоры, когда в этом нет необходимости.[
]Вместо того, чтобы использовать курсоры для проверки существования записей, можно воспользоваться пунктом EXISTS в пункте WHERE:
INSERT INTO users_friends
(user_id, friend_id)
VALUES
(inUserId, tempFriendId)
WHERE EXISTS(SELECT NULL
FROM users
WHERE email = inFriendEmail)
AND NOT EXISTS(SELECT NULL
FROM users_friends
WHERE user_id = tempFriendId
AND friend_id = tempFriendId);
Я внес изменения после прочтения комментариев Пола о втором запросе, и изменил логику так, чтобы вставка не добавляла дубликатов. В идеале это должно быть обработано как первичный ключ, являющийся составным ключом (включающим в себя два или более столбцов), что бы остановило необходимость проверки в коде.
Вау, я не знаю, что сказать, пожалуйста, пойдите и читайте и читайте и изучите SQL, без обиды, но это среди худшего SQL, который я когда-либо кажутся.
SQL - это установленный на основе языка, курсорами, в целом, плохие, существуют ситуации, когда они полезны, но они довольно редки. Ваше использование курсоров здесь совершенно неуместно.
Ваша логика во втором курсоре также не повреждена, поскольку она выберет любую запись, которая вписывает друга, а не только необходимую дружбу.
Если вы хотите исправить его, вы можете попробовать дать второй курсор разумное имя, но предпочтительно запускаться.
Установите соединение PK или уникальное ограничение на пользователя_Други, то вам не нужно беспокоиться о проверке отношений, то попробуйте что-то вроде этого.
INSERT INTO users_friends
SELECT
@inUserId,
users.user_id
FROM
users
WHERE
email = @inFriendEmail
Я знаю, что вы нашли лучшее решение, но я считаю, что ответ на ваш исходный вопрос состоит в том, что вам нужно установить SET Done = 0; между двумя курсорами, иначе второй курсор будет извлекать только одну запись перед выходом из цикла из-за Done = 1 из предыдущего обработчика.