Skip to main content
Version: 1.28 (Current)

Creating and Maintaining a Mapping Table

Audience Plant an App Administrators, Developers, and SQL-savvy Implementers

Skill Prerequisites Basic SQL knowledge, familiarity with Plant an App entities, understanding of joins and indexing concepts

Applies To Plant an App applications integrating with external SQL-based systems using shared identifiers

Purpose To provide a practical and reliable approach for creating and maintaining a mapping table that supports efficient and flexible joins between Plant an App entities and external systems


Overview

As discussed in Understanding Join Performance with External Systems, differences in data types between Plant an App and external systems can lead to inefficient joins and inconsistent performance.

A mapping table resolves this by:

  • aligning data types with the external system
  • enabling index-friendly joins
  • providing a stable and predictable execution path

This article focuses on:

  • designing the mapping table
  • populating it from a Plant an App entity
  • maintaining it over time as data changes

Unlike simplified approaches that store a single representative row per identifier, this pattern maintains a complete mapping between external identifiers and all related Plant an App records.


Mapping Table Design

Table Structure

CREATE TABLE dbo.ContactId_Map (
ContactId nchar(12) NOT NULL,
ContactHistoryId int NOT NULL,
CONSTRAINT PK_ContactId_Map
PRIMARY KEY CLUSTERED (ContactId, ContactHistoryId)
);

Key Design Points

  • ContactId uses nchar(12) to match the external system exactly
  • ContactHistoryId references the Plant an App entity record
  • The table contains one row per ContactHistory record
  • The composite primary key enforces uniqueness and supports efficient joins

Indexing Strategy

  • Clustered Primary Key on (ContactId, ContactHistoryId)
  • Optional: nonclustered index on ContactHistoryId if reverse lookups are required

Data Type Alignment

It is critical that:

  • the mapping table uses the same data type and length as the external system
  • joins between mapping and external tables remain fully sargable

Initial Population from ContactHistory

The mapping table should include every ContactHistory record, not a representative subset.

Insert All Mappings

INSERT INTO dbo.ContactId_Map (ContactId, ContactHistoryId)
SELECT
CAST(ch.ContactId AS nchar(12)),
ch.Id
FROM app.ContactHistory ch
WHERE ch.ContactId IS NOT NULL;

Notes

  • Each ContactHistory row produces one mapping row
  • CAST ensures alignment with nchar(12)
  • No aggregation is performed—this preserves the full relationship

To keep the mapping table synchronized with the source data, use a rebuild-and-sync pattern.

This ensures the table reflects all inserts, updates, and deletes without complex per-row logic.

Step 1: Rebuild a Staging Set

SELECT
CAST(ch.ContactId AS nchar(12)) AS ContactId,
ch.Id AS ContactHistoryId
INTO #ContactId_Stage
FROM app.ContactHistory ch
WHERE ch.ContactId IS NOT NULL;

Step 2: Synchronize Mapping Table

MERGE dbo.ContactId_Map AS target
USING #ContactId_Stage AS source
ON target.ContactId = source.ContactId
AND target.ContactHistoryId = source.ContactHistoryId

WHEN NOT MATCHED BY TARGET THEN
INSERT (ContactId, ContactHistoryId)
VALUES (source.ContactId, source.ContactHistoryId)

WHEN NOT MATCHED BY SOURCE THEN
DELETE;

Why This Pattern Works

  • Maintains a complete one-to-many mapping
  • Handles inserts and deletes in a single operation
  • Avoids drift over time
  • Keeps logic simple and predictable

Automation Strategy

The synchronization process should be executed automatically.

General Approach

Run the synchronization as a batch operation when data changes occur, such as:

  • inserts into app.ContactHistory
  • updates to ContactId
  • deletes of records

Implementation Guidance

  • Trigger via scheduled job or workflow
  • Avoid per-row execution; use batch processing
  • Run frequently enough to keep mappings current

This ensures:

  • minimal impact on user operations
  • consistent performance
  • reliable alignment between systems

Performance Considerations

Expected Size

  • One row per ContactHistory record
  • Similar in size to the source table

Efficiency

  • Narrow fixed-length key (nchar(12)) supports efficient indexing
  • Joins through the mapping table remain predictable and scalable

Stability

  • Eliminates implicit conversions in join predicates
  • Produces consistent execution plans across workloads

Validation and Troubleshooting

Verify Population

SELECT COUNT(*) FROM app.ContactHistory WHERE ContactId IS NOT NULL;
SELECT COUNT(*) FROM dbo.ContactId_Map;

Counts should closely align.

Identify Missing Entries

SELECT ch.Id, ch.ContactId
FROM app.ContactHistory ch
LEFT JOIN dbo.ContactId_Map m
ON ch.Id = m.ContactHistoryId
WHERE ch.ContactId IS NOT NULL
AND m.ContactHistoryId IS NULL;

Detect Duplicates (Should Not Exist)

SELECT ContactId, ContactHistoryId, COUNT(*)
FROM dbo.ContactId_Map
GROUP BY ContactId, ContactHistoryId
HAVING COUNT(*) > 1;

Example End-to-End Flow

  1. A ContactHistory record is created, updated, or deleted
  2. The synchronization job runs
  3. The mapping table is rebuilt and synchronized
  4. Queries join through ContactId_Map
  5. External data is retrieved efficiently and consistently

Summary

A mapping table provides a reliable way to maintain performant joins between Plant an App entities and external systems.

By:

  • storing a complete mapping of all related records
  • aligning data types with the external system
  • synchronizing using a rebuild-and-sync pattern

you ensure:

  • correct one-to-many relationships
  • predictable query performance
  • scalability as data volumes grow

Revision Date: 2026-03-23