sourcetip

두 개 이상의 항목을 루프할 때 루프가 끊기는 동안 MariaDB 절차

fileupload 2023. 8. 16. 22:32
반응형

두 개 이상의 항목을 루프할 때 루프가 끊기는 동안 MariaDB 절차

여러분, 안녕하십니까.

MariaDB 10.3 저장 프로시저에 대한 질문이 있습니다.절차의 논리에 따라 항목 표를 출력할 수 있는 절차를 생성하려고 합니다.

제가 디자인하고 있는 게임의 경우, 프로시저는 플레이어에게 무작위 보상을 출력해야 합니다. 무작위 아이템을 생성하기 위해, 저는 무작위 목록을 만드는 동안 사용할 매개 변수가 있는 옵션 테이블을 가지고 있습니다.

절차는 다음을 수행해야 합니다.

  1. 데이터베이스에서 1부터 5까지의 임의의 숫자를 가져옵니다.
  2. 무작위 항목을 생성하기 위해 매번 생성된 총 수량을 반복합니다.
  3. 모든 항목의 목록을 반환합니다(pphp로, 여기서 proc를 호출할 것입니다).

1: 수량 생성

'@loopquantity'라는 변수는 플레이어 보상으로 아이템을 생성해야 하는 횟수를 나타냅니다.이러한 카운트는 다음과 같은 테이블에 저장됩니다.

항목 수 난수 범위
1 0|0.59
2 0.59|0.84
3 0.84|0.965
4 0.965|0.99
5 .99|1.0

(테스트하려면 create + insert를 선택합니다):

CREATE TABLE IF NOT EXISTS `options` (
  `option_id` int(11) NOT NULL AUTO_INCREMENT,
  `option_class` varchar(100) NOT NULL,
  `option_name` varchar(500) NOT NULL,
  `option_value` varchar(999) NOT NULL,
  `sorter` int(11) DEFAULT NULL,
  PRIMARY KEY (`option_id`)
) ENGINE=InnoDB AUTO_INCREMENT=180 DEFAULT CHARSET=latin1;

/*!40000 ALTER TABLE `options` DISABLE KEYS */;
INSERT INTO `options` (`option_id`, `option_class`, `option_name`, `option_value`, `sorter`) VALUES
    (169, 'reward rarity', 'basic', '0|.8', 0),
    (170, 'reward rarity', 'rare', '.8|.95', 1),
    (171, 'reward rarity', 'unique', '.95|1', 2),
    (172, 'reward type', 'weapon', '0|.33', 1),
    (173, 'reward type', 'armor', '0.33|.66', 2),
    (174, 'reward type', 'gadget', '.66|1', 3),
    (175, 'reward count', '1', '0|0.29', 1),
    (176, 'reward count', '2', '0.29|0.84', 2),
    (177, 'reward count', '3', '0.84|0.965', 3),
    (178, 'reward count', '4', '0.965|.99', 4),
    (179, 'reward count', '5', '.99|1', 5);
/*!40000 ALTER TABLE `options` ENABLE KEYS */;

MariaDB의 RAND() 함수를 사용하여 0에서 1 사이의 난수를 생성할 수 있습니다.이 행운의 숫자를 사용하여 아래 조각은 데이터베이스에서 임의의 수량을 생성합니다.

select loopquantity into @loopquantity  
from (
    select CAST(option_name as int) loopquantity,
           CAST(SUBSTRING_INDEX(option_value,'|',1) as decimal(18,4)) AS random_number_start,
           CAST(SUBSTRING_INDEX(option_value,'|',-1) as decimal(18,4)) AS random_number_end
      from options
      where option_class = 'reward count' and option_name != '1' 
) rr  
inner join (
     select cast(rand() as decimal(18,4)) luckynumber 
) rl on rl.luckynumber > rr.random_number_start and rl.luckynumber <= rr.random_number_end; 

select @loop quantity를 실행하면 항상 1과 5 사이의 정수가 반환됩니다. 지금까지는 양호합니다.(코드로 하는 것보다 옵션 테이블에서 치수를 가져오는 이유는 게임이 실행 중일 때 코드를 변경할 필요 없이 보상 카운트 기회를 쉽게 변경할 수 있기 때문입니다.)

