Форум » GUI » TsBrowse в Минигуи (продолжение) » Ответить

TsBrowse в Минигуи (продолжение)

Vlad04: TsBrows определяется в виде строки ПАРМЕТРОВ объекта и их значений К примеру [quote] DEFINE TBROWSE oBrw2 ; AT 60,450 ; ALIAS cAlias ; OF Form1 ; WIDTH 330 ; HEIGHT 340 ; FONT "Verdana" ; SIZE 9 ; ON DBLCLICK CopyRec(); ON GOTFOCUS fModelo_Hab(2) ; AUTOFILTER ; CELLED EDIT; VALUE nRec; GRID [/quote] Здесь я собрал параметры из разных tBrows Можно или нет и какие парметры заменить выражением ( и каким) ? oBrw2:.... oBrw2:....

Ответов - 300, стр: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 All

SergKis: Haz пишет проще индекс держать с !Deleterd() Кто бы спорил, но у Андрея одно рабочее место без индекса, другое с условным индексом ... Что бы не ломать ту ситуацию новую можно так решать безболезненно. Да сложности особой нет

SergKis: PS + такая техника спокойно перейдет в LetoDB

Andrey: SergKis пишет: Кто бы спорил, но у Андрея одно рабочее место без индекса, другое с условным индексом ... Что бы не ломать ту ситуацию новую можно так решать безболезненно. Да сложности особой нет Да я уже согласился. Понял, что огород городить не надо. Надо открывать базу в SHARED и делать индекс !Deleterd() Только вот забыл как делать файл с уникальным именем в папке.. Сейчас сделаю свой вариант и покажу Tsbrowse.


SergKis: Andrey пишет Только вот забыл как делать файл с уникальным именем в папке.. А с именем самого dbf не пойдет. Его должны открывать ВСЕ приложения с модификацией (как SET AUTOPEN ON) иначе "нам удачи не видать"

SergKis: PS Если планируешь повторно исп. удаленные записи, то нужен тэг FOR Deleted()

Andrey: SergKis пишет: А с именем самого dbf не пойдет. Его должны открывать ВСЕ приложения с модификацией (как SET AUTOPEN ON) иначе "нам удачи не видать" Зачем ? Каждый узер открывает базу и делает свой индекс с !Deleterd() Вешаем таймер и каждые полминуты делаем Refresh() базы. Чем не решение ? Избавимся от создания общего индекса. Но можно держать и общий индекс с !Deleterd(). Это уже без разницы.

SergKis: Andrey пишет Вешаем таймер и каждые полминуты делаем Refresh() базы Знаем проходили, Чел пялится в свою запись на экране, вдруг раз она убежала то ли вверх то ли вниз - вставились\удалились записи рядом, попытка удержать курсор на ней может не получиться, пока он таращил глаза, наконец нашел, начал любоваться а она опять куда то улетела, причина см. выше и т.д. Избавимся от создания общего индекса По мне это минус. Если базы копеечные, то да так можно делать, но вопрос нужно ли ?

Andrey: SergKis пишет: Знаем проходили, Чел пялится в свою запись на экране, вдруг раз она убежала то ли вверх то ли вниз - вставились\удалились записи рядом, попытка удержать курсор на ней может не получиться, пока он таращил глаза, наконец нашел, начал любоваться а она опять куда то улетела, причина см. выше и т.д. Понял, так делать тогда не будем. Только для теста.... SergKis пишет: По мне это минус. Если базы копеечные, то да так можно делать, но вопрос нужно ли ? У меня такой механизм (условная индексация) работает уже лет 15. Условная индексация каждого юзера для всех его нужных полей базы, что хочет то и выбирает и ко мне не пристаёт.

Andrey: Что-то фигня получается с разными индексами... С одним общим лучше ! Вот так выглядит теперь: [img]https://i.imgur.com/gievW7U.png?3[/img] Вот проект и исходники (fpt) - https://cloud.mail.ru/public/GMwS/mbgXwsCEs Запустить две программы и можно тестировать. Посмотрите пожалуйста исходники, может что-то и упустил.

