การแก้ไขข้อผิดพลาด“ ไม่สามารถเปิดตารางใหม่” ของ MySQL


93

ขณะนี้ฉันกำลังยุ่งอยู่กับการติดตั้งตัวกรองประเภทที่ฉันต้องสร้างส่วนคำสั่ง 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 ดูเหมือนจะไม่สนใจว่าคุณสมบัติพื้นฐานดังกล่าว (ใช้ตารางมากกว่าหนึ่งครั้ง) ไม่ทำงานกับตารางชั่วคราว ฉันพบปัญหาเกี่ยวกับความสามารถในการปรับขนาดได้มากมายเกี่ยวกับปัญหานี้

มีวิธีแก้ปัญหาที่เป็นไปได้หรือไม่ที่ไม่ต้องการให้ฉันจัดการตารางชั่วคราวจำนวนมาก แต่เป็นจริงมาก ๆ หรือทำให้ฉันดูแลตารางขนาดใหญ่พร้อมข้อมูลทั้งหมดในนั้น

ขอแสดงความนับถือ Kris

[เพิ่มเติม]

คำตอบ GROUP_CONCAT ใช้ไม่ได้ในสถานการณ์ของฉันเนื่องจากเงื่อนไขของฉันมีหลายคอลัมน์ในลำดับที่เฉพาะเจาะจงซึ่งจะทำให้ OR จากสิ่งที่ฉันต้องการเป็น AND อย่างไรก็ตามมันช่วยฉันแก้ปัญหาก่อนหน้านี้ได้ดังนั้นตอนนี้จึงไม่จำเป็นต้องใช้ตารางอุณหภูมิหรือไม่อีกต่อไป เราแค่คิดทั่วไปเกินไปสำหรับปัญหาของเรา ขณะนี้แอปพลิเคชันทั้งหมดของตัวกรองได้ถูกนำกลับมาจากประมาณหนึ่งนาทีไปจนถึงหนึ่งในสี่ของวินาที


2
ฉันมีปัญหาเดียวกันกับการใช้ตารางชั่วคราวสองครั้งในแบบสอบถามเดียวกันโดยใช้ UNION
Sebastián Grignoli

คำตอบ:


0

หากเปลี่ยนไปใช้ MariaDB (ส้อมของ MySQL) เป็นไปได้ - ความน่ารำคาญนี้ได้รับการแก้ไขที่นั่นในฐานะของรุ่น 10.2.1: https://jira.mariadb.org/browse/MDEV-5535


129

วิธีแก้ปัญหาง่ายๆคือทำซ้ำตารางชั่วคราว ทำงานได้ดีถ้าโต๊ะมีขนาดค่อนข้างเล็กซึ่งมักเป็นกรณีที่มีตารางชั่วคราว


8
ที่จริงควรเป็นคำตอบที่เลือกเนื่องจากคำตอบนี้ตอบปัญหาโดยไม่ต้องวนเวียน
dyesdyes

4
มีคำแนะนำเกี่ยวกับวิธีที่คุณจะทำซ้ำตารางได้อย่างไร (ฉันหมายถึงวิธีการคัดลอกโดยไม่ใช้ข้อความค้นหาซ้ำ)
Hernán Eche

18
แม้ว่าตารางอุณหภูมิจะมีขนาดใหญ่ แต่แคชของ mysql ก็น่าจะช่วยคุณได้ เท่าที่คัดลอกจากตารางชั่วคราวหนึ่งไปยังอีกตารางหนึ่ง "สร้างตารางชั่วคราวแบบง่าย 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
สิ่งนี้ไม่ได้ช่วยแก้ปัญหาของฉันได้จริง แต่มันช่วยให้ฉันสามารถลดความซับซ้อนของปัญหาที่เกิดขึ้นได้ดังนั้นจึงไม่จำเป็นต้องมีสิ่งล่อใจ ขอบคุณ!
Kris

6

