This is an automated email from the ASF dual-hosted git repository.

bamaer pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new 4125834c3d Issue #8803 : Document high-volume insert-heavy SCD2 loads 
(#8807)
4125834c3d is described below

commit 4125834c3df7f2815cf90101e73e33fe4acd9eb5
Author: Matt Casters <[email protected]>
AuthorDate: Fri Oct 9 16:28:52 2026 +0200

    Issue #8803 : Document high-volume insert-heavy SCD2 loads (#8807)
    
    * Issue #8803 : Document high-volume insert-heavy SCD2 loads
    
    Add a how-to for building a type 2 dimension in one sorted pass, and link 
it from the dimension, merge, and repeat-fields pages.
    
    * Issue #8803 : Describe the incremental SCD2 update keys
    
    Look up the stored version on business_key and from_ts, and update to_ts 
when the end date is not 9999-12-31.
    
    * Issue #8803 : Set SCD2 from_ts to the change timestamp and insert only 
delta rows
---
 .../high-volume-scd2/analytic-query.png            | Bin 0 -> 12057 bytes
 .../how-to-guides/high-volume-scd2/group-by.png    | Bin 0 -> 14214 bytes
 .../how-to-guides/high-volume-scd2/if-null.png     | Bin 0 -> 13276 bytes
 .../high-volume-scd2/initial-load-pipeline.png     | Bin 0 -> 65382 bytes
 docs/hop-user-manual/modules/ROOT/nav.adoc         |   1 +
 .../ROOT/pages/getting-started/hop-next-steps.adoc |   1 +
 .../pages/how-to-guides/change-data-capture.adoc   |   3 +-
 .../ROOT/pages/how-to-guides/high-volume-scd2.adoc | 291 +++++++++++++++++++++
 .../modules/ROOT/pages/how-to-guides/index.adoc    |   1 +
 .../ROOT/pages/how-to-guides/joins-lookups.adoc    |   2 +
 .../pages/pipeline/transforms/dimensionlookup.adoc |   2 +
 .../pages/pipeline/transforms/repeatfields.adoc    |   1 +
 .../pipeline/transforms/sortedschemamerge.adoc     |   1 +
 13 files changed, 302 insertions(+), 1 deletion(-)

diff --git 
a/docs/hop-user-manual/modules/ROOT/assets/images/how-to-guides/high-volume-scd2/analytic-query.png
 
b/docs/hop-user-manual/modules/ROOT/assets/images/how-to-guides/high-volume-scd2/analytic-query.png
new file mode 100644
index 0000000000..09b2121964
Binary files /dev/null and 
b/docs/hop-user-manual/modules/ROOT/assets/images/how-to-guides/high-volume-scd2/analytic-query.png
 differ
diff --git 
a/docs/hop-user-manual/modules/ROOT/assets/images/how-to-guides/high-volume-scd2/group-by.png
 
b/docs/hop-user-manual/modules/ROOT/assets/images/how-to-guides/high-volume-scd2/group-by.png
new file mode 100644
index 0000000000..2e13f25664
Binary files /dev/null and 
b/docs/hop-user-manual/modules/ROOT/assets/images/how-to-guides/high-volume-scd2/group-by.png
 differ
diff --git 
a/docs/hop-user-manual/modules/ROOT/assets/images/how-to-guides/high-volume-scd2/if-null.png
 
b/docs/hop-user-manual/modules/ROOT/assets/images/how-to-guides/high-volume-scd2/if-null.png
new file mode 100644
index 0000000000..6f14ad9442
Binary files /dev/null and 
b/docs/hop-user-manual/modules/ROOT/assets/images/how-to-guides/high-volume-scd2/if-null.png
 differ
diff --git 
a/docs/hop-user-manual/modules/ROOT/assets/images/how-to-guides/high-volume-scd2/initial-load-pipeline.png
 
b/docs/hop-user-manual/modules/ROOT/assets/images/how-to-guides/high-volume-scd2/initial-load-pipeline.png
new file mode 100644
index 0000000000..69abf84fca
Binary files /dev/null and 
b/docs/hop-user-manual/modules/ROOT/assets/images/how-to-guides/high-volume-scd2/initial-load-pipeline.png
 differ
diff --git a/docs/hop-user-manual/modules/ROOT/nav.adoc 
b/docs/hop-user-manual/modules/ROOT/nav.adoc
index 39988ce0b9..210564cb43 100644
--- a/docs/hop-user-manual/modules/ROOT/nav.adoc
+++ b/docs/hop-user-manual/modules/ROOT/nav.adoc
@@ -626,6 +626,7 @@ under the License.
 *** xref:how-to-guides/cdc-filtered-selection.adoc[CDC: filtered selection]
 *** xref:how-to-guides/cdc-snapshot-comparison.adoc[CDC: comparing snapshots]
 ** xref:how-to-guides/joins-lookups.adoc[Joins and lookups]
+** xref:how-to-guides/high-volume-scd2.adoc[High-volume SCD2 loads]
 ** xref:how-to-guides/logging-pipeline-log.adoc[Logging pipeline data with 
pipeline log]
 ** xref:how-to-guides/logging-workflow-log.adoc[Logging workflow data with 
workflow log]
 ** xref:how-to-guides/loops-in-apache-hop.adoc[Loops in Apache Hop]
diff --git 
a/docs/hop-user-manual/modules/ROOT/pages/getting-started/hop-next-steps.adoc 
b/docs/hop-user-manual/modules/ROOT/pages/getting-started/hop-next-steps.adoc
index 86b1bd3489..751689af31 100644
--- 
a/docs/hop-user-manual/modules/ROOT/pages/getting-started/hop-next-steps.adoc
+++ 
b/docs/hop-user-manual/modules/ROOT/pages/getting-started/hop-next-steps.adoc
@@ -45,6 +45,7 @@ Where you go next depends on what you're trying to do.
 * xref:database/databases.adoc[Databases] - relational connections and the 
drivers Hop ships
 * xref:vfs.adoc[VFS] - read and write on AWS, Azure and GCP as if they were 
local files
 * xref:how-to-guides/change-data-capture.adoc[Change Data Capture] - log 
sniffing, filtered selection and snapshot comparison
+* xref:how-to-guides/high-volume-scd2.adoc[High-volume SCD2 loads] - a type 2 
dimension built in one sorted pass
 * xref:metadata-types/index.adoc[Metadata types] - everything you can define 
once and reuse
 
 == Run it somewhere else
diff --git 
a/docs/hop-user-manual/modules/ROOT/pages/how-to-guides/change-data-capture.adoc
 
b/docs/hop-user-manual/modules/ROOT/pages/how-to-guides/change-data-capture.adoc
index 3818d7901f..37a2512511 100644
--- 
a/docs/hop-user-manual/modules/ROOT/pages/how-to-guides/change-data-capture.adoc
+++ 
b/docs/hop-user-manual/modules/ROOT/pages/how-to-guides/change-data-capture.adoc
@@ -78,7 +78,7 @@ All three approaches end the same way: you have rows that 
should be inserted, up
 * xref:pipeline/transforms/insertupdate.adoc[Insert / Update] for upserts when 
deletes are out of scope
 * xref:pipeline/transforms/delete.adoc[Delete] when you have keys that 
disappeared
 * xref:pipeline/transforms/synchronizeaftermerge.adoc[Synchronize after merge] 
when Merge Rows already produced an `identical` / `changed` / `new` / `deleted` 
flag
-* xref:pipeline/transforms/dimensionlookup.adoc[Dimension lookup/update] when 
the *target* is a slowly changing dimension
+* xref:pipeline/transforms/dimensionlookup.adoc[Dimension lookup/update] when 
the *target* is a slowly changing dimension. An insert-heavy type 2 load is 
described in xref:how-to-guides/high-volume-scd2.adoc[High-volume SCD2 loads].
 
 NOTE: Slowly changing dimensions are a *target* pattern. They do not detect 
source changes. Pair them with one of the three CDC approaches above.
 
@@ -88,4 +88,5 @@ NOTE: Slowly changing dimensions are a *target* pattern. They 
do not detect sour
 * xref:how-to-guides/cdc-filtered-selection.adoc[CDC: filtered selection]
 * xref:how-to-guides/cdc-snapshot-comparison.adoc[CDC: comparing snapshots]
 * xref:how-to-guides/joins-lookups.adoc[Joins and lookups] — Merge Rows (diff) 
in the broader join landscape
+* xref:how-to-guides/high-volume-scd2.adoc[High-volume SCD2 loads] — a type 2 
dimension built in one sorted pass
 * xref:metadata-types/execution-information-location.adoc[Execution 
Information Location]
diff --git 
a/docs/hop-user-manual/modules/ROOT/pages/how-to-guides/high-volume-scd2.adoc 
b/docs/hop-user-manual/modules/ROOT/pages/how-to-guides/high-volume-scd2.adoc
new file mode 100644
index 0000000000..6331281f5c
--- /dev/null
+++ 
b/docs/hop-user-manual/modules/ROOT/pages/how-to-guides/high-volume-scd2.adoc
@@ -0,0 +1,291 @@
+////
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+  http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+////
+[[HighVolumeScd2]]
+:imagesdir: ../../assets/images
+:description: Build a type 2 slowly changing dimension in one sorted pass when 
a lookup per row is too slow.
+
+= High-volume SCD2 loads
+
+xref:pipeline/transforms/dimensionlookup.adoc[Dimension lookup/update] 
implements Ralph Kimball's slowly changing dimension, including a mix of type 1 
and type 2 columns, and it can look up the version that was valid at a given 
time. It does that lookup in the target table for every input row. The cache on 
that transform removes some of the round trips. On a clustered database, or a 
remote one such as Snowflake, the lookups that remain still dominate the 
runtime.
+
+This guide keeps the same functional result for a type 2 dimension: every 
change to a business topic (customers, products, and so on) is still visible. 
The pipeline does the work in one sorted pass and spends its time inserting 
rows. A run holds only the changes of the current business key in memory and 
handles hundreds of thousands of rows per second.
+
+The pattern below runs on the Hop engine. Sorted schema merge, Repeat fields, 
Analytic Query, and Group By follow the row order of their input. Use a local 
or single-threaded run configuration. With several copies, partition by the 
business key so one key stays on one copy. See the engine notes on 
xref:pipeline/transforms/groupby.adoc[Group By] and 
xref:pipeline/transforms/repeatfields.adoc[Repeat fields].
+
+== Read every source in the same order
+
+Read one or more sources that share a business key and a change timestamp. 
Give those columns the same names on every stream. This guide uses 
`business_key` and `changed_date_ts`.
+
+Ask the source database to sort. In 
xref:pipeline/transforms/tableinput.adoc[Table Input] that is an `ORDER BY 
business_key, changed_date_ts`. When the source is a file or another 
non-database input, sort it in the pipeline with 
xref:pipeline/transforms/sort.adoc[Sort Rows].
+
+On an incremental load, include the current target row only for a business key 
that appears in the delta, as one more source. A key with no new change is not 
read and not written. The current row carries the attributes already stored, so 
a later change can close its `to_ts`. Read that row with the stored `from_ts` 
as `changed_date_ts`, and set `record_source` to a value no delta source uses, 
for example `target`. The rebuilt `from_ts` is that same timestamp, so an 
update on `business_key [...]
+
+[WARNING]
+====
+Sort every source with the same collation. A database `ORDER BY` and a Hop 
sort that disagree on case, accents, or trailing spaces split one business key 
across the merge. Repeated attributes and date ranges are then wrong.
+====
+
+== Tag the source and align the timestamp
+
+On each stream, add a constant that identifies where the row came from. This 
guide calls that field `record_source`. Use 
xref:pipeline/transforms/addconstant.adoc[Add constants].
+
+With xref:pipeline/transforms/selectvalues.adoc[Select values], keep 
`business_key`, `changed_date_ts`, `record_source`, and only the attributes 
that this source owns. Leave every other attribute off the stream. The merge in 
the next step can then tell the streams apart by which columns are filled in.
+
+Rename the source's own change timestamp to `changed_date_ts` here when the 
upstream column has another name.
+
+== Merge to one sparse stream
+
+Combine the streams with 
xref:pipeline/transforms/sortedschemamerge.adoc[Sorted schema merge], sorted on 
`business_key` and `changed_date_ts`. Both keys have to exist on every input.
+
+The output is one stream in that order. It is a sparse matrix: a row carries 
values only for the source it came from, and the attributes of the other 
sources are null. On a first load, every source row is a change, so the stream 
contains those full extracts. On a later load it contains only the changes you 
read, plus the current target row for a business key that appears in those 
changes.
+
+== Repeat the last known attributes
+
+Fill the nulls from the previous row of the same business key with 
xref:pipeline/transforms/repeatfields.adoc[Repeat fields]. Group on 
`business_key`. For each attribute, use the repeat type *Current when 
indicated*: take the current value when `record_source` matches the source that 
owns the attribute, and otherwise keep the value already repeated from the 
previous row.
+
+The 
xref:pipeline/transforms/repeatfields.adoc#_current_when_indicated[customer 
example on the Repeat fields page] shows the same idea with three sources. 
After this transform every row is a complete version, still sorted by 
`business_key` and `changed_date_ts`.
+
+A second xref:pipeline/transforms/selectvalues.adoc[Select values] is a 
convenient place to drop helper columns and keep the keys, the timestamps, and 
the attributes you want on the dimension.
+
+== Calculate the validity range
+
+Each version needs a date range. `from_ts` is the row's own `changed_date_ts`, 
not the timestamp of the previous row. Copy `changed_date_ts` to `from_ts` in 
the Select values transform in front of this step: select `changed_date_ts` a 
second time and rename that copy to `from_ts`. See 
xref:pipeline/transforms/selectvalues.adoc#_duplicating_fields[Duplicating 
fields].
+
+`to_ts` is the next row's `changed_date_ts`. Add it with 
xref:pipeline/transforms/analyticquery.adoc[Analytic Query]. Group by 
`business_key`. The stream is already sorted by `business_key` and 
`changed_date_ts`, which is the order the transform requires.
+
+image:how-to-guides/high-volume-scd2/analytic-query.png[Analytic Query: to_ts 
leads changed_date_ts by one row, width="100%"]
+
+[cols="1,2,3,1",options="header"]
+|===
+|New field
+|Subject
+|Type
+|N
+
+|`to_ts`
+|`changed_date_ts`
+|Lead 1 row forward
+|1
+|===
+
+Inside one business key, `from_ts` is that row's `changed_date_ts` and `to_ts` 
is the change timestamp of the next row. The last row has a null `to_ts`. The 
first row does not: its `from_ts` is its own change timestamp.
+
+Take three versions of customer 1 after Repeat fields:
+
+[cols="2,2,2",options="header"]
+|===
+|changed_date_ts
+|city
+|status
+
+|2024-01-01 00:00:00
+|Gent
+|null
+
+|2024-06-01 00:00:00
+|Gent
+|active
+
+|2025-02-01 00:00:00
+|Brussels
+|active
+|===
+
+After that copy and the analytic query, the rows are:
+
+[cols="2,2,2,2,2",options="header"]
+|===
+|changed_date_ts
+|city
+|status
+|from_ts
+|to_ts
+
+|2024-01-01 00:00:00
+|Gent
+|null
+|2024-01-01 00:00:00
+|2024-06-01 00:00:00
+
+|2024-06-01 00:00:00
+|Gent
+|active
+|2024-06-01 00:00:00
+|2025-02-01 00:00:00
+
+|2025-02-01 00:00:00
+|Brussels
+|active
+|2025-02-01 00:00:00
+|null
+|===
+
+Neighboring ranges meet and do not overlap. A version starts at its own 
`changed_date_ts` and ends at the next one. A fact timestamp matches one row 
when it is greater than or equal to `from_ts` and strictly less than `to_ts`. 
An inclusive end matches two rows when the fact timestamp equals a change 
timestamp. A timestamp later than the latest `changed_date_ts` matches only the 
open row.
+
+== Close the open ends
+
+Replace the null ends with the start and end dates of your dimension. 
xref:pipeline/transforms/ifnull.adoc[If null] does that per field. The example 
uses the usual open-ended sentinels, with conversion mask `yyyy-MM-dd HH:mm:ss`:
+
+image:how-to-guides/high-volume-scd2/if-null.png[If null replaces a null 
from_ts with 1900-01-01 and a null to_ts with 9999-12-31, width="100%"]
+
+[cols="2,2,2",options="header"]
+|===
+|Field
+|Replace by value
+|Conversion mask
+
+|`from_ts`
+|1900-01-01 00:00:00
+|yyyy-MM-dd HH:mm:ss
+
+|`to_ts`
+|9999-12-31 23:59:59
+|yyyy-MM-dd HH:mm:ss
+|===
+
+Pick the sentinels your warehouse already uses. `from_ts` is the row's own 
`changed_date_ts`, so the first customer version above stays at 2024-01-01. 
That value is not null, and If null does not replace it with 1900-01-01. The 
1900-01-01 replacement applies only when `from_ts` itself is null. The last 
version has a null `to_ts`, which becomes 9999-12-31.
+
+== Collapse identical versions
+
+Sources sometimes emit a new row whose stored attributes did not change. That 
happens when you read part of a Data Vault satellite and another attribute on 
that satellite moved. Consecutive rows then differ only by timestamp and 
`record_source`.
+
+Compress those rows with xref:pipeline/transforms/groupby.adoc[Group By]. The 
stream is still sorted, so the transform only compares a row with the previous 
one to see if the group continues. Group on every field except `from_ts`, 
`to_ts`, `record_source`, and `changed_date_ts`. That includes `business_key` 
and the attributes.
+
+image:how-to-guides/high-volume-scd2/group-by.png[Group By aggregates for 
collapsing identical SCD2 versions, width="100%"]
+
+[cols="2,2,3",options="header"]
+|===
+|Name
+|Subject
+|Type
+
+|`from_ts`
+|`from_ts`
+|Minimum
+
+|`to_ts`
+|`to_ts`
+|Maximum
+
+|`record_source`
+|`record_source`
+|Concatenate distinct values separated by the separator in the Value column
+
+|`changed_date_ts`
+|`changed_date_ts`
+|Maximum
+|===
+
+The Value column of the `record_source` aggregate is the separator between 
source names. The figure is cropped before that column. One output row covers 
the whole run of identical attributes: the earliest `from_ts`, the latest 
`to_ts`, every source that contributed, and the latest `changed_date_ts`.
+
+Skip this transform when every source change is a change you want to keep as 
its own version.
+
+== Write the dimension
+
+=== Initial load
+
+On a first load, bulk load the stream into the target table. The example at 
the end of this page uses xref:pipeline/transforms/tableoutput.adoc[Table 
Output]. Use the database's own bulk loader when you have one and it accepts 
this row layout.
+
+=== Incremental load
+
+Read the current target rows for the delta's business keys only, as described 
above. After the timeline is rebuilt, insert a row only when it did not come 
from the target, and update a stored row only when a later change closed it.
+
+Filter the insert on `record_source`. Insert when that value does not contain 
the target source, `target` in this guide. That is a new version: the open row, 
whose `to_ts` is still `9999-12-31`, and a version whose `to_ts` is already 
closed because a later change arrived in the same run. Do not insert a row 
whose `record_source` contains `target`. That row is the stored current 
version, including one that Group By collapsed a delta into because the 
attributes did not change. Inserting it [...]
+
+Update the stored row in place with 
xref:pipeline/transforms/update.adoc[Update]:
+
+* The lookup keys are `business_key` and `from_ts`. Together they identify the 
version that is already stored. The rebuilt `from_ts` matches because the 
target row was read with the stored `from_ts` as `changed_date_ts`.
+* The field to update is `to_ts`.
+* Send a row to this update only when its `record_source` contains `target` 
and its end date is not `9999-12-31`. That is a stored version whose open end 
was closed because a later change arrived.
+
+A row that contains `target` and still ends at `9999-12-31` is unchanged. Do 
not insert it and do not update it.
+
+When that update is slow and the database can merge a staged set, use 
xref:pipeline/transforms/filterrows.adoc[Filter rows] to set aside the closed 
target rows, write them to a separate file, and merge them. The rows that do 
not contain `target` are still bulk loaded.
+
+== Example: a customer dimension from four satellites
+
+The pipeline below is an initial load of a customer 360 dimension from four 
Data Vault satellites. Each branch reads one satellite, stamps `record_source`, 
and selects the columns that satellite contributes.
+
+image:how-to-guides/high-volume-scd2/initial-load-pipeline.png[Initial load of 
a customer SCD2 dimension from four satellites, width="100%"]
+
+[cols="1,2",options="header"]
+|===
+|Transform
+|Role
+
+|`read_sat_customer_demo`, `read_sat_customer_contact`, 
`read_sat_customer_address`, `read_sat_customer_prefs`
+|Table Input. Each query is sorted by the shared business key and change 
timestamp.
+
+|`source_sat_customer_*`
+|Add constants. Sets `record_source` for that satellite.
+
+|`select_sat_customer_*`
+|Select values. Keeps the key, the shared `changed_date_ts`, `record_source`, 
and the attributes of that satellite.
+
+|`merge_sorted`
+|Sorted schema merge on the business key and `changed_date_ts`.
+
+|`repeat_sparse`
+|Repeat fields, *Current when indicated*, grouped by the business key.
+
+|`select_repeated`
+|Select values. Columns that continue to the dimension. Copies 
`changed_date_ts` to `from_ts`.
+
+|`analytic_customer_360_bv`
+|Analytic Query. Sets `to_ts` to the next `changed_date_ts`, as in the figure 
above.
+
+|`sentinels`
+|If null. Replaces a null `to_ts` with the end sentinel. A null `from_ts` 
would become the start sentinel, which the first version does not need.
+
+|`collapse_customer_360_bv`
+|Group By. Collapses consecutive versions whose attributes are identical.
+
+|`write_to_customer_360_bv`
+|Table Output. Bulk loads the first version of the dimension.
+|===
+
+An incremental run of the same dimension reads the current target row only for 
business keys that appear in the delta. That input sets `record_source` to 
`target` and exposes the stored `from_ts` as `changed_date_ts`. The run 
bulk-loads rows whose `record_source` does not contain `target`, and updates 
`to_ts` on the stored version matched by `business_key` and `from_ts` when that 
version came from the target and the new end date is not `9999-12-31`. A 
current row that is still open is no [...]
+
+== Link a fact to the SCD2 row and to a current SCD1
+
+A fact can carry two references:
+
+* an SCD2 version that contains the fact timestamp, `fact_ts >= from_ts` and 
`fact_ts < to_ts`, so the fact stays attached to one version
+* an SCD1 dimension that has only the current attributes
+
+Reports that follow history use the SCD2 reference. Reports that want the 
name, status, or address as it is today use the SCD1 reference. The cost is one 
extra column on the fact and one extra object to maintain.
+
+That SCD1 object is often a view over the SCD2 table. `now()` stands in for 
the current-timestamp function of the database that owns the view. The end is 
exclusive, so a timestamp that falls on a change matches the version that 
starts there and not the one that ends there:
+
+[source,sql]
+----
+SELECT *
+FROM customer_scd2
+WHERE from_ts <= now() AND now() < to_ts
+----
+
+== See also
+
+* xref:pipeline/transforms/dimensionlookup.adoc[Dimension lookup/update]
+* xref:how-to-guides/joins-lookups.adoc[Joins and lookups]
+* xref:how-to-guides/change-data-capture.adoc[Change Data Capture]
+* xref:pipeline/transforms/sortedschemamerge.adoc[Sorted schema merge]
+* xref:pipeline/transforms/repeatfields.adoc[Repeat fields]
+* xref:pipeline/transforms/analyticquery.adoc[Analytic Query]
+* xref:pipeline/transforms/ifnull.adoc[If null]
+* xref:pipeline/transforms/groupby.adoc[Group By]
+* xref:pipeline/transforms/update.adoc[Update]
diff --git a/docs/hop-user-manual/modules/ROOT/pages/how-to-guides/index.adoc 
b/docs/hop-user-manual/modules/ROOT/pages/how-to-guides/index.adoc
index 16d25f224b..5a190a80fc 100644
--- a/docs/hop-user-manual/modules/ROOT/pages/how-to-guides/index.adoc
+++ b/docs/hop-user-manual/modules/ROOT/pages/how-to-guides/index.adoc
@@ -27,6 +27,7 @@ This page contains a collection of how-to guides to perform a 
variety of tasks,
 ** xref:how-to-guides/cdc-filtered-selection.adoc[CDC: filtered selection]
 ** xref:how-to-guides/cdc-snapshot-comparison.adoc[CDC: comparing snapshots]
 * xref:how-to-guides/joins-lookups.adoc[Joins and lookups in Apache Hop]
+* xref:how-to-guides/high-volume-scd2.adoc[High-volume SCD2 loads]
 * xref:how-to-guides/loops-in-apache-hop.adoc[Loops in Apache Hop]
 * xref:how-to-guides/hdfs-parquet-openshift.adoc[Write Parquet to Cloudera 
HDFS from OpenShift]
 * xref:how-to-guides/scheduling-workflows-and-pipelines.adoc[Scheduling 
workflows and pipelines]
diff --git 
a/docs/hop-user-manual/modules/ROOT/pages/how-to-guides/joins-lookups.adoc 
b/docs/hop-user-manual/modules/ROOT/pages/how-to-guides/joins-lookups.adoc
index af978013b0..a5cc7a3b0a 100644
--- a/docs/hop-user-manual/modules/ROOT/pages/how-to-guides/joins-lookups.adoc
+++ b/docs/hop-user-manual/modules/ROOT/pages/how-to-guides/joins-lookups.adoc
@@ -160,3 +160,5 @@ The dialog options vary slightly, but the caching options 
offered by these trans
 * load all data from table (database lookup only): load all the table's data 
to memory before the pipeline starts.
 
 Consider your available memory resources when configuring caching. Caching can 
significantly speed up the lookup process, but memory is a finite resource.
+
+For a type 2 dimension that is too large for a lookup per row, see 
xref:how-to-guides/high-volume-scd2.adoc[High-volume SCD2 loads].
diff --git 
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/dimensionlookup.adoc
 
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/dimensionlookup.adoc
index 8cdeb00056..3e60426c7c 100644
--- 
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/dimensionlookup.adoc
+++ 
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/dimensionlookup.adoc
@@ -27,6 +27,8 @@ The Dimension Lookup/Update transform allows you to implement 
Ralph Kimball's ht
 
 This transform can be used to populate a dimension table or to look up values 
in a slowly changing dimension without making any changes.
 
+When a type 2 load is large enough that one lookup per row is too slow, build 
the timeline in a single sorted pass. See 
xref:how-to-guides/high-volume-scd2.adoc[High-volume SCD2 loads].
+
 == Options
 
 === Common fields
diff --git 
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/repeatfields.adoc 
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/repeatfields.adoc
index d2f42ee7e3..c1cd914633 100644
--- 
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/repeatfields.adoc
+++ 
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/repeatfields.adoc
@@ -103,6 +103,7 @@ source field as an indicator. We want to end up with this 
result:
 |===
 
 These 3 rows can then be used to create a detailed and correct timeline using 
the xref:pipeline/transforms/dimensionlookup.adoc[Dimension Lookup/Update] 
transform.
+The same rows are the input of the insert-heavy path in 
xref:how-to-guides/high-volume-scd2.adoc[High-volume SCD2 loads].
 
 TIP: Add the last record from your target slowly changing dimension to modify 
existing records.
 
diff --git 
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/sortedschemamerge.adoc
 
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/sortedschemamerge.adoc
index df929f7733..a16c349860 100644
--- 
a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/sortedschemamerge.adoc
+++ 
b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/sortedschemamerge.adoc
@@ -30,6 +30,7 @@ The Sorted schema merge transform combines the behavior of 
xref:pipeline/transfo
 * Fields that are not present on a given input stream are left *null* (sparse 
row layout) on rows that come from that stream.
 
 A typical use case is combining data from multiple Data Vault satellites that 
share business keys and load timestamps, then using 
xref:pipeline/transforms/repeatfields.adoc[Repeat fields] to fill previously 
known attribute values when building an SCD2 table.
+The steps around that merge are described in 
xref:how-to-guides/high-volume-scd2.adoc[High-volume SCD2 loads].
 
 IMPORTANT: Each input stream must already be sorted on the configured sort 
keys (for example with `ORDER BY` in 
xref:pipeline/transforms/tableinput.adoc[Table Input], or with 
xref:pipeline/transforms/sort.adoc[Sort Rows]). This transform does *not* 
re-sort within each stream; it only merges already-sorted streams.
 

Reply via email to