Search This Blog

Friday, October 6, 2023

How to Downgrade Or Recover a SQL Server Database To A Previous Version Using SqlPackage.exe?

How to Downgrade a SQL Server Database To A Previous Version?

Upgrading a SQL Server database to a newer version is one of the core duties of any DBA. In fact, this holds true for practically any database system or any computer system, for that matter.

But what if this wasn't an upgrade? What if you receive a database (MDFs, LDFs, etc.) or a database backup file/s for a specific version of SQL Server, but you need to use it on a lower version of SQL Server? Or, suppose the database upgrade succeeds, but the application breaks, and none of your database rollback options work, including restoring from the database backups taken just prior to the upgrade. You may still have previous, older backups, but they don't contain the most recent data. In that case, finding a way to downgrade the database to the prior version might be your most preferred option.  But that is not as straight forward or simple, is it?

Because justly so, SQL Server does not support directly restoring or attaching a database from a higher version to a lower version. 

You can however still copy/restore the database to the SQL Server version it was copied from. Then use one of the export/import tools (SSIS, BCP etc.) to copy the data from there to the target SQL Server. This obviously requires more work on your part, is complex and is a lengthier process. But it is still a solution that can get the job done, barring any compatibility issues.

Here, however, I want to discuss how to accomplish that using the SqlPackage.exe utility of the Data-tier applications (DAC) as I think it's more efficient and easier in comparison, among its other advantages over say SSIS, BCP etc.


What is SqlPackage.exe?

In a narrow sense, SqlPackage.exe is a command-line utility that allows you to export/extract an SQL Server database and optionally import into another SQL Server. Sqlpackage.exe is too a data export/import tool with additional features, and it uses the BCP utility (bcp.exe) behind the scene to copy data in and out of a database.

It essentially gives you a command line alternative to some of the APIs and functionalities of the Data-tier applications (DAC) that you may have seen in the SSMS.

Hold on, wait a second. if Sqlpackage.exe (supposedly) only offers a subset of functionality of DAC GUI in SSMS, and if I only need to do this once, why should even consider using this command line utility at all?

The short answer, generally you won't, until and unless you run into errors or issues where GUI is too rigid and inflexible to help you. Please see my notes on potential compatibility errors towards the end of this article.

The intended (and official at writing of this article) use case of Sqlpackage.exe  is to migrate a database to another server, whether on-premise or in the cloud, or to upgrade the database to a higher version. However, in these cases the database backup/restore or attach/detach methods are much easier and more efficient, simply because in background SQL Server instance will automatically upgrade the internal structure of the database to match it's version. Therefore, I rarely use SqlPackage.exe for that purpose.

SqlPackage.exe works with two types of packages: DACPAC and BACPAC. The DACPAC (Data-tier Application Package) mode, by default, extracts only the database schema without the data. On the other hand, BACPAC (Backup Component Package) extracts both the database schema and all the data.

For more information and download instructions to get the latest version, see:

https://learn.microsoft.com/en-us/sql/tools/sqlpackage/sqlpackage


First I will describe the steps for using BACPAC method.

For this demo, I will copy the Northwind database from a SQL Server 2022 instance to a SQL Server 2016 instance.

And, Before you begin, ensure you have SqlPackage.exe installed on your machine or access to a server where it's available. You can usually find it in the "DAC\bin" directory of your SQL Server installation, such as "C:\Program Files (x86)\Microsoft SQL Server\<SQL Server Version>\DAC\bin".



BACPAC (bacpac export) Method:

Open a command prompt (cmd.exe) or terminal window then switch to the directory where sqlpackage.exe is located:

cd C:\Program Files\Microsoft SQL Server\160\DAC\bin

REM : SqlPackage.exe version number
SqlPackage /Version:True


Export the Database

Execute the following command to export a SQL Server database to a .bacpac file. Replace the parameter values with your specific values:



SqlPackage.exe                      ^
 /a:Export                          ^
 /ssn:localhost                     ^
 /sdn:Northwind                     ^
 /tf:Northwind.bacpac               ^
 /SourceEncryptConnection:True      ^
 /SourceTrustServerCertificate:True ^



I am using Integrated/Windows Authentication to connect to the source SQL Server.  You can also specify SQL user/password by adding parameters /SourceUser:"{your_password}" and  /SourcePassword:"{your_password}"

