MySQL "Tablo yeniden açılamıyor" hatasını aşma


91

Şu anda filtrelemek için her "etiket" için bir INNER JOIN cümlesi oluşturmam gereken bir tür filtre uygulamakla meşgulüm.

Sorun şu ki, bir sürü SQL'den sonra, seçimimi yapmak için ihtiyacım olan tüm bilgileri içeren bir tablom var, ancak oluşturulan her INNER JOIN için buna tekrar ihtiyacım var.

Bu temelde şöyle görünür:

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

Bu işe yarıyor, ancak "arama" tablosunun geçici olmasını tercih ederim (normal bir tablo değilse birkaç büyüklük sırası daha küçük olabilir), ancak bu bana çok can sıkıcı bir hata veriyor: Can't reopen table

Bazı araştırmalar beni bu hata raporuna götürüyor, ancak MySQL'deki insanlar bu kadar basit bir özelliğin (bir tabloyu bir defadan fazla kullanma) geçici tablolarla çalışmamasını umursamıyor gibi görünüyor. Bu sorunla ilgili birçok ölçeklenebilirlik sorunuyla karşılaşıyorum.

Potansiyel olarak çok sayıda geçici ancak çok gerçek tabloyu yönetmemi gerektirmeyen veya içindeki tüm verilerle büyük bir tablo tutmamı sağlayan uygulanabilir herhangi bir geçici çözüm var mı?

Saygılarımızla, Kris

[ek]

GROUP_CONCAT cevabı benim durumumda işe yaramıyor çünkü koşullarım belirli bir sırayla birden fazla sütun, OR'leri AND olmama gerekenden çıkarır. Ancak, daha önceki bir sorunu çözmeme yardımcı oldu, bu yüzden şimdi tablo, geçici olsun veya olmasın, artık gerekli değil. Sadece sorunumuz için fazla genel düşünüyorduk. Filtrelerin tüm uygulaması şimdi yaklaşık bir dakikadan, saniyenin çeyreğinin çok altına getirildi.


2
UNION kullanarak aynı sorguda iki kez geçici bir tablo kullanarak aynı sorunu yaşadım.
Sebastián Grignoli

Yanıtlar:



127

Basit bir çözüm, geçici tabloyu kopyalamaktır. Tablo nispeten küçükse iyi çalışır, bu genellikle geçici tablolarda görülür.


8
Etrafta dolaşmadan soruna cevap verdiği için aslında seçilen cevap olmalıdır.
dyesdyes

4
Masayı nasıl kopyalarsınız konusunda herhangi bir tavsiye ? (Sorguyu tekrar
etmeden

18
Geçici tablo büyük olsa bile, mysql'in önbelleği size yardımcı olacaktır. Bir geçici tablodan diğerine kopyalamaya gelince, basit bir "GEÇİCİ TABLO OLUŞTUR tmp2 SEÇİMİ * Tmp1'DEN" bunu yapmalıdır.
AS7K

2
Cazip içeriği kopyalarsanız, dizin oluşturmayı da unutmayın, aksi takdirde sorgunuz oldukça yavaş olabilir.
gaborsch

1
@NgSekLong Evet. Her zaman. Açıkçası sorgu için uygulamanıza bağlı, ancak> 100.000'e kadar "büyük" performans sorunları görmüyorum. Bir ETL sürecinde, bu yöntemi 3.5mil tabloyla kullanıyorum. Bu uygulamanın hızı o kadar önemli değil.
Tanner Clark

49

Doğru, MySQL belgeleri şöyle diyor: " TEMPORARYAynı sorguda bir tabloya birden fazla kez başvuramazsınız."

İşte aynı satırları bulması gereken alternatif bir sorgu, ancak eşleşen satırların tüm koşulları ayrı sütunlarda olmayacak, virgülle ayrılmış bir listede olacaklar.

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
Bu aslında sorunumu çözmedi, ancak buna neden olan sorunu basitleştirmemi sağladı ve böylece cazip olana olan ihtiyacı ortadan kaldırdı. Teşekkürler!
Kris

6

Kalıcı bir "geçici" tablo oluşturarak ve benzersiz bir tablo adı oluşturmak için SPID'yi (üzgünüm, SQL Server alanından geliyorum) tablo adına ekleyerek bu sorunu çözdüm. Ardından, sorguları oluşturmak için dinamik SQL ifadeleri oluşturun. Kötü bir şey olursa, masa kaldırılacak ve yeniden oluşturulacaktır.

Daha iyi bir seçenek umuyorum. Hadi, MySQL Devs. 'Hata' / 'özellik isteği' 2008'den beri açık! Görünüşe göre karşılaşılan tüm 'böcekler' aynı gemide.

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;

Umarım artık Oracle hükümdarları devraldığına göre, MySQL'e iyi bir destek verebilir.
Pacerier

2
iç çekme şüpheliyim :(
beeks

3
Büyük bir iç çekiş . Temmuz 2016 ve bu geçici tablo hatası hala düzeltilmedi. Muhtemelen bu sorunu aşmak için kalıcı bir tablo adıyla (Oracle ülkesinden geliyorum) birleştirilmiş bir tür sıra numarası bulacağım.
TheWalkingData

Hattrick iç çekiyor ... Zaten 2019 olduğu düşünüldüğünde, asla düzeltilemeyebilir.
Zimano

3

Şahsen ben onu kalıcı bir masa yapardım. Bu tablolar için ayrı bir veritabanı oluşturmak isteyebilirsiniz (bu sorguların çoğu aynı anda yapılabileceğinden büyük olasılıkla benzersiz adlara ihtiyaç duyacaklardır), ayrıca izinlerin mantıklı bir şekilde ayarlanmasına izin vermek için (Veritabanları üzerinde izinler ayarlayabilirsiniz; t tablo joker karakterlerinde izinleri ayarlayın).

Daha sonra, zaman zaman eskileri kaldırmak için bir temizleme işine de ihtiyacınız olacak (MySQL, bir tablonun oluşturulduğu zamanı rahatça hatırlar, böylece bir temizlik gerektiğinde bunu sadece bunu çalışmak için kullanabilirsiniz)


9
Geçici tablolar, aynı anda birden çok sorgu çalıştırabilmeniz gibi olağanüstü bir avantaja sahiptir. Kalıcı tablolarla bu mümkün değildir.
Pacerier

Bence kalıcı tablo "çözüm" bir çözüm değil. Problemi kesin olarak çözer, ancak pratik değildir. Pek çok soru ortaya çıkıyor: Aynı anda birden fazla nasıl yaratırım? Adlandırma kuralıyla ve aynı adlandırılmış tabloların üzerine yazarak nasıl başa çıkarsınız? Kalıcı tabloyu silme süreci nedir? Bu soruları yanıtlarken kalıcı tablolar kullanarak uygulanabilir bir çözüm üzerinde ayrıntılı bilgi verebilirseniz, kulağım var!
Tanner Clark

0

Sorguyu kalıcı bir tabloya değiştirebildim ve bu benim için düzeltildi. (MicroStrategy'de VLDB ayarlarını değiştirdi, geçici tablo türü).


0

Daha sonra kaldıracağınız kalıcı bir tablo oluşturarak ya da sadece aynı verilerle 2 ayrı geçici tablo yaparak bunun üstesinden gelebilirsiniz.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.