T-SQL

Composable DML

Composable DML is an extension SQL Server 2008 extension of the OUTPUT clause which one was originally published in SQL Server 2005. OUTPUT is generally returning all rows from the DML where it’s used without an option to filter the results set. Composable DML extends this concept for INSERT…SELECT scenarios and brings a limited option to filter/slightly modify the result set provided by the OUTPUT clause. Let’s see how it’s working.

We will create a table [dbo].[SampleTable] and fill it with some dummy data first.

-- Create sample table
CREATE TABLE [dbo].[SampleTable]
(
    [Id] INT IDENTITY(1,1) PRIMARY KEY,
    [ProductName] VARCHAR(50) NOT NULL,
    [Price] MONEY NOT NULL
)
GO

-- Populate data
INSERT [dbo].[SampleTable] ([ProductName], [Price])
    SELECT 'Product1', 100 UNION ALL
    SELECT 'Product2', 200 UNION ALL
    SELECT 'Product3', 300
GO

-- Review sample data
SELECT [Id], [ProductName],	[Price] FROM dbo.[SampleTable]
GO

Table [dbo].[TrackedChanges] will be used to store changes history from DML we will run againts [dbo].[SampleTable]:

-- Create table for changes history
CREATE TABLE [dbo].[TrackedChanges]
(
    [RowId] INT,
    [OldValue] MONEY,
    [NewValue] MONEY,
    [ChangeDate] DATETIME DEFAULT (GETDATE())
)
GO

We will run the main Composable DML statement now: It’s an UPDATE statement to change Product price followed by OUTPUT clause. That all is nested in INSERT…SELECT statement with WHERE filter condition to track changes in [dbo].[TrackedChanges] table only in case when the updated [Price] is over 300.

-- Perform Composable DML
INSERT INTO [dbo].[TrackedChanges]
        ( [RowId], [OldValue], [NewValue] )
SELECT 
	[RowId], [OldValue], [NewValue]
FROM 
(
    UPDATE [dbo].[SampleTable]
        SET [Price] = [Price] * 1.5
    OUTPUT
        [INSERTED].[Id] AS [RowId],
        [DELETED].[Price] AS [OldValue],
        [INSERTED].[Price] AS [NewValue] 
) [upd] 
WHERE NewValue > 300
GO

-- Check results
SELECT [RowId], [OldValue], [NewValue],	[ChangeDate] FROM [dbo].[TrackedChanges]
GO

This query concept is mainly useful when handling SDC Type II in data warehouse scenarios.

Few limitations exist still and Composable DML is not fully done 10 years after publishing. I.e. we can’t use JOINs, TOP or GROUP BY clauses. Let’s demonstrate it on JOIN example:

INSERT INTO [dbo].[TrackedChanges]
        ( [RowId], [OldValue], [NewValue] )
SELECT 
	[RowId], [OldValue], [NewValue]
FROM 
(
    UPDATE [dbo].[SampleTable]
        SET [Price] = [Price] * 1.5
    OUTPUT
        [INSERTED].[Id] AS [RowId],
        [DELETED].[Price] AS [OldValue],
        [INSERTED].[Price] AS [NewValue] 
) [upd] 
	INNER JOIN [dbo].[SampleTable] st ON [st].[Id] = upd.Row_Id
WHERE NewValue > 300
GO

Besides these limitations, I really like it and have a lot of fun when junior developers are scratching heads what the hell is it for peace of code:)

Leave a Reply

Your email address will not be published. Required fields are marked *