QUIT - this is what we need, but unfortunately it has one side effect - the output of the program. Seriously, if there was a similar QUIT without exiting the program, it would really be what you need. I made a few functions to work with databases on the basis of low-level functions and working with databases larger than 2GB and more fields in 1600. Tried in field experiments to 100,000 and 100,000 records, it turns out the base 254Gb.
Code: Select all
**************************************************************************************************
*** Эксперимент по созданию и заполнению базы данных сверхбольшой размерности собственного формата
*** БД создается похожей на Abs, Prc1, Prc2, Inf1-Inf7, т.к. это нужно в первую очередь для них
*** (С) Е.В.Луценко, 01.08.2013
**************************************************************************************************
FUNCTION M_a_i_n() // Это ПРИМЕРЫ обращения к функциям работы с текстовой базой данных
LOCAL Getlist := {}, oProgress, oDialog
DC_IconDefault(1000)
**** Опрделение максимальной длины текстовой переменной для строки базы данных
**mTXT = ""
*DO WHILE .T.
* mTXT = mTXT + REPLICATE("#",1000000)
*ENDDO
* Оказалось текстовая переменная может содержать до 282 млн.символов. Этого более чем достаточно
CrLf = CHR(13)+CHR(10) // Конец строки (записи)
N_Cls = 1000 // Число классов
N_Rec = 1000 // Число признаков
N_Col = N_Cls+3 // Число полей
* ###########################################################################
********** Структура создаваемой базы ***********
aStructure := { { "Kod_pr", "N", 15, 0},; // 1
{ "Name" , "C", 15, 0} } // 2
FOR j=1 TO N_Cls
FieldName = "N"+ALLTRIM(STR(j,15))
AADD(aStructure, { FieldName, "N", 19, 2 })
NEXT
AADD(aStructure, { "Summa", "N", 19, 2 })
*************************************************
*DC_ASave(aStructure, "_AbsStruct.arx") // Когда БД создается - записывать структуру, когда открывается - считывать
*aStructure = DC_ARestore("_AbsStruct.arx")
N_Col = N_Cls+3 // Число полей
DB_name = "Max_DB.txt" // Имя базы данных
nHandle := FCreate( DB_name, FC_NORMAL ) // Создание БД (если она была, то все равно создается пустая) ######
*nHandle := FOpen( DB_name, FO_READWRITE ) // Открытие базы данных ############################################
IF nHandle = -1
MsgBox("Файл: "+DB_name+" не может быть создан. Ошибка:"+FERROR())
RETURN NIL
ENDIF
***** Формирование пустой записи
CrLf = CHR(13)+CHR(10) // Конец строки (записи)
Lc_buf = ""
FOR j=1 TO N_Col
S = IF(j=2*INT(j/2),"#","X") // Для отладки
* S = " " // Для работы
IF aStructure[j,4] = 0
Lc_buf = Lc_buf + REPLICATE(S, aStructure[j,3])
ELSE
Lc_buf = Lc_buf + REPLICATE(S, aStructure[j,3]-aStructure[j,4]-1)+"."+REPLICATE(S, aStructure[j,4])
ENDIF
NEXT
Lc_buf = Lc_buf + CrLf
Len_LcBuf = LEN(Lc_buf)
LC_DbCreate( DB_name, nHandle, Lc_buf, N_Rec ) // Создание БД.txt, содержащей N_Rec пустых записей ############
*DbCreate( "Max_DB", aStructure ) // Создание пустой БД.dbf (для отладки)
**** Рассчет массива начальных позиций полей в строке
PRIVATE aPos[N_Col]
aPos[1] = 1
FOR j=2 TO N_Col
aPos[j] = aPos[j-1] + aStructure[j-1,3]
NEXT
* ###########################################################################
*** Отображение начальных позиций полей (отладка)
*aM := {}
*FOR j=1 TO N_Col
* AADD(aM, STR(j)+" "+STR(aPos[j]))
*NEXT
*LB_Warning(aM)
*** Запись поля в БД (корректная) ***********
FOR i=1 TO N_Rec
FOR j=1 TO N_Col
IF aStructure[j,4] = 0
String = STR(i*1000+j,aStructure[j,3])
ELSE
String = STR(i*1000+j+0.12,aStructure[j,3],aStructure[j,4])
ENDIF
Flag_err = LC_FieldPut( DB_name, nHandle, i, j, String ) // Запись поля в БД (корректная) #####################
IF Flag_err
EXIT
ENDIF
NEXT
NEXT
*Flag_err = LC_FieldPut( DB_name, nHandle, Pos, String ) // Запись поля в БД (некорректная)
*** Считывание поля в БД (корректная) ***********
*** Формирование БД.dbf (для отладки)
USE Max_DB EXCLUSIVE NEW;ZAP
SELECT Max_DB
DBGOTOP()
FOR j=1 TO N_Rec
APPEND BLANK
NEXT
FOR i=1 TO N_Rec
FOR j=1 TO N_Col
String = LC_FieldGet( DB_name, nHandle, i, j ) // Считывание поля из БД (корректная) ################
DO CASE
CASE aStructure[j,2] = "C"
DBGOTO(i);FIELDPUT(j, String ) // Для отладки
CASE aStructure[j,2] = "N"
DBGOTO(i);FIELDPUT(j, VAL(String) ) // Для отладки
ENDCASE
IF EMPTY(String)
EXIT
ENDIF
NEXT
NEXT
*String = LC_FieldGet( DB_name, nHandle, Pos ) // Считывание поля из БД (некорректная)
******* Эксперимент по определеию скорости обращения к базам данных TXT и DBF
nTimeON := SECONDS()
FOR i=1 TO N_Rec
FOR j=1 TO N_Col
String = LC_FieldGet( DB_name, nHandle, i, j ) // Считывание поля из БД (корректная) ################
Flag_err = LC_FieldPut( DB_name, nHandle, i, j, String ) // Запись поля в БД (корректная) #####################
NEXT
NEXT
nTimeOFF := SECONDS()
MsgBox("Время исполнения для БД.TXT="+ALLTRIM(STR(nTimeOFF-nTimeON))+" сек.")
nTimeON := SECONDS()
SELECT Max_DB
FOR i=1 TO N_Rec
FOR j=1 TO N_Col
DBGOTO(i);Str = FieldGet( j )
DBGOTO(i);FIELDPUT(j, Str )
NEXT
NEXT
nTimeOFF := SECONDS()
MsgBox("Время исполнения для БД.DBF="+ALLTRIM(STR(nTimeOFF-nTimeON))+" сек.")
*** Результат: обращение к на чтение и запись происходит БД.txt почти в 3 раза быстрее, чем к БД.dbf
FClose( nHandle ) // Закрытие текстовой базы данных ######################################
RETURN NIL
***********************************************************
******** Создание Max_БД
******** - DB_name - имя создаваемой БД
******** - nHandle - идентификатор создаваемой БД
******** - Lc_buf - пустая запись (строка) базы данных
******** - N_Rec - количество строк (записей)
***********************************************************
FUNCTION LC_DbCreate( DB_name, nHandle, Lc_buf, N_Rec )
Len_LcBuf = LEN(Lc_buf)
nTimeON := SECONDS()
nMax = N_Rec
Mess = 'Создание файла: '+DB_name
@ 4,5 DCPROGRESS oProgr SIZE 80,1.1 MAXCOUNT nMax COLOR GRA_CLR_CYAN PERCENT EVERY 100
DCREAD GUI TITLE Mess PARENT @oDial FIT EXIT
oDial:show()
nTime = 0
DC_GetProgress(oProgr,0,nMax)
FOR i=1 TO N_Rec // Реальное число записей
Len_rec = FWrite( nHandle, Lc_buf, Len_LcBuf )
IF Len_rec < Len_LcBuf
MsgBox("Произошла ошибка записи файла: "+DB_name+". Ошибка:"+FERROR())
RETURN NIL
ENDIF
DC_GetProgress(oProgr, ++nTime, nMax)
NEXT
DC_GetProgress(oProgr,nMax,nMax)
*FClose( nHandle )
nTimeOFF := SECONDS()
*MsgBox("Время исполнения="+ALLTRIM(STR(nTimeOFF-nTimeON))+" сек.")
oDial:Destroy()
RETURN NIL
********************************************
******** Запись поля в Max_БД
********************************************
FUNCTION LC_FieldPut( DB_name, nHandle, mRec, mCol, String )
Pos = (mRec-1) * Len_LcBuf + aPos[mCol] - 1
FSEEK(nHandle, Pos, FS_SET) // Позиционирование начала поля
Len_str = LEN(String)
N_Write = FWrite( nHandle, String, Len_str )
IF N_Write < Len_str
Mess = 'Ошибка записи поля: [строка=@, колонка=$] в БД: "#"'
Mess = STRTRAN(Mess, "#", DB_Name)
Mess = STRTRAN(Mess, "@", ALLTRIM(STR(mRec)))
Mess = STRTRAN(Mess, "$", ALLTRIM(STR(mCol)))
MsgBox(Mess)
RETURN(.T.)
ENDIF
RETURN(.F.)
********************************************
******** Считывание поля из Max_БД
********************************************
FUNCTION LC_FieldGet( DB_name, nHandle, mRec, mCol )
Len_str = aStructure[mCol,3]
Pos = (mRec-1) * Len_LcBuf + aPos[mCol] - 1
FSEEK(nHandle, Pos, FS_SET) // Позиционирование начала поля
String = SPACE(Len_str)
N_Read = FRead( nHandle, @String, Len_str )
IF N_Read < Len_str
Mess = 'Ошибка считывания поля: [строка=@, колонка=$] БД: "#"'
Mess = STRTRAN(Mess, "#", DB_Name)
Mess = STRTRAN(Mess, "@", ALLTRIM(STR(mRec)))
Mess = STRTRAN(Mess, "$", ALLTRIM(STR(mCol)))
MsgBox(Mess)
RETURN("")
ENDIF
// Пробел в числовом поле рассматривается как "0"
IF aStructure[mCol,2] = "N" .AND. LEN(ALLTRIM(String)) = 0
String = "0"
ENDIF
RETURN(String)
There is only one problem. When you close these bases, there is an impression that they are still open, at least for a while.