How is it possible to drop primary key in one step? - sql

I would like to drop the primary key for a table, but keep the column (I know the name of the column, if it helps).
I use this script to get the name of the primary key:
-- Return the name of primary key.
SELECT key_name
FROM sys.key_constraints
WHERE type = 'PK' AND OBJECT_NAME(parent_object_id) = 'my_table';
Then I remove the key like this:
ALTER TABLE Production.TransactionHistoryArchive
DROP CONSTRAINT key_name ;
It works, but I have to run this script in one go. How it is possible to combine drop constraint with a select query?

Will throw this out there in case it's useful to you. Using Dynamic SQL you can build something that you could easily create a procedure for.
declare #Schema nvarchar(20)=N'Production',
#Table nvarchar(50)=N'TransactionHistoryArchive',
#sql nvarchar(100)=''
select #sql='ALTER TABLE '
+ QuoteName(#Schema) + '.'
+ QuoteName(#Table)
+ ' DROP CONSTRAINT '
+ QuoteName(name)
from sys.key_constraints
where type = 'PK' and Object_Name(parent_object_id) = #Table and [schema_id]=Schema_Id(#Schema)
print #sql
exec sp_executesql #sql

Related

Add Primary Key constraint automatically

I have the following table InfoSchema which contains the SchemaName and the TableName of each table in my test database :
SchemaName TableName
dbo Employee
dbo Department
Function Company
Finance Payslips
Sub ProjectSub
I want to add for each table the constraint PrimaryKey to the column ending with ID or Id :
In dbo.Employee there is one column EmployeeId so the query will be like below :
ALTER TABLE dbo.Employee
ADD CONSTRAINT Employee_pk PRIMARY KEY (EmployeeId);
For Sub.ProjectSub there are 3 columns ending with Id :
ProjectId
CompanyId
SubId
The constraint will be added at the first column appearing in the structure of the table.
As I mention in my comment, you can use a dynamic statement to create the statements. I very strongly suggest looking over the SQL generated, however, so I do not include an EXEC sp_executesql statement here. PRINT or SELECT the value of #SQL and check it over first, then run the statements as you need:
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SQL = STUFF((SELECT #CRLF + #CRLF +
N'ALTER TABLE ' + QUOTENAME(s.[name]) + N'.' + QUOTENAME(t.[name]) + #CRLF +
N'ADD CONSTRAINT ' + QUOTENAME(CONCAT(t.[name],N'_PK')) + N' PRIMARY KEY (' + QUOTENAME(c.[name]) + N');'
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
CROSS APPLY (SELECT TOP 1 *
FROM sys.columns c
WHERE c.object_id = t.object_id
AND c.name LIKE '%id'
ORDER BY c.column_id ASC) c
WHERE NOT EXISTS (SELECT 1
FROM sys.key_constraints k
WHERE k.[type] = 'PK'
AND k.parent_object_id = t.object_id)
FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,4,N'');
PRINT #SQL;
This assumes that the first column, ordinally, needs to be the PK, and it will not attempt to create a PK on a table that already has one.
Not possible. There is no automatism for this in SQL Server, so it will require at least a script to run over the db to identify tables and columns and issue modify statements. Which is not "automatic" as it will not RUN automatic - you need to run it in a second step.

SQL Server : drop multiple unknown constraints from the same table

I checked the answer given in this SO question , to drop constraints whose names are not known.
In SQL Server, can we assume they will always be in the same format given? (FK__TABLENAME__KEYPREFIX)?
What does T(c) define?
If two such constraints need to be dropped from the same table, is the following correct?
DECLARE #sql1 NVARCHAR(MAX);
DECLARE #sql2 NVARCHAR(MAX);
SELECT
#sql1 = c
FROM
(SELECT
'ALTER TABLE DOC_INVS_1 DROP CONSTRAINT ' + CONSTRAINT_NAME + '; '
FROM
INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE
TABLE_NAME = 'DOC_INVS_1'
AND CONSTRAINT_NAME LIKE 'FK__DOC_INVS___kntr%') T(c);
SELECT
#sql2 = c
FROM
(SELECT
'ALTER TABLE DOC_INVS_1 DROP CONSTRAINT ' + CONSTRAINT_NAME + '; '
FROM
INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE
TABLE_NAME = 'DOC_INVS_1'
AND CONSTRAINT_NAME LIKE 'FK__DOC_INVS___aaaa%') T(c);
EXEC(#sql1);
EXEC(#sql2);
Note
The actual end goal is to add a 'CASCADE ON DELETE' to a foreign key, which had been added to the wrong key. I learned that you can't just add the cascade option by altering, so the approach is to drop both and then create again.

Convert multiple int primary keys from different tables into Identity

I have a dozen or so different databases with similar structure, with around 50 different tables each, and some of these tables used a sequential int [Id] as Primary Key and Identity.
At some point, these databases were migrated to a different remote infrastructure, namely from Azure to AWS, and somewhere in the process, the Identity property was lost, and as such, new automated inserts are not working as it fails to auto-increment the Id and generate a valid primary key.
I've tried multiple solutions, but am struggling to get any of them to work, as SQL-Server seems extremely finicky with letting you mess with or alter value of Identity columns in any way, and it's driving me insane.
I need to re-enable the Identity in multiple different tables, in multiple databases, but the solutions I've found so far are either extremely convoluted or impractical, for what seems to be a relatively simple problem.
tl;dr - How can I enable Identity for all my int primary keys in multiple different tables at the same time?
My approach so far:
CREATE PROC Fix_Identity #tableName varchar(50)
AS
BEGIN
IF NOT EXISTS(SELECT * FROM sys.identity_columns WHERE OBJECT_NAME(object_id) = #tableName)
BEGIN
DECLARE #keyName varchar(100) = 'PK_dbo.' + #tableName;
DECLARE #reName varchar(100) = #tableName + '.Id_new';
EXEC ('Alter Table ' + #tableName + ' DROP CONSTRAINT ['+ #keyName +']');
EXEC ('Alter Table ' + #tableName + ' ADD Id_new INT IDENTITY(1, 1) PRIMARY KEY');
EXEC ('SET IDENTITY_INSERT [dbo].[' + #tableName + '] ON');
EXEC ('UPDATE ' + #tableName + ' SET [Id_new] = [Id]');
EXEC ('SET IDENTITY_INSERT [dbo].[' + #tableName + '] OFF');
EXEC ('Alter Table ' + #tableName + ' DROP COLUMN Id');
EXEC sp_rename #reName, 'Id', 'Column';
END
END;
I tried creating this procedure, to be executed once per table, but i'm having problems with the UPDATE statement, which I require to guarantee that the new values Identity column will have the same value as the old Id column, but this approach currently doesn't work because:
Cannot update identity column 'Id_new'.
There are assumptions made in that script that you might want to look out for specifically with assuming the PK constraint name. You might want to double check that on all of your tables before. The rest of your script seemed to make sense to me except you will need to reseed the index after updating the data in the new column.
See if this helps:
select t.name AS [Table],c.Name AS [Non-Indent PK],i.name AS [PK Constraint]
from sys.columns c
inner join sys.tables t On c.object_id=t.object_id
inner join sys.indexes i ON i.object_id=c.object_id
AND i.is_primary_key = 1
INNER JOIN sys.index_columns ic ON i.object_id=ic.object_id
AND i.index_id = ic.index_id
AND ic.column_id=c.column_id
WHERE c.Is_Identity=0
Instead of adding an identity, create a sequence and default constraint
declare #table nvarchar(50) = N'dbo.T'
declare #sql nvarchar(max) = (N'select #maxID = max(Id) FROM ' + #table);
declare #maxID int
exec sp_executesql #sql, N'#maxID int output', #maxID=#maxID OUTPUT;
set #sql = concat('create sequence ', #table, '_sequence start with ', #maxID + 1, ' increment by 1')
exec(#sql)
set #sql = concat('alter table ', #table, ' add default(next value for ', #table, '_sequence) for ID ')
exec(#sql)

SQL Server : change PK type from uniqueidentifier to int

I have designed a database that has primary key type uniqueidentifier for all tables. It has 50 tables and existing data. Then, I knew it was a bad idea. I want to change to int pk type from uniqueidentifier.
How can I do? How do I move the foreign key?
The general steps to take are (links are to MSDN information on performing these steps with SQL Server):
Delete the current Primary Key constraint - Delete Primary Key
Alter the table to drop your Unique Identifier field and create a new Integer field - Alter Table
Create a new Primary Key on the new Integer Field Create Primary Key
just done a script, tested on a couple of tables and works fine, test it yourself before you execute it in your production environment.
The Script does the following.
Find all the columns where Primary key has data type Uniqueidentifier.
Drop the primary key constraint.
drop the Uniqueidentifier column.
Add INT column with Identity starting with seed value of 1 and increment of 1.
Make that column the Primary key column in that table.
declare #table SYSNAME,#Schema SYSNAME
, #PkColumn SYSNAME, #ContName SYSNAME
,#Sql nvarchar(max)
DECLARE db_cursor CURSOR LOCAL FORWARD_ONLY FOR
SELECT OBJECT_NAME(O.object_id) AS ConstraintName
,SCHEMA_NAME(O.schema_id) AS SchemaName
,OBJECT_NAME(O.parent_object_id) AS TableName
,c.name ColumName
FROM sys.objects o
inner join sys.columns c ON o.parent_object_id = c.object_id
inner join sys.types t ON c.user_type_id = t.user_type_id
WHERE o.type_desc = 'PRIMARY_KEY_CONSTRAINT'
and t.name = 'uniqueidentifier'
Open db_cursor
fetch next from db_cursor into #ContName , #Schema , #table, #PkColumn
while (##FETCH_STATUS = 0)
BEGIN
SET #Sql= 'ALTER TABLE ' + QUOTENAME(#Schema) +'.'+ QUOTENAME(#table)
+ ' DROP CONSTRAINT ' + QUOTENAME(#ContName)
Exec sp_executesql #Sql
SET #Sql= 'ALTER TABLE ' + QUOTENAME(#Schema) +'.'+ QUOTENAME(#table)
+ ' DROP COLUMN ' + QUOTENAME(#PkColumn)
Exec sp_executesql #Sql
SET #Sql= 'ALTER TABLE ' + QUOTENAME(#Schema) +'.'+ QUOTENAME(#table)
+ ' ADD ' + QUOTENAME(#PkColumn)
+ ' INT NOT NULL IDENTITY(1,1) '
Exec sp_executesql #Sql
SET #Sql= 'ALTER TABLE ' + QUOTENAME(#Schema) +'.'+ QUOTENAME(#table)
+ ' ADD CONSTRAINT '+ QUOTENAME(#table+'_'+ #PkColumn)
+ ' PRIMARY KEY ('+QUOTENAME(#PkColumn)+')'
Exec sp_executesql #Sql
fetch next from db_cursor into #ContName , #Schema , #table, #PkColumn
END
Close db_cursor
deallocate db_cursor

How to drop a list of SQL Server tables, ignoring constraints?

I have a list of half a dozen MSSQL 2008 tables that I would like to remove at once from my database. The data has been entirely migrated to new tables. There is no reference in the new tables to the old tables.
The problem being that old tables comes with loads of inner FK constraints that have been autogenerated by a tool (aspnet_regsql actually). Hence dropping manually all constraints is a real pain.
How can I can drop the old tables ignoring all inner constraints?
It depends on how you want to drop the tables. If list of tables need to drop covers almost above 20 % of tables under your DB.
Then I will disable all the constraints in that DB under my script and drop the tables and Enable the constraints under the same script.
--To Disable a Constraint at DB level
EXEC sp_MSForEachTable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'
--Write the code to DROP tables
DROP TABLE TABLENAME
DROP TABLE TABLENAME
DROP TABLE TABLENAME
--To Enable a Constraint at DB level
EXEC sp_MSForEachTable 'ALTER TABLE ? CHECK CONSTRAINT ALL'
Finally to check the Status of your constraints fire up this Query.
--Checks the Status of Constraints
SELECT (CASE
WHEN OBJECTPROPERTY(CONSTID, 'CNSTISDISABLED') = 0 THEN 'ENABLED'
ELSE 'DISABLED'
END) AS STATUS,
OBJECT_NAME(CONSTID) AS CONSTRAINT_NAME,
OBJECT_NAME(FKEYID) AS TABLE_NAME,
COL_NAME(FKEYID, FKEY) AS COLUMN_NAME,
OBJECT_NAME(RKEYID) AS REFERENCED_TABLE_NAME,
COL_NAME(RKEYID, RKEY) AS REFERENCED_COLUMN_NAME
FROM SYSFOREIGNKEYS
ORDER BY TABLE_NAME, CONSTRAINT_NAME,REFERENCED_TABLE_NAME, KEYNO
If you dont want to disable the constraints at Database level then make a list of tables which you want to drop.
Step1 : Check the Constraints associated with thos tables
SELECT *
FROM sys.foreign_keys
WHERE referenced_object_id = object_id('dbo.Tablename')
Step2 : Disable the Constraints which are associated with these tables.
ALTER TABLE MyTable NOCHECK CONSTRAINT MyConstraint
Step3 : Drop the tables
DROP TABLE TABLENAME
A simple DROP TABLE dbo.MyTable will ignore all constraints (and triggers) except foreign keys (unless you drop the child/referencing table first) where you may have to drop these first.
Edit: after comment:
There is no automatic way. You'll have to iterate through sys.foreign_keys and generate some ALTER TABLE statements.
Run the following script to delete all the constraints in all tables under current DB and then run the drop table statements.
DECLARE #dropAllConstraints NVARCHAR(MAX) = N'';
SELECT #dropAllConstraints += N'
ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(parent_object_id))
+ '.' + QUOTENAME(OBJECT_NAME(parent_object_id)) +
' DROP CONSTRAINT ' + QUOTENAME(name) + ';'
FROM sys.foreign_keys;
EXEC sp_executesql #dropAllConstraints
I found a reasonable(ish) way to do it by making SQL write the SQL to drop the constraints:
select concat("alter table ", table_name, " drop ", constraint_type ," ", constraint_name, ";")
from information_schema.table_constraints
where table_name like 'somefoo_%'
and
constraint_type <> "PRIMARY KEY";
You will want to modify the table name to suit your needs, or possibly select against other column/values.
Also, this would select any non primary key constraint, which might be too big of a sledgehammer. Maybe you need to just set it to =?
I am not a DBA. there may be better ways to do this, but it worked well enough for my purposes.
I finally found the solution based on the script provided by Jason Presley. This script automatically removes all constraints in the DB. It's easy to add a WHERE clause so that it only applies to the set of concerned tables. After that, dropping all tables is a straightforward.
Be very careful with the following script, all tables, views, functions, stored procedures and user defined types from a database ignoring all constraints.
/*
Description: This script will remove all tables, views, functions, stored procedures and user defined types from a database.
*/
declare #n char(1)
set #n = char(10)
declare #stmt nvarchar(max)
-- procedures
select #stmt = isnull( #stmt + #n, '' ) +
'drop procedure [' + schema_name(schema_id) + '].[' + name + ']'
from sys.procedures
-- check constraints
select #stmt = isnull( #stmt + #n, '' ) +
'alter table [' + schema_name(schema_id) + '].[' + object_name( parent_object_id ) + '] drop constraint [' + name + ']'
from sys.check_constraints
-- functions
select #stmt = isnull( #stmt + #n, '' ) +
'drop function [' + schema_name(schema_id) + '].[' + name + ']'
from sys.objects
where type in ( 'FN', 'IF', 'TF' )
-- views
select #stmt = isnull( #stmt + #n, '' ) +
'drop view [' + schema_name(schema_id) + '].[' + name + ']'
from sys.views
-- foreign keys
select #stmt = isnull( #stmt + #n, '' ) +
'alter table [' + schema_name(schema_id) + '].[' + object_name( parent_object_id ) + '] drop constraint [' + name + ']'
from sys.foreign_keys
-- tables
select #stmt = isnull( #stmt + #n, '' ) +
'drop table [' + schema_name(schema_id) + '].[' + name + ']'
from sys.tables
-- user defined types
select #stmt = isnull( #stmt + #n, '' ) +
'drop type [' + schema_name(schema_id) + '].[' + name + ']'
from sys.types
where is_user_defined = 1
exec sp_executesql #stmt
I suspect that you would have to do an 'alter' command on the offending tables before the drop to remove the forigen key contraints.
ALTER TABLE Orders DROP FOREIGN KEY fk_PerOrders;
DROP TABLE Orders;
Of course if you drop the child tables first, then you wont have this problem.
(unless you have table A contraint to table B and table B constraint to A, then you will need to Alter one of the tables, e.g. A to remove the constraint)
e.g. this WONT work, since Orders has a contraint from Order_Lines
DROP TABLE Orders;
DROP TABLE Order_lines;
e.g. this will work
DROP TABLE Order_lines;
DROP TABLE Orders;