Правильная ссылка на эту страницу
http://az-design.ru/Support/DataBase/002b3210.shtml

Первый вариант заполнения таблицы Goods(Товары)

Архангельский Андрей

English

       Проблемы с заполнением таблицы Goods возникают именно из-за того, что в ней построено 5 деревьев и необходимо согласовать заполнение соответствующих родителей.
       Если бы не было нужды отображать эту таблицу в виде дерева, то этой проблемы не возникло бы. Но таблица Goods именно и создается для того, чтобы искать товар по разным деревьям, которые соответствуют разным классификациям. В чем суть проблемы?
       Допустим, что по одному из деревьев построена структура. В качестве корня дерева принимаем записи, у которых значение родительского поля равно 0. В результате изображается требуемое дерево и записи других деревьев, у которых родительские коды еще не определены. Мало того что это некрасиво, количество неопределенных записей может быть так велико, что основного дерева будет просто не видно.
       Если в качестве корня принять первую запись требуемого дерева, то записи с неопределенными кодами видны не будут, даже если они принадлежат требуемому дереву. Но в таком случае невозможно редактировать дерево с помощью перетаскивания узла мышью. И, несмотря на то, что можно (и нужно) ввести специальные гнезда <код не определен>, но так как пользователь может редактировать эти записи, то опираться на них нельзя.
       Как один из вариантов решения этой проблемы — разделить записи по типам: одни формируют структуру, другие описывают товар.
       Записи, формирующие структуру, имеет одинаковые значения во всех родительских полях и имеют тип "группа".
       Записи, описывающие товар, в родительских полях имеют значения указывающие на структуру в соответствующем дереве.
       Как следствие, необходимо "групповые" записи делать для каждого дерева в отдельности. Для того, чтобы знать к какому дереву они относятся введено поле GdTreeNo, в котором записан номер дерева. Запись, описывающая товар, в этом поле имеет значение 0.
       Пример такого заполнения приведен в примере {Example05}.
       Для начала заполним корни деревьев (скрипт FB_SQL\dat\Goods0.txt):

/* Таблица Goods */
Insert into Goods(GdParent,GdPrnTNK,GdPrnBAR,GdPrnFRM,GdTreeNo,
                  GdOrder,GdOrdTNK,GdOrdBAR,GdOrdFRM,GoodNamR,GdNames,
                  GdTNVED,GdEANCode,GdUPCCode,GdFirmCod,GdIntCode)
 values(0,0,0,0,1,0,0,0,0,
'Внутрифирменные коды продавца',
'Внутрифирменные коды продавца',
'Коды внутренние','Коды внутренние','Коды внутренние',
'Коды внутренние','Коды внутренние');
Insert into Goods(GdParent,GdPrnTNK,GdPrnBAR,GdPrnFRM,GdTreeNo,
                  GdOrder,GdOrdTNK,GdOrdBAR,GdOrdFRM,GoodNamR,GdNames,
                  GdTNVED,GdEANCode,GdUPCCode,GdFirmCod,GdIntCode)
 values(0,0,0,0,2,0,0,0,0,
'Товарная Номенклатура ВЭД СНГ',
'Товарная Номенклатура ВЭД СНГ',
'Коды ТН ВЭД СНГ','Коды ТН ВЭД СНГ','Коды ТН ВЭД СНГ',
'Коды ТН ВЭД СНГ','Коды ТН ВЭД СНГ');
Insert into Goods(GdParent,GdPrnTNK,GdPrnBAR,GdPrnFRM,GdTreeNo,
                  GdOrder,GdOrdTNK,GdOrdBAR,GdOrdFRM,GoodNamR,GdNames,
                  GdTNVED,GdEANCode,GdUPCCode,GdFirmCod,GdIntCode)
 values(0,0,0,0,3,0,0,0,0,
'Штрих-код EAN-13 (European Article Numbering) или UPC',
'Штрих-код EAN-13 (European Article Numbering)',
'Коды EAN-13','Коды EAN-13','Коды EAN-13','Коды EAN-13','Коды EAN-13');
Insert into Goods(GdParent,GdPrnTNK,GdPrnBAR,GdPrnFRM,GdTreeNo,
                  GdOrder,GdOrdTNK,GdOrdBAR,GdOrdFRM,GoodNamR,GdNames,
                  GdTNVED,GdEANCode,GdUPCCode,GdFirmCod,GdIntCode)
 values(0,0,0,0,4,0,0,0,0,
'Код по каталогу фирмы производителя',
'Код по каталогу фирмы производителя',
'Коды по каталогу','Коды по каталогу','Коды по каталогу',
'Коды по каталогу','Коды по каталогу');
commit;

       После чего можно заполнять сами деревья
       — Внутрифирменные коды продавца (скрипт FB_SQL\dat GoodsA0.txt):

/* Таблица GoodsA0 - Коды внутренние */
Insert into Goods(GdParent,GdPrnTNK,GdPrnBAR,GdPrnFRM,GdTreeNo,
                  GdOrder,GdOrdTNK,GdOrdBAR,GdOrdFRM,GoodNamR,GdNames,
                  GdTNVED,GdEANCode,GdUPCCode,GdFirmCod,GdIntCode)
  Select Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,1,0,0,0,0,
  '<INTCD> Код временно не определен',
  '<INTCD> Код временно не определен',
  '<INTCD>','<INTCD>','<INTCD>','<INTCD>','<INTCD>'
    from Goods Gd where GdNames='Внутрифирменные коды продавца'
                    and GdIntCode='Коды внутренние';
Insert into Goods(GdParent,GdPrnTNK,GdPrnBAR,GdPrnFRM,GdTreeNo,
                  GdOrder,GdOrdTNK,GdOrdBAR,GdOrdFRM,GoodNamR,GdNames,
                  GdTNVED,GdEANCode,GdUPCCode,GdFirmCod,GdIntCode)
  Select Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,1,0,0,0,0,
  'Компьютерная техника (Компьютеры, периферия и комплектующие)',
  'Компьютерная техника (Компьютеры, периферия и комплектующие)',
  'Комп.Техника','Комп.Техника','Комп.Техника','Комп.Техника','Комп.Техника'
    from Goods Gd where GdNames='Внутрифирменные коды продавца'
                    and GdIntCode='Коды внутренние';
