Iterate over a dataset that was populated with READ-XML without like columns for parent-child relationship? - progress-4gl

I'm currently on Openedge 10.1c. I'm using READ-XML to populate a dataset. I can't change the source XML and was was wondering how you would iterate over the dataset when there isn't a parent child relationship unique keys to use in my join.
I'll include sample XML and the display loop when I get to my desk.
In the meantime it's something like this...
<data>
<fulfillments>
<field1>field1</field1>
<field2>field2</field2>
<field3>field3</field3>
<customer>
<name>test</name>
</customer>
</fulfillments>
<fulfillments>
<field1>field11</field1>
<field2>field22</field2>
<field3>field33</field3>
<customer>
<name>test2</name>
</customer>
</fulfillments>
</data>
After I use read XML I get all the data but trying to iterate over it I don't know how to display customer in to each fulfillment. Instead it will just show all customers for each fulfillment.
Sample Code reading and displaying the dataset...
PROCEDURE _read_xml:
DEFINE INPUT-OUTPUT PARAMETER DATASET-HANDLE idshndl.
DEFINE INPUT PARAMETER ifileloc AS CHARACTER NO-UNDO.
DEFINE OUTPUT PARAMETER oreturn AS LOGICAL NO-UNDO.
DEFINE VARIABLE cSourceType AS CHARACTER NO-UNDO.
DEFINE VARIABLE cReadMode AS CHARACTER NO-UNDO.
DEFINE VARIABLE lOverrideDefaultMapping AS LOGICAL NO-UNDO.
DEFINE VARIABLE cFile AS CHARACTER NO-UNDO.
DEFINE VARIABLE cEncoding AS CHARACTER NO-UNDO.
DEFINE VARIABLE cSchemaLocation AS CHARACTER NO-UNDO.
DEFINE VARIABLE cFieldTypeMapping AS CHARACTER NO-UNDO.
DEFINE VARIABLE cVerifySchemaMode AS CHARACTER NO-UNDO.
IF SEARCH(ifileloc) <> ? THEN
DO:
ASSIGN
cSourceType = "file"
cFile = ifileloc
cReadMode = "empty"
cSchemaLocation = ?
lOverrideDefaultMapping = ?
cFieldTypeMapping = ?
cVerifySchemaMode = ?.
oreturn = idshndl:READ-XML(cSourceType,
cFile,
cReadMode,
cSchemaLocation,
lOverrideDefaultMapping,
cFieldTypeMapping,
cVerifySchemaMode).
END.
ELSE
DO:
oreturn = FALSE.
END.
END PROCEDURE.
PROCEDURE _fulfillment_display_data:
DISPLAY "DATA".
FOR EACH tt_biz_fulfillments NO-LOCK,
EACH tt_biz_ship_to:
DISPLAY "FULFILLMENTS".
DISPLAY tt_biz_fulfillments WITH SIDE-LABELS.
DISPLAY tt_biz_ship_to WITH SIDE-LABELS.
END.
END PROCEDURE.
Sample XML...
<?xml version="1.0" encoding="UTF-8"?>
<data>
<fulfillments>
<shipping_method>AIM - Direct</shipping_method>
<picker_id />
<sales_order_id>234722</sales_order_id>
<packaging_id>1</packaging_id>
<scheduled_delivery_date>07/01/2016</scheduled_delivery_date>
<net_weight>225</net_weight>
<shipper_name>TEST</shipper_name>
<external_note>Test Note</external_note>
<packaging_type>box</packaging_type>
<bill_to>sender</bill_to>
<shipping_cost />
<fulfillment_id>12345</fulfillment_id>
<ship_to>
<city>Pittsburgh</city>
<name>Bizowie</name>
<zip>15219</zip>
<is_residential>0</is_residential>
<company />
<address>429 Fourth Avenue Suite 1206</address>
<phone_extension />
<contact_id>8</contact_id>
<phone />
<state>PA</state>
<country />
<email />
</ship_to>
<location_id>1</location_id>
<parent_fulfillment_id />
<location_name>201 - PA</location_name>
<manifest_number />
<picker_name />
<scheduled_pick_date />
<pick_timestamp />
<delivery_timestamp />
<packaging_weight>0.150000</packaging_weight>
<insured_value />
<bill_to_zip />
<internal_note>test note internal</internal_note>
<bill_to_account />
<status>shipped</status>
<dock_number />
<route_id>1</route_id>
<delivery_latitude />
<shipper_id>1</shipper_id>
<scheduled_ship_date>06/29/2016</scheduled_ship_date>
<acceptance_timestamp />
<shipping_carrier />
<packaging_height>4.0000</packaging_height>
<packaging_length>4.0000</packaging_length>
<gross_weight>225.15</gross_weight>
<packaging_width>4.0000</packaging_width>
<delivery_longitude />
<delivery_signer_location />
<packing_layer />
<truck_number />
<route_name />
<flag_message />
<delivery_signer_name />
<tracking_number />
<packing_row />
<ship_timestamp>06/09/2016 07:15:15 AM</ship_timestamp>
</fulfillments>
<fulfillments>
<shipping_method>TEST</shipping_method>
<picker_id />
<sales_order_id>234722</sales_order_id>
<packaging_id>1</packaging_id>
<scheduled_delivery_date>08/05/2016</scheduled_delivery_date>
<net_weight>440</net_weight>
<shipper_name>BLAH</shipper_name>
<external_note>TESTING</external_note>
<packaging_type>box</packaging_type>
<bill_to>sender</bill_to>
<shipping_cost />
<fulfillment_id>12346</fulfillment_id>
<ship_to>
<city>Wyoming</city>
<name />
<zip>18644</zip>
<is_residential />
<company>Walmart</company>
<address>10 Moosic St</address>
<phone_extension />
<contact_id>1226058</contact_id>
<phone />
<state>PA</state>
<country />
<email />
</ship_to>
<location_id>1</location_id>
<parent_fulfillment_id />
<location_name>201 - PA</location_name>
<manifest_number />
<picker_name />
<scheduled_pick_date />
<pick_timestamp />
<delivery_timestamp />
<packaging_weight>0.150000</packaging_weight>
<insured_value />
<bill_to_zip />
<internal_note>TESTING NOTE</internal_note>
<bill_to_account />
<status>shipped</status>
<dock_number />
<route_id>1</route_id>
<delivery_latitude />
<shipper_id>1</shipper_id>
<scheduled_ship_date>07/18/2016</scheduled_ship_date>
<acceptance_timestamp />
<shipping_carrier />
<packaging_height>4.0000</packaging_height>
<packaging_length>4.0000</packaging_length>
<gross_weight>440.15</gross_weight>
<packaging_width>4.0000</packaging_width>
<delivery_longitude />
<delivery_signer_location />
<packing_layer />
<truck_number />
<route_name />
<flag_message />
<delivery_signer_name />
<tracking_number />
<packing_row />
<ship_timestamp>06/09/2016 07:15:15 AM</ship_timestamp>
</fulfillments>
</data>