Let's review the above command:

1. SqlPackage.exe: 
   - Executable file for the utility

2. ^
   - I added ^ (Caret) character is to  split the command into multiple lines for better readability.  It is not required nor it's part of the SqlPackage.exe utility

3. /a:Export: 
   - This parameter specifies the action to be taken. In this case, "Export" means that the database will be exported to a `.bacpac` file.

4. /ssn:localhost: 
   - The source sql server name. "localhost" means the SQL Server instance running on the local machine.

5. /sdn:Northwind: 
   - Name of the source database name, which in this case is "Northwind"

6. /tf:Northwind.bacpac: 
   - The output/target file to which the database will be exported. The output will be a `.bacpac` file named "Northwind.bacpac".

7. /SourceEncryptConnection:True: 
   - This to indicate that the connection to the sql server should be encrypted.

8. /SourceTrustServerCertificate:True: 
   - This to specify that the sql server certificate should be trusted without validation. Why? Because this SQL Server uses a self-signed certificate.


To see explanation and list of all the parameters for the sqlpackage.exe:

https://learn.microsoft.com/en-us/sql/tools/sqlpackage/sqlpackage


SqlPackage.exe will connect to the specified SQL Server instance, export the database schema and data, and save it as a .bacpac file at the specified location.


Import the Database Into Target SQL Instance

Execute the following command to import a SQL Server database from a .bacpac file. Please remember to update the parameter values with your specific values:



.\SqlPackage                         ^
  /a:Import                          ^
  /sf:"Northwind.bacpac"             ^
  /tsn:localhost\sql2016             ^
  /tdn:"Northwind"                   ^
  /TargetEncryptConnection:True      ^
  /TargetTrustServerCertificate:True



Let's review the above command:

1. SqlPackage: 
   - The executable file for the utility.

2. ^: 
   - I added this character is to  split the command into multiple lines for better readability.  It is not required nor it's part of the SqlPackage.exe utility.

3. /a:Import: 
   - This specifies the action to be taken and value "Import" means that the database schema and data will be imported from the specified `.bacpac` file.

4. /sf:"Northwind.bacpac": 
   - Name of the source file from which the data and schema will be imported. The file in this case is named "Northwind.bacpac".

5. /tsn:localhostsql\2016: 
   - The target sql server name where the data and schema will be imported.

6. /tdn:"Northwind": 
   - The target database name where the data and schema will be imported.

7. /TargetEncryptConnection:True: 
   - This to indicate that the connection to the sql server should be encrypted.

8. /TargetTrustServerCertificate:True: 
   - This to specify that the sql server certificate should be trusted without validation. Why? Because this SQL Server uses a self-signed certificate.

If all goes well, SqlPackage.exe will connect to the target SQL Server instance, create a new database (it will fail if a database with that name already exists), and import the schema and data from the .bacpac file. Notice the highlighted warning about the combability.


.\SqlPackage                         ^
More?   /Action:Import                     ^
More?   /sf:"northwind.bacpac"             ^
More?   /tsn:localhost\SQL2016AG01      ^
More?   /tdn:"northwind"                   ^
More?   /TargetEncryptConnection:True      ^
More?   /TargetTrustServerCertificate:True
Importing to database 'northwind' on server 'localhost\SQL2016AG01'.
Creating deployment plan
Initializing deployment
*** A project which specifies SQL Server 2022 or Azure SQL Database Managed Instance as the target platform may experience compatibility issues with SQL Server 2016.
Verifying deployment plan
Analyzing deployment plan
Importing package schema and data into database
Updating database
Importing data
Processing Import.
Disabling indexes.
Disabling index 'CategoryName'.
Disabling index 'PK_CustomerCustomerDemo'.
Disabling index 'PK_CustomerDemographics'.
Disabling index 'City'.
Disabling index 'CompanyName'.
.....








DACPAC (dacpac export) Method:

Execute the following command to extract a SQL Server database to a .dacpac file. Please don't forget to update the parameter values with your specific values:



.\SqlPackage                         ^
  /a:Extract                         ^
  /tf:"Northwind.dacpac"             ^
  /ssn:localhost\SQL2022AG01         ^
  /sdn:"Northwind"                   ^
  /SourceEncryptConnection:True      ^
  /SourceTrustServerCertificate:True ^
  /p:VerifyExtraction=False          ^
  /p:ExtractAllTableData=True  