Insert into Goods(GdParent,GdPrnTNK,GdPrnBAR,GdPrnFRM,GdTreeNo,
                  GdOrder,GdOrdTNK,GdOrdBAR,GdOrdFRM,GoodNamR,GdNames,
                  GdTNVED,GdEANCode,GdUPCCode,GdFirmCod,GdIntCode,GdVINCode)
  Select Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,1,0,0,0,0,
    'Компьютеры - Серверы, Настольные, Переносные (Notebooks), Наладонные',
    'Компьютеры - Серверы, Настольные, Переносные, Наладонные',
    'Computers','Computers','Computers','Computers','Computers','<нет данных>'
  from Goods Gd 
  where GdNames='Компьютерная техника (Компьютеры, периферия и комплектующие)'
    and GdIntCode='Комп.Техника';

       — Товарная Номенклатура ВЭД СНГ (скрипт FB_SQL\dat\GoodsB0.txt):

/* Таблица GoodsB0 - Товарная Номенклатура ВЭД СНГ */
Insert into Goods(GdParent,GdPrnTNK,GdPrnBAR,GdPrnFRM,GdTreeNo,
                  GdOrder,GdOrdTNK,GdOrdBAR,GdOrdFRM,GoodNamR,GdNames,
                  GdTNVED,GdEANCode,GdUPCCode,GdFirmCod,GdIntCode)
  Select Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,2,1,1,1,1,
'ТН ВЭД Раздел I - Живые животные; продукты животного происхождения',
'ТН ВЭД Раздел I - Живые животные; продукты животного происхождения',
'ТН ВЭД Разд. I','ТН ВЭД Разд. I','ТН ВЭД Разд. I',
'ТН ВЭД Разд. I','ТН ВЭД Разд. I'
    from Goods Gd where GdNames='Товарная Номенклатура ВЭД СНГ'
                    and GdTNVED='Коды ТН ВЭД СНГ';
/* Раздел I*/
Insert into Goods(GdParent,GdPrnTNK,GdPrnBAR,GdPrnFRM,GdTreeNo,
                  GdOrder,GdOrdTNK,GdOrdBAR,GdOrdFRM,GoodNamR,GdNames,
                  GdTNVED,GdEANCode,GdUPCCode,GdFirmCod,GdIntCode)
  Select Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,2,1,1,1,1,
'ТН ВЭД Группа 01 - Живые животные',
'ТН ВЭД Группа 01 - Живые животные',
'01.. .. ... .','01.. .. ...','01.. .. ...','01.. .. ...','01.. .. ...'
  from Goods Gd where GdNames='ТН ВЭД Раздел I - Живые животные; продукты животного происхождения'
                  and GdTNVED='ТН ВЭД Разд. I';
Insert into Goods(GdParent,GdPrnTNK,GdPrnBAR,GdPrnFRM,GdTreeNo,
                  GdOrder,GdOrdTNK,GdOrdBAR,GdOrdFRM,GoodNamR,GdNames,
                  GdTNVED,GdEANCode,GdUPCCode,GdFirmCod,GdIntCode)
  Select Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,2,2,2,2,2,
'ТН ВЭД Группа 02 - Мясо и пищевые мясные субпродукты',
'ТН ВЭД Группа 02 - Мясо и пищевые мясные субпродукты',
'02.. .. ... .','02.. .. ...','02.. .. ...','02.. .. ...','02.. .. ...'
    from Goods Gd where GdNames='ТН ВЭД Раздел I - Живые животные; продукты животного происхождения'
                    and GdTNVED='ТН ВЭД Разд. I';

       — Штрих-код EAN-13 (European Article Numbering) (скрипт FB_SQL\dat\GoodsC0.txt):

/* Таблица GoodsC0 - Штрих-код EAN-13 (European Article Numbering) */
Insert into Goods(GdParent,GdPrnTNK,GdPrnBAR,GdPrnFRM,GdTreeNo,
                  GdOrder,GdOrdTNK,GdOrdBAR,GdOrdFRM,GoodNamR,GdNames,
                  GdTNVED,GdEANCode,GdUPCCode,GdFirmCod,GdIntCode)
  Select Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,3,
   0,0,0,0,'Россия, СНГ','Россия, СНГ',
   '460-469','460-469','460-469','460-469','460-469'
  from Goods Gd where GdNames='Штрих-код EAN-13 (European Article Numbering)'
                and GdEANCode='Коды EAN-13';
Insert into Goods(GdParent,GdPrnTNK,GdPrnBAR,GdPrnFRM,GdTreeNo,
                  GdOrder,GdOrdTNK,GdOrdBAR,GdOrdFRM,GoodNamR,GdNames,
                  GdTNVED,GdEANCode,GdUPCCode,GdFirmCod,GdIntCode)
  Select Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,3,
    0,0,0,0,'<EAN13> Код временно не определен',
    '<EAN13> Код временно не определен',
    '<EAN13>','<EAN13>','<EAN13>','<EAN13>','<EAN13>'
  from Goods Gd where GdNames='Штрих-код EAN-13 (European Article Numbering)'
                and GdEANCode='Коды EAN-13';

       — Код по каталогу фирмы производителя (скрипт FB_SQL\dat\GoodsD0.txt):

/* Таблица GoodsD0 - Код по каталогу фирмы производителя */
Insert into Goods(GdParent,GdPrnTNK,GdPrnBAR,GdPrnFRM,GdTreeNo,
                  GdOrder,GdOrdTNK,GdOrdBAR,GdOrdFRM,GoodNamR,GdNames,
                  GdTNVED,GdEANCode,GdUPCCode,GdFirmCod,GdIntCode)
  Select Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,4,0,0,0,0,
'Автомобильная промышленность',
'Автомобильная промышленность',
'АвтоПром','АвтоПром','АвтоПром','АвтоПром','АвтоПром'
    from Goods Gd where GdNames='Код по каталогу фирмы производителя'
                  and GdFirmCod='Коды по каталогу';
Insert into Goods(GdParent,GdPrnTNK,GdPrnBAR,GdPrnFRM,GdTreeNo,
                  GdOrder,GdOrdTNK,GdOrdBAR,GdOrdFRM,GoodNamR,GdNames,
                  GdTNVED,GdEANCode,GdUPCCode,GdFirmCod,GdIntCode)
  Select Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,4,999,999,999,999,
