MySQL "Ca n't reopen table"오류 해결


91

저는 현재 필터링 할 모든 "태그"에 대해 INNER JOIN 클라우스를 생성해야하는 일종의 필터를 구현하는 데 바쁩니다.

문제는 전체 SQL 무리 후에 선택하는 데 필요한 모든 정보가 포함 된 테이블이 있지만 생성 된 모든 INNER JOIN에 대해 다시 필요하다는 것입니다.

이것은 기본적으로 다음과 같습니다.

SELECT
    *
FROM search
INNER JOIN search f1 ON f1.baseID = search.baseID AND f1.condition = condition1
INNER JOIN search f2 ON f2.baseID = search.baseID AND f2.condition = condition2
...
INNER JOIN search fN ON fN.baseID = search.baseID AND fN.condition = conditionN

이것은 작동하지만 "검색"테이블이 임시 테이블 인 것을 선호하지만 (일반 테이블이 아닌 경우 몇 배 더 작을 수 있음) 매우 성가신 오류가 발생합니다. Can't reopen table

일부 연구는 저를 이 버그 보고서로 이끌지 만 MySQL의 사람들은 그러한 기본 기능 (테이블을 두 번 이상 사용)이 임시 테이블에서 작동하지 않는다는 사실을 신경 쓰지 않는 것 같습니다. 이 문제로 많은 확장 성 문제가 있습니다.

잠재적으로 많은 임시이지만 매우 실제적인 테이블을 관리하거나 모든 데이터가 들어있는 거대한 테이블을 유지하도록 만들 필요가없는 실행 가능한 해결 방법이 있습니까?

감사합니다, 크리스

[추가]

GROUP_CONCAT 답변은 내 조건이 특정 순서로 여러 열이기 때문에 내 상황에서 작동하지 않으며 AND가 필요한 것에서 OR을 만들 것입니다. 그러나 이전 문제를 해결하는 데 도움이되었으므로 이제 temp 여부에 관계없이 테이블이 더 이상 필요하지 않습니다. 우리는 우리의 문제에 대해 너무 일반적이라고 생각했습니다. 이제 필터의 전체 적용이 약 1 분에서 1/4 초 미만으로 돌아 왔습니다.


2
UNION을 사용하는 동일한 쿼리에서 임시 테이블을 두 번 사용하여 동일한 문제가 발생했습니다.
— Sebastián Grignoli

답변:



125

간단한 해결책은 임시 테이블을 복제하는 것입니다. 테이블이 비교적 작은 경우에 잘 작동하며 임시 테이블의 경우가 많습니다.


8
이것이 문제에 답하기 때문에 실제로 선택된 답이어야합니다.
— dyesdyes 2014-08-25

4
테이블을 복제하는 방법 에 대한 조언이 있습니까? (I 쿼리를 반복하지 복사하는 방법을 의미)
— 에르난 Eche

16
임시 테이블이 크더라도 mysql의 캐시가 도움이 될 것입니다. 한 임시 테이블에서 다른 임시 테이블로 복사하는 한 간단한 "CREATE TEMPORARY TABLE tmp2 SELECT * FROM tmp1"이이를 수행해야합니다.
— AS7K

2
임시 가능한 콘텐츠를 복사하는 경우 인덱스도 만드는 것을 잊지 마십시오. 그렇지 않으면 쿼리가 상당히 느려질 수 있습니다.
— gaborsch

1
@NgSekLong 네. 항상. 분명히 쿼리에 대한 응용 프로그램에 따라 다르지만 100,000을 초과 할 때까지는 "거대한"성능 문제가 발생하지 않습니다. 하나의 ETL 프로세스에서 3.5mil 테이블과 함께이 방법을 사용합니다. 그 응용 프로그램의 속도는 그다지 중요하지 않습니다.
— Tanner Clark

49

맞습니다. MySQL 문서 에서는 " TEMPORARY같은 쿼리에서 테이블을 두 번 이상 참조 할 수 없습니다."라고 말합니다 .

다음은 동일한 행을 찾아야하는 대체 쿼리입니다. 일치하는 행의 모든 ​​조건이 별도의 열에있는 것은 아니지만 쉼표로 구분 된 목록에 있습니다.

