Skip to main content

Subtree introspection – UNPIVOT TREE


Description​

The Subtree introspection – UNPIVOT TREE mapping operation (UnpivotTreeOperation) describes a whole data subtree as a flat list of rows: one row for every record in the subtree, including the record the operation is attached to. Each row tells where the record sits, where it comes from (annotations, e.g. its binding to the EDMX data model) and what it holds (fields with values).

It is the extended sibling of the Conversion of fields to many records in a form of Key-Value pairs - UNPIVOT operation (UnpivotRecordOperation), which turns the fields of a single record into key-value pairs.

  • Input – any record in the schema (single or multiple) together with its whole subtree.
  • Output – a new, multiple child record UnpivotTree, added to the starting record.

When to use it​

The operation is useful when the same action needs to be performed on many structures, tables or business cases. Instead of building a separate mapping for each case, you can work on the description of the whole structure as if it were ordinary data — moving one level of abstraction higher.

For example: after a series of operations, e.g. Query to the data model on several related structures, a single operation turns the result into a list of descriptions – structure IDs, table and column names, value sources, field values. Subsequent operations (selection, removal, mapping) then turn this into, e.g., a log payload or key-value data.

Typical use cases:

  • archiving or anonymizing data across many tables at once,
  • generating SQL scripts based on the data structure,
  • analyzing which fields are mapped and where their values come from.

Input data​

Any record in the schema (single or multiple) together with its whole subtree – e.g. a record loaded by the Query to the data model operation, or any other record of the structure.

How to add the operation​

  1. In the schema, select the record where the description should start (e.g. a record loaded by the Query to the data model operation).
  2. From the record's context menu, choose Change/use record → Subtree introspection – UNPIVOT TREE (next to the Unpivot operation).
  3. The operation is created automatically – in the operation list it has an icon of arrows with a small tree.
  4. Optionally, set the Include technical records and fields property (see Operation properties).
tip

The operation describes the state of the data at the point where it is placed in the operation list – records and fields added by later operations are not included in the description.

image

You can further process the operation's result with subsequent mapping operations, e.g. copy, narrow it down to the needed elements, or remove unneeded data.

Example — a data model record and a list of two records through a relation​

The Customer data structure (table dbo.CUSTOMER) has a list relation Addresses to the Address structure (table dbo.ADDRESS). The mapping contains, in order:

#OperationPathNotes
1Add record\Customer
2Add field\Customer\Id
3Resolve field value — Copy value ..\Id\Customer\Idthe query key
4Add record\Customer\Addressesmarker of the relation to read
5Query data model — structure Customer\Customer
6Subtree introspection\Customer

After the query (operation 5) the customer has two addresses. Operation 6 appends UnpivotTree with three rows — one for the customer and one for each address:

Customer (read from the data model)
Id = 7
Name = "ACME"
Addresses (list, relation)
[0] Id = 71, CustomerId = 7, City = "Krakow"
[1] Id = 72, CustomerId = 7, City = "Gdansk"
UnpivotTree (added by the operation — 3 rows)
[0] SchemaPath = "" PathWithListIndices = ""
[1] SchemaPath = "Addresses" PathWithListIndices = "Addresses[0]"
[2] SchemaPath = "Addresses" PathWithListIndices = "Addresses[1]"

Row details (structure IDs are made up; the field list is shortened — in reality every field of the structure appears, empty and audit fields included):

Row [0] — the start record Customer

ColumnValue
SchemaPath / PathWithListIndices(empty — this is the start record itself)
Descriptionthe structure's description from the data model, e.g. Company customers
AnnotationsAEdmxRecUsage — Param1 = 5b1c0d3e-0a4f-4c2b-9e61-2d7f8a9b0c11 (structure ID), Param2 = Customer, Param3 = dbo.CUSTOMER, Param4 = (empty — query root)
FieldsId = 7, SourceType = ACopy, Source = ..\Id; annotations: AEdmxField (Id, business name, CU_ID, dbo.CUSTOMER), AMappingReadOnly
Name = ACME; annotation AEdmxField (Name, business name, CU_NAME, dbo.CUSTOMER)

Rows [1] and [2] — the items of the Addresses list

ColumnRow [1]Row [2]
SchemaPathAddressesAddresses
PathWithListIndicesAddresses[0]Addresses[1]
AnnotationsAEdmxRecUsage — Param1 = ID of the Address structure, Param2 = Address, Param3 = dbo.ADDRESS, Param4 = Addresses (relation name)same
Fields → CityKrakow; AEdmxField (City, …, AD_CITY, dbo.ADDRESS)Gdansk
Fields → CustomerId7; AEdmxField (…, AD_CUSTOMER_ID, dbo.ADDRESS)7

With three addresses there would be four rows; with none — one.

Result structure​

The operation adds a new, multiple child record UnpivotTree to the starting record – one row for every record of the subtree. The starting record is always the first row, followed by the remaining records in schema order, depth-first (parent, then its children).

All resulting fields are text. If the name UnpivotTree is already taken on the starting record, the resulting record gets a unique name with a suffix.

UnpivotTree – subtree record fields

