Problem
I was working on a development project and was getting these intermittent error messages like “Msg 2714… There is already an object named ‘pk_#PackageWeight’ in the database.” which plagued a group of stored procedures that create seemingly simple reports that use temporary tables. After a little digging I found the problem and this tip offers a solution to work around this error message.
Solution
The problem is the requirement to uniquely name all entries in sys.objects, also known as sysobjects in SQL Server 2000. Temporary tables get unique names because SQL Server modifies the name of the table before adding it to sys.objects. It doesn’t provide any help when you create a primary key on the temporary table and give it a name yourself. You must force the creation of a unique name for the primary key in order to avoid message 2714.
Lets start by taking a look at the way that SQL Server creates entries in sys.objects for temporary tables. This script creates a simple temporary table and adds a primary key to the table. It then queries the related entries in sys.objects:
use adventureworks |
These are the results of the final query in that script. I’ve shortened the name of the temporary table at the ellipsis so that you can see how SQL Server makes it unique.
object_id parent_object_id type name |
Primary keys are unique among indexes for requiring an entry in sys.objects. Indexes other than the primary key such as IX_#foo_b don’t have entries in sys.objects. However both the primary key and other indexes have entries in sys.indexes as seen here:
select object_id, Index_id, type, type_desc, name go object_id Index_id type type_desc name |
The following stored procedure is very much like the ones that caused the problem that I ran into. It creates a temporary table and inserts the contents into the table. It then creates a row that represents data from the rows that have null CarrierTrackingNumbers, so that those rows can be removed before CarrierTrackingNumber can be made to be non-nullable and the primary key created.
In this case the primary key can’t be created until the rows with null CarrierTrackingNumbers are removed. Another reason that primary keys are created after the table is for performance. Here’s the original stored procedure:
USE [AdventureWorks] |
To make it easier to generate message 2714 there is a WAITFOR statement near the end of the procedure that stands in for some other type of long running processing that a stored procedure might perform. This isn’t required, but makes it easier to recreate the problem in a test environment.
Now open two SSMS queries and execute the SP. Here’s the first query:
exec usp_Product_Shipping_Analysis |
Now the second query, which immediately runs into a problem:
exec usp_Product_Shipping_Analysis Warning: Null value is eliminated by an aggregate or other SET operation. |
If there is only one user executing the procedure, as might be typical during development, there’s no problem. The problem occurs when more than one user executes the procedure simultaneously. You might not encounter the problem every day, but sooner or later you’ll run into it and then find it very difficult to reproduce.
There are two potential solutions to the problem of creating a unique name for the primary key. The easiest would be to have SQL Server generate a unique name for you by including the primary key in the CREATE TABLE statement such as this one:
CREATE TABLE #PackageWeight ( object_id parent_object_id type name |
SQL Server has added a unique string to the end of the primary key for #PackageWeight so that there are no name collisions with other instances of the same code running in other sessions. Unfortunately, this solution isn’t well suited to usp_Product_Shipping_Analysis because it must remove the null rows before creating the primary key. It’s possible to get around this but at the cost of development effort.
An alternative solution is to create a uniquely named index by adding a UniqueIdentifer value or GUID to the end of the primary key name and using dynamic SQL to create the PK. Here’s the alternative stored procedure.
USE [AdventureWorks] |
Note that the square brackets around the primary key name are required, because uniqueidentifiers include dashes when converted to varchar. Execute this procedure in two sessions and you’ll find that both return the expected result:
exec usp_Product_Shipping_Analysis_Unique_PK |
The same script goes into the second session:
exec usp_Product_Shipping_Analysis_Unique_PK |
Next Steps
Anytime you have long running code that creates a named primary key be alert for the possibility of a name collision on the primary key. Either have SQL Server create the primary key name or if that doesn’t work well for you, be sure to create the primary key with a unique name.
Check out this tip for related information about temporary tables and when to use a table variable instead: Differences between SQL Server temporary tables and table variables