6

I have the following SQL:

 ALTER TABLE dbo.PS_userVariables DROP CONSTRAINT PK_PS_userVariables;
 ALTER TABLE dbo.PS_userVariables ADD PRIMARY KEY (varnumber, subjectID, userID, datasetID, listid, userVarTitle);

Since I have multiple environments, that PK_PS_userVariables constraint name is different on my different databases. How do I write a script that gets that name then adds it into my script?

cdub
  • 24,555
  • 57
  • 174
  • 303
  • Do you want to drop all the constraints, or just one in particular? – OMG Ponies Jul 13 '11 at 18:35
  • i want to drop all the primary keys on that table, and then add in my extra primary keys – cdub Jul 13 '11 at 18:40
  • http://stackoverflow.com/questions/6115451/how-to-generate-all-constraints-scripts, or: http://stackoverflow.com/search?q=[sql-server]+drop+constraints – OMG Ponies Jul 13 '11 at 18:41
  • 1
    There can only be one primary key on a table. That's why it's called primary. :-) – Aaron Bertrand Jul 13 '11 at 18:48
  • @aaron whay do you mean there can only be one primary key? Are you talking about the columns or the fact that the primary key means the data is normalized? – cdub Jul 13 '11 at 19:08
  • I'm saying that you can only add a single PRIMARY KEY constraint to a table (regardless of how many columns), so "all the primary keys on that table" is the same as "THE primary key on that table." – Aaron Bertrand Jul 13 '11 at 19:17

4 Answers4

17

While the typical best practice is to always explicitly name your constraints, you can get them dynamically from the catalog views:

DECLARE @table NVARCHAR(512), @sql NVARCHAR(MAX);

SELECT @table = N'dbo.PS_userVariables';

SELECT @sql = 'ALTER TABLE ' + @table 
    + ' DROP CONSTRAINT ' + name + ';'
    FROM sys.key_constraints
    WHERE [type] = 'PK'
    AND [parent_object_id] = OBJECT_ID(@table);

EXEC sp_executeSQL @sql;

ALTER TABLE dbo.PS_userVariables ADD CONSTRAINT ...
Bertrand Le Roy
  • 17,731
  • 2
  • 27
  • 33
Aaron Bertrand
  • 272,866
  • 37
  • 466
  • 490
3
SELECT 
   A.TABLE_NAME, 
   A.CONSTRAINT_NAME, 
   B.COLUMN_NAME
FROM 
   INFORMATION_SCHEMA.TABLE_CONSTRAINTS A, 
   INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE B
WHERE 
      CONSTRAINT_TYPE = 'PRIMARY KEY' 
   AND A.CONSTRAINT_NAME = B.CONSTRAINT_NAME
ORDER BY 
   A.TABLE_NAME 

Ref: Pinal Dave @ http://blog.sqlauthority.com/2008/09/06/sql-server-find-primary-key-using-sql-server-management-studio/

