As you would expect, SQL Data Warehouse supports transactions as part of the data warehouse workload. However, to ensure the performance of SQL Data Warehouse is maintained at scale some features are limited when compared to SQL Server. This article highlights the differences and lists the others.
Transaction isolation levels
SQL Data Warehouse implements ACID transactions. However, the Isolation of the transactional support is limited to
READ UNCOMMITTED and this cannot be changed. You can implement a number of coding methods to prevent dirty reads of data if this is a concern for you. The most popular methods leverage both CTAS and table partition switching (often known as the sliding window pattern) to prevent users from querying data that is still being prepared. Views that pre-filter the data is also a popular approach.
A single data modification transaction is limited in size. The limit today is applied "per distribution". Therefore, the total allocation can be calculated by multiplying the limit by the distribution count. To approximate the maximum number of rows in the transaction divide the distribution cap by the total size of each row. For variable length columns consider taking an average column length rather than using the maximum size.
In the table below the following assumptions have been made:
- An even distribution of data has occurred
- The average row length is 250 bytes
|DWU||Cap per distribution (GiB)||Number of Distributions||MAX transaction size (GiB)||# Rows per distribution||Max Rows per transaction|
The transaction size limit is applied per transaction or operation. It is not applied across all concurrent transactions. Therefore each transaction is permitted to write this amount of data to the log.
To optimize and minimize the amount of data written to the log please refer to the Transactions best practices article.
The maximum transaction size can only be achieved for HASH or ROUND_ROBIN distributed tables where the spread of the data is even. If the transaction is writing data in a skewed fashion to the distributions then the limit is likely to be reached prior to the maximum transaction size.
SQL Data Warehouse uses the XACT_STATE() function to report a failed transaction using the value -2. This means that the transaction has failed and is marked for rollback only
The use of -2 by the XACT_STATE function to denote a failed transaction represents different behavior to SQL Server. SQL Server uses the value -1 to represent an un-committable transaction. SQL Server can tolerate some errors inside a transaction without it having to be marked as un-committable. For example
SELECT 1/0 would cause an error but not force a transaction into an un-committable state. SQL Server also permits reads in the un-committable transaction. However, SQL Data Warehouse does not let you do this. If an error occurs inside a SQL Data Warehouse transaction it will automatically enter the -2 state and you will not be able to make any further select statements until the statement has been rolled back. It is therefore important to check that your application code to see if it uses XACT_STATE() as you may need to make code modifications.
For example, in SQL Server you might see a transaction that looks like this:
SET NOCOUNT ON; DECLARE @xact_state smallint = 0; BEGIN TRAN BEGIN TRY DECLARE @i INT; SET @i = CONVERT(INT,'ABC'); END TRY BEGIN CATCH SET @xact_state = XACT_STATE(); SELECT ERROR_NUMBER() AS ErrNumber , ERROR_SEVERITY() AS ErrSeverity , ERROR_STATE() AS ErrState , ERROR_PROCEDURE() AS ErrProcedure , ERROR_MESSAGE() AS ErrMessage ; IF @@TRANCOUNT > 0 BEGIN PRINT 'ROLLBACK'; ROLLBACK TRAN; END END CATCH; IF @@TRANCOUNT >0 BEGIN PRINT 'COMMIT'; COMMIT TRAN; END SELECT @xact_state AS TransactionState;
If you leave your code as it is above then you will get the following error message:
Msg 111233, Level 16, State 1, Line 1 111233;The current transaction has aborted, and any pending changes have been rolled back. Cause: A transaction in a rollback-only state was not explicitly rolled back before a DDL, DML or SELECT statement.
You will also not get the output of the ERROR_* functions.
In SQL Data Warehouse the code needs to be slightly altered:
SET NOCOUNT ON; DECLARE @xact_state smallint = 0; BEGIN TRAN BEGIN TRY DECLARE @i INT; SET @i = CONVERT(INT,'ABC'); END TRY BEGIN CATCH SET @xact_state = XACT_STATE(); IF @@TRANCOUNT > 0 BEGIN PRINT 'ROLLBACK'; ROLLBACK TRAN; END SELECT ERROR_NUMBER() AS ErrNumber , ERROR_SEVERITY() AS ErrSeverity , ERROR_STATE() AS ErrState , ERROR_PROCEDURE() AS ErrProcedure , ERROR_MESSAGE() AS ErrMessage ; END CATCH; IF @@TRANCOUNT >0 BEGIN PRINT 'COMMIT'; COMMIT TRAN; END SELECT @xact_state AS TransactionState;
The expected behavior is now observed. The error in the transaction is managed and the ERROR_* functions provide values as expected.
All that has changed is that the
ROLLBACK of the transaction had to happen before the read of the error information in the
It is also worth noting that SQL Data Warehouse does not implement or support the ERROR_LINE() function. If you have this in your code you will need to remove it to be compliant with SQL Data Warehouse. Use query labels in your code instead to implement equivalent functionality. Please refer to the LABEL article for more details on this feature.
Using THROW and RAISERROR
THROW is the more modern implementation for raising exceptions in SQL Data Warehouse but RAISERROR is also supported. There are a few differences that are worth paying attention to however.
- User defined error messages numbers cannot be in the 100,000 - 150,000 range for THROW
- RAISERROR error messages are fixed at 50,000
- Use of sys.messages is not supported
SQL Data Warehouse does have a few other restrictions that relate to transactions.
They are as follows:
- No distributed transactions
- No nested transactions permitted
- No save points allowed
- No named transactions
- No marked transactions
- No support for DDL such as
CREATE TABLEinside a user defined transaction