I try to drop primary key:
alter table mytable DROP (
SELECT CONSTRAINT_NAME FROM information_schema.table_constraints
where table_name='mytable' and constraint_type = 'PRIMARY KEY')
How to do it correctly?
First, don't use INFORMATION_SCHEMA views. Second, always use the schema prefix when referencing objects in DDL or DML. Third, you need to use dynamic SQL, because you can't parameterize DDL that way.
DECLARE #sql NVARCHAR(MAX);
SELECT #sql = N'ALTER TABLE dbo.mytable DROP CONSTRAINT '
+ QUOTENAME(k.name) + ';'
FROM sys.key_constraints k
INNER JOIN sys.objects AS o
ON k.parent_object_id = o.[object_id]
INNER JOIN sys.schemas AS s
ON o.[schema_id] = s.[schema_id]
WHERE o.name = N'mytable'
AND s.name = N'dbo';
PRINT #sql;
-- EXEC sp_executesql #sql;
Now, this won't necessarily work, for example if there are other tables with foreign keys referencing this primary key, you will need to seek those out and fix them first.
Related
I have got the following code in t-SQL:
alter table Persons
drop primary key;
And the message:
Msg 156, Level 15, State 1, Line 10
Incorrect syntax near the keyword 'primary'.
I have checked different combinations of syntax and none have worked.
What is wrong here?
This is how a table has been created - it is just beggining of studying, so very simple one with only two constraints.
create table Persons(
PersonID int not null primary key,
LastName varchar(255),
FirstName varchar(255),
Address varchar(255),
City varchar(255)
);
It's not DROP PRIMARY KEY it's DROP CONSTRAINT {Object Name}. For example:
CREATE TABLE dbo.YourTable (ID int NOT NULL);
GO
ALTER TABLE dbo.YourTable ADD CONSTRAINT PK_YourTable PRIMARY KEY CLUSTERED (ID);
GO
ALTER TABLE dbo.YourTable DROP CONSTRAINT PK_YourTable;
GO
DROP TABLE dbo.YourTable;
This is why it's so important to explicitly name your objects, as shown above, as you now don't know what the name of the CONSTRAINT is. You could, however, get the name with the following:
SELECT kc.[name]
FROM sys.key_constraints kc
JOIN sys.tables t ON kc.parent_object_id = t.object_id
JOIN sys.schemas s ON t.schema_id = t.schema_id
WHERE s.[name] = N'dbo'
AND t.[name] = N'YourTable'
AND kc.[type] = 'PK';
If you really didn't want to find out the name and then write the statement, you could use a dynamic statement:
DECLARE #SQL nvarchar(MAX);
SET #SQL = (SELECT N'ALTER TABLE ' + QUOTENAME(s.[name]) + N'.' + QUOTENAME(t.[name]) + N' DROP CONSTRAINT ' + QUOTENAME(kc.[name]) + N';'
FROM sys.key_constraints kc
JOIN sys.tables t ON kc.parent_object_id = t.object_id
JOIN sys.schemas s ON t.schema_id = t.schema_id
WHERE s.[name] = N'dbo'
AND t.[name] = N'YourTable'
AND kc.[type] = 'PK');
EXEC sys.sp_executesql #SQL;
I have several tables with a foreign key constraint that has the option ON DELETE CASCADE. Every table belongs to the same schema called datasets.
I'm able to retrieve the complete list of tables using :
SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA ='datasets'
For each table I would like to remove the ON DELETE CASCADE option on the foreign key constraint named FK_[TABLENAME]_SerieID where [TABLENAME] corresponds to the name of the table (and SerieId is the same foreign key across tables).
I am able to perform the operation for a particular table, for instance the table called Table1 using :
ALTER TABLE datasets.Table1
DROP CONSTRAINT FK_Table1_SerieID
ALTER TABLE datasets.Table1
ADD CONSTRAINT FK_Table1_SerieID
FOREIGN KEY (Serie_Id) REFERENCES[dbo].[Serie](SerieID)
ON DELETE NO ACTION
GO
I would like to perform the above operation for each table that belong to the schema datasets . I'm new to T-SQL and I don't know how to do it.
Should I use a cursor? Can you help me with this?
I'm using SQL Server 2016.
I would not reinvent the wheel. There is excellent script written by Aaron Bertrand: Drop and Re-Create All Foreign Key Constraints in SQL Server.
You could easily extend it to handle NO ACTION case and specific schema by adding simple WHERE restriction:
DECLARE #drop NVARCHAR(MAX) = N'',
#create NVARCHAR(MAX) = N'';
-- drop is easy,just build a simple concatenated list from sys.foreign_keys:
SELECT #drop += N'
ALTER TABLE ' + QUOTENAME(cs.name) + '.' + QUOTENAME(ct.name)
+ ' DROP CONSTRAINT ' + QUOTENAME(fk.name) + ';'
FROM sys.foreign_keys AS fk
INNER JOIN sys.tables AS ct
ON fk.parent_object_id = ct.[object_id]
INNER JOIN sys.schemas AS cs
ON ct.[schema_id] = cs.[schema_id]
WHERE delete_referential_action_desc <> 'NO_ACTION' -- here
AND cs.name = 'datasets';
-- create is a little more complex. We need to generate the list of
-- columns on both sides of the constraint, even though in most cases
-- there is only one column.
SELECT #create += N'
ALTER TABLE '
+ QUOTENAME(cs.name) + '.' + QUOTENAME(ct.name)
+ ' ADD CONSTRAINT ' + QUOTENAME(fk.name)
+ ' FOREIGN KEY (' + STUFF((SELECT ',' + QUOTENAME(c.name)
-- get all the columns in the constraint table
FROM sys.columns AS c
INNER JOIN sys.foreign_key_columns AS fkc
ON fkc.parent_column_id = c.column_id
AND fkc.parent_object_id = c.[object_id]
WHERE fkc.constraint_object_id = fk.[object_id]
ORDER BY fkc.constraint_column_id
FOR XML PATH(N''), TYPE).value(N'.[1]', N'nvarchar(max)'),1,1,N'')
+ ') REFERENCES ' + QUOTENAME(rs.name) + '.' + QUOTENAME(rt.name)
+ '(' + STUFF((SELECT ',' + QUOTENAME(c.name)
-- get all the referenced columns
FROM sys.columns AS c
INNER JOIN sys.foreign_key_columns AS fkc
ON fkc.referenced_column_id = c.column_id
AND fkc.referenced_object_id = c.[object_id]
WHERE fkc.constraint_object_id = fk.[object_id]
ORDER BY fkc.constraint_column_id
FOR XML PATH(N''), TYPE).value(N'.[1]', N'nvarchar(max)'),1,1,N'') + ');'
FROM sys.foreign_keys AS fk
INNER JOIN sys.tables AS rt -- referenced table
ON fk.referenced_object_id = rt.[object_id]
INNER JOIN sys.schemas AS rs
ON rt.[schema_id] = rs.[schema_id]
INNER JOIN sys.tables AS ct -- constraint table
ON fk.parent_object_id = ct.[object_id]
INNER JOIN sys.schemas AS cs
ON ct.[schema_id] = cs.[schema_id]
WHERE rt.is_ms_shipped = 0 AND ct.is_ms_shipped = 0
AND delete_referential_action_desc <> 'NO_ACTION' -- here
AND cs.name = 'datasets';
print(#drop);
print(#create);
-- ...
DBFiddle Demo
One warning! Please avoid adding ORDER BY.
This script uses
SELECT #drop += N'...'
<=>
SELECT #drop = #drop + N'...'
and it may start producing incorrect results. More nvarchar concatenation / index / nvarchar(max) inexplicable behavior
How to drop a column which is having Default constraint in SQL Server 2008?
My query is
alter table tbloffers
drop column checkin
I am getting below error
ALTER TABLE DROP COLUMN checkin failed because one or more objects access this column.
Can anyone correct my query to drop a column with constraint?
First you should drop the problematic DEFAULT constraint, after that you can drop the column
alter table tbloffers drop constraint [ConstraintName]
go
alter table tbloffers drop column checkin
But the error may appear from other reasons - for example the user defined function or view with SCHEMABINDING option set for them.
UPD:
Completely automated dropping of constraints script:
DECLARE #sql NVARCHAR(MAX)
WHILE 1=1
BEGIN
SELECT TOP 1 #sql = N'alter table tbloffers drop constraint ['+dc.NAME+N']'
from sys.default_constraints dc
JOIN sys.columns c
ON c.default_object_id = dc.object_id
WHERE
dc.parent_object_id = OBJECT_ID('tbloffers')
AND c.name = N'checkin'
IF ##ROWCOUNT = 0 BREAK
EXEC (#sql)
END
Here's another way to drop a default constraint with an unknown name without having to first run a separate query to get the constraint name:
DECLARE #ConstraintName nvarchar(200)
SELECT #ConstraintName = Name FROM SYS.DEFAULT_CONSTRAINTS
WHERE PARENT_OBJECT_ID = OBJECT_ID('__TableName__')
AND PARENT_COLUMN_ID = (SELECT column_id FROM sys.columns
WHERE NAME = N'__ColumnName__'
AND object_id = OBJECT_ID(N'__TableName__'))
IF #ConstraintName IS NOT NULL
EXEC('ALTER TABLE __TableName__ DROP CONSTRAINT ' + #ConstraintName)
You can also drop the column and its constraint(s) in a single statement rather than individually.
CREATE TABLE #T
(
Col1 INT CONSTRAINT UQ UNIQUE CONSTRAINT CK CHECK (Col1 > 5),
Col2 INT
)
ALTER TABLE #T DROP CONSTRAINT UQ ,
CONSTRAINT CK,
COLUMN Col1
DROP TABLE #T
Some dynamic SQL that will look up the names of dependent check constraints and default constraints and drop them along with the column is below
(but not other possible column dependencies such as foreign keys, unique and primary key constraints, computed columns, indexes)
CREATE TABLE [dbo].[TestTable]
(
A INT DEFAULT '1' CHECK (A=1),
B INT,
CHECK (A > B)
)
GO
DECLARE #TwoPartTableNameQuoted nvarchar(500) = '[dbo].[TestTable]',
#ColumnNameUnQuoted sysname = 'A',
#DynSQL NVARCHAR(MAX);
SELECT #DynSQL =
'ALTER TABLE ' + #TwoPartTableNameQuoted + ' DROP' +
ISNULL(' CONSTRAINT ' + QUOTENAME(OBJECT_NAME(c.default_object_id)) + ',','') +
ISNULL(check_constraints,'') +
' COLUMN ' + QUOTENAME(#ColumnNameUnQuoted)
FROM sys.columns c
CROSS APPLY (SELECT ' CONSTRAINT ' + QUOTENAME(OBJECT_NAME(referencing_id)) + ','
FROM sys.sql_expression_dependencies
WHERE referenced_id = c.object_id
AND referenced_minor_id = c.column_id
AND OBJECTPROPERTYEX(referencing_id, 'BaseType') = 'C'
FOR XML PATH('')) ck(check_constraints)
WHERE c.object_id = object_id(#TwoPartTableNameQuoted)
AND c.name = #ColumnNameUnQuoted;
PRINT #DynSQL;
EXEC (#DynSQL);
Find the default constraint with this query here:
SELECT
df.name 'Constraint Name' ,
t.name 'Table Name',
c.NAME 'Column Name'
FROM sys.default_constraints df
INNER JOIN sys.tables t ON df.parent_object_id = t.object_id
INNER JOIN sys.columns c ON df.parent_object_id = c.object_id AND df.parent_column_id = c.column_id
This gives you the name of the default constraint, as well as the table and column name.
When you have that information you need to first drop the default constraint:
ALTER TABLE dbo.YourTable
DROP CONSTRAINT name-of-the-default-constraint-here
and then you can drop the column
ALTER TABLE dbo.YourTable DROP COLUMN YourColumn
The following worked for me against a SQL Azure backend (using SQL Server Management Studio), so YMMV, but, if it works for you, it's waaaaay simpler than the other solutions.
ALTER TABLE MyTable
DROP CONSTRAINT FK_MyColumn
CONSTRAINT DK_MyColumn
-- etc...
COLUMN MyColumn
GO
Based on the previous answers, I have added it as a stored procedure to simplify the deletion of a column when it has attached constraints
CREATE OR ALTER PROC DROP_COLUMN(#TableName nvarchar(200), #ColumnName nvarchar(200))
AS
BEGIN
DECLARE #ConstraintName nvarchar(200)
SELECT #ConstraintName = Name FROM SYS.DEFAULT_CONSTRAINTS
WHERE PARENT_OBJECT_ID = OBJECT_ID(#TableName)
AND PARENT_COLUMN_ID = (SELECT column_id FROM sys.columns
WHERE NAME = #ColumnName
AND object_id = OBJECT_ID(#TableName))
IF #ConstraintName IS NOT NULL
EXEC('ALTER TABLE '+#TableName+' DROP CONSTRAINT ' + #ConstraintName)
EXEC('ALTER TABLE '+#TableName+' DROP COLUMN IF EXISTS ' + #ColumnName)
END
GO
--example:
EXEC DROP_COLUMN N'VEHICLES', N'SCMT'
EXEC DROP_COLUMN N'VEHICLES', N'SSC'
EXEC DROP_COLUMN N'VEHICLES', N'RS'
EXEC DROP_COLUMN N'VEHICLES', N'RCEC'
DROP PROCEDURE IF EXISTS DROP_COLUMN
I got the same:
ALTER TABLE DROP COLUMN failed because one or more objects access this column message.
My column had an index which needed to be deleted first. Using sys.indexes did the trick:
DECLARE #sql VARCHAR(max)
SELECT #sql = 'DROP INDEX ' + idx.NAME + ' ON tblName'
FROM sys.indexes idx
INNER JOIN sys.tables tbl ON idx.object_id = tbl.object_id
INNER JOIN sys.index_columns idxCol ON idx.index_id = idxCol.index_id
INNER JOIN sys.columns col ON idxCol.column_id = col.column_id
WHERE idx.type <> 0
AND tbl.NAME = 'tblName'
AND col.NAME = 'colName'
EXEC sp_executeSql #sql
GO
ALTER TABLE tblName
DROP COLUMN colName
I have updated script a little bit to my SQL server version
DECLARE #sql nvarchar(max)
SELECT #sql = 'ALTER TABLE `table_name` DROP CONSTRAINT ' + df.NAME
FROM sys.default_constraints df
INNER JOIN sys.tables t ON df.parent_object_id = t.object_id
INNER JOIN sys.columns c ON df.parent_object_id = c.object_id AND df.parent_column_id = c.column_id
where t.name = 'table_name' and c.name = 'column_name'
EXEC sp_executeSql #sql
GO
ALTER TABLE table_name
DROP COLUMN column_name;
It's not always just a default constraint that prevents from droping a column and sometimes indexes can also block you from droping the constraint.
So I wrote a procedure that drops any index or constraint on a column and the column it self at the end.
IF OBJECT_ID ('ADM_delete_column', 'P') IS NOT NULL
DROP procedure ADM_delete_column;
GO
CREATE procedure ADM_delete_column
#table_name_in nvarchar(300)
, #column_name_in nvarchar(300)
AS
BEGIN
/* Author: Matthis (matthis#online.ms at 2019.07.20)
License CC BY (creativecommons.org)
Desc: Administrative procedure that drops columns at MS SQL Server
- if there is an index or constraint on the column
that will be dropped in advice
=> input parameters are TABLE NAME and COLUMN NAME as STRING
*/
SET NOCOUNT ON
--drop index if exist (search first if there is a index on the column)
declare #idx_name VARCHAR(100)
SELECT top 1 #idx_name = i.name
from sys.tables t
join sys.columns c
on t.object_id = c.object_id
join sys.index_columns ic
on c.object_id = ic.object_id
and c.column_id = ic.column_id
join sys.indexes i
on i.object_id = ic.object_id
and i.index_id = ic.index_id
where t.name like #table_name_in
and c.name like #column_name_in
if #idx_name is not null
begin
print concat('DROP INDEX ', #idx_name, ' ON ', #table_name_in)
exec ('DROP INDEX ' + #idx_name + ' ON ' + #table_name_in)
end
--drop fk constraint if exist (search first if there is a constraint on the column)
declare #fk_name VARCHAR(100)
SELECT top 1 #fk_name = CONSTRAINT_NAME
from INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE
where TABLE_NAME like #table_name_in
and COLUMN_NAME like #column_name_in
if #fk_name is not null
begin
print concat('ALTER TABLE ', #table_name_in, ' DROP CONSTRAINT ', #fk_name)
exec ('ALTER TABLE ' + #table_name_in + ' DROP CONSTRAINT ' + #fk_name)
end
--drop column if exist
declare #column_name VARCHAR(100)
SELECT top 1 #column_name = COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME like concat('%',#column_name_in,'%')
if #column_name is not null
begin
print concat('ALTER TABLE ', #table_name_in, ' DROP COLUMN ', #column_name)
exec ('ALTER TABLE ' + #table_name_in + ' DROP COLUMN ' + #column_name)
end
end;
GO
--to run the procedure use this execute and fill the parameters
execute ADM_delete_column
#table_name_in = ''
, #column_name_in = ''
;
I am using SQL Server. I want to add a single column named [DateCreated] to multiple tables. Is it possible that with a single statement I could add this column to all the tables in my database?
I stumble upon an answer by Joe Steffaneli in which he suggested a query which in turn returns rows consisting Alter table statements.
Query is as follows :
select 'alter table ' + quotename(s.name) + '.' + quotename(t.name) + ' add [DateModified] datetime'
from sys.columns c
inner join sys.tables t
on c.object_id = t.object_id
inner join sys.schemas s
on t.schema_id = s.schema_id
left join sys.columns c2
on t.object_id = c2.object_id
and c2.name = 'DateModified'
where c.name = 'DateCreated'
and t.type = 'U'
and c2.column_id is null /* DateModified column does not already exist */
Is there any way that I can execute returned rows? Sorry for English.
You probably need something like this. Check that the script does what you want before running it (adds a non null column with a default value of getdate())!
DECLARE #Dynsql nvarchar(max)
SET #Dynsql = ''
SELECT #Dynsql = #Dynsql + '
alter table ' + QUOTENAME(SCHEMA_NAME(schema_id))+ '.' + QUOTENAME(name) +
' add [DateCreated] datetime not null default getdate()'
FROM sys.tables
WHERE type='U' and object_id NOT IN (select object_id from sys.columns where name='DateCreated')
EXEC (#Dynsql)
You can use this query
I use the where clause in this query (it is optional) to find all tables that have ID and add the new field to them.
you can change where clause for example find all tables that have BusinessId column and add new filed or remove where clause and add for all tables
DECLARE #SQL varchar(max)
SELECT #SQL= STUFF((SELECT ';' + 'ALTER TABLE ' + t.TABLE_SCHEMA+'.' +t.TABLE_NAME+ ' ADD newfield nvarchar(max)'
from information_schema.tables t
inner join information_schema.columns c on c.table_name = t.table_name
and c.table_schema = t.table_schema
where c.column_name = 'id'
and t.table_schema not in ('information_schema', 'pg_catalog')
and t.table_type = 'BASE TABLE'
FOR XML PATH('')),1,1,'')
EXEC (#SQL)
This stored procedure works fine
USE databaseName;
exec sp_msforeachtable 'alter table ? Add [DateCreated] datetime not null default getdate()';
declare #i int
set #i=1
while(#i<45)
begin
declare #sql varchar(200)
with TempTable as (select Row_number() over(order by stdID) as RowNo,* from SomeTable)
select #sql= 'alter table Table'+(select Name from TempTable where RowNo=#i)+' add NewColumn int'
exec (#sql)
set #i=#i+1
end
No, there is no single statement that will add a column to all the tables in your database.
Next time please tag your question with the RDBMS you're using. If there were a way, we wouldn't be able to give you the command without knowing which database system you are using.
I have a set of tables (say Account, Customer) in a schema (say dbo) and I have some other tables (say Order, OrderItem) in another schema (say inventory). There's a relationship between the Order table and the Customer table. I want to delete all the relationships between the tables in the first schema (dbo) and the tables in the second schema (inventory), without deleting the relationships between tables inside the same schema.
Is that possible? Any help appreciated.
Use the metadata:
SELECT *
FROM INFORMATION_SCHEMA.CONSTRAINT_TABLE_USAGE
WHERE CONSTRAINT_NAME IN ( SELECT CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE CONSTRAINT_TYPE = 'FOREIGN KEY' )
SELECT *
FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE
WHERE CONSTRAINT_NAME IN ( SELECT CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE CONSTRAINT_TYPE = 'FOREIGN KEY' )
SELECT *
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE CONSTRAINT_TYPE = 'FOREIGN KEY'
Add filter criteria to find the constraints you want to DROP, then insert them in the template:
ALTER TABLE {TABLE_SCHEMA}.{TABLE_NAME} DROP {CONSTRAINT_NAME}
And execute with dynamic SQL.
Please use the script below by copying and pasting it to the SQL Studio:
SELECT 'ALTER TABLE ' + TABLE_SCHEMA + '.[' + TABLE_NAME + '] DROP [' + CONSTRAINT_NAME + ']'
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE CONSTRAINT_TYPE = 'FOREIGN KEY'
you can get the available FKs that exist in your db by below script and then delete them :
select * from sys.objects o
join sys.schemas s on o.schema_id = s.schema_id
where o.type = 'F'
after that delete like below
ALTER TABLE {TABLE_SCHEMA}.{TABLE_NAME} DROP {CONSTRAINT_NAME}
I won't swear this will work on SQL 2005, as I don't have an instance to test it with, but if it does it will make it a simple copy/paste job in SSMS. I'll leave it to you to iterate the results and execute if that's what you want.
Replace schema_1 and schema_2 with the schema names you're trying to find relationships between.
declare #s1 int
declare #s2 int
set #s1 = schema_id( 'schema_1' )
set #s2 = schema_id( 'schema_2' )
select
N'alter table [' + s.name + N'].[' + o_p.name + N'] drop constraint [' + fk.name + N']'
from sys.foreign_keys fk
join sys.schemas s on ( s.schema_id = fk.schema_id )
join sys.objects o_p on ( o_p.object_id = fk.parent_object_id )
join sys.objects o_r on ( o_r.object_id = fk.referenced_object_id )
where
( o_p.schema_id = #s1 and o_r.schema_id = #s2 )
or ( o_p.schema_id = #s2 and o_r.schema_id = #s1 )