Notice the parameter /p:ExtractAllTableData=True to also export the data.  This is required because by default dacpac method would only extract schema definitions.


Publish (Import) the Database Into Target Instance

Execute the following command to publish a SQL Server database from a .dacpac file. Please don't forget to update the parameter values with your specific values:



.\SqlPackage                         ^
  /a:Publish                         ^
  /sf:"northwind.dacpac"             ^
  /tsn:"localhost\SQL2016AG01"       ^
  /tdn:"northwind"                   ^
  /TargetEncryptConnection:True      ^
  /TargetTrustServerCertificate:True ^
  /p:AllowIncompatiblePlatform=True  









Compatibility Errors:

I have found SqlPackage.exe far more efficient in terms for overall performance and ease of use, compare to other options. A major drawback of it though is the potential compatibility issues you may run into that may prevent you from using this tool. This limitation though is due to the underlying Data-tier applications APIs that it uses therefore you would generally run into same issues even when you use the Data-tier applications feature of SSMS. In fact often the main impetus for turning to sqlpackage.exe is because it offers great flexibility to modify and customize it's behavior by way of setting parameter values and xml config file, something that SSMS cannot do due to its GUI nature. Unfortunately, the list of parameters is too big for me to cover in a single blog article.

Often, the compatibility issues arise because the source database you are trying to downgrade is now using features, configuration and syntax that are not available in the older, target SQL Server version. 

SqlPackage.exe will typically give you a descriptive error message. Use that as a starting point to understand the cause for the error. To display more detailed log, add the /Diagnostics:True switch to the command, and optionally add /DiagnosticsFile:<filename> if you also want the detailed log to be stored in a file.

For example in SQL Server 2022 you can have ORDERED COLUMNSTORE INDEXES. Trying to downgrade a database with ORDERED COLUMNSTORE INDEXES, to say even SQL Server 2019, will throw an error. Here is an actual error I received when I first tried using the WideWorldImportersDW database for this demo.


Creating deployment plan
Initializing deployment
*** A project which specifies SQL Server 2022 or Azure SQL Database Managed Instance as the target platform may experience compatibility issues with SQL Server 2016.
Verifying deployment plan
Analyzing deployment plan
Importing package schema and data into database
Updating database
*** Error importing database:Could not import package.
Warning SQL0: A project which specifies SQL Server 2022 or Azure SQL Database Managed Instance as the target platform may experience compatibility issues with SQL Server 2016.
Error SQL72014: Framework Microsoft SqlClient Data Provider: Msg 156, Level 15, State 1, Line 6 Incorrect syntax near the keyword 'ORDER'.
Error SQL72045: Script execution error.  The executed script:
CREATE CLUSTERED INDEX [CCX_Fact_Order]
    ON [Fact].[Order]([Order Key])
    ON [PS_Date] ([Order Date Key]);
CREATE CLUSTERED COLUMNSTORE INDEX [CCX_Fact_Order]
    ON [Fact].[Order] ORDER([Order Key]) WITH (DROP_EXISTING = ON)
    ON [PS_Date] ([Order Date Key]);

Error SQL72014: Framework Microsoft SqlClient Data Provider: Msg 319, Level 15, State 1, Line 6 Incorrect syntax near the keyword 'with'. If this statement is a common table expression, an xmlnamespaces clause or a change tracking context clause, the previous statement must be terminated with a semicolon.
Error SQL72045: Script execution error.  The executed script:
CREATE CLUSTERED INDEX [CCX_Fact_Order]
    ON [Fact].[Order]([Order Key])
    ON [PS_Date] ([Order Date Key]);
CREATE CLUSTERED COLUMNSTORE INDEX [CCX_Fact_Order]
    ON [Fact].[Order] ORDER([Order Key]) WITH (DROP_EXISTING = ON)
    ON [PS_Date] ([Order Date Key]);

Time elapsed 0:00:07.54.

Often you are able to get around such errors by either using one of the parameter switches, or removing the offending object first, provided it won't cause an actual data loss, then add/create it back. I decided not to go that route here and instead switched my demo to use the good old Northwind sample database.


Conclusion

Downgrading a SQL Server database version is a complex task that should be approached with caution. It's essential to plan and test the process thoroughly in a controlled environment before attempting it on a production database.