ฉันแก้ไขสิ่งนี้ได้โดยการสร้างตาราง "ชั่วคราว" แบบถาวรและต่อท้าย SPID (ขออภัยฉันมาจาก SQL Server land) เป็นชื่อตารางเพื่อสร้างชื่อตารางเฉพาะ จากนั้นสร้างคำสั่ง SQL แบบไดนามิกเพื่อสร้างแบบสอบถาม หากมีสิ่งไม่ดีเกิดขึ้นตารางจะถูกทิ้งและสร้างขึ้นใหม่

ฉันหวังว่าจะได้ตัวเลือกที่ดีกว่านี้ C'mon, 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

3
ใหญ่ถอนหายใจ กรกฎาคม 2016 และจุดบกพร่องของตารางชั่วคราวนี้ยังไม่ได้รับการแก้ไข ฉันอาจจะหาหมายเลขลำดับบางประเภทที่เชื่อมต่อกับชื่อตารางถาวร (ฉันมาจาก Oracle land) เพื่อหลีกเลี่ยงปัญหานี้
TheWalkingData

Hattrick ถอนหายใจ ... มันอาจจะไม่ได้รับการแก้ไขเนื่องจากเป็นปี 2019 แล้ว
Zimano

3

โดยส่วนตัวฉันแค่ทำให้มันเป็นตารางถาวร คุณอาจต้องการสร้างฐานข้อมูลแยกต่างหากสำหรับตารางเหล่านี้ (น่าจะต้องการชื่อที่ไม่ซ้ำกันเนื่องจากสามารถทำแบบสอบถามเหล่านี้ได้พร้อมกันจำนวนมาก) และเพื่อให้สามารถตั้งค่าการอนุญาตได้อย่างสมเหตุสมผล (คุณสามารถตั้งค่าการอนุญาตบนฐานข้อมูลคุณสามารถ ' t ตั้งค่าการอนุญาตบนสัญลักษณ์แทนตาราง)

จากนั้นคุณจะต้องมีงานล้างข้อมูลเพื่อลบรายการเก่าเป็นครั้งคราว (MySQL จะจำเวลาที่สร้างตารางได้อย่างสะดวกดังนั้นคุณสามารถใช้สิ่งนั้นในการทำงานเมื่อจำเป็นต้องล้างข้อมูล)


9
ตารางชั่วคราวมีข้อได้เปรียบอย่างยิ่งที่คุณสามารถมีการสืบค้นหลายรายการพร้อมกันได้ สิ่งนี้ไม่สามารถทำได้กับตารางถาวร
Pacerier

ฉันคิดว่า "วิธีแก้ปัญหา" ของตารางถาวรไม่ใช่วิธีแก้ปัญหา มันแก้ปัญหาได้แน่นอน แต่ใช้ไม่ได้จริง มีคำถามมากมายเกิดขึ้น: ฉันจะสร้างมากกว่าหนึ่งในเวลาเดียวกันได้อย่างไร? คุณจะจัดการกับหลักการตั้งชื่อและเขียนทับตารางที่มีชื่อเดียวกันได้อย่างไร? กระบวนการลบตารางถาวรคืออะไร? หากคุณสามารถอธิบายวิธีแก้ปัญหาที่เป็นไปได้โดยใช้ตารางถาวรในขณะที่ตอบคำถามเหล่านี้ฉันก็หูผึ่ง!
Tanner Clark

0

ฉันสามารถเปลี่ยนแบบสอบถามเป็นตารางถาวรและสิ่งนี้ได้รับการแก้ไขให้ฉัน (เปลี่ยนการตั้งค่า VLDB ใน MicroStrategy ประเภทตารางชั่วคราว)


0

คุณสามารถแก้ไขได้โดยการสร้างตารางถาวรซึ่งคุณจะลบออกในภายหลังหรือเพียงแค่สร้างตารางชั่วคราว 2 ตารางที่มีข้อมูลเดียวกัน


-1

ป้อนคำอธิบายภาพที่นี่

นี่คือเอกสาร MYSQL เกี่ยวกับปัญหานี้ ฉันใช้ตารางชั่วคราวที่ซ้ำกันเหมือนคำตอบข้างต้นอย่างไรก็ตามคุณอาจมีสถานการณ์ที่ CTE เหมาะสม!

https://dev.mysql.com/doc/refman/8.0/en/tem Contemporary-table-pro issues.html

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.