DB Browser for SQLite, конечно, удобная оболочка для базы данных SQLite, но выполнять рутину… не, не то. Поэтому был написан этот скрипт…
А, за одно, так сказать, хорошенько размяты мозги.
Скрипт позволяет извлекать из базы данных основные сведения по протестированным элементам питания и сохраняет их в отчет.
Помимо этого, если скрипт видит, что это сырая, еще не обработанная таблица, он предлагает ее усечь и, при согласии, усекает, делая резервную копию и создавая текстовый лог изменений, включая список диапазонов измененных строк по которому видно, если при тестировании батарейки были аномалии, что усечено лишнее.
Код достаточно прокомментирован, поэтому добавить нечего.
@ECHO OFF :: Written by Nikolay Soloshin (nikolay@soloshin.su) for SQLite version 3.50.4 @ 2025.08 @CHCP 65001 > NUL TITLE Скрипт для работы с базой данных батареек. COLOR 02 SET dbName=database.db SET dbDir=%cd% SET outFileName=report.txt SET tempDir=%temp%\Batteries SET logDir=logs REM Проверяем наличие файла базы данных. DIR "%dbDir%\%dbName%" > NUL IF %errorlevel% == 1 ( ECHO. ECHO Отсутствует файл базы данных "%dbName%" по пути "%dbDir%"! ECHO. ECHO Вы ищите,.. а мы пока выходим! (: TIMEOUT 10 > NUL GOTO END ) REM Проверяем наличие или доступность файла "sqlite3.exe", который может находиться в папке со скриптом или быть доступным через переменную "PATH". Удобнее всего разместить его по пути "C:\Windows". sqlite3.exe "%dbDir%\%dbName%" .databases > NUL IF %errorlevel% EQU 9009 ( REM https://stackoverflow.com/questions/23091906/ ECHO. ECHO Для работы этого скрипта необходим sqlite3.exe который по всей видимости отсутствует... ECHO. ECHO Файл должен находиться в папке с этим скриптом или быть доступным через переменную PATH. REM Исторически не использую SETLOCAL в контексте всего скрипта, поэтому пришлось в этом месте повторно его добавить для успешного разрешения переменной. Т.е. это не ошибка, так должно быть. ) SETLOCAL ENABLEDELAYEDEXPANSION ECHO. CHOICE /c YN /m "Открыть страницу загрузки?" IF !errorlevel! == 1 START https://sqlite.org/download.html GOTO END ) ECHO ВНИМАНИЕ! Используется файл базы данных "%dbDir%\%dbName%"! :START ECHO. ECHO | SET /p = "Введите порядковый номер батарейки: " SET /p batteryNumber= SET tableName=%batteryNumber% REM Ищем первую и ЕДИНСТВЕННУЮ таблицу - поэтому номера должны быть уникальны - , которая начинается с указанных выше цифр. FOR /f "delims=" %%a IN (' sqlite3.exe "%dbDir%\%dbName%" "SELECT name FROM sqlite_master WHERE name LIKE '%batteryNumber% %%' LIMIT 1;" ') DO SET "tableName=%%a" REM Механизм определения наличия нужной таблицы в базе простой - если таблица есть, ее название записывается в переменную, если таблицы нет, то значение переменной остается прежним и равно номеру батарейки. IF "%tableName%" == "%batteryNumber%" ( ECHO. ECHO Таблица, соответствующая этому номеру, не найдена! ECHO. ECHO В базе данных имеются следующие таблицы: ECHO. sqlite3.exe "%dbDir%\%dbName%" "SELECT group_concat(name, ', ') FROM sqlite_master WHERE type = 'table'" ECHO. ECHO Цифры в начале являются сквозным порядковым номером батарейки. GOTO START ) ECHO. ECHO | SET /p = "Выбрана таблица с названием "%tableName%". " CHOICE /c YN /t 15 /d N /m "Продолжаем?" IF %errorlevel% == 2 GOTO START REM Проверяем, есть ли вначале таблицы не усеченные данные - в данном контексте мы считаем, что напряжение не может быть меньше 0.5 вольта, т.е., по сути, это обозначает, что батарейка не была еще вставлена. FOR /f "delims=" %%a IN (' sqlite3.exe "%dbDir%\%dbName%" "SELECT `Voltage(V)` FROM `%tableName%` LIMIT 1;" ') DO SET "firstVolt=%%a" IF "%firstVolt%" lss "0.5" ( REM Это костыль, чтобы в контексте "SETLOCAL ENABLEDELAYEDEXPANSION" можно было отображать восклицательный знак, епт. SET exclamationMark=! REM Да, выше, это костыль... https://forum.script-coding.com/viewtopic.php?pid=41425#p41425 SETLOCAL ENABLEDELAYEDEXPANSION ECHO. ECHO | SET /p = "ВНИМАНИЕ^! Вероятно в таблице имеются не обрезанные данные^! " CHOICE /c YN /t 15 /d N /m "Усечь таблицу?" IF !errorlevel! == 1 ( IF NOT EXIST %tempDir% MKDIR %tempDir% REM Делаем единственную резервную копию. COPY /Y "%dbDir%\%dbName%" "%tempDir%\%dbName%.bak" > NUL IF !errorlevel! == 1 ( ECHO. ECHO ВНИМАНИЕ!exclamationMark! При попытке сделать резервную копию произошла ошибка. Выходим. TIMEOUT 5 > NUL GOTO END ) IF NOT EXIST %logDir% MKDIR %logDir% REM С этого места начинаем писать лог в лог. )) REM Для удобства... - да, мне так удобнее! - текст, если он повторяется несколько раз, я зачастую переношу в переменную. SET textString=Резервная копия находится в ECHO. ECHO !textString! "%tempDir%". ECHO !textString! "%tempDir%\%dbName%.bak" > %logDir%\%batteryNumber%-truncate.txt ECHO.>> %logDir%\%batteryNumber%-truncate.txt ECHO Время создания копии: %date%%time% >> %logDir%\%batteryNumber%-truncate.txt ECHO.>> %logDir%\%batteryNumber%-truncate.txt ECHO ВАЖНО!exclamationMark! Резервные копии перезаписываются!exclamationMark! >> %logDir%\%batteryNumber%-truncate.txt REM Вычисляем середину таблицы, учитывая только время теста. Это необходимо, чтобы при обрезании сначала, не обрезать в конце и наоборот. Да, схема не надежная и, если были аномалии при тестировании батарейки, может съедаться лишнее, но ничего более другого гггг я пока не придумал и в подавляющем большинстве случаев оно работает прекрасно. FOR /f "delims=" %%a IN (' sqlite3.exe "%dbDir%\%dbName%" "SELECT ( ( SELECT ROWID FROM `%tableName%` WHERE `Current(A)` > 0.001 ORDER BY `Timestamp(s)` DESC LIMIT 1 ) - ( SELECT ROWID FROM `%tableName%` WHERE `Current(A)` > 0.001 LIMIT 1 ) ) / 2;" ') DO SET "middleRowid=%%a" REM В переменную currentLine записываются данные для вывода на экран и в лог. В данном случае из базы извлекается первая, еще не усеченная, строка. FOR /f "delims=" %%a IN (' sqlite3.exe "%dbDir%\%dbName%" "SELECT ROWID, * FROM `%tableName%` LIMIT 1;" ') DO SET "currentLine=%%a" ECHO. ECHO Удаляем строки, которые меньше 1 вольта, из начала таблицы. sqlite3.exe "%dbDir%\%dbName%" "DELETE FROM `%tableName%` WHERE `Voltage(V)` < 1 AND ROWID < !middleRowid!;" SET textString=Изменения в начале файла. ECHO. ECHO !textString! ECHO.>> %logDir%\%batteryNumber%-truncate.txt ECHO !textString! >> %logDir%\%batteryNumber%-truncate.txt ECHO Было: "!currentLine!" ECHO Было: "!currentLine!" >> %logDir%\%batteryNumber%-truncate.txt REM Теперь извлекаем новую первую строчку. FOR /f "delims=" %%a IN (' sqlite3.exe "%dbDir%\%dbName%" "SELECT ROWID, * FROM `%tableName%` LIMIT 1;" ') DO SET "currentLine=%%a" ECHO Стало: "!currentLine!" ECHO Стало: "!currentLine!" >> %logDir%\%batteryNumber%-truncate.txt REM Тут извлекаем последнюю. FOR /f "delims=" %%a IN (' sqlite3.exe "%dbDir%\%dbName%" "SELECT ROWID, * FROM `%tableName%` ORDER BY `Timestamp(s)` DESC LIMIT 1;" ') DO SET "currentLine=%%a" ECHO. ECHO Удаляем строки, которые меньше 0.9 вольта, из конца таблицы. sqlite3.exe "%dbDir%\%dbName%" "DELETE FROM `%tableName%` WHERE `Timestamp(s)` >= ( SELECT `Timestamp(s)` FROM `%tableName%` WHERE `Voltage(V)` < 0.9 AND ROWID > !middleRowid! LIMIT 1 );" ECHO Удаляем строки, которые меньше 0.001 ампера, из конца таблицы. sqlite3.exe "%dbDir%\%dbName%" "DELETE FROM `%tableName%` WHERE `Timestamp(s)` >= ( SELECT `Timestamp(s)` FROM `%tableName%` WHERE `Current(A)` < 0.001 AND ROWID > !middleRowid! LIMIT 1 );" SET textString=Изменения в конце файла. ECHO. ECHO !textString! ECHO.>> %logDir%\%batteryNumber%-truncate.txt ECHO !textString! >> %logDir%\%batteryNumber%-truncate.txt ECHO Было: "!currentLine!" ECHO Было: "!currentLine!" >> %logDir%\%batteryNumber%-truncate.txt REM И, наконец, новую последнюю! Эти строчки пойдут в отчет для косвенного визуального определения правильности усечения таблицы. FOR /f "delims=" %%a IN (' sqlite3.exe "%dbDir%\%dbName%" "SELECT ROWID, * FROM `%tableName%` ORDER BY `Timestamp(s)` DESC LIMIT 1;" ') DO SET "currentLine=%%a" ECHO После "!currentLine!" ECHO Стало: "!currentLine!" >> %logDir%\%batteryNumber%-truncate.txt REM Выполняем несколько команд в одном подключении. Во-первых, подключаем резервную копию базы данных, где эта таблица еще в первозданном виде. Во-вторых, удаляем временную таблицу "exceptionTable", если вдруг она осталась по ошибке. В-третьих, создаем таблицу заново и заполняем ее только теми строками, которые были ранее удалены. sqlite3.exe "%dbDir%\%dbName%" "ATTACH DATABASE `%tempDir%\%dbName%.bak` as backup" "DROP TABLE IF EXISTS exceptionTable;" "CREATE TABLE exceptionTable AS SELECT ROWID, `Timestamp(s)`, `Voltage(V)`, `Current(A)`, mAh from backup.`%tableName%` EXCEPT SELECT ROWID, `Timestamp(s)`, `Voltage(V)`, `Current(A)`, mAh from `%tableName%`;" SET textString=Итого были удалены следующие диапазоны: ECHO. ECHO !textString! set tempFile=%tempDir%\tempOutput.txt REM Вычисляем и выводим связанные диапазоны из таблицы "exceptionTable" во временный файл, т.к. нельзя передать в переменную многострочный вывод. sqlite3.exe "%dbDir%\%dbName%" ".mode tabs" "SELECT MIN(ROWID) AS MIN, MAX(ROWID) AS MAX FROM ( SELECT ROWID, SUM(CASE WHEN previousValue IS NULL OR ROWID - previousValue > 1 THEN 1 ELSE 0 END) OVER (ORDER BY ROWID) AS groupId FROM ( SELECT ROWID, LAG(ROWID, 1, NULL) OVER (ORDER BY ROWID) AS previousValue FROM exceptionTable ) ) GROUP BY groupId;" > !tempFile! REM Построчно выводим содержимое файла на экран. FOR /f "delims=" %%a IN (' TYPE "!tempFile!"' ) DO ECHO %%a ECHO. >> %logDir%\%batteryNumber%-truncate.txt ECHO !textString! >> %logDir%\%batteryNumber%-truncate.txt REM Это костыль! Если не менять каталог, а писать, как везде, то в каталоге с логом ничего не добавляется командой copy, зато появляется дубль файла на этом уровне... CD %logDir% REM Копируем данные по диапазонам из временного файла в отчет. COPY /a %batteryNumber%-truncate.txt + !tempFile! > NUL CD %dbDir% DEL !tempFile! SET textString=Диапазонов должно быть ВСЕГО ДВА - один сначала, другой с конца ECHO !textString!!exclamationMark! ECHO !textString!!exclamationMark! >> %logDir%\%batteryNumber%-truncate.txt SET textString=Если это не так, то необходимо восстановить состояние из резервной копии и вручную обрезать таблицу. ECHO !textString! ECHO !textString! >> %logDir%\%batteryNumber%-truncate.txt SET textString=Перечень удаленных строк ECHO.>> %logDir%\%batteryNumber%-truncate.txt ECHO !textString!. >> %logDir%\%batteryNumber%-truncate.txt REM Добавляем в отчет сами удаленные строки. sqlite3.exe "%dbDir%\%dbName%" ".mode column" "SELECT * FROM exceptionTable;" >> %logDir%\%batteryNumber%-truncate.txt sqlite3.exe "%dbDir%\%dbName%" ".mode column" "DROP TABLE IF EXISTS exceptionTable;" ECHO. ECHO !textString! и прочие данные сохранены в файл ECHO "%logDir%\%batteryNumber%-truncate.txt" ECHO. ECHO Постройте график и проверьте связность данных, чтобы не было усечено лишнего... ) ECHO. CHOICE /c YN /m "Извлечь данные по току, емкости и времени из таблицы?" IF !errorlevel! == 2 GOTO END ENDLOCAL ) CLS ECHO Результирующие данные для батарейки #%batteryNumber%. ECHO. FOR /f "delims=" %%a IN (' sqlite3.exe "%dbDir%\%dbName%" "SELECT AVG( `Current(A)` ) * 1000 FROM `%tableName%`;" ') DO SET "avgCurrent=%%a" ECHO Средний ток (mA): %avgCurrent:.=,% FOR /f "delims=" %%a IN (' sqlite3.exe "%dbDir%\%dbName%" "SELECT mAh / 1000 AS Ah FROM `%tableName%` ORDER BY "mAh" DESC LIMIT 1;" ') DO SET "totalCapacity=%%a" ECHO Итоговая емкость (Ah): %totalCapacity:.=,% FOR /f "delims=" %%a IN (' sqlite3.exe "%dbDir%\%dbName%" "SELECT ( ( SELECT `Timestamp(s)` FROM `%tableName%` ORDER BY "mAh" DESC LIMIT 1 ) - ( SELECT `Timestamp(s)` FROM `%tableName%` WHERE `Current(A)` >= 0.1 LIMIT 1 ) ) / 60 AS totalTime;" ') DO SET "dischargeTime=%%a" ECHO Время разряда (m): %dischargeTime:.=,% ECHO. CHOICE /c YN /m "Сохранить данные в файл?" IF %errorlevel% == 1 ( ECHO Результат для батарейки #%batteryNumber%.>> %outFileName% ECHO Средний ток: %avgCurrent:.=,%>> %outFileName% ECHO Итоговая емкость: %totalCapacity:.=,%>> %outFileName% ECHO Время разряда: %dischargeTime:.=,%>> %outFileName% ECHO.>> %outFileName% ECHO. ECHO Готово! ) ECHO. CHOICE /c YN /m "Извлечь данные по другой батарейки?" IF %errorlevel% == 1 ( CLS GOTO START ) :END :: With love from Vladivostok.
Пример базы отчета, лога и базы можно загрузить по ссылке или посмотреть ниже по тексту.
Таблица и ее название в базе данных должны соответствовать этому запросу.
CREATE TABLE "13 USB_Meter_Log_20250809_113316" ( "Timestamp(s)" REAL, "Voltage(V)" REAL, "Current(A)" REAL, "Power(W)" REAL, "Temperature(C)" REAL, "DP(V)" REAL, "DN(V)" REAL, "mAh" REAL );
Результат для батарейки #15. Средний ток: 104,831240369347 Итоговая емкость: 0,502114159987279 Время разряда: 286,555833333333 Результат для батарейки #13. Средний ток: 102,445275133283 Итоговая емкость: 0,30312820364156 Время разряда: 176,977833333333
Резервная копия находится в "C:\Users\NSOLOS~1\AppData\Local\Temp\Batteries\database.db.bak" Время создания копии: 16.08.202512:16:07,56 ВАЖНО! Резервные копии перезаписываются! Изменения в начале файла. Было: "1|2.26|0.19951|0.0|0.0|33.2|1.436|1.592|0.0" Стало: "17|18.49|1.6495|0.0|0.0|33.3|1.432|1.589|0.00041327071984609" Изменения в конце файла. Было: "26422|26796.99|1.29099|0.00027|0.0003485673|31.8|1.547|1.6|521.556238536586" Стало: "17019|17260.51|0.90056|0.10461|0.0942075816|31.6|1.549|1.598|502.114159987279" Итого были удалены следующие диапазоны: 1 16 17020 26422 Диапазонов должно быть ВСЕГО ДВА - один сначала, другой с конца! Если это не так, то необходимо восстановить состояние из резервной копии и вручную обрезать таблицу. Перечень удаленных строк. rowid Timestamp(s) Voltage(V) Current(A) mAh ----- ------------ ---------- ---------- -------------------- 1 2.26 0.19951 0.0 0.0 2 3.54 0.19983 0.0 0.0 3 4.77 0.19951 0.00018 6.16201162338257e-05 4 5.3 0.19936 0.00015 8.37201476097107e-05 ... 13 14.43 0.19983 0.00027 0.000371020652850469 14 15.44 0.20014 0.0 0.000371020652850469 15 16.46 0.20061 0.00015 0.00041327071984609 16 17.47 0.19889 0.0 0.00041327071984609 17020 17261.52 0.89994 0.10464 502.143633647875 17021 17262.54 0.89994 0.10464 502.173107294611 17022 17263.55 0.90119 0.10455 502.202555591307 17023 17264.57 0.90135 0.10471 502.232048961676 ... 26419 26793.95 1.29006 0.00015 521.556162486447 26420 26794.97 1.28959 0.0 521.556162486447 26421 26795.98 1.29052 0.0 521.556162486447 26422 26796.99 1.29099 0.00027 521.556238536586
Обсуждение