'<FIRMS> Код временно не определен',
'<FIRMS> Код временно не определен',
'<FIRMS>','<FIRMS>','<FIRMS>','<FIRMS>','<FIRMS>'
    from Goods Gd where GdNames='Код по каталогу фирмы производителя'
                  and GdFirmCod='Коды по каталогу';

       Когда структура построена можно заполнять описания товаров. Но встает другая проблема — как получить 5 родительских полей в одном запросе? На помощь может придти вспомогательная таблица TempInt (она используется и в других запросах):

Create table TempInt
      (Int1    AZInt32 default 0,
       Int2    AZInt32 default 0,
       Int3    AZInt32 default 0,
       Int4    AZInt32 default 0,
       Int5    AZInt32 default 0,
       Int6    AZInt32 default 0,
       Int7    AZInt32 default 0,
       Int8    AZInt32 default 0);
Commit;

       Заполнение таблицы Goods происходит в несколько этапов — очистка таблицы TempInt, выборка родительских кодов в таблицу TempInt, построение целевого запроса на вставку записи:

Delete from TempInt;
Commit;

       таблица TempInt теперь пустая, и можно собирать родительские коды

Insert into TempInt(Int1)
 Select GoodsNo from Goods
 where GdNames='Автомобили ВАЗ-2109 "Хетчбек" с карбюраторным двигателем'
 and GdIntCode='ВАЗ-2109(ХтчКрб)';
Insert into TempInt(Int2)
 Select GoodsNo from Goods 
 where GdNames='Автомобили ВАЗ-2109 "Хетчбек" с карбюраторным двигателем'
   and GdTNVED='ВАЗ-2109[ХтчКрб]';
Insert into TempInt(Int3)
 Select GoodsNo from Goods 
 where GdNames='Автомобили ВАЗ-2109 "Хетчбек" с карбюраторным двигателем'
 and GdEANCODE='ВАЗ-2109<ХтчКрб>';
Insert into TempInt(Int4)
 Select GoodsNo from Goods 
 where GdNames='Автомобили ВАЗ-2109 "Хетчбек" с карбюраторным двигателем'
 and GdFirmCod='ВАЗ-2109{ХтчКрб}';

       Затем формируется запись, которая уже включает родительские коды, полученные из таблицы TempInt. Если несколько записей имеют одинаковые родительские коды, то их можно вводить последовательно, не изменяя данных во вспомогательной таблице TempInt:

Insert into Goods(GdParent,GdPrnTNK,GdPrnBAR,GdPrnFRM,GdTreeNo,
  GdTNVED,GDEANCode,GDFIRMCOD,GDINTCODE,GdNames,GoodNamR,GdVINCode)
  Select Sum(Int1),Sum(Int2),Sum(Int3),Sum(Int4),0,
  'ВАЗ-2109    /10','ВАЗ-2109    /10','ВАЗ-2109    /10','ВАЗ-2109    /10',
  'Автомобиль ВАЗ-2109 (Компл.10) выпуск с янв.2000г',
  'Автомобиль ВАЗ-2109 (Компл.10) выпуск с янв.2000г, Хетчбек, "норма" для внутреннего рынка, двигатель - 2108 - карбюраторный с бесконтактной системой зажигания, 1300, панель приборов - 2108 "низкая"','XTA21090' 
 from TempInt; 
Insert into Goods(GdParent,GdPrnTNK,GdPrnBAR,GdPrnFRM,GdTreeNo,
  GdTNVED,GDEANCode,GDFIRMCOD,GDINTCODE,GdNames,GoodNamR,GdVINCode)
  Select Sum(Int1),Sum(Int2),Sum(Int3),Sum(Int4),0,
  'ВАЗ-21091   /10','ВАЗ-21091   /10','ВАЗ-21091   /10','ВАЗ-21091   /10',
  'Автомобиль ВАЗ-21091 (Компл.10) выпуск с май.1997г',
  'Автомобиль ВАЗ-21091 (Компл.10) выпуск с май.1997г, Хетчбек, "стандарт" для внутреннего рынка, двигатель - 21081-10 - карбюраторный с бесконтактной системой зажигания, 1100, панель приборов - 2108 "низкая"','XTA21091' 
 from TempInt; 
Insert into Goods(GdParent,GdPrnTNK,GdPrnBAR,GdPrnFRM,GdTreeNo,
  GdTNVED,GDEANCode,GDFIRMCOD,GDINTCODE,GdNames,GoodNamR,GdVINCode)
  Select Sum(Int1),Sum(Int2),Sum(Int3),Sum(Int4),0,
  'ВАЗ-21093   /10','ВАЗ-21093   /10','ВАЗ-21093   /10','ВАЗ-21093   /10',
  'Автомобиль ВАЗ-21093 (Компл.10)',
  'Автомобиль ВАЗ-21093 (Компл.10), Хетчбек, "норма" для внутреннего рынка, двигатель - 21083-50 - карбюраторный с бесконтактной системой зажигания, 1500, панель приборов - 2108 "низкая"','XTA21093' 
 from TempInt; 
Commit;

       Таким образом получается полностью построенное дерево, которое нужно только отобразить.

Отображение таблицы Goods (первый вариант)


Рис.3-04 Отображение таблицы Goods(первый вариант)

Пример отображения таблицы Goods реализован в проекте {Example05}.

       Само отображение дерева принципиально не отличается от уже описанного. Основная процедура GoodsDatasetOpen:

procedure TForm1.GoodsDatasetOpen(); {Example05}
Var
   RowCount,RowChild : Integer;
   x,y,z : Integer;
   FldString, FildsParent,FildChild,sFld,sCod: String;
   PrntNode,ChildNode,NewNode : TTreeNode;
