How to: Access SQL Timestamp from C/AL Code
How do you access SQL Server timestamp values from C/AL code? When you search for this solution, you typically encounter four common approaches, each with significant drawbacks. Here’s a simpler fifth approach that works seamlessly with NAV 2013 and later versions.
The Traditional Approaches (And Why They Fall Short)
When developers need to access SQL Server timestamp values in NAV, they usually find these solutions:
1. “You Can’t Do It”
The Problem: This isn’t a solution—it’s giving up before trying.
2. ADO.NET with SQL Queries
The Problem: Using ADO.NET to execute SQL queries is overkill for accessing a simple field value.
// Overly complex approach
// Requires SQL connection management
// Breaks NAV's data access patterns
3. SQL Views with Linked Tables
The Problem: This approach creates unnecessary complexity:
- Requires database schema modifications (views)
- Complicates upgrade processes
- Consumes table objects for each timestamp source
- Doesn’t scale for multiple tables
4. Native Timestamp Fields (NAV 2016+)
The Good: Microsoft introduced proper timestamp support in NAV 2016 (detailed in this blog post).
The Limitations:
- Requires table customizations (adding BigInteger fields with SQL Timestamp property)
- Creates upgrade complications
- Not available for older NAV versions
- Modifies standard tables
The Elegant Solution: Three Lines of Code
Here’s a clean, simple approach that works without customizations:
IF NOT DataTypeManagement.GetRecordRef(_RelatedRecord, RecRef) THEN
EXIT(0);
EVALUATE(timestamp, FORMAT(RecRef.FIELD(0).VALUE));
This solution leverages NAV’s built-in capability to access the SQL timestamp through the virtual field 0, which represents the SQL Server rowversion column.
How It Works
Understanding Field 0
Every table in NAV has a virtual field 0 that corresponds to the SQL Server timestamp (rowversion) column. This field:
- Automatically exists for all tables
- Requires no customization or table modifications
- Contains the SQL timestamp as a binary value
- Updates automatically whenever the record changes
Implementation Details
PROCEDURE GetTimestamp(RelatedRecord : Variant) : Decimal
VAR
RecRef : RecordRef;
timestamp : Decimal;
BEGIN
// Get RecordRef from any record type
IF NOT DataTypeManagement.GetRecordRef(RelatedRecord, RecRef) THEN
EXIT(0);
// Access field 0 (SQL timestamp) and convert to decimal
EVALUATE(timestamp, FORMAT(RecRef.FIELD(0).VALUE));
EXIT(timestamp);
END;
Usage Examples
For any table record:
// Customer record
Customer.GET('10000');
CustomerTimestamp := GetTimestamp(Customer);
// Item record
Item.GET('1000');
ItemTimestamp := GetTimestamp(Item);
// Sales Header
SalesHeader.GET(SalesHeader."Document Type"::Order, '1001');
OrderTimestamp := GetTimestamp(SalesHeader);
Practical Applications
1. Optimistic Concurrency Control
// Store original timestamp
OriginalTimestamp := GetTimestamp(Customer);
// Perform operations...
ProcessCustomerData(Customer);
// Verify record hasn't changed
Customer.GET(Customer."No.");
IF GetTimestamp(Customer) <> OriginalTimestamp THEN
ERROR('Record was modified by another user.');
2. Change Detection
// Before modification
BeforeTimestamp := GetTimestamp(Item);
// Make changes
Item.Description := 'Updated Description';
Item.MODIFY;
// After modification
AfterTimestamp := GetTimestamp(Item);
// Verify change was applied
IF AfterTimestamp = BeforeTimestamp THEN
ERROR('Record was not updated properly.');
3. Audit Trail Enhancement
// Enhanced audit logging with timestamp
AuditEntry.INIT;
AuditEntry."Table ID" := DATABASE::Customer;
AuditEntry."Record ID" := Customer.RECORDID;
AuditEntry."SQL Timestamp" := GetTimestamp(Customer);
AuditEntry."Modified DateTime" := CURRENTDATETIME;
AuditEntry.INSERT;
Version Compatibility
✅ Supported Versions
- NAV 2013 and later versions
- Business Central (all versions)
- Works with both Classic Client and RTC
❌ Not Supported
- NAV 2009 R2 and older versions
- These versions don’t expose field 0 through C/AL
Implementation Notes
Complete Function Example
PROCEDURE GetTimestamp(RelatedRecord : Variant) : Decimal
VAR
DataTypeManagement : Codeunit "Data Type Management";
RecRef : RecordRef;
timestamp : Decimal;
BEGIN
// Validate input and get RecordRef
IF NOT DataTypeManagement.GetRecordRef(RelatedRecord, RecRef) THEN
EXIT(0);
// Access virtual field 0 (SQL timestamp)
IF NOT EVALUATE(timestamp, FORMAT(RecRef.FIELD(0).VALUE)) THEN
EXIT(0);
EXIT(timestamp);
END;
Error Handling Considerations
- Invalid records: Function returns 0 for invalid or empty records
- Conversion errors: EVALUATE returns FALSE if timestamp conversion fails
- Null values: New records may have null timestamps until first save
Performance Benefits
This approach offers several advantages:
- No database overhead: No additional views or tables required
- No SQL queries: Uses NAV’s native data access
- Minimal code: Three lines accomplish the task
- Universal compatibility: Works with any table
- Upgrade-safe: No customizations to maintain
Common Use Cases
Data Synchronization
Perfect for tracking record changes in integration scenarios:
// Check if record changed since last sync
IF GetTimestamp(Customer) > LastSyncTimestamp THEN
SynchronizeCustomer(Customer);
Conflict Resolution
Implement optimistic locking without table modifications:
// Store timestamp before long operation
StoredTimestamp := GetTimestamp(SalesHeader);
// After user interaction, verify no changes occurred
IF GetTimestamp(SalesHeader) <> StoredTimestamp THEN
MESSAGE('Record was modified by another user. Please refresh.');
Download and Try It
Ready to implement this solution? Download the complete implementation:
- Download FOB (NAV 2013 format)
- Includes the
GetTimestampfunction and usage examples - Compatible with NAV 2013 through Business Central
Summary
Accessing SQL Server timestamps from C/AL doesn’t require complex solutions. By leveraging NAV’s virtual field 0, you can:
- Access timestamps from any table without customizations
- Implement optimistic concurrency control easily
- Enhance audit trails with precise change tracking
- Support data synchronization scenarios effectively
This approach provides a clean, maintainable solution that works across NAV versions while avoiding the complexity and limitations of traditional methods.
Related Posts
- Try/Catch in C/AL: My Two Cents and a Pre-NAV 2016 Trick - Error handling patterns in C/AL
- How to Calculate Dimension Set ID - Working with complex data structures in NAV
Have you used SQL timestamps in your NAV projects? Share your experiences in the comments or connect via LinkedIn or Twitter.