После перехода на FireDAC у меня возникли проблемы с тем, чтобы этот код работал на MSSQL/Oracle:
with DataFormsettings do
begin
Close;
if Params.Count=0 then FetchParams;
Params.ParamByName('TT_EMP_ID').Asinteger := AEmpID;
Params.ParamByName('TT_FORM').AString := UpperCase(AKey);
Open;
if (RecordCount>0) then
S := FieldByName('TT_VIEWDATA').Asstring;
end;
AKey и S являются строками.
Оператор Open выдает ошибку
[FireDAC][Phys][MSSQL]-338 Param type changed from [ftString] to [ftWidestring]
[FireDAC][Phys][Ora]-338 Param type changed from [ftString] to [ftWidestring]
при подключении к базе данных MSSQL или Oracle; не при подключении к FireBird.
После FetchParams
DataFormsettings.params[1].datatype
всегда стоит ftString
.
Если я заменю
Params.ParamByName('TT_FORM').AString := UpperCase(AKey);
с
Params.ParamByName('TT_FORM').Value := UpperCase(AKey);
... в операторе Open нет ошибок. Я думал, что решил это, хотя я не очень понял ошибку. В конце концов, это должны быть все типы Delphi String по умолчанию...
Но теперь присваивание S не работает для Oracle (не FireBird или MSSQL) в том смысле, что я вижу, что возвращаются 2-байтовые символы. С содержит:
\'#0'S'#0'o'#0'f'#0't'#0'w'#0'a'#0'r'#0'e'#0'\'#0'T'#0'i'#0'm'#0'e'#0'T'#0'e'#0'l'#0'l'#0'...
Я могу справиться с этим, например.
S := TEncoding.Unicode.GetString(FieldByName('TT_VIEWDATA').AsBytes);
для Oracle, но (конечно) при использовании двух других типов баз данных это не работает:
No mapping for the Unicode character exists in the target multi-byte code page
Что мне здесь не хватает? В частности, я хотел бы просто заставить работать извлечение/назначение AsString.
Обратите внимание, что Установка свойства AsString устанавливает для свойства DataType значение ftWideString или ftString в примечании документация FireDAC TFDParam.AsString. Кажется, что присваивание значения параметра просто переключает тип с ftString на ftWideString (на что указывает исходная ошибка).
DataFormSettings
— это TClientDataSet
в клиентском приложении, подключенном к серверному приложению, где находятся TDataSetProvider
и TFDQuery
. Запрос
select
TT_FORMSETTINGS_ID,
TT_EMP_ID,
TT_FORM,
TT_VERSION,
TT_VIEWDATA
from TT_FORMSETTINGS
where TT_EMP_ID=:TT_EMP_ID
and TT_FORM=:TT_FORM
Таблицы были созданы следующим образом:
Огненная птица:
CREATE TABLE TT_FORMSETTINGS
(
TT_FORMSETTINGS_ID INTEGER DEFAULT 0 NOT NULL,
TT_EMP_ID INTEGER,
TT_FORM VARCHAR(50),
TT_VERSION INTEGER,
TT_VIEWDATA BLOB SUB_TYPE TEXT SEGMENT SIZE 80,
TT_TAG INTEGER,
TT_TAGTYPE INTEGER,
TT_TAGDATE TIMESTAMP
);
Оракул:
CREATE TABLE TT_FORMSETTINGS
(
TT_FORMSETTINGS_ID NUMBER(10,0) DEFAULT 0 NOT NULL,
TT_EMP_ID NUMBER(10,0),
TT_FORM VARCHAR(50),
TT_VERSION NUMBER(10,0),
TT_VIEWDATA CLOB,
TT_TAG NUMBER(10,0),
TT_TAGTYPE NUMBER(10,0),
TT_TAGDATE DATE
);
MSSQL:
CREATE TABLE TT_FORMSETTINGS
(
TT_FORMSETTINGS_ID INTEGER NOT NULL CONSTRAINT TT_C0_FORMSETTINGS DEFAULT 0,
TT_EMP_ID INTEGER NULL,
TT_FORM VARCHAR(50) NULL,
TT_VERSION INTEGER NULL,
TT_VIEWDATA TEXT NULL,
TT_TAG INTEGER NULL,
TT_TAGTYPE INTEGER NULL,
TT_TAGDATE DATETIME NULL
);
Я проверил, что TT_VIEWDATA
содержит правильные данные во всех базах данных; это длинная строка, содержащая CRLF:
\Software\TimeTell\Demo8\Forms\TFormTileMenu'#$D#$A'Version,1,80502'#$D#$A'\Software\TimeTell\Demo8\Forms\TFormTileMenu\TileControlMenu'#$D#$A'\Software\TimeTell\Demo8\Forms\TFormTileMenu\TileControlMenu\FormTileMenu.TileControlMenu'#$D#$A'Version,4,2'#$D#$A'\Software\TimeTell\Demo8\Forms\TFormTileMenu\TileControlMenu\FormTileMenu.TileControlMenu...
Примечания:
- В настоящее время тестируется на SQL Server 2008 и Oracle 10, но я ожидаю, что это не будет отличаться для других версий.
- FWIW,
select * from NLS_database_PARAMETERS where parameter like '%CHARACTERSET%'
возвращаетNLS_CHARACTERSET=WE8MSWIN1252
иNLS_NCHAR_CHARACTERSET=AL16UTF16
ЗапросSELECT dump(dbms_lob.substr(tt_viewdata,100,1), 1016), tt_viewdata FROM tt_formsettings
подтверждает, что CLOB содержит байты ASCII для кодовой страницы Win1252:Typ=1 Len=100 CharacterSet=WE8MSWIN1252: 5c,53,6f,66,74,77,61,72,65,5c,54,69,6d,65,54,65,6c,6c,5c,44,65,...
FieldByName().AsANSIString
дает те же результаты, что иFieldByName().AsString
Дополнительная информация: это устаревшее приложение с постоянными определениями полей в файле DataFormsettings
TClientDataset
. TT_VIEWDATA
определяется как TMemoField
:
DataFormsettingsTT_VIEWDATA: TMemoField;
В небольшом тестовом приложении (напрямую подключенном к Oracle, а не клиент-серверном) я позволил Delphi добавить определения полей, а затем он сказал:
DataFormsettingsTT_VIEWDATA: TWideMemoField;
Если я использую это в основном приложении, Oracle работает нормально, но тогда я получаю «мусор» для MSSQL.
Я также экспериментировал с настройкой правил сопоставления для соединения с Oracle, например (много вариантов):
with AConnection.FormatOptions.MapRules.Add do
begin
SourceDataType := dtWideMemo;
TargetDataType := dtMemo;
end;
AConnection.FormatOptions.OwnMapRules := true;
но это не помогло.
FetchParams
методом)? Это произошло так же, как здесь (см. среднюю часть поста). Бьюсь об заклад, если вы уберете вызовFetchParams
, у вас не будет никаких проблем. FireDAC автоматически преобразует значения параметров для подготовленного оператора. Но он не позволяет подготовить оператор (описывающий параметры), изменить типы данных параметров и выполнить оператор. - person Victoria   schedule 06.12.2017DataFormsettings
являетсяTFDQuery
и что вы запускаете команды, которые вы показали. - person Victoria   schedule 07.12.2017varchar(50)
(по крайней мере, для MSSQL) не является юникодом.string
на стороне Delphi является юникодом, поэтому предполагается, что если доступ к параметру осуществляется с помощьюAsString
, используется юникод, тип данных установлен наftWideString
. Например. вData.DB
:procedure TParam.SetAsString(const Value: string); begin if FDataType <> ftFixedWideChar then FDataType := ftWideString; Self.Value := Value; end;
. Имеет ли это смысл? Вам действительно нужно иметьvarchar
полей? - person nil   schedule 07.12.2017string
в Delphi, но на самом деле это не так, поскольку они не являются юникодными. Я чувствую, что вы смешиваете здесь юникод и не юникод. Используемые вами поля и параметры поддерживаютAsAnsiString
? - person nil   schedule 11.12.2017