SELECT f1.baseID, GROUP_CONCAT(f1.condition)
FROM search f1
WHERE f1.condition IN (<condition1>, <condition2>, ... <conditionN>)
GROUP BY f1.baseID
HAVING COUNT(*) = <N>;

2
이것은 실제로 내 문제를 해결하지는 못했지만 문제를 일으킨 문제를 단순화 할 수있게하여 temptable의 필요성을 무효화했습니다. 감사!
— Kris

6

이 문제를 해결하려면 영구 "임시"테이블을 만들고 테이블 이름에 SPID (죄송합니다. 저는 SQL Server 영역에서 왔습니다)를 접미사로 붙여 고유 한 테이블 이름을 만듭니다. 그런 다음 동적 SQL 문을 만들어 쿼리를 만듭니다. 문제가 발생하면 테이블이 삭제되고 다시 생성됩니다.

더 나은 옵션을 원합니다. 어서, MySQL Devs. 2008 년부터 '버그'/ '기능 요청'이 오픈되었습니다! 만난 모든 '버그'가 같은 보트에있는 것 같습니다.

select concat('ReviewLatency', CONNECTION_ID()) into @tablename;

#Drop "temporary" table if it exists
set @dsql=concat('drop table if exists ', @tablename, ';');
PREPARE QUERY1 FROM @dsql;
EXECUTE QUERY1;
DEALLOCATE PREPARE QUERY1;

