Are there disadvantages to contained databases?

The primary purpose of contained databases is to make it easier to port your database to a new server without a lot of scaffolding around it. With that in mind, I'll treat a few potential issues that will make this migration more difficult - and most revolve around the fact that contained databases are only partially contained in SQL Server 2012 (containment is not actually enforced).


Connection strings

Connection strings to a contained database must explicitly specify the database in the connection string. You can no longer rely on the login's default database to establish a connection; if you don't specify a database, SQL Server is not going to step through all the contained databases and try to find any database where your credentials may match.


Cross-db queries

Even if you create the same user with the same password in two different contained databases on the same server, your application will not be able to perform cross-database queries. The usernames and passwords may be the same, but they're not the same user. The reason for this? If you have contained databases on a hosted server, you shouldn't be prevented from having the same contained user as someone else who happens to be using the same hosted server. When full containment arrives (likely in the version after SQL Server 2012 never), cross-database queries will absolutely be prohibited anyway. I highly, highly, highly recommend that you don't create server-level logins with the same name as contained database users, and try to avoid creating the same contained user name across contained databases. If you need to run queries that hit multiple contained databases, do so using a server-level login that has appropriate privileges (you might think this is sysadmin, but for read-only queries, this is CONNECT ANY DATABASE and SELECT ALL USER SECURABLES).


Synonyms

Most 3- and 4-part names are easy to identify, and appear in a DMV. However if you create a synonym that points to a 3- or 4-part name, these do not show up in the DMV. So if you make heavy use of synonyms, it is possible that you will miss some external dependencies, and this can cause problems at the point where you migrate the database to a different server. I complained about this issue, but it was closed as "by design" and didn't survive the migration to the new feedback system. Note that the DMV will also miss 3- and 4-part names that are constructed via dynamic SQL.


Password policy

If you have created a contained database user on a system without a password policy in place, you may find it hard to create the same user on a different system that does have a password policy in place. This is because the CREATE USER syntax does not support bypassing the password policy. I filed a bug about this problem, and it remains open (and it also didn't survive the move when Connect was retired). And it seems strange to me that on a system with a password policy in place, you can create a server-level login that easily bypasses the policy, but you can't create a database user that does so - even though this user is inherently less of a security risk.


Collation

Since we can no longer rely on the collation of tempdb, you may need to change any code that currently uses explicit collation or DATABASE_DEFAULT to use CATALOG_DEFAULT instead. See this BOL article for some potential issues.


IntelliSense

If you connect to a contained database as a contained user, SSMS will not fully support IntelliSense. You'll get basic underlining for syntax errors, but no auto-complete lists or tooltips and all the fun stuff. I filed a bug about this issue, and it remains open - and one more that did not survive the move.


SQL Server Data Tools

If you are planning to use SSDT for database development, there currently isn't full support for contained databases. Which really just means that building the project won't fail if you use some feature or syntax that breaks containment, since SSDT currently doesn't know what containment is and what might break it.


ALTER DATABASE

When running an ALTER DATABASE command from within the context of a contained database, rRather than ALTER DATABASE foo you will need to use ALTER DATABASE CURRENT - this is so that if the database is moved, renamed, etc. these commands don't need to know anything about their external context or reference.


A few others

Some things you probably shouldn't still be using but nonetheless should be mentioned in the list of things that aren't supported or are deprecated and shouldn't be used in contained databases:

  • numbered procedures
  • temporary procedures
  • collation changes in bound objects
  • change data capture
  • change tracking
  • replication

That all said, these aren't necessarily disadvantages to using contained databases, they're just issues you should be aware of and aren't all explicitly disclosed in the official documentation.

You'll also need to be sure that if a contained database is going to be migrated, or is part of an availability group or is being mirrored, that all potential destination servers have the sp_configure option contained database authentication set to 1.

You may find this blog post useful, as well as this one, even though they pre-date RTM.


For those who are interested in getting more details about contained databases, I can recommend them to read this article http://www.sqlshack.com/contained-databases-in-sql-server/

The article pinpoints main advantages/disadvantages of using contained databases.

Disadvantages

Partially contained databases cannot use features like replication, change data capture, change tracking, schema-bound objects that depend on built-in functions with collation changes.

Advantages

On the other hand, as already mentioned, there are some benefits of using contained DBs, such as:

  • It is quite easy to move the database from one server to another,
    as there won’t be orphaned user issues
  • Metadata is stored on contained databases so it is easier and more portable
  • It is possible to have both SQL Server and Windows authentication for contained DB users

Article also helps with:

  • creating a new contained database (by making containment type as Partial in the Options page in SQL Server, and using T-SQL query to create a database afterwards)
  • connecting to the contained DB using SQL Server Management Studio (it is needed to specify contained DB name in the connection parameter)
  • converting existing database to a contained database
  • working on a contained database and listing all logins that are of contained user type

One disadvantage is that a contained database user cannot be forced to change their own password like logins could (MUST_CHANGE). Users cannot manage their own password unless you grant them an alter user permission and tell them how to change it using a SQL statement. There is nowhere easy for them to manage it via user interfaces or at least I don't know how.

Additional note is that, I found the unexpected and undocumented metadata usage in "PIVOT" AND "UNPIVOT" clause which I thought it should be only in the system catalog only (sys.tables/sys.columns/etc). As documented in msdn :

In a contained database, the catalog collation Latin1_General_100_CI_AS_WS_KS_SC. This collation is the same for all contained databases on all instances of SQL Server and cannot be changed.

But they didn't mention that the "PIVOT" AND "UNPIVOT" clause also use the system catalogs as an execution mechanism. so it produce a collation conflicted error everywhere near the use of "PIVOT" AND "UNPIVOT" clause during the migration. here is some repro:

/*step1 create a table belongs to a contained database and populate some data*/
create  table dbo.test1 (col1 varchar(100),col2 varchar(100))
insert  dbo.test1 values('a','x')
insert  dbo.test1 values('b','y')
insert  dbo.test1 values('c','z')

/*step2 lets see its collation you will see it is correctly as same as its (contained) database */
select name,collation_name from sys.columns where object_name(object_id) = 'test1'

/*step3 reproduce an unpivoted column*/
select * into dbo.test2
from (select * from dbo.test1) a unpivot (val for col in (col1,col2)) a


/*step4 lets check its collation you will see the column specified at "FOR" clause is created as Latin1_General_100_CI_AS_KS_WS_SC */
select name,collation_name from sys.columns where object_name(object_id) = 'test2'

/*step5 make use of the unpivoted table without awareness will cause an error*/
select val + ' = ' + col from dbo.test2 

/*step6 clean up*/
drop table dbo.test1
drop table dbo.test2

you can also see that the articles about contained database are mostly incomplete. so deciding to use it needs a very good improvisation.