Пишу конвертер из экселя в ФБ, вопрос по вставке в справочник

Trabuditor
Дата: 01.07.2009 04:14:25
В общем ситуевина такая. Есть скажем файл Эксель со структурой:

Имя - - Сертификат
Вася - - Серт1
Вася - - Серт2
Вася - - Серт3
Петя - - Серт1
Петя - - Серт3
Вова - - Серт4

Соединяюсь с экселем через АДО, пишу процедуру вставки значений поля Сертификат экселя в поле C_TITLE firebird БД:
procedure TForm1.Button1Click(Sender: TObject);
var
i, z, x: integer;
begin
with Table1 do begin
First;
  while not Table1.Eof do
     begin
      if Table1.Fields.Fields[1].AsString<>Dscert.Fields.Fields[1].AsString then
      begin
          DScert.Insert;
          Dscert.FieldByName('C_TITLE').AsString:= Table1.Fields.Fields[1].AsString;
          z:=Dscert.FieldbyName('C_ID').AsInteger;

          Dscert.Post;
          Dscert.Refresh;
          end;
     Next;


 end;
end;
end;
где Table1 - это файл эксель, а DSCert - FIBDataset связанный с БД.

Посредством данной процедуры ВСЕ записи из поля сертификат тупо копируются в C_TITLE. При этом, строка
if Table1.Fields.Fields.AsString<>Dscert.Fields.Fields[1].AsString then 
должна проверять, чтобы значение, вставляемое из Table1 (это эксель), уже не было в DSCert (это БД), то есть чтобы в справочнике значения не повторялись ( мне ведь не нужны дубликаты в справочнике). Однако она этого не делает, поскольку находится внутри цикла и видит только одну строку в DSCert - текущую. Как мне сделать так, чтобы перед вставкой значения в DSCert оно проверялось на наличие во всех строках поля C-TITLE БД и вставлялось только после того, как известно, что такого значения там еще нет? Надеюсь не очень сумбурно объяснил .
Gluck99
Дата: 01.07.2009 04:37:34
Дуремарство какое-то. Фильтровать надо до того как начинается обход в цикле. Делать SQL запрос без дублей, например. И отказаться от Table (FB). За это сразу расстреливают без права переписки.
Trabuditor
Дата: 01.07.2009 04:53:31
Упс, тут стоит пояснить. В БД есть три таблицы - таблица сертификатов (айди, название), таблица работников (айди, имя), таблица отношения работник/сертификат (айди, айди работника, айди сертификата). Смысл работы конвертера в том, что он:

"1 Читает очередную запись из исходной таблицы
2 Берет поле "Работник" и проверяет нет ли записи с таким наименованием в справочнике работников. .
3 запоминает Id только что обработанного работника
4 все тоже самое для поля сертификат
5 вставляет Id работника и Id сертификата в таблицу отношения
6 если еще не достигнут EOF переходит к пункту 1"

Так что тут фильтровать сразу нельзя, ведь тогда я не смогу заполнить таблицу отношений, разве нет? А насчет Table - согласен, уже поставил Query.
Граур Станислав
Дата: 01.07.2009 05:44:32
Trabuditor
В общем ситуевина такая. Есть скажем файл Эксель со структурой:

Имя - - Сертификат
Вася - - Серт1
Вася - - Серт2
Вася - - Серт3
Петя - - Серт1
Петя - - Серт3
Вова - - Серт4

Однако она этого не делает, поскольку находится внутри цикла и видит только одну строку в DSCert - текущую. Как мне сделать так, чтобы перед вставкой значения в DSCert оно проверялось на наличие во всех строках поля C-TITLE БД и вставлялось только после того, как известно, что такого значения там еще нет? Надеюсь не очень сумбурно объяснил .


Я бы просто один раз написал сохраненную процедуру, которая бы принимала на вход параметры
USER_NAME, CERT_NAME
а внутри делала бы следующее
- Проверяла есть ли USER_NAME в USER_SPR
id_user = null;
select id  from user_spr t1 where  t1.user_name = :user_name into :id;