begin
  inherited;
  isNoClose := False;  iTblFocus := 1;
  If qrTVGoods.Database.Connected then begin
     tvGoods.Items.Clear;
     tvGoods.Items.BeginUpdate;
     trTVGoods.Active := True;
     qrTVGoods.Close;  qrTVGoods.SQL.Clear;
     qrTVGoods.SQL.Add(sqlSelect);
     qrTVGoods.SQL.Add(sqlWher0);
     qrTVGoods.SQL.Add(sqlOrder);
     qrTVGoods.Open;
     qrTVGoods.First;
     isNoClose := True;
     While not qrTVGoods.Eof do begin
        sCod := '';
        If not VarIsNull(qrTVGoods.FieldValues[sqlField]) then begin
           sCod := qrTVGoods.FieldValues[sqlField];
           sCod := sCod + ' - ';
           end;
        sFld := qrTVGoods.FieldValues['GdNames'];
        z := qrTVGoods.FieldValues['GoodsNo'];
        RowChild := qrTVGoods.FieldValues[sqlCount];
        NewNode := tvGoods.Items.Add(tvGoods.TopItem,sCod+sFld);
        NewNode.ImageIndex := z;
        If RowChild>0 then 
           ChildNode := tvGoods.Items.AddChild(NewNode,IntToStr(RowChild));
        qrTVGoods.Next;
     end; // While
     trTVGoods.Active := False;
     tvGoods.Items.EndUpdate;
     tvGoods.Update;
  end;
end;

       Но запрос для построения дерева состоит из нескольких заготовок, которые определены как константы:

Const
  sqlSelct1 = 'Select GoodsNo,GDParent,GDCount,GdOrder,GdTreeNo,GDIntCode,'
             +'GDNames from Goods';
  sqlSelct2 = 'Select GoodsNo,GDPrnTNK,GdCntTNK,GdOrdTNK,GdTreeNo,GDTNVED,'
             +'GDNames from Goods';
  sqlSelct3 = 'Select GoodsNo,GDPrnBAR,GDCntBAR,GDOrdBAR,GdTreeNo,GDEANCode,'
             +'GDNames from Goods';
  sqlSelct4 = 'Select GoodsNo,GDPrnFrm,GDCntFrm,GDOrdFrm,GdTreeNo,GDFirmCod,'
             +'GDNames from Goods';
  sqlSelct5 = 'Select GoodsNo,GDPrnBAR,GDCntBAR,GDOrdBAR,GdTreeNo,GDUPCCode,'
             +'GDNames from Goods';
  sqlSelct6 = 'Select GoodsNo,GDPrnAls,GDCntAls,GDOrdAls,GdTreeNo,GDFirmCod,'
             +'GDNames from Goods';
  sqlWhere1 = ' where GoodsNo<>0 and GDParent=0';
  sqlWhere2 = ' where GoodsNo<>0 and GDPrnTNK=0';
  sqlWhere3 = ' where GoodsNo<>0 and GDPrnBAR=0';
  sqlWhere4 = ' where GoodsNo<>0 and GDPrnFrm=0';
  sqlWhere5 = ' where GoodsNo<>0 and GDPrnBAR=0';
  sqlWhere6 = ' where GoodsNo<>0 and GDPrnAls=0';
  sqlWher1a = ' where GoodsNo<>0 and GDParent=';
  sqlWher2a = ' where GoodsNo<>0 and GDPrnTNK=';
  sqlWher3a = ' where GoodsNo<>0 and GDPrnBAR=';
  sqlWher4a = ' where GoodsNo<>0 and GDPrnFrm=';
  sqlWher5a = ' where GoodsNo<>0 and GDPrnBAR=';
  sqlWher6a = ' where GoodsNo<>0 and GDPrnAls=';
  sqlOrder1 = ' order by GdTreeNo desc,GDOrder asc';
  sqlOrder2 = ' order by GdTreeNo desc,GDOrdTNK asc';
  sqlOrder3 = ' order by GdTreeNo desc,GDOrdBAR asc';
  sqlOrder4 = ' order by GdTreeNo desc,GDOrdFrm asc';
  sqlOrder5 = ' order by GdTreeNo desc,GDOrdBAR asc';
  sqlOrder6 = ' order by GdTreeNo desc,GDOrdAls asc';
  sqlInsVal1 = ',GDIntCode';
  sqlInsVal2 = ',GDTNVED';
  sqlInsVal3 = ',GDEANCode';
  sqlInsVal4 = ',GDFirmCod';
  sqlInsVal5 = ',GDUPCCode';
  sqlInsVal6 = ',GDFirmCod';
  sqlCount1 = 'GDCount';
  sqlCount2 = 'GDCntTNK';
  sqlCount3 = 'GDCntBAR';
  sqlCount4 = 'GDCntFrm';
  sqlCount5 = 'GDCntBAR';
  sqlCount6 = 'GDCntAls';

       Для выбора соответствующих констант используется ComboBox cbxTreeSel и связанная с ним процедура:

procedure TForm1.cbxTreeSelSelect(Sender: TObject); {Example05}
begin
   Case cbxTreeSel.ItemIndex  of
     1 : Begin iCurrentTree := 1;
           sqlSelect := sqlSelct1;     sqlWher0 := sqlWhere1;
           sqlWhere  := sqlWher1a;     sqlCount := sqlCount1;
           sqlOrder  := sqlOrder1;     sqlField := 'GDIntCode';
           sqlInsVal := sqlInsVal1;
         end;
     2 : Begin iCurrentTree := 2;
           sqlSelect := sqlSelct2;     sqlWher0 := sqlWhere2;
           sqlWhere  := sqlWher2a;     sqlCount := sqlCount2;
           sqlOrder  := sqlOrder2;     sqlField := 'GDTNVED';
           sqlInsVal := sqlInsVal2;
         end;
     3 : Begin iCurrentTree := 3;
           sqlSelect := sqlSelct3;     sqlWher0 := sqlWhere3;
           sqlWhere  := sqlWher3a;     sqlCount := sqlCount3;
           sqlOrder  := sqlOrder3;     sqlField := 'GDEANCode';
           sqlInsVal := sqlInsVal3;
         end;
     4 : Begin iCurrentTree := 4;
           sqlSelect := sqlSelct4;     sqlWher0 := sqlWhere4;
           sqlWhere  := sqlWher4a;     sqlCount := sqlCount4;
           sqlOrder  := sqlOrder4;     sqlField := 'GDFirmCod';
           sqlInsVal := sqlInsVal4;
         end;
     5 : Begin iCurrentTree := 5;
           sqlSelect := sqlSelct6;     sqlWher0 := sqlWhere6;
           sqlWhere  := sqlWher6a;     sqlCount := sqlCount6;
           sqlOrder  := sqlOrder6;     sqlField := 'GDFirmCod';
           sqlInsVal := sqlInsVal6;
         end;
   end;
   self.Caption := cbxTreeSel.SelText;
   GoodsDatasetOpen();
   tvGoods.SetFocus;
