2017-02-01 5 views
0

동시 쿼리가 작업을 엉망으로 만들지 않도록 쿼리를 작성하고 있습니다. 나는 그것을 할 수있는 훨씬 더 좋은 방법이 있다고 확신합니다, 그래서 여기에 있습니다.INSERT 문에서 다중 SELECT를 사용하여 경쟁 조건 방지

기본적으로 테이블의 컬럼은 다음과 같이 :

  • 지난 경우 : 나는 새 행을 삽입 할 때

    id  : INT(10) unsigned auto_incremented 
    number : INT(6) unsigned zerofill 
    date : DATETIME 
    

    가, 여기에 내가 그 값을 설정해야하는 방법이다 삽입 된 행의 날짜가 일 때 번호 = 마지막으로 삽입 된 행 번호 +1

  • 전자 마지막으로 삽입 된 행의 날짜 어제 또는 내가 생각

1 = 다음 번호, 전에 대해 먼저 마지막 행의 날짜와 번호를 선택, PHP를 사용하여 새 번호를 설정 한 다음 내 삽입을합니다.

$pdo->beginTransaction(); 

$stmt = $pdo->prepare("SELECT `number`, `date` FROM `table` ORDER BY `id` DESC LIMIT 1"); 
$stmt->execute(); 
$infos = $stmt->fetch(PDO::FETCH_ASSOC); 

$date = date("Y-m-d H:i:s"); 
if (date("Ymd", strtotime($infos['date'])) < date("Ymd", strtotime($date))) { 
    $number = 1; 
} else { 
    $number = $infos['number'] + 1; 
} 

$stmt2 = $pdo->prepare("INSERT INTO `table` (`number`, `date`) VALUES (:number, :date)"); 
$stmt2->execute(array(":number" => $number, ":date" => $date)); 

$pdo->commit(); 

는 같은 번호를 선택 다른 스레드를 방지하기 위해 충분한 거래인가 및 그 난에 노력하고 같은 번호를 삽입하려고?

친애하는 친구 인 Google과 SO를 사용해도 직접 질문에 대답 할 수 없었기 때문에 "나는 MySQL에서 모든 작업을 수행 할 수 있습니까?"라고 생각했습니다. 이 쿼리와 함께 나온 :

INSERT INTO `table`(`number`, `date`) 
SELECT IF(
    DATE((SELECT `date` FROM `table` ORDER BY `id` DESC LIMIT 1)) < CURDATE(), 
    '1', 
    (SELECT `number` + 1 FROM `table` ORDER BY `id` DESC LIMIT 1) 
), NOW() 

더 좋은 방법인가요?

아무것도 수행하기 전에 테이블을 고정해야합니까?

이 작업을 수행하는 방법을보기에는 너무 바보일까요? ,

먼저 테이블을 변경

+0

시나리오를 살펴보면 날짜가 증가하는 순서로 숫자를 삽입하려는 것으로 결론을 내었습니다. 날짜와 숫자 열에 복합 고유 인덱스를 만들 수 있으며 두 개의 동시 쿼리가 진행될 경우 예외를 catch 블록에서 처리 할 수 ​​있습니다 같은 날에 같은 번호를 입력하십시오. –

+0

예, 그냥 메모였습니다. 나는 거래가 다른 거래 상태에 의존한다고 생각하지 않는다. 한 번에 30 명의 사용자가로드 한 경우 30 명의 사용자가 먼저 29 개의 모든 트랜잭션을 완료해야합니다. 나는 당신이 시간을 허락하는'update'를 '선택'하지 않았고, 한번에 모든 것을 할 수 있습니다. – chris85

+0

트랜잭션이 동일한 번호를 선택하는 것만으로는 충분하지 않지만 키가 고유 한 경우 두 번째 삽입이 실패하고 트랜잭션을 취소 한 다음 다시 완전히 시도 할 수 있습니다. – awiebe

답변

0

이 충분할 수있다. No id, date is a DATE`, "natural"PK.

number INT(6) unsigned zerofill, 
date DATE, 
PRIMARY KEY(date, number) 

그런 다음 트랜잭션이

BEGIN; 
($not_first, $number) = SELECT date = CURDATE(), number 
        FROM tbl 
        ORDER BY date DESC, number DESC 
        LIMIT 1 
        FOR UPDATE; -- important 
$number = $not_first ? $number+1 : 1; 
INSERT INTO tbl (date, number, ...) 
    VALUES 
    (CURDATE(), $number, ...); 
COMMIT; 

이다 그러나이 버그는 SELECT 실행하면 자정 직전 여전히 있지만, INSERT 실행 직후. 다른 연결이 일을 할 수 -

는 상관없이 당신이 그것을 할 방법
INSERT INTO tbl (date, number, ...) 
    VALUES 
    (CURDATE(), 
     IFNULL((SELECT MAX(id) FROM tbl WHERE date = CURDATE()), 
        1) AS number, 
     ... -- whatever else you are inserting into `tbl` 
    ); 

, 당신은 오류를 검사해야합니다 문 원자해야하기 때문에

그래서,이 버그를 방지하고, 거래를 방지 할 수있다 똑같은 일로 교착 상태에 빠지게됩니다.