Дальше если ид есть -- хорошо, если нет генерим ид и вставляем в user_spr нашего юзера.
Аналогично для сертификата.

Далее имея id_user и id_sert делаем инсерт в таблицу связей.
Все.

Эту сохраненку вызываем в твоем цикле.Все.
Senya_L
Дата: 01.07.2009 09:08:08
Trabuditor
Смысл работы конвертера в том, что он:

"1 Читает очередную запись из исходной таблицы
2 Берет поле "Работник" и проверяет нет ли записи с таким наименованием в справочнике работников. .
3 запоминает Id только что обработанного работника
4 все тоже самое для поля сертификат
5 вставляет Id работника и Id сертификата в таблицу отношения
6 если еще не достигнут EOF переходит к пункту 1"

Так что тут фильтровать сразу нельзя, ведь тогда я не смогу заполнить таблицу отношений, разве нет? А насчет Table - согласен, уже поставил Query.
Я тебе один умный имхо скажу, а ты сам думай использовать или нет. Я бы сделал так. По твоим постам в ветке FB я понимаю, что используется FB2.1. Так вот, нечего "конвертеру" ничего конвертировать. Заливаешь данные во временные таблицы (RTFM - GTT), а затем с помощью SQL или PSQL раскидываешь по таблицам. Все. Проверки на дубликаты проще сделать на SQL, чем свой велосипед изобретать
Trabuditor
Дата: 01.07.2009 13:43:06
Senya_L, спасибо за совет. А где можно почитать про ГТТ? Релиз нотесы о нем упоминают очень вскользь, а с другими СУБД опыта у меня нет.

Граур Станислав,
Написал вот такую процедурку:
 create or alter procedure NEW_PROCEDURE (
    EMP_NAME varchar(30),
    CERT_NAME varchar(30))
as
declare variable EMPID integer;
declare variable CID integer;
begin

select distinct cer.c_id from certificate cer
where cer.c_title=:cert_name into :cid;

select distinct emp.e_id from employee emp
where emp.name=:emp_name into :empid;

if (:cid is null and :empid is null) then
    begin
        insert into certificate(c_title) values (:cert_name);
        insert into employee(name) values (:emp_name);
        insert into empcert(certificate_c_id, employee_e_id)
            values (gen_id(gen_certificate_c_id, 0), gen_id(gen_employee_e_id, 0));
    end
else
if (:cid is null and :empid is not null) then
    begin
        insert into certificate(c_title) values (:cert_name);
        insert into empcert(certificate_c_id, employee_e_id)
            values (gen_id(gen_certificate_c_id, 0),:empid);
    end
else
if (:cid is not null and :empid is null) then
    begin
        insert into employee(name) values (:emp_name);
        insert into empcert(certificate_c_id, employee_e_id)
            values (:cid,gen_id(gen_employee_e_id, 0));
    end
else
if (:cid is not null and :empid is not null) then
insert into empcert(certificate_c_id, employee_e_id)
            values (:cid, :empid);
end^

При ручной вставке значений имен людей и сертификатов в Эксперте - работает замечательно... Но при вызове процедуры из приложения получаю кучу пустых строк между строками, сгенерированными правильно как в справочниках, так и в табле связей. Код процедуры в Делфи:
begin
with ADOQuery1 do begin
 First;
  while not ADOQuery1.Eof do
    begin
    DSCert.Insert;
    DSBoth.Insert;
    DSEmpl.Insert;
    pFIBStoredProc1.ParamByName('CERT_NAME').AsString:=ADOQuery1.Fields.Fields[1].AsString;
    pFIBStoredProc1.ParamByName('EMP_NAME').Asstring:=ADOQuery1.Fields.Fields[0].AsString;
    pfibstoredproc1.ExecProc;
    DSCert.Post;
    DSBoth.Post;
    DSEmpl.Post;
    DSCert.FullRefresh;
    DSBoth.FullRefresh;
    DSEmpl.FullRefresh;
    Next;
    end;

 end;