#Due to MySQL bug not allowing multiple queries in DSQL, we have to break it up...
#Also due to MySQL bug, you cannot join a temporary table to itself,
#so we create a real table, but append the SPID to it for uniqueness.
set @dsql=concat('
create table ', @tablename, ' (
    `EventUID` int(11) not null,
    `EventTimestamp` datetime not null,
    `HasAudit` bit not null,
    `GroupName` varchar(255) not null,
    `UserID` int(11) not null,
    `EventAuditUID` int(11) null,
    `ReviewerName` varchar(255) null,
    index `tmp_', @tablename, '_EventUID` (`EventUID` asc),
    index `tmp_', @tablename, '_EventAuditUID` (`EventAuditUID` asc),
    index `tmp_', @tablename, '_EventUID_EventTimestamp` (`EventUID`, `EventTimestamp`)
) ENGINE=MEMORY;');
PREPARE QUERY2 FROM @dsql;
EXECUTE QUERY2;
DEALLOCATE PREPARE QUERY2;

#Insert into the "temporary" table
set @dsql=concat('
insert into ', @tablename, ' 
select e.EventUID, e.EventTimestamp, e.HasAudit, gn.GroupName, epi.UserID, eai.EventUID as `EventAuditUID`
    , concat(concat(concat(max(concat('' '', ui.UserPropertyValue)), '' (''), ut.UserName), '')'') as `ReviewerName`
from EventCore e
    inner join EventParticipantInformation epi on e.EventUID = epi.EventUID and epi.TypeClass=''FROM''
    inner join UserGroupRelation ugr on epi.UserID = ugr.UserID and e.EventTimestamp between ugr.EffectiveStartDate and ugr.EffectiveEndDate 
    inner join GroupNames gn on ugr.GroupID = gn.GroupID
    left outer join EventAuditInformation eai on e.EventUID = eai.EventUID
    left outer join UserTable ut on eai.UserID = ut.UserID
    left outer join UserInformation ui on eai.UserID = ui.UserID and ui.UserProperty=-10
    where e.EventTimestamp between @StartDate and @EndDate
        and e.SenderSID = @FirmID
    group by e.EventUID;');
PREPARE QUERY3 FROM @dsql;
EXECUTE QUERY3;
DEALLOCATE PREPARE QUERY3;

#Generate the actual query to return results. 
set @dsql=concat('
select rl1.GroupName as `Group`, coalesce(max(rl1.ReviewerName), '''') as `Reviewer(s)`, count(distinct rl1.EventUID) as `Total Events`
    , (count(distinct rl1.EventUID) - count(distinct rl1.EventAuditUID)) as `Unreviewed Events`
    , round(((count(distinct rl1.EventUID) - count(distinct rl1.EventAuditUID)) / count(distinct rl1.EventUID)) * 100, 1) as `% Unreviewed`
    , date_format(min(rl2.EventTimestamp), ''%W, %b %c %Y %r'') as `Oldest Unreviewed`
    , count(distinct rl3.EventUID) as `<=7 Days Unreviewed`
    , count(distinct rl4.EventUID) as `8-14 Days Unreviewed`
    , count(distinct rl5.EventUID) as `>14 Days Unreviewed`
from ', @tablename, ' rl1
left outer join ', @tablename, ' rl2 on rl1.EventUID = rl2.EventUID and rl2.EventAuditUID is null
left outer join ', @tablename, ' rl3 on rl1.EventUID = rl3.EventUID and rl3.EventAuditUID is null and rl1.EventTimestamp > DATE_SUB(NOW(), INTERVAL 7 DAY) 
left outer join ', @tablename, ' rl4 on rl1.EventUID = rl4.EventUID and rl4.EventAuditUID is null and rl1.EventTimestamp between DATE_SUB(NOW(), INTERVAL 7 DAY) and DATE_SUB(NOW(), INTERVAL 14 DAY)
left outer join ', @tablename, ' rl5 on rl1.EventUID = rl5.EventUID and rl5.EventAuditUID is null and rl1.EventTimestamp < DATE_SUB(NOW(), INTERVAL 14 DAY)
group by rl1.GroupName
order by ((count(distinct rl1.EventUID) - count(distinct rl1.EventAuditUID)) / count(distinct rl1.EventUID)) * 100 desc
;');
PREPARE QUERY4 FROM @dsql;
EXECUTE QUERY4;
DEALLOCATE PREPARE QUERY4;

#Drop "temporary" table
set @dsql = concat('drop table if exists ', @tablename, ';');
PREPARE QUERY5 FROM @dsql;
EXECUTE QUERY5;
DEALLOCATE PREPARE QUERY5;

이제 Oracle이 통치권을 차지하고 있으므로 MySQL에 좋은 추진력을 줄 수 있습니다.
— Pacerier

2
한숨 나는 그것을 의심합니다 :(
— beeks

3
큰 한숨 . 2016 년 7 월 및이 임시 테이블 버그는 아직 수정되지 않았습니다. 이 문제를 피하기 위해 영구적 인 테이블 이름 (저는 Oracle 땅에서 왔습니다)과 연결된 일종의 시퀀스 번호를 생각해 낼 것입니다.
— TheWalkingData

Hattrick의 한숨 ... 이미 2019 년으로 보아 고쳐지지 않을 수도 있습니다.
— Zimano

3

개인적으로 나는 그것을 영구 테이블로 만들 것입니다. 이러한 테이블에 대해 별도의 데이터베이스를 만들고 (이러한 쿼리를 한 번에 많이 수행 할 수 있으므로 고유 한 이름이 필요할 수 있음) 권한을 현명하게 설정할 수도 있습니다 (데이터베이스에 대한 권한을 설정할 수 있습니다. t 테이블 와일드 카드에 대한 권한 설정).

그런 다음 가끔 오래된 것을 제거하기 위해 정리 작업이 필요합니다 (MySQL은 테이블이 생성 된 시간을 편리하게 기억하므로 정리가 필요할 때이를 해결하는 데 사용할 수 있습니다)


9
임시 테이블은 여러 쿼리를 동시에 실행할 수 있다는 큰 장점이 있습니다. 영구 테이블에서는 불가능합니다.
— Pacerier

영구 테이블 "솔루션"은 해결책이 아니라고 생각합니다. 확실히 문제를 해결하지만 실용적이지 않습니다. 너무 많은 질문이 제기됩니다. 동시에 여러 개를 만들려면 어떻게해야합니까? 명명 규칙을 어떻게 처리하고 동일한 명명 된 테이블을 덮어 쓰겠습니까? 영구 테이블을 삭제하는 프로세스는 무엇입니까? 이러한 질문에 답하면서 영구 테이블을 사용하여 실행 가능한 솔루션에 대해 자세히 설명 할 수 있다면 모두 귀입니다!
— Tanner Clark

0

쿼리를 영구 테이블로 변경할 수 있었고 이로 인해 해결되었습니다. (MicroStrategy, 임시 테이블 유형에서 VLDB 설정 변경).


-1

나중에 제거 할 영구 테이블을 만들거나 동일한 데이터로 2 개의 별도 임시 테이블을 만들어 문제를 해결할 수 있습니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.