Skip to main content
Loading lesson map...

Biml Basics for Relational Databases

Why Model Databases in Biml?​

Manually creating staging tables is tedious and error-prone:

  • A 50-table source system requires 50 CREATE TABLE statements
  • Column changes in the source require manual updates everywhere
  • Inconsistent naming conventions creep in across tables
  • Deploying schema changes across Dev/Test/Prod is manual work

Biml solves this with metadata-driven table generation:

  • Import source schema once, generate all staging tables automatically
  • Add audit columns (LoadDate, SourceSystem) to every table with one template
  • Regenerate DDL instantly when source schemas change
  • Deploy consistent schemas across all environments

Time savings: A 100-table staging layer that takes days to build manually can be generated in minutes with Biml.

Quick Start: Import an Existing Database​

This example imports tables from a SQL Server database and creates staging table definitions with audit columns:

<#@ template language="C#" tier="10" #>
<#@ import namespace="Varigence.Biml.CoreLowerer.SchemaManagement" #>
<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<Connections>
<OleDbConnection Name="Source"
ConnectionString="Provider=SQLNCLI11;Server=localhost;
Initial Catalog=AdventureWorks;Integrated Security=SSPI;" />
</Connections>

<#
var importResult = ExternalDataAccess.GetDatabaseSchema(
RootNode.Connections["Source"],
new[] { "Sales", "Production" }, // Schemas to import
null // All tables
);
#>

<Databases>
<Database Name="Staging" ConnectionName="Source" />
</Databases>

<Tables>
<# foreach (var table in importResult.TableNodes) { #>
<Table Name="STG_<#=table.Name#>" SchemaName="Staging.[default]">
<Columns>
<# foreach (var col in table.Columns) { #>
<Column Name="<#=col.Name#>"
DataType="<#=col.DataType#>"
Length="<#=col.Length#>"
IsNullable="true" />
<# } #>
<!-- Audit columns added to every table -->
<Column Name="ETL_LoadDateTime" DataType="DateTime" />
<Column Name="ETL_SourceSystem" DataType="String" Length="50" />
</Columns>
</Table>
<# } #>
</Tables>
</Biml>

Result: One build creates staging table definitions for all imported tables, each with standardized audit columns.

Core Concepts​

Connections​

Define database connections to use throughout your Biml project:

<Connections>
<OleDbConnection Name="SourceDB"
ConnectionString="Provider=SQLNCLI11;Server=localhost;
Initial Catalog=AdventureWorks;Integrated Security=SSPI;" />
</Connections>

Common Connection Types:

TypeUse Case
OleDbConnectionSQL Server, most databases (recommended for SSIS)
AdoNetConnection.NET data providers
OdbcConnectionODBC drivers
OracleConnectionOracle (uses Attunity in SSIS)
TeradataConnectionTeradata (uses Attunity in SSIS)

Databases and Schemas​

Model your target database structure:

<Databases>
<Database Name="Staging" ConnectionName="SourceDB" />
</Databases>

<Schemas>
<Schema Name="stg" DatabaseName="Staging" />
</Schemas>

Every database automatically has a default schema. Reference it as DatabaseName.[default].

Tables and Columns​

Define tables with columns, data types, and constraints:

<Tables>
<Table Name="Customer" SchemaName="Staging.[default]">
<Columns>
<Column Name="CustomerID" DataType="Int32" IsNullable="false" />
<Column Name="CustomerName" DataType="String" Length="100" />
<Column Name="Balance" DataType="Decimal" Precision="18" Scale="2" />
<Column Name="CreatedDate" DataType="DateTime" />
</Columns>
</Table>
</Tables>

Column Attributes​

Define column properties to accurately model your database schema.

Common Attributes​

AttributeTypePurposeExample
NameStringColumn identifierCustomerID
DataTypeEnumSQL data typeInt32, String, DateTime, Decimal
LengthIntString/binary length (-1 for MAX)50 for VARCHAR(50)
PrecisionIntTotal digits for decimal18
ScaleIntDecimal places2 for (18,2)
IsNullableBooleanAllow NULL valuestrue, false
IdentitySeedLongIdentity start value1
IdentityIncrementLongIdentity increment1

Data Type Mappings​

Biml DataTypeSQL ServerUse Case
Int32INTStandard integers
Int64BIGINTLarge integers
StringVARCHAR/NVARCHARText (use Length)
AnsiStringVARCHARNon-Unicode text
DecimalDECIMALPrecise numbers (use Precision, Scale)
DateTimeDATETIMELegacy dates
DateTime2DATETIME2Modern dates (recommended)
BooleanBITTrue/false
BinaryVARBINARYBinary data
CurrencyMONEYFinancial values
GuidUNIQUEIDENTIFIERGUIDs

Example: Complete Column Definitions​