end;

В чем может быть проблема?
Граур Станислав
Дата: 01.07.2009 14:12:34
Trabuditor


Написал вот такую процедурку:
 create or alter procedure NEW_PROCEDURE (
    EMP_NAME varchar(30),
    CERT_NAME varchar(30))
as
declare variable EMPID integer;
declare variable CID integer;
declare variable linkID integer;
begin

   cid = null;
   select cer.c_id from certificate cer
   where cer.c_title=:cert_name into :cid;

   empid = null;
   select  emp.e_id from employee emp
   where emp.name=:emp_name into :empid;

   if (:empid is null) then
   begin
        empid = gen_id(gen_employee_e_id, 1);
        insert into employee(id, name) values (:empid, :emp_name);
   end;

   if (:cid is null ) then
   begin
        cid = gen_id(gen_certificate_c_id, 1);
        insert into certificate(id, c_title) values (:cid, :cert_name);
   end;

   linkid = null;
   select linkid from empcert t1 
   where t1.certificate_c_id = :cid and  t1.employee_e_id = :empid into :linkid;
 

   if (:linkid is null) then
   begin
        insert into empcert(certificate_c_id, employee_e_id)
        values (:cid, :empid);
    end
end


При ручной вставке значений имен людей и сертификатов в Эксперте - работает замечательно... Но при вызове процедуры из приложения получаю кучу пустых строк между строками, сгенерированными правильно как в справочниках, так и в табле связей. Код процедуры в Делфи:
begin
with ADOQuery1 do begin
 First;
  while not ADOQuery1.Eof do
    begin
    pFIBStoredProc1.ParamByName('CERT_NAME').AsString:=ADOQuery1.Fields.Fields[1].AsString;
    pFIBStoredProc1.ParamByName('EMP_NAME').Asstring:=ADOQuery1.Fields.Fields[0].AsString;
    pfibstoredproc1.ExecProc;
    Next;
    end;
 end;
end;

В чем может быть проблема?


Может где ; пропустил или очепятался.
После цикла можешь открыть датасеты и показать пользователю чего получилось.
Senya_L
Дата: 01.07.2009 14:16:24
Trabuditor
Senya_L, спасибо за совет. А где можно почитать про ГТТ? Релиз нотесы о нем упоминают очень вскользь, а с другими СУБД опыта у меня нет.
К тому, что написано здесь не знаю что и добавить. Разве что...
GTT - почти что обычные таблицы (существуют в базе постоянно) с некоторыми ограничениями. Есть два типа: ON COMMIT PRESERVE ROWS и ON COMMIT DELETE ROWS. В первом случае данные "живут", пока существует соединение, в котором они занесены, во втором случае они живут только внутри транзакции, в которой они занесены. Данные изолированы в рамках коннекта/транзакции, т.е. другие коннекты/транзакции не видят "чужих" строк. Вроде бы все, что надо для эксплуатации сабжа.

ЗЫ. Если что непонятно - можешь сюда обратиться. ;)
Trabuditor
Дата: 01.07.2009 14:18:59
Граур Станислав,
Граур Станислав,
После цикла можешь открыть датасеты и показать пользователю чего получилось.


Дык у меня на форме четыре грида и датасета - один на АДОКвери и три на каждую таблицу в БД. Так что у меня все показывается сразу. В таблице связей например нормальная строка, нулевая строка, опять нормальная, опять нулевая... В справочнике сертификатов сертификат, пять строк нулей, опять сертификат... В Работниках вообще только одно имя... Видать что-то где-то в цикле, а вот что именно - никак не пойму...
Граур Станислав
Дата: 01.07.2009 14:23:30
я же тебя привел исправленный вариант )

После экспорта просто сделай closeOpen датасетам, чтобы пользователь все увидел.