I'm not 100% sure if this works in 10.c or not. It certainly works in 11.6.
If you use a PARENT-ID-RELATION when you define the dataset a record id of the parent buffer will be added to all children when you READ-XML
I've added a simplified version here. You might want to change data types. The PARENT-FIELDS-BEFORE and PARENT-FIELDS-AFTER are just needed to make the output dataset look the same (basically if places the "ship_to" part in the right place). It's not needed for just reading the xml and iterating through the records.
DEFINE TEMP-TABLE ttFulfillments NO-UNDO SERIALIZE-NAME "fulfillments"
FIELD shipping_method AS CHARACTER
FIELD picker_id AS CHARACTER
FIELD sales_order_id AS CHARACTER
FIELD packaging_id AS CHARACTER
FIELD scheduled_delivery_date AS CHARACTER
FIELD net_weight AS CHARACTER
FIELD shipper_name AS CHARACTER
FIELD external_note AS CHARACTER
FIELD packaging_type AS CHARACTER
FIELD bill_to AS CHARACTER
FIELD shipping_cost AS CHARACTER
FIELD fulfillment_id AS CHARACTER
FIELD location_id AS CHARACTER
FIELD parent_fulfillment_id AS CHARACTER
FIELD location_name AS CHARACTER
FIELD manifest_number AS CHARACTER
FIELD picker_name AS CHARACTER
FIELD scheduled_pick_date AS CHARACTER
FIELD pick_timestamp AS CHARACTER
FIELD delivery_timestamp AS CHARACTER
FIELD packaging_weight AS CHARACTER
FIELD insured_value AS CHARACTER
FIELD bill_to_zip AS CHARACTER
FIELD internal_note AS CHARACTER
FIELD bill_to_account AS CHARACTER
FIELD fullfill_status AS CHARACTER SERIALIZE-NAME "status"
FIELD dock_number AS CHARACTER
FIELD route_id AS CHARACTER
FIELD delivery_latitude AS CHARACTER
FIELD shipper_id AS CHARACTER
FIELD scheduled_ship_date AS CHARACTER
FIELD acceptance_timestamp AS CHARACTER
FIELD shipping_carrier AS CHARACTER
FIELD packaging_height AS CHARACTER
FIELD packaging_length AS CHARACTER
FIELD gross_weight AS CHARACTER
FIELD packaging_width AS CHARACTER
FIELD delivery_longitude AS CHARACTER
FIELD delivery_signer_location AS CHARACTER
FIELD packing_layer AS CHARACTER
FIELD truck_number AS CHARACTER
FIELD route_name AS CHARACTER
FIELD flag_message AS CHARACTER
FIELD delivery_signer_name AS CHARACTER
FIELD tracking_number AS CHARACTER
FIELD packing_row AS CHARACTER
FIELD ship_timestamp AS CHARACTER.
DEFINE TEMP-TABLE ttShipTo NO-UNDO SERIALIZE-NAME "ship_to"
FIELD parent_recid AS RECID SERIALIZE-HIDDEN
FIELD city AS CHARACTER
FIELD shipto_name AS CHARACTER SERIALIZE-NAME "name"
FIELD zip AS CHARACTER
FIELD is_residential AS CHARACTER
FIELD company AS CHARACTER
FIELD address AS CHARACTER
FIELD phone_extension AS CHARACTER
FIELD contact_id AS CHARACTER
FIELD phone AS CHARACTER
FIELD state AS CHARACTER
FIELD country AS CHARACTER
FIELD email AS CHARACTER.
DEFINE DATASET dsData SERIALIZE-NAME "data"
FOR ttFulfillments, ttShipTo
PARENT-ID-RELATION pr1 FOR ttFulFillments, ttShipTo
PARENT-ID-FIELD parent_recid
PARENT-FIELDS-BEFORE (shipping_method, picker_id, sales_order_id, packaging_id, scheduled_deliv, net_weight, shipper_name, external_note, packaging_type, bill_to, shipping_cost, fulfillment_id )
PARENT-FIELDS-AFTER (location_id,parent_fulfillment_id,location_name,manifest_number,picker_name,scheduled_pick_date,pick_timestamp, delivery_timestamp, packaging_weight, insured_value, bill_to_zip, internal_note, bill_to_account, fullfill_status, dock_number, route_id, delivery_latitude, shipper_id, scheduled_ship_date, acceptance_timestamp, shipping_carrier, packaging_height, packaging_length, gross_weight, packaging_width, delivery_longitude, delivery_signer_location, packing_layer, truck_number, route_name, flag_message, delivery_signer_name, tracking_number, packing_row, ship_timestamp).
DATASET dsData:READ-XML("file", "c:\temp\sample-data.xml", "empty", ?, ?, ?, ?).
/* Output dataset just to have a reference to compare to the input... */
DATASET dsData:WRITE-XML("file","c:\temp\output-data.xml").
/* Iterate on temp-tables */
FOR EACH ttFulfillments :
DISPLAY ttFulfillments.
FOR EACH ttShipTo WHERE ttShipTo.parent_recid = RECID(ttFulfillments):
DISPLAY ttShipTo EXCEPT ttShipTo.parent_recid.
END.
END.