SergKis: Andrey Так поправь[pre2] FUNCTION UseOpenBase() LOCAL aStr := {} LOCAL cDbf := GetStartUpFolder() + "\TEST" LOCAL cIndx := cDbf LOCAL lDbfNo LOCAL aAlias := {} LOCAL n := 0 IF ( lDbfNo := ! File( cDbf+'.dbf' ) ) AAdd( aStr, { 'F1', 'D', 8, 0 } ) AAdd( aStr, { 'F2', 'C', 60, 0 } ) AAdd( aStr, { 'F3', 'N', 10, 2 } ) AAdd( aStr, { 'F4', 'L', 1, 0 } ) dbCreate( cDbf, aStr ) ENDIF IF lDbfNo .OR. !FILE(cIndx+'.cdx') // если нет базы или индекса USE ( cDbf ) ALIAS "TEST" EXCLUSIVE NEW WHILE TEST->( RecCount() ) < 10 TEST->( dbAppend() ) TEST->F1 := Date() + n++ TEST->F2 := Str( n ) TEST->F3 := n TEST->F4 := ( n % 2 ) == 0 END GO TOP INDEX ON RECNO() TAG NN FOR !Deleted() INDEX ON RECNO() TAG NO FOR Deleted() USE ENDIF SET AUTOPEN ON USE ( cDbf ) ALIAS "TEST" SHARED NEW OrdSetFocus('NN') GO TOP // ORDLISTADD( cIndx ) AADD( aAlias, ALIAS() ) // запомнить базу для закрытия RETURN aAlias [/pre2]

Andrey: Всё таки есть небольшие сбои в Tsbrowse или в примерах Tsb_Shared нужно что-то добавлять. Непонятки возникли: 1) если удалить 2-3 записи в таблице, то нарушается показ в вертикальном скролинге. 2) demo.exe - если таблица без вертикального скролинга, то при добавлении записей вертик.скролинг не появляется (иногда появляется) 3) demo2.exe - если таблица без вертикального скролинга, то при добавлении записей вертик.скролинг появляется но без нижней стрелки, огрызок какой то... Последний проект Tsb_Basic(1.8).7z здесь - https://cloud.mail.ru/public/8WrP/cpCzTtqKp

SergKis: Andrey Просто добавь воды строки[pre2] STATIC FUNCTION RecnoInsert(oBrw) ... oBrw:ResetVScroll( .T. ) oBrw:oHScroll:SetRange( 0, 0 ) ENDIF RETURN Nil ... STATIC FUNCTION RecnoDelete(oBrw) ... with object oBrw If :nLen > :nRowCount() .and. :nRowPos < :nRowCount() (:cAlias)->( dbSkip(-:nRowCount()) ) :nRowPos := :nRowCount() :Refresh(.T.) EndIf end with oBrw:ResetVScroll( .T. ) oBrw:oHScroll:SetRange( 0, 0 ) RETURN Nil ... [/pre2] выделенное синим, можешь не ставить.

Andrey: SergKis пишет: Просто добавь воды строки Спасибо ! Помогло !

Andrey: Всем доброй ночи ! Вот столкнулся с такой проблемой, не знаю как сделать перемещение записи в таблице вверх или вниз. Какой нужно сделать алгоритм показа ? Ввести новое поле в базу или по другому ? Я делал ранее алгоритм пересчёта поля на лету, но там база была очень маленькой. А для больших баз как это реализовать ? Вот заготовку примера сделал - https://cloud.mail.ru/public/FDWs/KgajUDnAw И ещё одна важная особенность ! Юзер хочет добавить запись под/перед курсором/маркером бровса, а не в конец базы.

Haz: Andrey пишет: Юзер хочет добавить запись под/перед курсором/маркером бровса, а не в конец базы. В чем проблема? Держи активный индекс по которому сортируются записи. Хоть вычисляемый, хоть по значению в поле. При добавлении позаботься о том чтобы у новой записи индекс получил нужное значение. Варианты реализации - все что угодно на твой вкус, главное результат. Вопрос вообще ни каким боком к Tsbrowse не относится. Если тупо оценить его то это, "как логически поместить запись в нужную позицию" Это же и к предыстории к вопроса относится. Как решишь, далее нужно всего лишь применить новую редакцию ::gotorec() с указанием нужной строки

