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.