Related

Postgres XML parsing - Calculate the sum of multiple nodes of the same name using XPath

I have an xml snippet as below:
<OpCodeLaborInfo JobStatus="F" UpSellFlag="N" JobNo="1" OpCode="02TTZ10K" OpCodeDesc="10K SERVICE">
<TechInfo ActualHrsWorked="2.50" CustTechRate="27.00" TechHrs="0.00" TechName="JEFF SELLERS" TechNo="4816" />
<TechInfo ActualHrsWorked="0.00" CustTechRate="27.00" TechHrs="0.70" TechName="JEFF SELLERS" TechNo="4816" />
<BillTimeRateHrs BillRate="129.97" />
<CCCStmts Correction="PERFORMED 10K SERVICE" Complaint="LUBE OIL FILTER CHANGE, TIRE ROTATION, PERFORM MULTI POINT" />
<CCCStmts Correction="X" Complaint="INSPECTION, INSPECT FILTERS AND RECOMMEND, INSPECT BRAKES," />
<CCCStmts Complaint="BELTS AND HOSES" />
<RoAmts DlrCost="18.90" PayType="Cust" AmtType="Job" TotalAmt="59.12" />
</OpCodeLaborInfo>
<OpCodeLaborInfo JobStatus="F" UpSellFlag="N" JobNo="2" OpCode="02TTZ10K" OpCodeDesc="10K SERVICE">
<TechInfo ActualHrsWorked="2.50" CustTechRate="27.00" TechHrs="1.00" TechName="JEFF SELLERS" TechNo="4816" />
<TechInfo ActualHrsWorked="0.00" CustTechRate="27.00" TechHrs="0.00" TechName="JEFF SELLERS" TechNo="4816" />
<BillTimeRateHrs BillRate="129.97" />
<CCCStmts Correction="PERFORMED 10K SERVICE" Complaint="LUBE OIL FILTER CHANGE, TIRE ROTATION, PERFORM MULTI POINT" />
<CCCStmts Correction="X" Complaint="INSPECTION, INSPECT FILTERS AND RECOMMEND, INSPECT BRAKES," />
<CCCStmts Complaint="BELTS AND HOSES" />
<RoAmts DlrCost="18.90" PayType="Cust" AmtType="Job" TotalAmt="59.12" />
</OpCodeLaborInfo>
I need to calculate the sum of the TechInfo/#TechHrs for each OpCodeLaborInfo. I tried the following:
unnest(xpath('sum(//dns:RepairOrder/dns:RoRecord/dns:Rolabor/dns:OpCodeLaborInfo/dns:TechInfo/#TechHrs[1])'::text,
data_detail.ro_data_xml,
ARRAY[ARRAY['dns'::text, 'http://www.starstandards.org/STAR'::text]]))::text::numeric AS lbrsoldhours
but this seems to return the sum of the Tech Hours inside both the OpCodeLaborInfo nodes. Could someone be able to tell me how I can tweak the xpath so as to get the desired result.
So basically I need :
Job
Tech Hrs
1
sum(0.00+0.70)
2
sum(1.00+0.00)
I would solve this using xmltable()
select d.job, sum(t.hours)
from data_detail d
cross join xmltable (
'/OpCodeLaborInfo/TechInfo'
passing d.ro_data_xml
columns hours numeric path '#TechHrs') as t
group by d.job;
Online example
The XPath is probably not correct as the XPath you have shown doesn't match your sample XML data. Your sample XML also doesn't contain a namespace, so I am not sure why you are passing one to xpath()
But if you need one, you can use something like this:
cross join xmltable (
xmlnamespaces ('http://www.starstandards.org/STAR' as dns),
'/dns:OpCodeLaborInfo/dns:TechInfo'
passing d.ro_data_xml
columns hours numeric path '#TechHrs') as t
xpath can also work fine:
SELECT job
, SUM((xpath('//#TechHrs', element))[1]::text::decimal)
FROM data_detail
, LATERAL (SELECT unnest(xpath('/OpCodeLaborInfo/TechInfo', ro_data_xml))) u(element)
GROUP BY job;
fiddle
(based on the fiddle of #a_horse_with_no_name)

XQUERY-SQL Oracle 12C Concatenate repeated tags

new to xquery here.
I'm trying to insert to a table, a list of values from an xml table.
Question: How do I concatenate <record> into 1 db record in 1 column, is that possible?
Currently I'm getting this error with the SQL query below: ORA-19279: XPTY0004 - XQuery dynamic type mismatch: expected singleton sequence - got multi-item sequence
XML:
<namespace>
<tag1>
<tag2>
<records>
<others>
</others>
<others2>
</others2>
<record>
</record>
<record>
</record>
</records>
</tag2>
</tag1>
SQL:
SELECT t.FILENAME,
recsdetail.others,
recsdetail.others2,
recsdetail.record
FROM XMLtable t,
XMLTable(
xmlnamespace('blah:blah' as "foo"),
'foo:tag1' PASSING t.filecontent
COLUMNS "tag2" XMLTYPE PATH '*:tag2/*') rec,
XMLTABLE(
xmlnamespace('blah:blah' as "foo"),
'*/*:records'
PASSING rec."rec2"
COLUMNS
others varchar(10) path '//others'
others2 varchar(10) path '//others2'
record varchar(10) path '//record'
)recsdetail
Working on ORACLE 12C Enterprise

Where to add a separator for mybatis collections(List)

Mapper.xml
<insert id="courseUploads" parameterType="com.technoshinelabs.eduskill.bean.Course" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into
course_uploads
(course_id, training_provider_logo, assignment_material, course_image, trainer_image, created_date, updated_date)
values(
#{courseId},
<foreach item="Course" collection="trainingProviderPath" >
#{Course}
</foreach>,
<foreach item="Course" collection="assignmentMaterialPath">
#{Course}
</foreach>,
#{courseImagePath},
<foreach item="Course" collection="trainerImagePath">
#{Course}
</foreach>,
now(), now()
)
</insert>
How do I add a separator for the above list(item="Course"), this list will have multiple data, each data needs to be separated by some separator, well the data needs to be stored in the database, please do help me on this.
You can use the separator attribute.
e.g.)
<foreach item="Course" collection="foo" separator="||">
#{Course}
</foreach>
Specifically, what of SQL do you want to generate?