end;

       И для раскрытия дерева используется процедура:

procedure TForm1.tvGoodsExpanding(Sender: TObject; Node: TTreeNode;
                                     var AllowExpansion: Boolean); {Example05}
Var
   RowChild : Integer;
   z : Integer;
   sFld,sCod : String;
   ChildNode,NewNode : TTreeNode;
begin
  inherited;
  If Node.HasChildren then begin
     Node.DeleteChildren; z := Node.ImageIndex;
     qrTVGoods.Close;  qrTVGoods.SQL.Clear;
     qrTVGoods.SQL.Add(sqlSelect);
     qrTVGoods.SQL.Add(sqlWhere+IntToStr(z));
     qrTVGoods.SQL.Add(sqlOrder);
     qrTVGoods.Open;   qrTVGoods.First;
     tvGoods.Items.BeginUpdate;
     While not qrTVGoods.Eof do begin
        sCod := '';
        If not VarIsNull(qrTVGoods.FieldValues[sqlField]) then begin
           sCod := qrTVGoods.FieldValues[sqlField];   sCod := sCod + ' - ';
           end;
        sFld := qrTVGoods.FieldValues['GdNames'];
        z := qrTVGoods.FieldValues['GoodsNo'];
        newNode := tvGoods.Items.AddChild(Node,sCod+sFld);
        newNode.ImageIndex := z;
        RowChild := qrTVGoods.FieldValues[sqlCount];
        If RowChild>0 then 
           ChildNode := tvGoods.Items.AddChild(NewNode,IntToStr(RowChild));
        qrTVGoods.Next;
     end; // While not qrTVGoods.Eof
  end;// If Node.HasChildren then
  tvGoods.Items.EndUpdate;
  tvGoods.Update;
end;

       Эта процедура принципиально ничем не отличается от такой же процедуры для одиночного дерева.

 

Сохранение таблицы Goods в SQL-скрипте

       Наиболее актуальная задача в этом случае — это сохранение не всей таблицы, а отдельной ветки для переноса в другую БД. Такой подход позволит собирать целевую БД из тематических разделов по заказу. Так как записи в таблице разделены на "структурные" и "детальные", то и задача разделяется на две:
       — сохранение структуры — заведено на соответствующую кнопку. Процедура сохраняет структуру таблицы выбранного дерева с выбранного узла.

procedure TForm1.btnSaveChildToSQLClick(Sender: TObject);
Var
  rpPathName : String;
  flReprt : TextFile;
  Str1,Str2,Str3,Str4,sFld,sTree,GdCode : String;
  SvCursor : TCursor;
begin
   rpPathName := ExtractFilePath(Application.ExeName);
   SvCursor := Screen.Cursor;
   Screen.Cursor := crHourGlass;
// Создание файла и каталога, если необходимо
   If not DirectoryExists(rpPathName+'\DtSave') then
      If not CreateDir(rpPathName+'\DtSave') then
         raise Exception.Create('Cannot create '+rpPathName+'\DtSave');
   AssignFile(flReprt,rpPathName+'\DtSave\GoodsGroup.txt');
   ReWrite(flReprt);
   WriteLn(flReprt,'/* Заполнение таблицы Goods - структура */');

       Файл скрипта создается в каталоге DtSave, который при необходимости создается там же где находится приложение. В скрипт сразу записывается информационный заголовок.

// Очистка вспомогательной таблицы GoodsLev
   qrExeProc.Close;      qrExeProc.SQL.Clear;
   qrExeProc.SQL.Add('Delete from GoodsLev');
   qrExeProc.ExecSQL;    qrExeProc.Close;

       Очищается вспомогательная таблица GoodsLev, в которой будет собираться цепочка потомков от текущего узла.

// Выборка ID всех потомков от указанного узла
   sTree := IntToStr(cbxTreeSel.ItemIndex);
   Str1 := IntToStr(tvGoods.Selected.ImageIndex);
   Str2 := IntToStr(tvGoods.Selected.Parent.ImageIndex);

       Получаем исходные данные — номер дерева, ID узла и его родителя

  qrExeProc.Close;      qrExeProc.SQL.Clear;
  qrExeProc.SQL.Add('Insert into GoodsLev(GoodsNo,GdParent,GdsTree,GdsLevel)');
  qrExeProc.SQL.Add(' values('+Str1+','+Str2+','+sTree+',0)');
  qrExeProc.ExecSQL;
  qrExeProc.Close;      qrExeProc.SQL.Clear;
  qrExeProc.SQL.Add('Execute procedure GoodsChildLev('+Str1+','+sTree+',0)');
  qrExeProc.ExecSQL;

       Текущий узел записывается в таблицу GoodsLev, затем выполняется хранимая процедура GoodsChildLev, которая получает всех потомков текущего узла и записывает их во вспомогательную таблицу GoodsLev.