SergKis: Andrey Использую без индексов C функции (вроде от Pasha они), пробни, может подойдет [pre2] #define HB_OS_WIN_USED #include "hbapi.h" #include "hbapiitm.h" #include "hbapirdd.h" #include "hbrdddbf.h" #include "hbapicdp.h" //#include "hbdate.h" #ifdef __XHARBOUR__ #define HB_SUCCESS SUCCESS #endif /* HB_FUNC( MILLIS ) { hb_retnl( hb_dateMilliSeconds() ); } */ /* HB_FUNC( DBSETCDP ) { AREAP pArea = hb_rddGetCurrentWorkAreaPointer(); if( pArea && ISCHAR(1) ) { char * pCdp = hb_parc(1); if( pCdp ) pArea->cdPage = hb_cdpFind( (char *) pCdp ); } } */ // ------------------------------- // Static func bMemoGetBlock(nPos) // Return {|| dbGetMemoBlock(nPos)} // ------------------------------- HB_FUNC( DBGETMEMOBLOCK ) { DBFAREAP pArea = ( DBFAREAP ) hb_rddGetCurrentWorkAreaPointer(); USHORT uiField = hb_parni( 1 ) - 1; #ifdef __XHARBOUR__ LPFIELD pField = pArea->lpFields + uiField; #else LPFIELD pField = pArea->area.lpFields + uiField; #endif char * ptr = ( char * ) pArea->pRecord + pArea->pFieldOffset[uiField]; ULONG ulBlock = 0; if( pField->uiLen == 4 ) ulBlock = HB_GET_LE_UINT32( ptr ); else { BYTE bByte; USHORT uiCount; for( uiCount = 0; uiCount < 10; uiCount++ ) { bByte = ptr[ uiCount ]; if( bByte >= '0' && bByte <= '9' ) ulBlock = ulBlock * 10 + ( bByte - '0' ); } } hb_retnl( ulBlock ); } // --------------------------- // Получить значение поля как строка // if FieldType(nPos) == "C"; xVal := FieldGet(nPos) // else ; xVal := dbGetValue(nPos) // endif // --------------------------- HB_FUNC( DBGETVALUE ) { DBFAREAP pArea = ( DBFAREAP ) hb_rddGetCurrentWorkAreaPointer(); USHORT uiField = hb_parni( 1 ) - 1; #ifdef __XHARBOUR__ LPFIELD pField = pArea->lpFields + uiField; #else LPFIELD pField = pArea->area.lpFields + uiField; #endif hb_retclen( ( char * ) pArea->pRecord + pArea->pFieldOffset[uiField], pField->uiLen ); } // ----------------------------- // if FieldType(nPos) == "C"; FieldPut(nPos, xVal) // else ; dbPutValue(nPos, xVal) // endif // ----------------------------- HB_FUNC( DBPUTVALUE ) { DBFAREAP pArea = ( DBFAREAP ) hb_rddGetCurrentWorkAreaPointer(); USHORT uiField = hb_parni( 1 ) - 1; #ifdef __XHARBOUR__ LPFIELD pField = pArea->lpFields + uiField; #else LPFIELD pField = pArea->area.lpFields + uiField; #endif if( ISCHAR(2) && ( hb_parclen(2) <= pField->uiLen ) ) { memcpy( ( char * ) pArea->pRecord + pArea->pFieldOffset[uiField], hb_parc(2), hb_parclen(2) ); pArea->fRecordChanged = TRUE; pArea->fDataFlush = TRUE; } hb_ret(); } // ------------------------ // Вставить запись // nKol := 1; nRec := RecNo() // dbGoto(nRec); dbInsert(nRec, nKol) // ------------------------ HB_FUNC( DBINSERT ) { DBFAREAP pArea = ( DBFAREAP ) hb_rddGetCurrentWorkAreaPointer(); BOOL bOk = TRUE; if( pArea && ! pArea->fReadonly && ! pArea->fShared ) { ULONG ulRec, ulCount = ISNUM(2) ? hb_parnl(2) : 1; #ifdef __XHARBOUR__ ULONG hFile = pArea->hDataFile; #else HB_FHANDLE hFile = hb_fileHandle( pArea->pDataFile ); #endif if( ISNUM(1) ) ulRec = hb_parnl(1); else SELF_RECNO( ( AREAP ) pArea, &ulRec ); if( ulRec == 0 || ulRec > pArea->ulRecCount ) bOk = FALSE; if( bOk && SELF_GOCOLD( ( AREAP ) pArea ) != HB_SUCCESS ) bOk = FALSE; else { ULONG ulIndex; for( ulIndex = 0; ulIndex < ulCount; ulIndex ++) SELF_APPEND( ( AREAP ) pArea, TRUE ); SELF_FLUSH( ( AREAP ) pArea ); /* pArea->fUpdateHeader = TRUE; pArea->ulRecCount += ulCount; if( SELF_WRITEDBHEADER( ( AREAP ) pArea ) != HB_SUCCESS ) bOk = FALSE; */ if( bOk ) { ULONG ulLen = (pArea->ulRecCount - ulRec) * pArea->uiRecordLen; ULONG ulLen1 = ulCount*pArea->uiRecordLen; // ULONG ulRecNo; char * pData = hb_xgrab( ulLen + 1 ); char * pZero = hb_xgrab( ulLen1 + 1 ); hb_fsSeekLarge( hFile, ( HB_FOFFSET ) pArea->uiHeaderLen + ( HB_FOFFSET ) pArea->uiRecordLen * ( HB_FOFFSET ) (ulRec - 1), FS_SET ); hb_fsReadLarge( hFile, pData, ulLen ); hb_fsSeekLarge( hFile, ( HB_FOFFSET ) pArea->uiHeaderLen + ( HB_FOFFSET ) pArea->uiRecordLen * ( HB_FOFFSET ) (pArea->ulRecCount - ulCount), FS_SET ); hb_fsReadLarge( hFile, pZero, ulLen1 ); hb_fsSeekLarge( hFile, ( HB_FOFFSET ) pArea->uiHeaderLen + ( HB_FOFFSET ) pArea->uiRecordLen * ( HB_FOFFSET ) (ulRec - 1), FS_SET ); hb_fsWriteLarge( hFile, pZero, ulLen1 ); hb_fsSeekLarge( hFile, ( HB_FOFFSET ) pArea->uiHeaderLen + ( HB_FOFFSET ) pArea->uiRecordLen * ( HB_FOFFSET ) (ulRec + ulCount - 1), FS_SET ); hb_fsWriteLarge( hFile, pData, ulLen ); hb_xfree( pData ); hb_xfree( pZero ); /* for( ulRecNo = ulRec + ulCount - 1; ulRecNo >= ulRec; ulRecNo --) { SELF_GOTO( ( AREAP ) pArea, ulRecNo ); SELF_SETBLANKRECORD( ( AREAP ) pArea, 2 ); // HB_BLANK_EOF } */ } } if( bOk && SELF_GOTO( ( AREAP ) pArea, ulRec ) != HB_SUCCESS ) bOk = FALSE; } hb_retl( bOk ); } // ---------------------------- // Физическое удаление записей // nKol := 1; nRec := RecNo() // dbGoto(nRec); dbDelRecord(nRec, nKol) // ---------------------------- HB_FUNC( DBDELRECORD ) { DBFAREAP pArea = ( DBFAREAP ) hb_rddGetCurrentWorkAreaPointer(); BOOL bOk = TRUE; if( pArea && ! pArea->fReadonly && ! pArea->fShared ) { ULONG ulRec, ulCount = ISNUM(2) ? hb_parnl(2) : 1; #ifdef __XHARBOUR__ ULONG hFile = pArea->hDataFile; #else HB_FHANDLE hFile = hb_fileHandle( pArea->pDataFile ); #endif if( ISNUM(1) ) ulRec = hb_parnl(1); else SELF_RECNO( ( AREAP ) pArea, &ulRec ); if( ulRec == 0 || (ulRec + ulCount - 1) > pArea->ulRecCount ) bOk = FALSE; if( bOk && SELF_GOCOLD( ( AREAP ) pArea ) != HB_SUCCESS ) bOk = FALSE; else { char pData; if( (ulRec + ulCount - 1) < pArea->ulRecCount ) { ULONG ulLen = (pArea->ulRecCount - (ulRec + ulCount - 1)) * pArea->uiRecordLen; char * pData = hb_xgrab( ulLen + 1 ); hb_fsSeekLarge( hFile, ( HB_FOFFSET ) pArea->uiHeaderLen + ( HB_FOFFSET ) pArea->uiRecordLen * ( HB_FOFFSET ) (ulRec + ulCount - 1), FS_SET ); hb_fsReadLarge( hFile, pData, ulLen ); hb_fsSeekLarge( hFile, ( HB_FOFFSET ) pArea->uiHeaderLen + ( HB_FOFFSET ) pArea->uiRecordLen * ( HB_FOFFSET ) (ulRec - 1), FS_SET ); hb_fsWriteLarge( hFile, pData, ulLen ); hb_xfree( pData ); } pArea->fUpdateHeader = TRUE; pArea->ulRecCount -= ulCount; } if( bOk && SELF_WRITEDBHEADER( ( AREAP ) pArea ) != HB_SUCCESS ) bOk = FALSE; if( bOk && SELF_GOTO( ( AREAP ) pArea, ulRec ) != HB_SUCCESS ) bOk = FALSE; } hb_retl( bOk ); } // -------------------------- // Обрезать файл после записи ... // nRec := RecNo(); dbGoto(nRec) // dbTruncate(nRec) // -------------------------- HB_FUNC( DBTRUNCATE ) { DBFAREAP pArea = ( DBFAREAP ) hb_rddGetCurrentWorkAreaPointer(); BOOL bOk = TRUE; if( pArea && ! pArea->fReadonly && ! pArea->fShared ) { ULONG ulRec; if( ISNUM(1) ) ulRec = hb_parnl(1); else SELF_RECNO( ( AREAP ) pArea, &ulRec ); if( ulRec == 0 || ulRec > pArea->ulRecCount ) bOk = FALSE; if( bOk && SELF_GOCOLD( ( AREAP ) pArea ) != HB_SUCCESS ) bOk = FALSE; else { pArea->fUpdateHeader = TRUE; pArea->ulRecCount = ulRec; } if( bOk && SELF_WRITEDBHEADER( ( AREAP ) pArea ) != HB_SUCCESS ) bOk = FALSE; if( bOk && SELF_GOTO( ( AREAP ) pArea, ulRec ) != HB_SUCCESS ) bOk = FALSE; } hb_retl( bOk ); } HB_FUNC( DBRECORDGET ) { AREAP pArea = ( AREAP ) hb_rddGetCurrentWorkAreaPointer(); PHB_ITEM pItem = hb_itemPutNI( NULL, 0 ); BYTE *pRec; if( pArea && (SELF_INFO( (AREAP) pArea, DBI_GETRECSIZE, pItem ) == HB_SUCCESS) && ( SELF_GETREC( (AREAP) pArea, &pRec ) == HB_SUCCESS ) ) { hb_retclen( pRec, hb_itemGetNI(pItem) ); } hb_itemRelease( pItem ); } HB_FUNC( DBRECORDPUT ) { AREAP pArea = ( AREAP ) hb_rddGetCurrentWorkAreaPointer(); PHB_ITEM pItem = hb_itemPutNI( NULL, 0 ); if( pArea && ISCHAR(1) && (SELF_INFO( (AREAP) pArea, DBI_GETRECSIZE, pItem ) == HB_SUCCESS) && hb_parclen(1) >= (ULONG) hb_itemGetNI(pItem) ) { SELF_PUTREC( pArea, hb_parc(1) ); } hb_itemRelease( pItem ); } static void SetFieldType( LPFIELD pField, char bType ) { switch( bType ) { case 'C': pField->uiType = HB_FT_STRING; break; case 'L': pField->uiType = HB_FT_LOGICAL; break; case 'D': pField->uiType = HB_FT_DATE; break; case 'I': case '2': case '4': pField->uiType = HB_FT_INTEGER; break; case 'Y': pField->uiType = HB_FT_CURRENCY; break; case 'N': pField->uiType = HB_FT_LONG; break; case 'F': pField->uiType = HB_FT_FLOAT; break; case '8': case 'B': pField->uiType = HB_FT_DOUBLE; } } // ------------------------ // Переименовать поле, поменять его тип и дробную часть // в пределах длины поля, заданную при создании dbf // FieldRename( nPos, FieldNameNew[, FieldTypeNew[, FieldDecNew]] ) // ------------------------ HB_FUNC( FIELDRENAME ) { DBFAREAP pArea = ( DBFAREAP ) hb_rddGetCurrentWorkAreaPointer(); USHORT uiField = hb_parni( 1 ); char szFieldName[12]; #ifdef __XHARBOUR__ if( uiField && uiField <= pArea->uiFieldCount && ISCHAR(2) && hb_parclen(2) <= 11 ) #else if( uiField && uiField <= pArea->area.uiFieldCount && ISCHAR(2) && hb_parclen(2) <= 11 ) #endif { char * szType = hb_parc( 3 ); #ifdef __XHARBOUR__ ULONG hFile = pArea->hDataFile; LPFIELD pField = pArea->lpFields + uiField - 1; #else HB_FHANDLE hFile = hb_fileHandle( pArea->pDataFile ); LPFIELD pField = pArea->area.lpFields + uiField - 1; #endif ULONG ulOffset = sizeof( DBFHEADER ) + (uiField-1)*sizeof( DBFFIELD ); memset(szFieldName, 0, 12); memcpy(szFieldName, hb_parc(2), hb_parclen(2) ); hb_fsSeekLarge( hFile, ( HB_FOFFSET ) ulOffset, FS_SET ); hb_fsWriteLarge( hFile, szFieldName, 11 ); pField->sym = ( void * ) hb_dynsymGetCase( szFieldName ); if( szType ) { hb_fsSeekLarge( hFile, ( HB_FOFFSET ) ( ulOffset + 11 ), FS_SET ); hb_fsWriteLarge( hFile, szType, 1 ); SetFieldType( pField, szType[0] ); } if( ISNUM( 4 ) ) { USHORT uiDec = hb_parni( 4 ); hb_fsSeekLarge( hFile, ( HB_FOFFSET ) ( ulOffset + 17 ), FS_SET ); hb_fsWriteLarge( hFile, (char *) &uiDec, 1 ); pField->uiDec = uiDec; } } } HB_FUNC( DBFRECCOUNT ) { DBFAREAP pArea = ( DBFAREAP ) hb_rddGetCurrentWorkAreaPointer(); if( pArea ) { if( ISNUM( 1 ) ) { HB_PUT_LE_UINT32( pArea->dbfHeader.ulRecCount, hb_parnl( 1 ) ); pArea->fUpdateHeader = TRUE; } hb_retnl( HB_GET_LE_UINT32( pArea->dbfHeader.ulRecCount ) ); } else hb_retnl( 0 ); } #include <windows.h> HB_FUNC( GETSYSTEMDEFAULTLANGID ) { hb_retnl( GetSystemDefaultLangID() ); } [/pre2]