Send query to CFWheels selectTag form helper, or list with commas in string elements

I have a query that returns names in <lastnamd>, <firstname> format such as
<cfquery name="instructorSelectList" dataSource="GIRSReport">
SELECT instructor_DBID,
last_name + ', ' + first_name as instructor_name,
hid
FROM instructors
WHERE working_status = 'active'
ORDER BY last_name, first_name
</cfquery>
I want to use this query for a selectTag form helper. If I do:
#selectTag
(
name="inst",
id="program",
options="#ValueList(instructorSelectList.instructor_name)#",
valueField="#ValueLIst(instructorSelectList.instructor_DBID)#",
display="#ValueList(instructorSelectList.instructor_name)#",
selected="",
label="HID",
multiple="no",
includeBlank="true",
size=1,
class="form-control",
prepend="<br/>"
)#
Then I get a list like <lastname1>, <firstname1>, <lastname2>, <firstname2>, ...
which is obviously not what I want.
If I just try to pass the options parameter a query, such as options="#instructorSelectList.instructor_name#", the options don't fill properly.
The idea is to use a form helper equivalent to
<cfselect
name="inst"
query="instructorSelectList"
queryPosition="below"
value="instructor_DBID"
display="instructor_name"
label="HID" size=1
class="form-control">
<option value=""></option>
</cfselect>
I think what you're needing is to pass a query name to Options, without quotes (or in quotes surrounded by hashes) while the column names are quoted.
#selectTag
(
name="inst",
id="program",
options=instructorSelectList,
valueField="instructor_DBID",
textField="instructor_name",
selected="",
label="HID",
multiple="no",
includeBlank="true",
size=1,
class="form-control",
prepend="<br/>"
)#
More Info: CFWheels selectTag() documentation