FieldContent
SchemaPaththe path from the starting record, without list indices, e.g. Adresy; empty for the starting record itself
PathWithListIndicesthe same path with the indices of multiple records, e.g. Adresy[1] or Zamowienia[0]\Pozycje[2]
Descriptionthe record's description (the Description annotation)
Technicalthe kind of technical data (the Technical data annotation), if the record is technical
SourceTypethe short name of the annotation that determines the value (ACopy, AConst, AExpression), if present on the record
Sourcethe text of that annotation: the source path (copy), the value (constant), or the expression's content

Fields – the record's fields

FieldContent
Namethe field's name
Valuethe value as text – per the block's conversion policy, the same as in the Unpivot operation
Descriptionthe field's description (the Description annotation)
SourceType, Sourcewhere the field got its value in this mapping – same as for the record (e.g. ACopy / ..\Id)
Technicalthe kind of technical data of the field, if it is technical

Annotations – annotations of a record or a field

FieldContent
TypeNamethe short name of the annotation type, e.g. AEdmxRecUsage, AEdmxField, AKey
FullTypeNamethe full type name (with namespace)
ToStringthe textual form of the annotation
Param1…Param4the "popular" parameters of the annotation – only for the annotations listed below; empty for the others

Annotations that already have their own fields above (Description, Technical data, the value source) are skipped, as are internal annotations of the mapping editor. The full list of annotations is described in Annotations.

Param1–Param4 by annotation type​

All values are text. "–" means the parameter is always empty. The most convenient way to select rows is by TypeName – these are the names given next to the headings below. Next to each heading there is also information on whether the annotation occurs on a record or on a field.

Data model (EDMX)​

Entity model record – AEdmxRecUsage (in the data), AEdmxRec · record

A record linked to a data structure: the root of a query to the data model, a relation (single or list), or a substructure.

