Odac, TOraDataSetField

Обсуждение возникших проблем, предложений и ошибок ODAC компонентов
indapublic
Сообщения: 21
Зарегистрирован: Чт 17 ноя 2011 23:40

Odac, TOraDataSetField

Сообщение indapublic » Чт 17 ноя 2011 23:48

Добрый день. Хочу поработать со вложенными таблицами через Odac.

Oracle Database 10g Enterprise Edition Release 10.2.0.2.0 - 64bit
Созданы объекты:

Код: Выделить всё

CREATE OR REPLACE TYPE address as OBJECT...
CREATE OR REPLACE TYPE address_tab as table of ADDRESS...
CREATE OR REPLACE TYPE k_obj as OBJECT (ID VARCHAR2(12), ADDRESSES ADDRESS_TAB...
CREATE TABLE k_obj_tab OF K_OBJ (ID...
Запрос в TSmartOraQuery

Код: Выделить всё

select
    kot.ID, kot.ADDRESSES
from
    K_OBJ_TAB kot
where
    kot.ID = :p_ID
Поле DataSet.FieldByName('ADDRESSES') является TOraDataSetField, я могу получить доступ к TOraDataSetField(DataSet.FieldByName('ADDRESSES')).NestedDataSet, я могу проводить с ним манипуляции - но могу ли я записать его в сам датасет через UpdateSql? Если да - то какой должен быть UpdateSql?

Поизучал демки в комплекте к Odac. Попробовал сделать через OraSql (хотя, конечно, это более неудобно, чем через Query).

Код: Выделить всё

select ADDRESSES into :RES from K_OBJ_TAB where ID = :p_ID
Но возникла проблема при чтении из Delphi:

Код: Выделить всё

ParamByName('p_ID').Value := mdRecordID.AsVariant;
ParamByName('RES').DataType := ftTable;
ParamByName('RES').ParamType := ptOutput;
ParamByName('RES').AsTable.CreateObject(dmDB.orsn.OCISvcCtx, 'ADDRESS_TAB');
Execute;
Получаю ошибку

Код: Выделить всё

ORA-01036:_illegal variable name/number

Alexp
Devart Team
Сообщения: 349
Зарегистрирован: Пн 27 дек 2010 10:34

Сообщение Alexp » Пт 18 ноя 2011 13:55

Добрый день,

Для работы с Nested table Вы можете использовать приведенный ниже код:

Код: Выделить всё

CREATE TYPE rec_of_data as object (id1 number, id2  number); 

CREATE TYPE tab_of_data as table of rec_of_data; 

CREATE TABLE TABLE_OBJ 
( 
f_id NUMBER, 
f_obj tab_of_data 
) 
NESTED TABLE f_obj STORE AS f_obj_tab;

Код: Выделить всё

var 
  OraQuery: TOraQuery; 
  OraNestedTable: TOraNestedTable; 
  i: integer; 
begin 
  OraQuery:= TOraQuery.Create(nil); 
  OraNestedTable:= TOraNestedTable.Create(nil); 

  OraQuery.Session := OraSession1; 
  OraQuery.SQL.Text:='SELECT * FROM TABLE_OBJ'; 
  OraQuery.KeyFields := 'F_ID'; 
  OraQuery.ObjectView:= true; 
  OraQuery.Open; 

  OraNestedTable.DataSetField:=TDataSetField(OraQuery.FieldByName('F_OBJ')); 
  OraQuery.Append; 
  OraQuery.FieldByName('F_ID').AsInteger:= 1; 


  for i:= 1 to 10 do 
  begin 
    OraNestedTable.Append; 
    OraNestedTable.FieldByName('ID1').AsInteger := i; 
    OraNestedTable.FieldByName('ID2').AsInteger := 1+i; 
    OraNestedTable.Post 
  end; 
  OraQuery.Post; 

indapublic
Сообщения: 21
Зарегистрирован: Чт 17 ноя 2011 23:40

Сообщение indapublic » Вс 20 ноя 2011 23:21

Спасибо за ответ. Все получилось. Изменю немного тему и задачу.

Допустим, у нас есть супертип:

Код: Выделить всё

CREATE OR REPLACE 
TYPE k_obj as OBJECT 
) NOT INSTANTIABLE NOT FINAL;
/
два подтипа:

Код: Выделить всё

CREATE OR REPLACE 
TYPE k_org UNDER K_OBJ (
);
/

CREATE OR REPLACE 
TYPE k_pers UNDER K_OBJ (
);
/
и та же таблица:

Код: Выделить всё

CREATE TABLE k_obj_tab
OF K_OBJ
Согласно документации Oracle я могу создать в этой таблице экземпляры потомков K_OBJ. Каким образом я могу это задать? В режиме по умолчанию я получаю ошибку

Код: Выделить всё

ORA-22826: cannot construct an instance of a non instantiable type
Только через явное указание UpdateSql?

А если еще больше усложнить задачу. Возможно ли управлять ситуацией, когда в NestedTable может записываться не супертип, а подтип?

Alexp
Devart Team
Сообщения: 349
Зарегистрирован: Пн 27 дек 2010 10:34

Сообщение Alexp » Пн 21 ноя 2011 09:02

Добрый день,

Для вставки данных в такие таблицы Вы не сможете использовать автогененрируемые Update/Insert, Вам необходимо явно вызывать конструкторы объектов следующим образом

Код: Выделить всё

CREATE OR REPLACE 
TYPE k_obj as OBJECT
(
  id NUMBER  
) 
 NOT INSTANTIABLE NOT FINAL; 
/

CREATE OR REPLACE 
TYPE k_org UNDER K_OBJ ( 
); 
 /

CREATE OR REPLACE 
TYPE k_pers UNDER K_OBJ ();
 / 
 
CREATE TABLE k_obj_tab 
OF K_OBJ;
/

INSERT INTO k_obj_tab
  VALUES(k_pers(1));
/

INSERT INTO k_obj_tab
  VALUES(k_org(2));
/

indapublic
Сообщения: 21
Зарегистрирован: Чт 17 ноя 2011 23:40

Сообщение indapublic » Пн 21 ноя 2011 13:30

Да, я так и понял. Спасибо за ответы

indapublic
Сообщения: 21
Зарегистрирован: Чт 17 ноя 2011 23:40

Сообщение indapublic » Ср 23 ноя 2011 00:34

Еще один вопрос:
Имея на руках сформированный OraNestedTable, как я могу его передать в TOraSQL? Приведение параметра

Код: Выделить всё

ParamByName('p_ADDRESSES').AsTable := ntAddresses;
говорит о несоответствии типов,

Код: Выделить всё

ParamByName('p_ADDRESSES').AsDataSet := ntAddresses;
говорит

Код: Выделить всё

Type of object must be defined
а приведение к типу

Код: Выделить всё

ParamByName('p_ADDRESSES').AsTable.CreateObject(dmDB.orsn.OCISvcCtx, 'MYUSER.ADDRESS');
ParamByName('p_ADDRESSES').AsDataSet := ntAddresses;
говорит

Код: Выделить всё

Type of object must be Array
Перечитал демки, где ошибаюсь - непонятно

Alexp
Devart Team
Сообщения: 349
Зарегистрирован: Пн 27 дек 2010 10:34

Сообщение Alexp » Ср 23 ноя 2011 13:21

Добрый день,

Для использования сформированного OraNestedTable в качестве параметра Вы можете использовать следующий код:

Код: Выделить всё

var 
  OraNestedTable: TOraNestedTable; 
begin 
  OraNestedTable:= TOraNestedTable.Create(nil); 
  OraNestedTAble.Table :=  TOraNestTable.Create(TOraType.create(OraSession1.OCISvcCtx,'MYUSER.ADDRESS')); 
  OraNestedTable.Open; 
  //заполнение NestedTable
  //........
  ....ParamByName('p_ADDRESSES').AsTable:= OraNestedTAble.Table; 
  ...
end;

indapublic
Сообщения: 21
Зарегистрирован: Чт 17 ноя 2011 23:40

Сообщение indapublic » Ср 23 ноя 2011 23:45

На строке

Код: Выделить всё

OraNestedTable.Table := TOraNestTable.Create(TOraType.Create(dmDB.orsn.OCISvcCtx, 'KDB.ADDRESS'));
получаю ошибку

Код: Выделить всё

Type of object must be Array

Alexp
Devart Team
Сообщения: 349
Зарегистрирован: Пн 27 дек 2010 10:34

Сообщение Alexp » Чт 24 ноя 2011 09:25

Добрый день,

Пожалуйста приведите полный текст скрипта для создания всех используемых Вами объектов.

indapublic
Сообщения: 21
Зарегистрирован: Чт 17 ноя 2011 23:40

Сообщение indapublic » Чт 24 ноя 2011 10:18

Код: Выделить всё

CREATE OR REPLACE 
TYPE address as OBJECT(
  ID_PLACE INTEGER,
  A_TYPE VARCHAR2(12),
  PINDEX VARCHAR2(12),
  MEMO VARCHAR2(200),
  H_STR VARCHAR2(6),
  C_STR VARCHAR2(6),
  R_STR VARCHAR2(6),
  constructor function ADDRESS (ID_PLACE INTEGER default NULL, A_TYPE VARCHAR2 default NULL, PINDEX VARCHAR2 default NULL,
    MEMO VARCHAR2 default NULL, H_STR VARCHAR2 default NULL, C_STR VARCHAR2 default NULL, R_STR VARCHAR2 default NULL)
  return SELF as result,
  member function FULL_ADDRESS(INCLUDE_PINDEX VARCHAR2 default 'N') return VARCHAR2,
  member function CITY_NAME return VARCHAR2
)
/

CREATE OR REPLACE 
TYPE k_obj as OBJECT (
  ID VARCHAR2(12),
  NAME VARCHAR2(240),
  SHORTNAME VARCHAR2(64),
  ACTIVE_SWITCH CHAR(1),
  ADDRESSES ADDRESS_TAB,
  member function GET_NEW_ID (
    p_PREFIX VARCHAR2)
    return VARCHAR2
) NOT INSTANTIABLE NOT FINAL;
/

CREATE OR REPLACE 
TYPE k_org UNDER K_OBJ (
  INN VARCHAR2(9),
  KPP VARCHAR2(9),
  constructor function K_ORG (
    ID IN VARCHAR2 default NULL,
    NAME IN VARCHAR2 default NULL,
    SHORTNAME IN VARCHAR2 default NULL,
    ACTIVE_SWITCH IN VARCHAR2 default 'Y',
    INN IN VARCHAR2 default NULL,
    KPP IN VARCHAR2 default NULL)
    return SELF as result
);
/

CREATE OR REPLACE 
TYPE k_pers UNDER K_OBJ (
  INN VARCHAR2(12),
  BIRTH_DATE DATE,
  constructor function K_PERS (
    ID IN VARCHAR2 default NULL,
    NAME IN VARCHAR2 default NULL,
    SHORTNAME IN VARCHAR2 default NULL,
    ACTIVE_SWITCH IN VARCHAR2 default 'Y',
    INN IN VARCHAR2 default NULL,
    BIRTH_DATE IN DATE default NULL)
    return SELF as result
);

CREATE TABLE k_obj_tab
OF K_OBJ
    (id                              
  ,
  CONSTRAINT K_OBJ_TAB_PK
  PRIMARY KEY (id))
/

indapublic
Сообщения: 21
Зарегистрирован: Чт 17 ноя 2011 23:40

Сообщение indapublic » Сб 26 ноя 2011 03:02

up

indapublic
Сообщения: 21
Зарегистрирован: Чт 17 ноя 2011 23:40

Сообщение indapublic » Пн 28 ноя 2011 09:47

up

Alexp
Devart Team
Сообщения: 349
Зарегистрирован: Пн 27 дек 2010 10:34

Сообщение Alexp » Вт 29 ноя 2011 08:34

Добрый день,

с приведенным вами объектом address на строке

Код: Выделить всё

OraNestedTable.Table := TOraNestTable.Create(TOraType.Create(dmDB.orsn.OCISvcCtx, 'KDB.ADDRESS'));
ошибок не возникает,
пожалуйста укажите Вашу точную версию ODAC

indapublic
Сообщения: 21
Зарегистрирован: Чт 17 ноя 2011 23:40

Сообщение indapublic » Вт 06 дек 2011 03:33

Прошу прощения, ошибка происходит при

Код: Выделить всё

OraNestedTable.Open;

Alexp
Devart Team
Сообщения: 349
Зарегистрирован: Пн 27 дек 2010 10:34

Сообщение Alexp » Пн 12 дек 2011 11:00

Добрый день,

Судя по вашему скрипту Вы не используете NestedTable, а используете обычные объекты (пример создания NestedTable Я приводил выше). Для работы с вашими объектами Вы можете использовать следующий код :

Код: Выделить всё

  OraQuery1.SQL.Text := 'select * from K_ORG_TAB';
  OraQuery1.SQLInsert.Text := 'INSERT INTO K_ORG_TAB VALUES(K_ORG(:ID, :NAME, :SHORTNAME, :ACTIVE_SWITCH, :ADDRESSES, :INN, :KPP))';
  OraQuery1.Insert;
  OraQuery1.FieldByName('ID').AsString := '1';
  OraQuery1.FieldByName('NAME').AsString := '2';
  OraQuery1.FieldByName('SHORTNAME').AsString := '3';
  OraQuery1.FieldByName('ACTIVE_SWITCH').AsString := '4';
  OraQuery1.FieldByName('INN').AsString := '5';
  OraQuery1.FieldByName('KPP').AsDateTime := StrToDate('01.01.2010');
  TADTField(OraQuery1.FieldByName('ADDRESSES')).Fields.FieldByName('ID_PLACE').AsInteger := 1;
  TADTField(OraQuery1.FieldByName('ADDRESSES')).Fields.FieldByName('A_TYPE').AsString := '1';
  TADTField(OraQuery1.FieldByName('ADDRESSES')).Fields.FieldByName('MEMO').AsString := '2';
  TADTField(OraQuery1.FieldByName('ADDRESSES')).Fields.FieldByName('H_STR').AsString := '3';
  TADTField(OraQuery1.FieldByName('ADDRESSES')).Fields.FieldByName('C_STR').AsString := '4';
  TADTField(OraQuery1.FieldByName('ADDRESSES')).Fields.FieldByName('R_STR').AsString := '5';
  OraQuery1.Post;

Закрыто