2: 항목을 반복합니다.

잠시 후 루프를 사용하여 실제 항목 생성을 반복합니다.먼저, 나는 다음과 같이 하여 루프 int 변수를 만듭니다.SET @loopstart = 1;그런 다음 다음 다음과 같은 시간 루프를 추가합니다.

 WHILE @loopstart <= @loopquantity DO
    [create the item]
    SET @loopstart = @loopstart + 1;
 END WHILE;

3: 데이터 반환

데이터를 반환하기 위해 결과라는 임시 테이블을 만듭니다. 여기서 while 루프의 각 반복은 에 삽입할 수 있습니다.그러고 나서 잠시 후, 저는 그냥 합니다.select * from results.

작성 내용은 다음과 같습니다.

DROP TEMPORARY TABLE IF EXISTS results;
CREATE TEMPORARY TABLE results (
      item_id          INT default null,
      item_name        VARCHAR(50) default null,
      item_description VARCHAR(500) default null,
      rarity           VARCHAR(10) default null,
      type             VARCHAR(10) default null,
      icon             VARCHAR(100) default null,
      item_level       int default null,
      quantity         int default null,
      loopquantity     int default null
);

전체 proc 생성 코드(phpmyadmin에서 내보내기)

DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `mission_get_unidentified_rewards`()
BEGIN 
        SET @loopstart = 1;
        
        select loopquantity into @loopquantity
        from (select cast(option_name as int) loopquantity, 
                     CAST(SUBSTRING_INDEX(option_value,'|',1) as decimal(18,4)) AS random_number_start,
                     CAST(SUBSTRING_INDEX(option_value,'|',-1) as decimal(18,4)) AS random_number_end
              from options
              where option_class = 'reward count') rr
        inner join (select cast(rand() as decimal(18,4)) luckynumber) rl on rl.luckynumber > rr.random_number_start and rl.luckynumber <= rr.random_number_end;
        
        if @loopquantity is null then set @loopquantity = 1; end if;
        
        DROP TEMPORARY TABLE IF EXISTS results;
        CREATE TEMPORARY TABLE results (
            item_id INT default null,
            item_name VARCHAR(50) default null,
            item_description VARCHAR(500) default null,
            rarity VARCHAR(10) default null,
            type VARCHAR(10) default null,
            icon VARCHAR(100) default null,
            item_level int default null,
            quantity int default null,
            loopquantity int default null
        );

        WHILE @loopstart <= @loopquantity DO
            insert into results
            with rarity_rates as (
                select option_name rarity, 
                CAST(SUBSTRING_INDEX(option_value,'|',1) as decimal(18,4)) AS random_number_start,
                CAST(SUBSTRING_INDEX(option_value,'|',-1) as decimal(18,4)) AS random_number_end
                from options
                where option_class = 'reward rarity'
            ),
            rarity_luckynum as (
                select cast(rand() as decimal(18,4)) luckynumber
            ),
            rarity as (
                select rarity
                from rarity_rates rr
                inner join rarity_luckynum rl on rl.luckynumber > rr.random_number_start and rl.luckynumber <= rr.random_number_end
            ),
            type_rates as (
                select option_name `type`, 
                CAST(SUBSTRING_INDEX(option_value,'|',1) as decimal(18,4)) AS random_number_start,
                CAST(SUBSTRING_INDEX(option_value,'|',-1) as decimal(18,4)) AS random_number_end
                from options
                where option_class = 'reward type'
            ),
            type_luckynum as (
                select cast(rand() as decimal(18,4)) luckynumber
            ),
            item_type as (
                select `type`
                from type_rates rr
                inner join type_luckynum rl on rl.luckynumber > rr.random_number_start and rl.luckynumber <= rr.random_number_end
            ),
            item_level as (
                select item_level
                from (
                    select option_name item_level, 
                           CAST(SUBSTRING_INDEX(option_value,'|',1) as decimal(18,4)) AS random_number_start,
                           CAST(SUBSTRING_INDEX(option_value,'|',-1) as decimal(18,4)) AS random_number_end
                    from options
                    where option_class = 'reward count'
                ) rr
                inner join (select cast(rand() as decimal(18,4)) luckynumber) rl on rl.luckynumber > rr.random_number_start and rl.luckynumber <= rr.random_number_end

            )
            select gi.item_id, gi.item_name, gi.item_description, gi.rarity, gi.type, gi.icon, item_level, 1 quantity, @loopquantity
            from rarity ra
            cross join item_type it
            cross join item_level il
            inner join game_items gi on ra.rarity = gi.rarity and it.type = gi.type and item_name like 'unidentified%';

            SET @loopstart = @loopstart + 1;
        END WHILE;
        select * from results;