Chains
  • 12,541
  • 8
  • 45
  • 62
  • 1
    Unless you need to strictly adhere and cater to cross-platform, I would caution against using the INFORMATION_SCHEMA views. In this case it might be okay but in a lot of cases, since the I_S views are not being maintained, they are missing key information that you need to go to the catalog views for anyway (e.g. indexes - INCLUDE columns, filters, etc. are nowhere to be found in INFORMATION_SCHEMA). – Aaron Bertrand Jul 13 '11 at 18:49
  • @Aaron: I agree, but I'm all for easier is better when I can get away with it, and Info schema should do fine in this particular case, and will scale better anyway. – Chains Jul 13 '11 at 19:11
  • I'm more for consistent code than easier code. People learning to use INFORMATION_SCHEMA then struggle with the information it doesn't include in those outlier cases. Anyway, how will that "scale" better? Do you mean you can copy that exact code and run it on some other RDBMS? That is not what most people refer to as "scale" but rather "portability"... – Aaron Bertrand Jul 13 '11 at 19:18
  • Not trying to argue semantics -- easy is not contrary to consistent. Consistency is good, but that's an argument FOR info schema, not against it. sys views will be re-defined over time, with new versions of the server, and that's what I mean by scalable -- info schema dependencies you write today should still be viable with the next version. Sys view dependencies, on the other hand, will need to be double-checked. Regardless, your way works, my way works, and I think they're BOTH good to know, but one isn't necessarily better than the other -- just depends on the job at hand. – Chains Jul 13 '11 at 19:29
  • Not sure how consistency is an argument FOR INFORMATION_SCHEMA in the case where you are only dealing with SQL Server (most people don't jump to different RDBMS platforms throughout the day, and most companies don't migrate to different platforms overnight). What I'm talking about when I'm saying consistency is using INFORMATION_SCHEMA for primary keys but sys.indexes etc. for indexes. – Aaron Bertrand Jul 13 '11 at 19:32
  • I guess I'd just use whatever I needed. I use both sys views and info schema (and other meta data views, for that matter, not just those two). Are you saying you'd NEVER use info schema, and just ALWAYS use sys views, no matter what? Not sure what the value of consistency is there, but OK -- I'm sure that will work for you. Why do you think MS came-up with info-schema then? Maybe because it anticipates and provides for the most common needs, and toward that end, they wanted to make things...wait for it...*easier*? :-) Just playing with you. You already won the question anyway. :-P – Chains Jul 13 '11 at 19:39
  • Also not sure why you think INFORMATION_SCHEMA dependencies are more reliable than sys views. Anyway, if you want to keep using INFORMATION_SCHEMA when it makes sense for you, by all means, go ahead. I just want to make sure future readers understand that there may be some reasons they don't want to do that (again, because these views are not being developed, they are never going to contain information about new features, object types, or properties in SQL Server). – Aaron Bertrand Jul 13 '11 at 19:40
  • Microsoft came up with INFORMATION_SCHEMA because that is part of the standard. Unfortunately, because they have added welcome features that don't fit into the standard views, the catalog views make a lot more sense for any objects where features are being developed (such as indexes). While I could name a bunch of things I can get from the catalog views that I can't get from the INFORMATION_SCHEMA views, I don't know of any data I can get from an INFORMATION_SCHEMA view that I can't get from a catalog view. Can you name anything? – Aaron Bertrand Jul 13 '11 at 19:42
  • You made your point -- I agree that sys views have more information in them than information schema does. Not relevant to this question, but true nonetheless. – Chains Jul 13 '11 at 19:42
1
DECLARE @TableName varchar(128)
DECLARE @IndexName varchar(128)
DECLARE @Command varchar(1000)

SET @TableName = 'PS_userVariables'

SELECT @IndexName = si.name
FROM sys.tables st
JOIN sys.indexes si ON st.object_id = si.object_id
WHERE st.name = @TableName
  AND si.is_primary_key = 1

SET @Command = 'ALTER TABLE dbo.' + QUOTENAME(@Tablename) + ' DROP CONSTRAINT ' + QUOTENAME(@IndexName) + ';
ALTER TABLE dbo.' + QUOTENAME(@Tablename) + ' ADD PRIMARY KEY (varnumber, subjectID, userID, datasetID, listid, userVarTitle);'
bobs
  • 21,844
  • 12
  • 67
  • 78
0

My use case was updating primary key constraint names generated by Entity Framework 6 to match the primary key naming convention of Entity Framework Core.

As EF uses migrations, I created both an Up and Down migration script, and created a temporary long-lived table to store the old and new constraint names so that I could roll back if needed.

This is the SQL I used to update the primary key constraint names:

-- create a temporary long-lived table
-- it can be deleted when rollback is no longer needed
CREATE TABLE dbo.__OldPrimaryKeyConstraintNames (
  SchemaName NVARCHAR(128) NOT NULL DEFAULT 'dbo',
  TableName NVARCHAR(128) NOT NULL,
  OldPrimaryKeyConstraintName NVARCHAR(128) NOT NULL,
  NewPrimaryKeyConstraintName NVARCHAR(128) NOT NULL
);

-- create a temporary table to hold the data for the script
DECLARE @tbl TABLE (SchemaName NVARCHAR(3), TableName NVARCHAR(128), PrimaryKeyConstraintName NVARCHAR(128));