<Table Name="Customer" SchemaName="Sales.dbo">
<Columns>
<!-- Identity column -->
<Column Name="CustomerID" DataType="Int32" IdentitySeed="1" IdentityIncrement="1"/>

<!-- String with length -->
<Column Name="FirstName" DataType="String" Length="50" IsNullable="false"/>
<Column Name="LastName" DataType="String" Length="50" IsNullable="false"/>
<Column Name="Email" DataType="String" Length="255" IsNullable="true"/>

<!-- Decimal with precision/scale -->
<Column Name="CreditLimit" DataType="Decimal" Precision="18" Scale="2" IsNullable="true"/>

<!-- Date columns -->
<Column Name="CreatedDate" DataType="DateTime2" IsNullable="false"/>
<Column Name="ModifiedDate" DataType="DateTime2" IsNullable="true"/>
</Columns>
</Table>

SCD Type Attribute (Data Warehousing)​

For dimension tables, the ScdType attribute indicates Slowly Changing Dimension handling:

ScdTypeBehaviorExample Columns
0Fixed attribute (never changes)CustomerID, AccountNumber
1Overwrite (keep current only)Email, Phone
2Track history (add new row)Address, MaritalStatus
3Keep previous and currentLimited use, stores one prior value
<Columns>
<Column Name="CustomerID" DataType="Int32" ScdType="0"/>
<Column Name="Email" DataType="String" Length="255" ScdType="1"/>
<Column Name="Address" DataType="String" Length="200" ScdType="2"/>
</Columns>

Note: ScdType is metadata used by BimlFlex and custom templates; standard Biml compilation doesn't automatically generate SCD logic.

Importing Database Metadata​

GetDatabaseSchema​

The most flexible method for importing existing database structures:

var importResult = ExternalDataAccess.GetDatabaseSchema(
RootNode.Connections["Source"], // Connection to use
new[] { "Sales", "HumanResources" }, // Schemas to include
null, // Tables to include (null = all)
ImportOptions.ExcludeForeignKey | ImportOptions.ExcludeIndex
);

// Access imported objects
foreach (var table in importResult.TableNodes) {
// table.Name, table.Columns, etc.
}

Import Options​

Control what gets imported:

OptionEffect
ExcludeForeignKeySkip FK constraints (common for staging)
ExcludeIndexSkip indexes
ExcludePrimaryKeySkip PK constraints
ExcludeViewsImport tables only
ExcludeIdentityRemove identity specification
ExcludeCheckConstraintSkip check constraints
ExcludeColumnDefaultSkip default values

Combine options with |: ImportOptions.ExcludeForeignKey | ImportOptions.ExcludeIndex

Alternative Methods​

For simpler scenarios:

  • ImportDB(connection, schemaFilter, tableFilter) - Full database import with wildcard filters (%)
  • ImportTableNodes(connection, schema, tableFilter) - Single schema import

Generating DDL Scripts​

Generate CREATE TABLE scripts from your Biml table definitions:

<#@ template language="C#" tier="20" #>
<# foreach (var table in RootNode.Tables) { #>
-- DDL for <#=table.Name#>
<#=table.GetDropAndCreateDdl()#>
GO

<# } #>

This outputs platform-appropriate DDL based on your connection type.

Keys and Indexes​

Primary and Unique Keys​

<Table Name="Customer" SchemaName="Staging.[default]">
<Columns>
<Column Name="CustomerID" DataType="Int32" IsNullable="false" IdentityIncrement="1" />
<Column Name="CustomerKey" DataType="String" Length="20" IsNullable="false" />
<Column Name="CustomerName" DataType="String" Length="100" />
</Columns>
<Keys>
<PrimaryKey Name="PK_Customer">
<Columns>
<Column ColumnName="CustomerID" />
</Columns>
</PrimaryKey>
<UniqueKey Name="UK_Customer_Key">
<Columns>
<Column ColumnName="CustomerKey" />
</Columns>
</UniqueKey>
</Keys>
</Table>

Indexes​

<Indexes>
<Index Name="IX_Customer_Name">
<Columns>
<Column ColumnName="CustomerName" />
</Columns>
</Index>
</Indexes>

Table References (Foreign Keys)​

Model relationships between tables using TableReference for foreign keys.

Basic Foreign Key​

<Table Name="Order" SchemaName="Sales.dbo">
<Columns>
<Column Name="OrderID" DataType="Int32" IdentitySeed="1" IdentityIncrement="1"/>
<Column Name="OrderDate" DataType="DateTime2"/>
<TableReference Name="CustomerID" TableName="Sales.Customer"/>
</Columns>
</Table>

The TableReference creates a foreign key relationship to the primary key of the referenced table.

Dimension/Fact Table Pattern​

For data warehouse designs, use TableReference to model dimension relationships:

