perf: оптимизировано массовое удаление строк

- Добавлен batch delete метод на бэкенде (удаление множества строк за один запрос)
- Использование WHERE IN для удаления нескольких строк одним SQL запросом
- Добавлен прогресс-бар при удалении большого количества строк
- Удаление 1000 строк теперь занимает секунды вместо минут
- Добавлена поддержка транзакций для атомарности операций
- Оптимизирован размер батчей для баланса производительности и надежности
This commit is contained in:
2026-01-21 04:17:26 +03:00
parent 0fc427f11d
commit 23c9c04a87
3 changed files with 219 additions and 25 deletions

View File

@@ -569,7 +569,7 @@ function escapeHtml(text) {
return div.innerHTML;
}
// ✅ УДАЛИТЬ
// ✅ УДАЛИТЬ (с реальным прогрессом для больших объемов)
document.getElementById('btnDelete').addEventListener('click', async () => {
if (!table || !currentSchema || !currentTable) return;
@@ -586,40 +586,106 @@ document.getElementById('btnDelete').addEventListener('click', async () => {
if (!confirm(confirmMsg)) return;
try {
let deleted = 0;
let errors = 0;
const rowsArray = Array.from(selectedRowsData.values());
// Для очень больших удалений - показываем прогресс
const BATCH_SIZE = 1000; // Удаляем по 1000 строк за раз
const batches = [];
for (let i = 0; i < rowsArray.length; i += BATCH_SIZE) {
batches.push(rowsArray.slice(i, i + BATCH_SIZE));
}
if (batches.length > 1) {
// Множество батчей - показываем реальный прогресс
const modal = createProgressModal(`Удаление ${count} записей...`);
document.body.appendChild(modal);
// Удаляем все выделенные строки из selectedRowsData
for (const [key, rowData] of selectedRowsData) {
try {
await api('/api/table/delete', 'POST', {
const progressBar = modal.querySelector('.progress-bar');
const progressText = modal.querySelector('.spinner').parentElement;
let totalDeleted = 0;
let totalErrors = 0;
let allErrorMessages = [];
try {
for (let i = 0; i < batches.length; i++) {
const batch = batches[i];
progressText.innerHTML = `<span class="spinner" style="display: inline-block; animation: spin 1s linear infinite;">⏳</span> Обработка ${i + 1} из ${batches.length} батчей...`;
const result = await api('/api/table/delete-batch', 'POST', {
schema: currentSchema,
table: currentTable,
row: rowData
rows: batch
});
deleted++;
} catch (e) {
console.error(`Ошибка удаления строки ${key}:`, e);
errors++;
totalDeleted += result.deleted;
totalErrors += result.errors;
if (result.errorMessages) {
allErrorMessages.push(...result.errorMessages);
}
// Обновляем прогресс
const progress = ((i + 1) / batches.length) * 100;
progressBar.style.width = progress + '%';
}
document.body.removeChild(modal);
selectedRowsData.clear();
updateSelectionCounter();
await table.replaceData();
if (totalErrors > 0) {
const errorsToShow = allErrorMessages.slice(0, 10);
const moreErrors = allErrorMessages.length > 10
? `\n... и еще ${allErrorMessages.length - 10} ошибок`
: '';
alert(`Удалено строк: ${totalDeleted}\nОшибок: ${totalErrors}\n\nПервые ошибки:\n${errorsToShow.join('\n')}${moreErrors}`);
} else {
alert(`✓ Успешно удалено строк: ${totalDeleted}`);
}
} catch (e) {
document.body.removeChild(modal);
console.error(e);
alert('Ошибка удаления: ' + e.message);
}
selectedRowsData.clear();
updateSelectionCounter();
await table.replaceData();
if (errors > 0) {
alert(`Удалено строк: ${deleted}\nОшибок: ${errors}`);
} else {
alert(`✓ Удалено строк: ${deleted}`);
} else {
// Один батч - используем простое модальное окно
const modal = createProgressModal('Удаление записей...');
document.body.appendChild(modal);
try {
const result = await api('/api/table/delete-batch', 'POST', {
schema: currentSchema,
table: currentTable,
rows: rowsArray
});
document.body.removeChild(modal);
selectedRowsData.clear();
updateSelectionCounter();
await table.replaceData();
if (result.errors > 0) {
const errorsToShow = result.errorMessages.slice(0, 10);
const moreErrors = result.errorMessages.length > 10
? `\n... и еще ${result.errorMessages.length - 10} ошибок`
: '';
alert(`Удалено строк: ${result.deleted}\nОшибок: ${result.errors}\n\nПервые ошибки:\n${errorsToShow.join('\n')}${moreErrors}`);
} else {
alert(`✓ Удалено строк: ${result.deleted}`);
}
} catch (e) {
document.body.removeChild(modal);
console.error(e);
alert('Ошибка удаления: ' + e.message);
}
} catch (e) {
console.error(e);
alert('Ошибка удаления: ' + e.message);
}
});
// ========== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ CSV ==========
function detectDelimiter(text) {