ParameterContent
Param1the data structure ID (version GUID); empty for a substructure
Param2the structure name; empty for a substructure
Param3the database table, schema.table; empty for a substructure (it belongs to the parent's table)
Param4the name of the relation or substructure through which the record is reached; empty for the query root

Entity model field – AEdmxField, AEdmxFieldUsage (a query condition field) · field

ParameterContent
Param1the field name in the data model
Param2the field's business name; empty when not defined
Param3the database column
Param4the table that owns the field, schema.table

Write to the data model – AEdmxInsert · record

A record written by the data-model write operation. AEdmxQuery has the same shape, but it is only visible in the mapping editor's preview, not in the data during processing.

ParameterContent
Param1the data structure ID; empty when no structure was selected
Param2the structure name (a hint remembered at selection time)
Param3the database name, if specified
Param4–

Dictionary attribute – ADicAttributeRec · record

The record of a dictionary element's compound attribute, loaded together with the dictionary.

ParameterContent
Param1the attribute type's name
Param2the attribute type's display value
Param3the attribute value's physical type (GUID)
Param4the attribute type's position in the dictionary structure

Database​

Table/column name – ADbObjectName · record or field

The database table name (for a record) or column name (for a field), when it differs from the name in the schema.

ParameterContent
Param1the name in the database (may contain parameters in {…})
Param2a name used only at design time; empty when not given
Param3, Param4–

Database operation – ADbAction · record

What the database write should do with the record.

ParameterContent
Param1the operation kind: None, Skip, Update, Insert, Delete, AUTO_InsertUpdate, AUTO_InsertOnly, Replace, TakeFromParent, Dynamic
Param2the data source name; empty = the database writer block's own data source
Param3the expression that determines the operation (for Dynamic)
Param4the name of the field that receives the marker of the operation performed

Primary key, Foreign key, Autogeneration – APrimaryKey, AForeignKey, AAutoGenerate · field

Plain markers: the field is a primary key, a foreign key, or its value is determined by the database. All parameters are empty – only the presence of a row with the given TypeName matters.

Dictionaries and keys​

Dictionary – ADictionary · field

Mapping of the field's value through a dictionary.

ParameterContent
Param1the dictionary name
Param2the key column
Param3the value column
Param4the reaction to a missing key in the dictionary: TRY_RECOVER, ERROR, EMPTY_DATA, SKIP

Custom keys – AKey · record

ParameterContent
Param1the key field names separated by ;, e.g. Kod;Oddzial
Param2…Param4–

Value constraints​

Default value – ADefaultValue · field

ParameterContent
Param1the default value as text
Param2…Param4–

Range – ARange · field

ParameterContent
Param1the lower bound; empty = no limit
Param2the upper bound; empty = no limit
Param3whether the lower bound is excluded from the range: True / False
Param4whether the upper bound is excluded from the range: True / False

Operation properties​

PropertyDefaultMeaning
Include technical records and fields (ProcessTechnicalRecords)nowhen disabled, technical records (together with their whole subtree, e.g. error records) and technical fields are skipped. When enabled, they are included in the description, and their kind is shown in the Technical column.

Good to know​

  • Field value sources stay in the schema. Having the operation in the mapping means that all value-determining operations in this mapping leave their annotations (copy, constant, expression, parser, dictionary) on the schema's fields – otherwise the SourceType/Source columns would be empty. For very large schemas this means a bit more memory use.
  • Loading relations. The operation traverses all records of the subtree, so data-model relations that have not yet been loaded (lazy mode) will be loaded from the database at this point. For large trees, place the introspection on the narrowest possible record.
  • Only records present in the data. A record defined in the schema but absent from the instance (e.g. an empty list) does not produce a row.
  • The result of another introspection is an ordinary subtree. If this operation already ran earlier on the same subtree, its UnpivotTree will be described like any other record. The operation's own result is not described.

What to do next with the result​

Typical next steps within the same mapping:

  • removing unneeded rows with a condition on SchemaPath, TypeName or Value (e.g. keeping only fields with AEdmxField and a non-empty value),
  • moving UnpivotTree to a record intended for a log or for sending,
  • mapping Name / Value pairs (or Param3 = column / Value) onto the target key-value structure.

Example of use​

Below is a simple example you can reproduce in archITekt Studio: a customer, their orders and order lines — three levels of a tree.

Database tables​

CREATE TABLE Customer (
Id INT PRIMARY KEY,
Name NVARCHAR(100),
Email NVARCHAR(100),
NationalId NVARCHAR(11)
);

CREATE TABLE CustomerOrder (
Id INT PRIMARY KEY,
CustomerId INT REFERENCES Customer(Id),
Number NVARCHAR(20),
OrderDate DATE
);

CREATE TABLE OrderLine (
Id INT PRIMARY KEY,
OrderId INT REFERENCES CustomerOrder(Id),
Product NVARCHAR(100),
Quantity INT
);

INSERT INTO Customer VALUES (1, 'John Smith', 'john@example.com', '0123456789');
INSERT INTO CustomerOrder VALUES (2, 1, 'ORD/2026/003', '2026-10-01');
INSERT INTO OrderLine VALUES (3, 2, 'Monitor', 5), (1, 2, 'Keyboard', 3);

Data structure​

Create a structure with three levels of records:

Customer (annotation: table = Customer)
├─ Id, Name, Email, NationalId
└─ Orders (list) (annotation: table = CustomerOrder)
├─ Id, Number, OrderDate
└─ Lines (list) (annotation: table = OrderLine)
└─ Id, Product, Quantity

Important elements:

  • Annotations with the table name on every record. These are the ones expected to show up in the result as parameters.
  • Additionally, to check how mappings are reported: add a calculated or copied field on the Customer record, e.g. DisplayName as a copy of Name. This lets you see whether the operation reports the copy and its source.
  • Optionally: mark sensitive fields (Email, NationalId) with an annotation, e.g. as personal data. This makes it possible to later filter out the fields to anonymize.

Mapping​

  1. Load the data: the customer with Id = 1 together with their orders and order lines.
  2. Add a Primary key annotation on the Id fields (root record, and the Orders and Lines records) – this lets you verify the APrimaryKey annotation in the result.
  3. Add a Copy value from field operation copying Name to a new DisplayName field – this lets you verify the ACopy annotation.
  4. On the Orders\OrderDate field, add an operation that computes the value with an expression, e.g. = DateTimeNow() – this lets you verify the AExpression annotation.
  5. Finally, add the Subtree introspection – UNPIVOT TREE operation at the level of the whole record (path \).

image

  1. Optionally: add a filter that keeps only the fields annotated as personal data.

What to expect​

The operation creates an UnpivotTree record with a list in which each row corresponds to one record of the subtree (Customer, Orders, Lines), together with a nested Fields list describing that record's fields.

image

The screenshot shows, among other things:

  • SchemaPath indicating the record's path without list indices, e.g. Orders, Orders\Lines,
  • PathWithListIndices – the same path with indices, e.g. Orders[0], Orders[0]\Lines[0], Orders[0]\Lines[1],
  • an APrimaryKey annotation on the Id fields,
  • SourceType = ACopy and Source = Name on the DisplayName field – confirming that the operation does report the copy and its source,
  • SourceType = AExpression and Source = DateTimeNow() on the OrderDate field, indicating that the field's value is computed by an expression.

The Email and NationalId fields from the Customer record are ready material for generating an anonymization script, e.g. UPDATE Customer SET Email = ..., NationalId = ....

Example: anonymizing historical data​

Problem: historical VBP data needs to be anonymized across dozens of business cases and hundreds of tables. Traditionally this would require a very large number of mapping operations, one for each case.

Solution using Subtree introspection – UNPIVOT TREE:

  1. Load the data tree.
  2. Perform the Subtree introspection – UNPIVOT TREE operation. This produces a list of records and fields together with the table names extracted from the annotations.
  3. Use subsequent mapping operations to select the elements you need, e.g. the fields to anonymize and their sources, and discard the rest.
  4. Based on this, run a single SQL script that anonymizes the data across all historical tables.

Effect: a handful of mapping operations and a single SQL script instead of hundreds or thousands of manually created operations.

See also​

Conversion of fields to many records in a form of Key-Value pairs - UNPIVOT

Annotations

Creating a new structure

History changes