<!-- Dimension Table -->
<Table Name="DimCustomer" SchemaName="DW.dbo">
<Columns>
<Column Name="CustomerKey" DataType="Int32" IdentitySeed="1" IdentityIncrement="1"/>
<Column Name="CustomerID" DataType="Int32"/>
<Column Name="CustomerName" DataType="String" Length="100"/>
</Columns>
<Keys>
<PrimaryKey Name="PK_DimCustomer">
<Columns><Column ColumnName="CustomerKey"/></Columns>
</PrimaryKey>
</Keys>
</Table>

<!-- Fact Table with Dimension References -->
<Table Name="FactSales" SchemaName="DW.dbo">
<Columns>
<Column Name="SalesKey" DataType="Int64" IdentitySeed="1" IdentityIncrement="1"/>
<TableReference Name="CustomerKey" TableName="DW.DimCustomer"/>
<TableReference Name="ProductKey" TableName="DW.DimProduct"/>
<TableReference Name="DateKey" TableName="DW.DimDate">
<ReferenceColumns>
<ReferenceColumn ColumnName="DateKey"/>
</ReferenceColumns>
</TableReference>
<Column Name="Quantity" DataType="Int32"/>
<Column Name="Amount" DataType="Decimal" Precision="18" Scale="2"/>
</Columns>
</Table>

Staging Tables (Skip FK Creation)​

For staging tables, skip FK constraint creation since referential integrity isn't enforced:

<TableReference Name="CustomerID" TableName="Staging.[default].Customer"
ForeignKeyConstraintMode="DoNotCreate" />

Benefits for Automation​

When you define TableReference relationships:

  • BimlScript can traverse relationships (table.Columns.OfType<AstTableReferenceNode>())
  • Generate DDL with proper foreign key constraints
  • Create data flow mappings that respect referential integrity
  • Build incremental load sequences based on dependencies

Common Patterns​

Staging Layer with Audit Columns​

Add standard audit columns to every imported table:

<# foreach (var sourceTable in importResult.TableNodes) { #>
<Table Name="STG_<#=sourceTable.Name#>" SchemaName="Staging.[default]">
<Columns>
<# foreach (var col in sourceTable.Columns) { #>
<Column Name="<#=col.Name#>"
DataType="<#=col.DataType#>"
Length="<#=col.Length#>"
Precision="<#=col.Precision#>"
Scale="<#=col.Scale#>"
IsNullable="true" />
<# } #>
<!-- Standard audit columns -->
<Column Name="ETL_LoadDateTime" DataType="DateTime" IsNullable="false" />
<Column Name="ETL_SourceSystem" DataType="String" Length="100" />
<Column Name="ETL_BatchID" DataType="Int64" />
</Columns>
</Table>
<# } #>

Excluding Foreign Keys for Staging​

Staging tables typically don't need FK constraints:

var importResult = ExternalDataAccess.GetDatabaseSchema(
RootNode.Connections["Source"],
new[] { "Sales" },
null,
ImportOptions.ExcludeForeignKey | ImportOptions.ExcludeIndex | ImportOptions.ExcludeIdentity
);

Data Vault Hash Keys​

For Data Vault implementations, use HashedKey columns:

<Columns>
<Column Name="CustomerKey" DataType="String" Length="50" IsNullable="false" />
<Column Name="CustomerName" DataType="String" Length="100" />
<HashedKey Name="HK_Customer" ConstraintName="UK_CustomerKey" />
</Columns>
<Keys>
<UniqueKey Name="UK_CustomerKey">
<Columns>
<Column ColumnName="CustomerKey" />
</Columns>
</UniqueKey>
</Keys>

Automation Benefits​

Time Savings​

ApproachTime for 50-Table Staging Layer
Manual DDL creation25-50 hours
Biml metadata import~30 minutes
Time saved98%

Consistency Benefits​

  • Every table gets identical audit columns
  • Naming conventions enforced automatically
  • Data type mappings standardized across all tables
  • Changes propagate instantly on rebuild

CI/CD Integration​

Generate DDL as part of your build pipeline:

# Azure DevOps example
- task: MSBuild@1
displayName: 'Generate Staging DDL'
inputs:
solution: '**/*.mst'
msbuildArguments: '/p:OutputPath=$(Build.ArtifactStagingDirectory)/DDL'

Beyond SSIS: Cloud Platform Support​

While this guide focuses on SQL Server with BimlExpress and BimlStudio, BimlFlex extends relational modeling to cloud platforms:

  • Azure Data Factory - Generate linked services and datasets
  • Databricks - Generate Delta Lake DDL and notebooks
  • Snowflake - Generate stages, tables, and stored procedures
  • Microsoft Fabric - Generate lakehouse tables and pipelines

For cloud platform automation, see the BimlFlex documentation.

Next Steps​

Now that you understand relational modeling in Biml:

Reference Documentation​