//  Получение всех полей для формирования скрипта
   qrExeProc.Close;  qrExeProc.SQL.Clear;
   If cbxTreeSel.ItemIndex=0 then Begin
      qrExeProc.SQL.Add('Select p.GdIntCode as PCode, p.GDNames as PNames,');
      GdCode := 'GdIntCode=''';
   end;
   If cbxTreeSel.ItemIndex=1 then Begin
      qrExeProc.SQL.Add('Select p.GdIntCode as PCode, p.GDNames as PNames,');
      GdCode := 'GdIntCode=''';
   end;
   If cbxTreeSel.ItemIndex=2 then Begin
      qrExeProc.SQL.Add('Select p.GdTNVED as PCode, p.GDNames as PNames,');
      GdCode:='GdTNVED=''';
   end;
   If cbxTreeSel.ItemIndex=3 then Begin
      qrExeProc.SQL.Add('Select p.GdEANCode as PCode, p.GDNames as PNames,');
      GdCode := 'GdEANCode=''';
   end;
   If cbxTreeSel.ItemIndex=4 then Begin
      qrExeProc.SQL.Add('Select p.GdFirmCod as PCode, p.GDNames as PNames,');
      GdCode := 'GdFirmCod=''';
   end;
   If cbxTreeSel.ItemIndex=5 then Begin
      qrExeProc.SQL.Add('Select p.GdIntCode as PCode, p.GDNames as PNames,');
      GdCode := 'GdIntCode=''';
   end;
   qrExeProc.SQL.Add('  g.*, l.GdsLevel from Goods g, Goods p, GoodsLev l');
   qrExeProc.SQL.Add('  where g.GoodsNo=l.GoodsNo and p.GoodsNo=l.GdParent');
   qrExeProc.SQL.Add('    and g.GdTreeNo='+sTree);
   qrExeProc.SQL.Add('  order by l.GdsLevel');
   qrExeProc.Open;    qrExeProc.First;

       Подготавливается запрос, который выбирает все необходимые строки для вывода в скрипт. Первая строка запроса зависит от выбранного дерева. Для исключения ошибок cbxTreeSel.ItemIndex=0 приравнивается по значению к cbxTreeSel.ItemIndex=1. Результат сортируется по уровню, который занимает потомок на дереве.

   While not qrExeProc.EOF do Begin
      Str1 := 'Insert into Goods(GdParent,GdPrnTNK,GdPrnBAR,GdPrnFRM,GdPrnAls,'
             +'GdTreeNo';
      Str2 := 'Select Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,Gd.GoodsNo,'
              +IntToStr(cbxTreeSel.ItemIndex);

       Далее в цикле в скрипт выводятся запросы для заполнения таблицы. Основа скрипта формируется на двух переменных Str1 и Str2. В Str1 формируется строка "Insert into ...", в которой перечисляются наименования полей, а в Str2 формируется строка " Select ...", в которой перечисляются значения соответствующих полей.

      If qrExeProc.FieldValues['GdOrder']<>0 then Begin
         Str1 := Str1 + ',GdOrder';
         Str2 := Str2 + ','+ IntToStr(qrExeProc.FieldValues['GdOrder']);
       end;
      If qrExeProc.FieldValues['GdOrdTNK']<>0 then Begin
         Str1 := Str1 + ',GdOrdTNK';
         Str2 := Str2 + ','+ IntToStr(qrExeProc.FieldValues['GdOrdTNK']);
       end;
      If qrExeProc.FieldValues['GdOrdBAR']<>0 then Begin
         Str1 := Str1 + ',GdOrdBAR';
         Str2 := Str2 + ','+ IntToStr(qrExeProc.FieldValues['GdOrdBAR']);
       end;
      If qrExeProc.FieldValues['GdOrdFRM']<>0 then Begin
         Str1 := Str1 + ',GdOrdFRM';
         Str2 := Str2 + ','+ IntToStr(qrExeProc.FieldValues['GdOrdFRM']);
       end;
      If qrExeProc.FieldValues['GdOrdAls']<>0 then Begin
         Str1 := Str1 + ',GdOrdAls';
         Str2 := Str2 + ','+ IntToStr(qrExeProc.FieldValues['GdOrdAls']);
       end;
      If not VarIsNull(qrExeProc.FieldValues['GdNames']) then begin
         If qrExeProc.FieldValues['GdNames']<>'' then Begin
            Str1 := Str1 + ',GdNames';
            Str2 := Str2 + ','+
+''''
                         + Trim(qrExeProc.FieldValues['GdNames'])+'''';
            end;
         end;
      If not VarIsNull(qrExeProc.FieldValues['GoodNamR']) then begin
         If qrExeProc.FieldValues['GoodNamR']<>'' then Begin
            Str1 := Str1 + ',GoodNamR';
            Str2 := Str2 + ','+
+''''
                         + Trim(qrExeProc.FieldValues['GoodNamR'])+'''';
            end;
         end;

       В дальнейшем последовательно в каждом поле проверяется наличие значения и, если оно есть, то оно преобразуется в строку и добавляется к переменной Str2, а к переменной Str1 добавляется наименование этого поля.

 . . . . . . . . . . . . . . . 
      If not VarIsNull(qrExeProc.FieldValues['GdIntCode']) then begin
         If qrExeProc.FieldValues['GdIntCode']<>'' then Begin
            Str1 := Str1 + ',GdIntCode';
            Str2 := Str2 + ','+
+''''
                         + Trim(qrExeProc.FieldValues['GdIntCode'])+'''';
            end;
         end;
 . . . . . . . . . . . . . . .
      If not VarIsNull(qrExeProc.FieldValues['GdVINCode']) then begin
         If qrExeProc.FieldValues['GdVINCode']<>'' then Begin
            Str1 := Str1 + ',GdVINCode';
            Str2 := Str2 + ','+