-- get all primary key constraint names as well as it's schema and table
INSERT INTO @tbl
SELECT SCHEMA_NAME(pk.schema_id), t.name, pk.name
FROM sys.key_constraints pk
INNER JOIN sys.objects t on t.object_id = pk.parent_object_id
WHERE  pk.type = 'PK'

-- row count used for iterating through @tbl
DECLARE @RowCount INT = (SELECT COUNT(*) FROM @tbl);

-- variables used when used for iterating through @tbl
DECLARE @SchemaName NVARCHAR(128)
DECLARE @TableName NVARCHAR(128)
DECLARE @OldPrimaryKeyConstraintName NVARCHAR(128)
DECLARE @NewPrimaryKeyConstraintName NVARCHAR(128)
DECLARE @RenameSql NVARCHAR(MAX)

WHILE @RowCount > 0 BEGIN
  -- get the primary key constraint name, schema, and table name for this iteration
  SELECT @SchemaName = SchemaName, @TableName = TableName, @OldPrimaryKeyConstraintName = PrimaryKeyConstraintName, @NewPrimaryKeyConstraintName = CONCAT('PK_', TableName)
  FROM @tbl
  ORDER BY PrimaryKeyConstraintName DESC OFFSET @RowCount - 1 ROWS FETCH NEXT 1 ROWS ONLY;

  -- store the old and new primary key constraint names
  INSERT __OldPrimaryKeyConstraintNames (SchemaName, TableName, OldPrimaryKeyConstraintName, NewPrimaryKeyConstraintName)
  VALUES (@SchemaName, @TableName, @OldPrimaryKeyConstraintName, @NewPrimaryKeyConstraintName)

  -- perform the rename
  SET @RenameSql = 'sp_rename ' + '''' + @SchemaName + '.' + QUOTENAME(@OldPrimaryKeyConstraintName) + '''' + ', ' + '''' + @NewPrimaryKeyConstraintName + ''''
  EXEC sp_executeSQL @RenameSql

  -- move to the next row
  SET @RowCount -= 1;
END

After running this script, dbo.__OldPrimaryKeyConstraintNames should be populated with the old and new constraint names.

This allows us to revert the renaming if required for whatever reason.

This is the SQL I used to revert the primary key constraint names:

-- create a temporary table to hold the data for the script
DECLARE @tbl TABLE (SchemaName NVARCHAR(3), OldPrimaryKeyConstraintName NVARCHAR(128), NewPrimaryKeyConstraintName NVARCHAR(128));

-- get the old and new constraint names as well as it's schema and table name 
INSERT INTO @tbl
SELECT SchemaName, OldPrimaryKeyConstraintName, NewPrimaryKeyConstraintName
FROM dbo.__OldPrimaryKeyConstraintNames

-- row count used for iterating through @tbl
DECLARE @RowCount INT = (SELECT COUNT(*) FROM @tbl);

-- variables used when used for iterating through @tbl
DECLARE @SchemaName NVARCHAR(128)
DECLARE @TableName NVARCHAR(128)
DECLARE @OldPrimaryKeyConstraintName NVARCHAR(128)
DECLARE @NewPrimaryKeyConstraintName NVARCHAR(128)
DECLARE @RenameSql NVARCHAR(MAX)

WHILE @RowCount > 0 BEGIN
  -- get the old and new constraint name and it's schema for this iteration
  SELECT @SchemaName = SchemaName, @OldPrimaryKeyConstraintName = OldPrimaryKeyConstraintName, @NewPrimaryKeyConstraintName = NewPrimaryKeyConstraintName
  FROM @tbl
  ORDER BY OldPrimaryKeyConstraintName DESC OFFSET @RowCount - 1 ROWS FETCH NEXT 1 ROWS ONLY;

  -- revert the rename
  SET @RenameSql = 'sp_rename ' + '''' + @SchemaName + '.' + QUOTENAME(@NewPrimaryKeyConstraintName) + '''' + ', ' + '''' + @OldPrimaryKeyConstraintName + ''''
  SELECT @RenameSql
  EXEC sp_executeSQL @RenameSql

  -- move to the next row
  SET @RowCount -= 1;
END

-- drop the temporary long-lived table as it is not required 
DROP TABLE IF EXISTS dbo.__OldPrimaryKeyConstraintNames
br3nt
  • 9,017
  • 3
  • 42
  • 63