END$$
DELIMITER ;

그 문제는

@loopquantity는 1이고, proc는 예상대로 작동합니다.@loop 수량이 1보다 크면 시간이 더 오래 걸리고 오류가 발생합니다.

분석 중 2개의 오류가 발견되었습니다.

인식할 수 없는 문 유형입니다.(0 위치에서 "WHERE" 근처)

인식할 수 없는 문 유형입니다.(위치 2584의 "끝" 근처)

MySQL에서 다음과 같이 말했습니다.문서화

#2006 - MySQL 서버가 사라졌습니다.

따라서 수량이 1인 경우에는 while 루프가 좋습니다.1개 이상의 루프를 통과해야 할 때는 끊어집니다.

@loop 수량이 다음일 때 proc/code가 중단됩니다.> 1내가 알 수 있는 가장 좋은 것은 while 루프가 무한정 지속되고 따라서 MariaDB가 시간 초과된다는 것입니다.1회 반복에 대해 실행되는 코드는 밀리초 단위이므로 반복당 여러 초가 걸리지 않습니다.저도 프로세스에 문제가 있는 것을 발견할 수 없습니다. 한 번의 실행이 완벽하게 작동한다는 사실은 모든 것이 작동한다는 것을 보여줍니다.

반복 작업을 여러 번 해야 할 때 쉬는 이유를 아는 사람이 있습니까?

편집:

완전성을 위해 game_dll 테이블의 생성 및 값을 다음과 같이 입력합니다.

CREATE TABLE IF NOT EXISTS `game_items` (
  `item_id` int(11) NOT NULL AUTO_INCREMENT,
  `created_by` int(11) NOT NULL DEFAULT 0,
  `item_name` varchar(50) DEFAULT NULL,
  `item_description` varchar(250) DEFAULT NULL,
  `faction` int(11) DEFAULT NULL ,
  `icon` varchar(150) DEFAULT NULL,
  `flags` text DEFAULT NULL ,
  `type` varchar(50) DEFAULT NULL ,
  `subtype` varchar(50) DEFAULT NULL ,
  `rarity` varchar(50) DEFAULT NULL ,
  `level_requirement` int(11) DEFAULT NULL,
  `vendor_value` int(11) DEFAULT NULL 
  PRIMARY KEY (`item_id`)
)

insert in game_items (created_by, item_name, item_description, 파벌, 아이콘, 플래그, 유형, 하위 유형, 희귀, 레벨_요건, 벤더_value) 값 (0, 'M3 트렌치 나이프: 미군 트렌치 나이프, 6.75인치 블레이드 및 놋쇠 너클 핸들 포함), 1, NULL, 'Weon', 'Wapon', 1, 10', 기본 나이프, 10, 10', 10' 나이프)


Also, I have tried to switch the WHILE to a FOR as its numerical between 1 and 5, but i cant get that to work at all. Proc wont save with that. 

mysql 구성 파일 하단에 있는 코드의 아래 줄을 사용합니다.( my.ini ) 또는 단순히 max_allowed_message 값 변경

max_allowed_packet=10240M

위치는 설치 및 운영 체제에 따라 다릅니다.

예를 들어 Xampp의 경우 xampp\mysql\bin\my.ini에 위치합니다.

그런 다음 mysql 서비스를 다시 시작합니다.

또한 글로벌 값을 업데이트하여 확인할 수 있습니다.

SET GLOBAL max_allowed_packet=1073741824;

언급URL : https://stackoverflow.com/questions/76145618/mariadb-procedure-while-loop-breaking-when-looping-through-more-than-1-item

반응형