+''''
                         + Trim(qrExeProc.FieldValues['GdVINCode'])+'''';
            end;
         end;
 . . . . . . . . . . . . . . . 
      Str3 := ' from Goods gd where '+ GdCode;
      Str3 := Str3 + Trim(qrExeProc.FieldValues['PCode'])+'''';
      Str4 := ' and GdNames='''+Trim(qrExeProc.FieldValues['PNames'])+''';';

       Формируется завершение запроса

      WriteLn(flReprt,Str1+')');  WriteLn(flReprt,Str2);
      WriteLn(flReprt,Str3);      WriteLn(flReprt,Str4);
      WriteLn(flReprt,'');
      qrExeProc.Next;

       Полученный результат выводится в выходной файл и все начинается с начала.

   end; // While not qrExeProc.EOF do
   WriteLn(flReprt,'Commit;');    WriteLn(flReprt,'');
   CloseFile(flReprt);  // Закрыть файл
   qrExeProc.Close;
Screen.Cursor := SvCursor;
end;

       — сохранение деталей — также заведено на соответствующую кнопку. Процедура сохраняет строки таблицы, которые описывают товары.

procedure TForm1.btnSaveDetalToSQLClick(Sender: TObject);
Var
  rpPathName : String;
  flReprt : TextFile;
  Str1,Str2,Str3,Str4,sFld,sTree,sGoodsNo : String;
  StrA,StrB,StrC,GdCode,GdNames : String;
  SvCursor : TCursor;
begin
   rpPathName := ExtractFilePath(Application.ExeName);
   SvCursor := Screen.Cursor;
   Screen.Cursor := crHourGlass;
// Создание файла и каталога, если необходимо
   If not DirectoryExists(rpPathName+'\DtSave') then
      If not CreateDir(rpPathName+'\DtSave') then
         raise Exception.Create('Cannot create '+rpPathName+'\DtSave');
   AssignFile(flReprt,rpPathName+'\DtSave\GoodsDetal.txt');
   ReWrite(flReprt);
   WriteLn(flReprt,'/* Заполнение таблицы Goods Detal*/');

       Файл скрипта создается в каталоге DtSave, который при необходимости создается там же где находится приложение. В скрипт сразу записывается информационный заголовок.

// Очистка вспомогательной таблицы GoodsLev
   qrExeProc.Close;      qrExeProc.SQL.Clear;
   qrExeProc.SQL.Add('Delete from GoodsLev');
   qrExeProc.ExecSQL;    qrExeProc.Close;

       Очищается вспомогательная таблица GoodsLev, в которой будет собираться цепочка потомков от текущего узла.

// Выборка ID всех потомков от указанного узла
   sTree := IntToStr(cbxTreeSel.ItemIndex);
   Str1 := IntToStr(tvGoods.Selected.ImageIndex);
   Str2 := IntToStr(tvGoods.Selected.Parent.ImageIndex);

       Получаем исходные данные — номер дерева, ID узла и его родителя.

  qrExeProc.Close;      qrExeProc.SQL.Clear;
  qrExeProc.SQL.Add('Insert into GoodsLev(GoodsNo,GdParent,GdsTree,GdsLevel)');
  qrExeProc.SQL.Add(' values('+Str1+','+Str2+','+sTree+',0)');
  qrExeProc.ExecSQL;
  qrExeProc.Close;      qrExeProc.SQL.Clear;
  qrExeProc.SQL.Add('Execute procedure GoodsChildLev('+Str1+','+sTree+',0)');
  qrExeProc.ExecSQL;

       Текущий узел записывается в таблицу GoodsLev, затем выполняется хранимая процедура GoodsChildLev, которая получает всех потомков текущего узла и записывает их во вспомогательную таблицу GoodsLev.

//  Получение всех полей для формирования скрипта
   qrExeProc.Close;      qrExeProc.SQL.Clear;
   qrExeProc.SQL.Add('Select  g.*, l.GdsLevel from Goods g,  GoodsLev l');
   qrExeProc.SQL.Add('  where g.GoodsNo=l.GoodsNo');
   qrExeProc.SQL.Add('    and g.GdTreeNo=0');
   qrExeProc.SQL.Add('  order by l.GdsLevel');
   qrExeProc.Open;    qrExeProc.First;

       Подготавливается запрос, который выбирает все необходимые строки для вывода в скрипт. Результат сортируется по уровню, который занимает потомок на дереве. Но в отличии от предыдущей процедуры здесь не требуется получать значение ключевых полей для родительской записи, потому что получать их необходимо для ПЯТИ деревьев, а это в одном запросе делать невозможно.

   While not qrExeProc.EOF do Begin
     WriteLn(flReprt,'Delete from TempInt;');

       Для заполнения таблицы Goods записями "деталей" используется вспомогательная таблица TempInt. Перез записью каждой строки она должна быть очищена.

     sGoodsNo := IntToStr(qrExeProc.FieldValues['GdParent']);
     qrExeProc2.Close;   qrExeproc2.SQL.Clear;
     qrExeproc2.SQL.Add('Select  GdIntCode,GdNames from Goods where GoodsNo='
                        +sGoodsNo);
     qrExeProc2.Open;
     GdCode  := Trim(qrExeProc2.FieldValues['GdIntCode']);
     GdNames := Trim(qrExeProc2.FieldValues['GdNames']);
     StrA := 'Insert into TempInt(Int1) Select GoodsNo from Goods ';
     StrB := ' where GdIntCode='''+GdCode+''' and GdNames='''+GdNames+''';';
     WriteLn(flReprt,StrA);  WriteLn(flReprt,StrB);

       Для каждого дерева получается ID родителя и с помощью простого запроса находятся значения его ключевых полей. На основе этих значений на переменных StrA и StrB строится запрос, который и выводится в выходной файл SQL-скрипта.

     sGoodsNo := IntToStr(qrExeProc.FieldValues['GdPrnTNK']);
     qrExeProc2.Close;   qrExeproc2.SQL.Clear;
     qrExeproc2.SQL.Add('Select  GdTNVED,GdNames from Goods where GoodsNo='
                        +sGoodsNo);
     qrExeProc2.Open;
     GdCode  := Trim(qrExeProc2.FieldValues['GdTNVED']);
     GdNames := Trim(qrExeProc2.FieldValues['GdNames']);
     StrA := 'Insert into TempInt(Int2) Select GoodsNo from Goods ';
     StrB := ' where GdTNVED='''+GdCode+''' and GdNames='''+GdNames+''';';
     WriteLn(flReprt,StrA);  WriteLn(flReprt,StrB);
     sGoodsNo := IntToStr(qrExeProc.FieldValues['GdPrnBAR']);
     qrExeProc2.Close;   qrExeproc2.SQL.Clear;
     qrExeproc2.SQL.Add('Select  GdEANCode,GdNames from Goods where GoodsNo='
                        +sGoodsNo);
     qrExeProc2.Open;
     GdCode  := Trim(qrExeProc2.FieldValues['GdEANCode']);
     GdNames := Trim(qrExeProc2.FieldValues['GdNames']);
     StrA := 'Insert into TempInt(Int3) Select GoodsNo from Goods ';
     StrB := ' where GdEANCode='''+GdCode+''' and GdNames='''+GdNames+''';';
     WriteLn(flReprt,StrA);  WriteLn(flReprt,StrB);
     sGoodsNo := IntToStr(qrExeProc.FieldValues['GdPrnFrm']);
     qrExeProc2.Close;   qrExeproc2.SQL.Clear;
     qrExeproc2.SQL.Add('Select  GdFirmCod,GdNames from Goods where GoodsNo='
                        +sGoodsNo);
     qrExeProc2.Open;
     GdCode  := Trim(qrExeProc2.FieldValues['GdFirmCod']);
     GdNames := Trim(qrExeProc2.FieldValues['GdNames']);
     StrA := 'Insert into TempInt(Int4) Select GoodsNo from Goods ';
     StrB := ' where GdFirmCod='''+GdCode+''' and GdNames='''+GdNames+''';';
     WriteLn(flReprt,StrA);  WriteLn(flReprt,StrB);
     sGoodsNo := IntToStr(qrExeProc.FieldValues['GdPrnAls']);
     qrExeProc2.Close;   qrExeproc2.SQL.Clear;
     qrExeproc2.SQL.Add('Select  GdIntCode,GdNames from Goods where GoodsNo='
                        +sGoodsNo);
     qrExeProc2.Open;
     GdCode  := Trim(qrExeProc2.FieldValues['GdIntCode']);
     GdNames := Trim(qrExeProc2.FieldValues['GdNames']);
     StrA := 'Insert into TempInt(Int5) Select GoodsNo from Goods ';
     StrB := ' where GdIntCode='''+GdCode+''' and GdNames='''+GdNames+''';';
     WriteLn(flReprt,StrA);  WriteLn(flReprt,StrB);

       Эта процедура повторяется для остальных деревьев.
       Затем начинается построение основного запроса для самой записи о детали.

      Str1 := 'Insert into Goods(GdParent,GdPrnTNK,GdPrnBAR,GdPrnFRM,GdPrnAls,'
             +'GdTreeNo';
      Str2 := 'Select Sum(Int1),Sum(Int2),Sum(Int3),Sum(Int4),Sum(Int5),0';

       Далее в цикле в скрипт выводятся запросы для заполнения таблицы. Основа скрипта формируется на двух переменных Str1 и Str2. В Str1 формируется строка "Insert into ...", в которой перечисляются наименования полей, а в Str2 формируется строка " Select ...", в которой перечисляются значения соответствующих полей. Но для получения родительский полей суммируются столбцы вспомогательной таблицы TempInt.

      If qrExeProc.FieldValues['GdOrder']<>0 then Begin
         Str1 := Str1 + ',GdOrder';
         Str2 := Str2 + ','+ IntToStr(qrExeProc.FieldValues['GdOrder']);
       end;
      If qrExeProc.FieldValues['GdOrdTNK']<>0 then Begin
         Str1 := Str1 + ',GdOrdTNK';
         Str2 := Str2 + ','+ IntToStr(qrExeProc.FieldValues['GdOrdTNK']);
       end;
      If qrExeProc.FieldValues['GdOrdBAR']<>0 then Begin
         Str1 := Str1 + ',GdOrdBAR';
         Str2 := Str2 + ','+ IntToStr(qrExeProc.FieldValues['GdOrdBAR']);
       end;
      If qrExeProc.FieldValues['GdOrdFRM']<>0 then Begin
         Str1 := Str1 + ',GdOrdFRM';
         Str2 := Str2 + ','+ IntToStr(qrExeProc.FieldValues['GdOrdFRM']);
       end;
      If qrExeProc.FieldValues['GdOrdAls']<>0 then Begin
         Str1 := Str1 + ',GdOrdAls';
         Str2 := Str2 + ','+ IntToStr(qrExeProc.FieldValues['GdOrdAls']);
       end;
      If not VarIsNull(qrExeProc.FieldValues['GdNames']) then begin
         If qrExeProc.FieldValues['GdNames']<>'' then Begin
            Str1 := Str1 + ',GdNames';
            Str2 := Str2 + ','+
+''''
                         + Trim(qrExeProc.FieldValues['GdNames'])+'''';
            end;
         end;
      If not VarIsNull(qrExeProc.FieldValues['GoodNamR']) then begin
         If qrExeProc.FieldValues['GoodNamR']<>'' then Begin
            Str1 := Str1 + ',GoodNamR';
            Str2 := Str2 + ','+
+''''
                         + Trim(qrExeProc.FieldValues['GoodNamR'])+'''';
            end;
         end;

       В дальнейшем последовательно в каждом поле проверяется наличие значения и, если оно есть, то оно преобразуется в строку и добавляется к переменной Str2, а к переменной Str1 добавляется наименование этого поля.

 . . . . . . . . . . . . . . . 
      If not VarIsNull(qrExeProc.FieldValues['GdIntCode']) then begin
         If qrExeProc.FieldValues['GdIntCode']<>'' then Begin
            Str1 := Str1 + ',GdIntCode';
            Str2 := Str2 + ','+
+''''
                         + Trim(qrExeProc.FieldValues['GdIntCode'])+'''';
            end;
         end;
 . . . . . . . . . . . . . . . .
      If not VarIsNull(qrExeProc.FieldValues['GdVINCode']) then begin
         If qrExeProc.FieldValues['GdVINCode']<>'' then Begin
            Str1 := Str1 + ',GdVINCode';
            Str2 := Str2 + ','+
+''''
                         + Trim(qrExeProc.FieldValues['GdVINCode'])+'''';
            end;
         end;
 . . . . . . . . . . . . . . . . 
      WriteLn(flReprt,Str1+')');            WriteLn(flReprt,Str2);
      WriteLn(flReprt,' from TempInt;');    WriteLn(flReprt,'');
      qrExeProc.Next;

       Формируется завершение запроса и полученный результат выводится в выходной файл. Потом все начинается с начала.

   end; // While not qrExeProc.EOF do
   WriteLn(flReprt,'Commit;');    WriteLn(flReprt,'');
   CloseFile(flReprt);
   qrExeProc.Close;
Screen.Cursor := SvCursor; // Закрыть файл
end;

Оставшиеся проблемы

       При описанном способе формирования таблицы Goods остается проблема структурирования "групповых" товаров. Например, пачка бумаги для принтера — самостоятельный товар, описывается простой записью. А как описать коробку из 5 пачек этой бумаги? С одной стороны — это самостоятельный товар, с другой — группа товаров, так как имеет дочерние элементы.
       Таким образом, описанный вариант оказался не очень удачным, хотя и поработал некоторое время.

© 01.07.2007, Архангельский А.Г.

<<Пред. Оглавление
Начало раздела
Главная страница
След.>>




Дата последнего изменения:
Thursday, 21-Aug-2014 09:10:44 MSK


Постоянный адрес статьи:
http://az-design.ru/Support/DataBase/002b3210.shtml