Prieš Progress OpenEdge 12.8: tradiciniai duomenų perkėlimo būdai
Prieš Progress OpenEdge 12.8 versiją programuotojai turėjo kelis nusistovėjusius duomenų eksportavimo ir importavimo metodus. Pažvelkime į juos atidžiau.
1 variantas: įrankių (utilities) naudojimas (.d failai)
Vienas seniausių ir patikimiausių metodų – naudoti integruotus Progress OpenEdge įrankius:
proutil mydb -C dump sports.customer customer.d
proutil mydb -C load sports.customer customer.d
Šios komandos yra greitos, paprastos ir patikimos, todėl puikiai tinka pilno lentelės eksportui. Tačiau jos nėra labai lanksčios: negalite filtruoti įrašų, pasirinkti konkrečių laukų ar keisti formato. Arba eksportuojate viską, arba nieko.
Data Administration įrankis viduje naudoja būtent šiuos pačius utilities.
2 variantas: temp-tables naudojimas (JSON/XML)
Temp-tables yra dar vienas populiarus būdas eksportuoti ir importuoti duomenis OpenEdge aplinkoje. Jie lankstūs, lengvai suprantami ir gerai veikia su JSON ar XML formatais.
Štai paprastas pavyzdys:
// Export
define temp-table ttCustomer no-undo like Customer.
for each Customer no-lock:
create ttCustomer.
buffer-copy Customer to ttCustomer.
end.
temp-table ttCustomer:write-json("file", "Customer.json", true).
// or
temp-table ttCustomer:write-xml("file", "Customer.xml", true).
// Import
define temp-table ttCustomer no-undo like Customer.
temp-table ttCustomer:read-json("file", "customer.json").
// or
temp-table ttCustomer:read-xml("file", "customer.xml", "empty", ?, ?, ?).
for each ttCustomer:
create Customer.
buffer-copy ttCustomer to Customer.
end.
Kartais reikia kažko dinamiškesnio, pavyzdžiui, eksportuoti visas duomenų bazės lenteles be poreikio žodynu (hardcode) nurodyti kiekvieną atskirai. Tai galima padaryti skaitant lentelių metaduomenis iš schemos lentelių _File ir _Field. Procesas atrodo taip:
VAR HANDLE hTable.
FOR EACH _File NO-LOCK WHERE
_File._Tbl-Type EQ "T":
CREATE TEMP-TABLE hTable.
FOR EACH _Field NO-LOCK WHERE
_Field._File-recid EQ RECID(_File):
hTable:ADD-NEW-FIELD(_Field._Field-Name, _Field._Data-Type).
END.
hTable:TEMP-TABLE-PREPARE("tt" + _File._File-Name).
END.
Po to lieka tik užklausti duomenų iš duomenų bazės, užpildyti temp-table ir eksportuoti pasirinktu formatu, pavyzdžiui, JSON:
CREATE BUFFER hBuffer FOR TABLE _File._File-Name.
CREATE BUFFER hTTBuffer FOR TABLE hTable.
CREATE QUERY hQuery.
hQuery:SET-BUFFERS(hBuffer).
hQuery:QUERY-PREPARE("for each " + hBuffer:NAME).
hQuery:QUERY-OPEN().
hQuery:GET-FIRST().
// Query the DB and copy the buffer to dynamic Temp-Table
DO WHILE NOT hQuery:QUERY-OFF-END:
hTTBuffer:BUFFER-CREATE().
hTTBuffer:BUFFER-COPY(hBuffer).
hQuery:GET-NEXT().
END.
// Export the data
hTable:WRITE-JSON("file", "./export/" + _File._File-Name + ".json", TRUE).
Jei norite dinamiškai eksportuoti kiekvieną lentelę, štai pilnas kodas:
VAR HANDLE hTable.
VAR HANDLE hBuffer.
VAR HANDLE hTTBuffer.
VAR HANDLE hQuery.
FOR EACH _File NO-LOCK WHERE
_File._Tbl-Type = "T":
CREATE TEMP-TABLE hTable.
FOR EACH _Field NO-LOCK WHERE
_Field._File-recid EQ RECID(_File):
hTable:ADD-NEW-FIELD(_Field._Field-Name, _Field._Data-Type).
END.
hTable:TEMP-TABLE-PREPARE("tt" + _File._File-Name).
CREATE BUFFER hBuffer FOR TABLE _File._File-Name.
CREATE BUFFER hTTBuffer FOR TABLE hTable.
CREATE QUERY hQuery.
hQuery:SET-BUFFERS(hBuffer).
hQuery:QUERY-PREPARE("for each " + hBuffer:NAME).
hQuery:QUERY-OPEN().
hQuery:GET-FIRST().
DO WHILE NOT hQuery:QUERY-OFF-END:
hTTBuffer:BUFFER-CREATE().
hTTBuffer:BUFFER-COPY(hBuffer).
hQuery:GET-NEXT().
END.
hQuery:QUERY-CLOSE().
hTable:WRITE-JSON("file", "./export/" + _File._File-Name + ".json", TRUE).
DELETE OBJECT hQuery.
DELETE OBJECT hTTBuffer.
DELETE OBJECT hTable.
DELETE OBJECT hBuffer.
END.
Šis būdas yra aiškus ir patogus programuotojui. Jis leidžia dinamiškai eksportuoti kiekvieną duomenų bazės lentelę kaip JSON failus, nerašant atskiros logikos kiekvienai lentelei.
Trūkumas tas, kad temp-tables saugo viską operatyviojoje atmintyje. Mažesniems duomenų rinkiniams tai nėra problema, tačiau didesniems prireiks partijavimo (batching), o tai prideda sudėtingumo.
3 variantas: ABL Export/Import naudojimas
Kitas variantas – integruoti EXPORT ir IMPORT sakiniai. Šis būdas puikiai tinka, kai norite visą procesą valdyti kode:
// Export
OUTPUT TO VALUE("customers.d").
FOR EACH Customer NO-LOCK:
EXPORT DELIMITER "," Customer.CustNum Customer.Name Customer.SalesRep.
END.
OUTPUT CLOSE.
// Import
INPUT FROM VALUE("customers.d").
REPEAT ON ENDKEY UNDO, LEAVE:
CREATE Customer.
IMPORT DELIMITER "," Customer.CustNum Customer.Name Customer.SalesRep.
END.
INPUT CLOSE.
Logika čia paprasta: paleidžiama užklausa, tuomet naudojama EXPORT rašymui arba IMPORT skaitymui. Tai suteikia daugiau kontrolės formatavimui, nes galite pasirinkti skirtukus (delimiters) ir tiksliai nurodyti, kuriuos laukus įtraukti.
Tačiau norint padaryti tai dinamiška, reikia rankiniu būdu pereiti per visus įrašus ir laukus, naudojant buferius ir PUT sakinį:
// Dynamic Export Example
CREATE BUFFER hBuffer FOR TABLE cTable.
CREATE QUERY hQuery.
hQuery:SET-BUFFERS(hBuffer).
hQuery:QUERY-PREPARE("FOR EACH " + cTable + " NO-LOCK").
hQuery:QUERY-OPEN().
hQuery:GET-FIRST().
OUTPUT TO VALUE(cFileName).
DO WHILE NOT hQuery:QUERY-OFF-END:
IF hQuery:QUERY-OFF-END THEN LEAVE.
DO iCount = 1 TO hBuffer:NUM-FIELDS:
hField = hBuffer:BUFFER-FIELD(iCount).
PUT UNFORMATTED (IF iCount > 1 THEN "," ELSE "")
hField:BUFFER-VALUE.
END.
PUT SKIP.
hQuery:GET-NEXT().
END.
OUTPUT CLOSE.
hQuery:QUERY-CLOSE().
Ši versija veikia, tačiau turi kompromisų. Kadangi naudojamas PUT, reikia rankiniu būdu tvarkyti skirtuką, laukų eiliškumą ir formatavimą. Tai taip pat reiškia, kad jūs tiesiogiai atsakote už tai, kaip atrodys eksportuoti duomenys, o tai padidina klaidų tikimybę ir apsunkina kodo priežiūrą.
Naujasis eksportas, paremtas buferiais (12.8 versija)
Tai buvo tradiciniai duomenų perkėlimo būdai – tvirti, bet su savais apribojimais. Su Progress OpenEdge 12.8 versija programuotojai pagaliau gauna švaresnį ir dinamiškesnį būdą tvarkyti duomenų eksportą ir importą, naudojant buferius.
Anksčiau duomenų eksportas reiškė rankinį perėjimą per kiekvieną lauką, štai taip:
DO iCount = 1 TO hBuffer:NUM-FIELDS:
hField = hBuffer:BUFFER-FIELD(iCount).
PUT UNFORMATTED (IF iCount > 1 THEN "," ELSE "")
hField:STRING-VALUE.
END.
Dabar tai – viena eilutė:
hBuffer:BUFFER-EXPORT().
BUFFER-EXPORT metodas automatiškai eksportuoja visus buferio duomenis ir leidžia pritaikyti parametrus, tokius kaip skirtukas, laukų pasirinkimas, LOB praleidimas ir kt. Jis veikia tiek statinėse, tiek dinaminėse užklausose ir yra žymiai greitesnis rašyti bei prižiūrėti.
Vienintelis apribojimas pasireiškia, kai reikia eksportuoti skaičiuojamus ar išvestinius laukus, kurie nėra tiesiogiai saugomi duomenų bazėje. Tai galite tvarkyti dviem būdais:
- Naudoti temp-table su skaičiuojamais laukais.
- Sujungti BUFFER-EXPORT su tradiciniu EXPORT.
Štai pavyzdys, naudojant temp-table su skaičiuojamu lauku:
/* Example: Calculated fields using a temp-table */
DEFINE TEMP-TABLE ttOrder NO-UNDO LIKE Order
FIELD DiscountPct AS DECIMAL FORMAT ">>9.99". /* Additional calculated field */
OUTPUT TO VALUE("orders.csv").
FOR EACH Order NO-LOCK:
/* Create one record in ttOrder per Order */
CREATE ttOrder.
BUFFER-COPY Order TO ttOrder.
RUN calculateFields.
/* Export row */
BUFFER ttOrder:BUFFER-EXPORT().
DELETE ttOrder.
END.
OUTPUT CLOSE.
PROCEDURE calculateFields:
DEFINE VARIABLE dTotalOrderPrice AS DECIMAL NO-UNDO.
DEFINE VARIABLE dOrderPrice AS DECIMAL NO-UNDO.
DEFINE VARIABLE dTotalDiscount AS DECIMAL NO-UNDO.
/* Business logic calculations: Sum up line totals and discounts */
FOR EACH OrderLine NO-LOCK WHERE OrderLine.OrderNum EQ Order.OrderNum:
ASSIGN
dOrderPrice = OrderLine.Price * OrderLine.Qty
dTotalOrderPrice += dOrderPrice
dTotalDiscount += dOrderPrice * (OrderLine.Discount / 100).
END.
/* Calculate the discount percentage */
ttOrder.DiscountPct = IF dTotalOrderPrice NE 0
THEN 100 * (dTotalDiscount) / dTotalOrderPrice
ELSE 0.
END PROCEDURE.
Sukuriame temp-table, atspindinčią Order lentelę, ir pridedame papildomą lauką DiscountPct. Kiekvienam užsakymui nukopijuojame įrašą į temp-table, apskaičiuojame reikšmę ir tuomet eksportuojame naudodami BUFFER-EXPORT.
Tokiu būdu eksportuotas failas turi tiek originalius, tiek skaičiuojamus laukus, su minimaliu kodu ir be poreikio rankiniu būdu pereiti per laukus. Dabar panagrinėkime, kaip OpenEdge 12.8 veikia, palyginti su senesniais metodais.
Veikimas: koks jis iš tikrųjų greitas?
Viena įdomiausių šios funkcijos dalių – kaip ji veikia praktikoje.
Norėdami pamatyti, kaip naujasis būdas lyginasi su kitais, testavome duomenų eksportą Progress OpenEdge 12.8 versijoje, naudodami 4 skirtingus metodus:
- naudojant Buffer-Export;
- eksportuojant JSON su temp-tables;
- eksportuojant XML su temp-tables;
- naudojant put unformatted.
Kadangi Export ir Import sakiniai nepalaiko dinaminio eksportavimo, jie buvo pakeisti PUT UNFORMATTED versija palyginimui.
Tikėtasi, kad BUFFER-EXPORT veiks panašiai kaip PUT UNFORMATTED, o JSON ir XML eksportai bus lėtesni dėl papildomos duomenų konvertavimo apkrovos.
Testavimui naudojome modifikuotą sports2020 duomenų bazę, kurioje buvo žymiai daugiau duomenų nei standartinėje versijoje. Testinė duomenų bazė buvo apie 350 MB, palyginti su įprastais 13–15 MB.
Specialūs duomenų tipai, tokie kaip BLOB, CLOB, MEMPTR ir LONGCHAR, buvo atmesti, kad testas išliktų nuoseklus. Visos procedūros buvo beveik identiškos, vienintelis skirtumas – išvesties metodas.
Ir rezultatai buvo netikėti:

Pilnos duomenų bazės eksportui į CSV/JSON/XML užtruko:
- Buffer-Export: 36,2 sekundės iki CSV.
- JSON eksportas naudojant temp-tables: 56,6 sekundės iki JSON.
- XML eksportas naudojant temp-tables: 73,8 sekundės iki XML.
- Put unformatted: 86,8 sekundės iki CSV.
Rezultatai parodė, kad BUFFER-EXPORT metodas buvo žymiai greitesnis – užtruko mažiau nei perpus trumpiau nei PUT UNFORMATTED.
Kaip ir tikėtasi, eksportavimas į JSON ar XML užtruko ilgiau, nes duomenis pirmiausia reikėjo įkelti į temp-tables, o tuomet įrašyti į failą. XML taip pat atsiliko nuo JSON, kas logiška, nes XML yra žymiai daugiažodis (verbose) formatas.
Trumpai tariant, BUFFER-EXPORT pasiūlė geriausią greičio, paprastumo ir lankstumo balansą.
Metodų santrauka: senas vs naujas
Išvada: Progress OpenEdge 12.8 paruoštas perimti vairą
12.8 versija žymi tikrą pokytį tame, kaip programuotojai tvarko duomenų perkėlimą. Naujieji buferiais paremti eksporto ir importo metodai daro darbą su duomenimis švaresnį ir modernesnį: mažiau rankinio ciklų rašymo, mažiau apėjimo sprendimų ir žymiai greitesni rezultatai.
Be to, BUFFER-EXPORT suteikia tvirtą paprastumo ir galios balansą. Jis:
- pasiekia aukštą veikimą su minimaliu kodu;
- išlaiko aiškią tradicinio EXPORT ir IMPORT logiką;
- sklandžiai veikia tiek statiniame, tiek dinaminiame kontekste;
- leidžia lengvai įtraukti arba praleisti konkrečius laukus.
Tai daro buffer handles geriausiu pasirinkimu Progress OpenEdge projektams, kurie perkelia duomenis per kodą ir kuriems reikia lankstumo bei plečiamumo.
Nors jie nepakeičia temp-tables struktūrizuotam JSON ar XML eksportui, jie efektyviai perima tradicinių EXPORT ir IMPORT sakinių vaidmenį.
Tikimės, kad tai suteikė aiškesnį vaizdą, kaip naujasis metodas lyginasi praktikoje. Jei dirbate su Progress OpenEdge ir reikia ekspertų pagalbos, mūsų komanda pasiruošusi padėti – užsisakykite konsultaciją.
Dažniausiai užduodami klausimai
Kas yra BUFFER-EXPORT Progress OpenEdge 12.8 versijoje?
Tai naujas metodas, leidžiantis vienu sakiniu eksportuoti visus buferio duomenis, automatiškai tvarkant skirtukus, laukų pasirinkimą ir LOB praleidimą, be poreikio rankiniu būdu rašyti ciklą per kiekvieną lauką.
Ar BUFFER-EXPORT greitesnis už temp-tables ar PUT UNFORMATTED?
Taip. Testuose BUFFER-EXPORT eksportavo pilną duomenų bazę į CSV per 36,2 sekundes, o tai buvo žymiai greičiau nei PUT UNFORMATTED (86,8 s), JSON eksportas su temp-tables (56,6 s) ar XML eksportas su temp-tables (73,8 s).
Ar BUFFER-EXPORT pakeičia temp-tables struktūrizuotam JSON ar XML eksportui?
Ne pilnai. BUFFER-EXPORT puikiai tinka greitam, paprastam eksportui (pvz., CSV), tačiau temp-tables vis dar geriausiai tinka struktūrizuotiems JSON ar XML formatams ir skaičiuojamų laukų apdorojimui.
Kaip eksportuoti skaičiuojamus laukus naudojant BUFFER-EXPORT?
Kadangi BUFFER-EXPORT eksportuoja tik tiesiogiai duomenų bazėje saugomus laukus, skaičiuojamiems laukams reikia naudoti temp-table su papildomu lauku arba derinti BUFFER-EXPORT su tradiciniu EXPORT sakiniu.