using INSERT for an xml statement

how would you go about inserting an xml document of information into an existing table, I cannot figure out how the insert statement would work my code is below:
USE MyGuitarShop;
DECLARE #CustomerUpdate XML;
SET #CustomerUpdate = '
<NewCustomers>
<Customer LastName="Chan" FirstName="Isabella" Password="" EmailAddress="izzychan#yahoo.com"/>
<Customer LastName="Prine" FirstName="John" Password="" EmailAddress="johnprine#gmail.com"/>
<Customer LastName="Kitchen" FirstName="Kathy" Password="" EmailAddress="kathykitchen#sbcglobal.net"/>
</NewCustomers>
'
;
INSERT INTO Customers (LastName, Password, EmailAddress)
VALUES (#CustomerUpdate.value('(/NewCustomers/LastName)[1]', 'varchar(50)'),
(#CustomerUpdate.value('(/NewCustomers/FirstName)[1]', 'varchar(50)'),
(#CustomerUpdate.value('(/NewCustomers/Password)[1]', 'varchar(50)'),
(#CustomerUpdate.value('(/NewCustomers/EmailAddress)[1]', 'varchar(50)');
You're selecting the node LastName from NewCustomers, while NewCustomers contains only Customer nodes, which then contain LastName attribute.
In order to select the last name, use the following query instead:
value('(/NewCustomers/Customer/#LastName)[1]', 'varchar(50)')
Since you're extracting the data from a single XML value, the selection is pretty straightforward:
declare #CustomerUpdate xml;
set #CustomerUpdate = '
<NewCustomers>
<Customer LastName="Chan" FirstName="Isabella" Password="" EmailAddress="izzychan#yahoo.com"/>
<Customer LastName="Prine" FirstName="John" Password="" EmailAddress="johnprine#gmail.com"/>
<Customer LastName="Kitchen" FirstName="Kathy" Password="" EmailAddress="kathykitchen#sbcglobal.net"/>
</NewCustomers>';
select
t.Customer.value('#LastName', 'nvarchar(50)') as [LastName],
t.Customer.value('#FirstName', 'nvarchar(50)') as [FirstName],
t.Customer.value('#Password', 'nvarchar(50)') as [Password],
t.Customer.value('#EmailAddress', 'nvarchar(50)') as [EmailAddress]
from #CustomerUpdate.nodes('(/NewCustomers/Customer)') t(Customer)
If you were selecting the data from a row of XML values, you would have used cross apply instead.
Important note: DON'T STORE PASSWORDS IN PLAIN TEXT IN A DATABASE. If you're actually doing it, you do it wrong. If you don't understand why, learn about hash and salt, or, better, let others handle personal information for you: OpenID is one of the ways of moving the responsibility of securing sensitive data from you to Google-scale companies.