Haz: SergKis пишет: Использую без индексов Сергей привет. Навеяно исходниками. Году в 90 занимался вставкой записей используя функции семейства fread, fwrite итд. Зная структуру dbf эта задача прммитивная, но тогда.... и базы были короче и валялись они локально. А может и оптимизма в голове было побольше. Сейчас я только пожелаю флаг в руки и вперёд в атаку. А я тут подожду.

SergKis: Haz пишет А я тут подожду. Как говорится "Хозяин-барин", но dbInsert очень хорошо легла на: 1. Разбиение отчета печати по листам, когда нельзя разрывать лист на строках заголовков, итогов и т.д., т.е. разрывать лист можно только на детальной строке а далее как душа ляжет ... Вставляю строку типа chr(12) 2. Выполнен отбор типа select * ...., далее по уточнению различные группирования, заголовки\подзаголовки\итоги\под итоги и т.д. с выходом на печать и п.1 3. Ввод в какой то документ, где пользователь сам определяет перед\после какой строкой[и] делаем запись new 4. Export или подсовывание запроса с базы где нет полей с определенными названиями в какой то уже работающий отчет. Делаем запрос и потом меняем названия полей на нужные или на раб. месте за дробью надо не 7 знаков, а по установке, округляем и меняем в структуре через FieldRename(...) Это основное, что использую.

Haz: SergKis пишет: Как говорится "Хозяин-барин", но Плавали, знаем все тоже делаю в sql запросах Select top 50 * Select next Select summa as [ выручка с НДС ] Группировки так вообще для этого скуль и придумали а вставку в определённую логическую позицию по спец полю определяющему сортировку и вложенность структуры. Но тут кто как привык. Все подходы правильные когда работают

SergKis: Haz пишет все тоже делаю в sql запросах На ADS у нас только немцы приходили в начале 2000-х, но их выжили скандинавы. Из за его стоимости никто из клиентов не хочет(ел) приобретать лицензию, даже богатый Латв.энерго. От исп. mysqll и firebird мы отказались сами из за администр. затрат эксплуатации на чужих администраторах\рс. Но тут кто как привык Схему select * или select (...) as (...) ... поддерживаю на DBFCDX(letodb), распределяя "основной" запрос на сервер, с доработкой "до кондиции" на клиенте.



полная версия страницы