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?
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 ...
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/
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);'
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
Related
Due to storing values from language specific diacritics (Spanish, French, German) I am trying to change the datatype of a column to Unicode datatype.
Varchar to nvarchar
char to nchar
So all column datatype to its respective Unicode datatype.
For all tables in a specific database.
Can it be possible to do in a single statement? Because doing with alter statement is time consuming.
ALTER TABLE dbo.Employee
ALTER COLUMN FirstName NVARCHAR(255) NOT NULL
Many thanks.
First we create a temporary table and define the variables we need for the changes.
Then we fill the table by fetching the columns that need to change.
I entered the fetch command below and everything is clear.
Then, for each row in the table, we make the desired changes in the database.
Also, a column in a table may have a value of NULL, so you must replace the NULL values with a value before changing the column.
I replaced the NULL values with a value of 0.
The code below works perfectly and correctly.
Only I entered dbo for schema name in search of columns. See what your database schema name is
--Container to Insert Id which are to be iterated
Declare #temp1 Table
(
tablename varchar(100),
columnname varchar(100),
columnlength varchar(100),
columntype varchar(100)
)
--Container to Insert records in the inner select for final output
Insert into #temp1
SELECT t.TABLE_NAME,c.COLUMN_NAME,c.CHARACTER_MAXIMUM_LENGTH,c.DATA_TYPE FROM information_schema.tables t
join INFORMATION_SCHEMA.COLUMNS c on t.TABLE_NAME = c.TABLE_NAME
WHERE t.table_schema='dbo'
and (DATA_TYPE = 'varchar' OR DATA_TYPE = 'char')
-- Keep track of #temp1 record processing
Declare #tablename varchar(100)
Declare #columnname varchar(100)
Declare #columnlength varchar(100)
Declare #columntype varchar(100)
Declare #SQL VarChar(1000)
Declare #vary varchar(100)
Declare #final varchar(1000)
While((Select Count(*) From #temp1)>0)
Begin
Set #tablename=(Select Top 1 tablename From #temp1)
Set #columnname=(Select Top 1 columnname From #temp1)
Set #columnlength=(Select Top 1 columnlength From #temp1)
Set #columntype=(Select Top 1 columntype From #temp1)
if(#columntype = 'varchar')
Set #columntype='nvarchar'
else
Set #columntype='nchar'
--set null value with 0 value
SELECT #SQL = 'UPDATE ' + #tablename + ' SET ' + #columnname + ' = 0 WHERE ' + #columnname + ' IS NULL'
Exec ( #SQL)
SELECT #SQL = 'ALTER TABLE '
SELECT #SQL = #SQL + #tablename
select #vary = ' ALTER COLUMN ' + #columnname + ' ' + #columntype + '(' + #columnlength + ') NOT NULL'
select #final = #sql + #vary
--select #final
Exec ( #final)
Delete #temp1 Where tablename=#tablename and columnname = #columnname
End
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)
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
I have table prsl which have auto generated name of the constraint. I want to search where the Database kept these name.
ALTER TABLE [dbo].[PRSL] DROP CONSTRAINT [PK__PRSL__1C1D47DC0BF1ACC7]
Actually, i want to drop these constraints dynamically.
For Example
SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[PRSL]')
drop all the constraint which are on a table.
Drop constraint 'when found'
If you're willing to display all constraints of a given table
select * from INFORMATION_SCHEMA.TABLE_CONSTRAINTS
where TABLE_NAME = 'YOUR TABLE NAME'
If you want to drop all constraints of the given table use this:
DECLARE #database nvarchar(50)
DECLARE #table nvarchar(50)
set #database = 'dotnetnuke'
set #table = 'tabs'
DECLARE #sql nvarchar(255)
WHILE EXISTS(select * from INFORMATION_SCHEMA.TABLE_CONSTRAINTS where constraint_catalog = #database and table_name = #table)
BEGIN
select #sql = 'ALTER TABLE ' + #table + ' DROP CONSTRAINT ' + CONSTRAINT_NAME
from INFORMATION_SCHEMA.TABLE_CONSTRAINTS
where constraint_catalog = #database and
table_name = #table
exec sp_executesql #sql
END
It worked for me...Hope it helps...
I need to replace whitespace with NULL in every cell in my database. SQL server 2008 r2. I'm looking for something efficient, but looks like cursor is only way?
First find all tables and columns that are Nullable and of type CHAR or VARCHAR, using INFORMATION_SCHEMA:
SELECT TABLE_NAME, COLUMN_NAME
FROM MyDatabase.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'dbo'
AND IS_NULLABLE = 'YES'
Cursors are NEVER the answer :)
ypercube did the hard part. You need to take that info and iterate through it, executing an update statement for each column in each table. You can do that using a WHILE statement. Here is an UNTESTED example of how you could do this:
--Set database to use
USE [MyDatabase];
GO
--Create table variable to hold table/column pairs
DECLARE #table TABLE (
[Key] BIGINT PRIMARY KEY IDENTITY (1, 1),
[TABLE_NAME] VARCHAR(100),
[COLUMN_NAME] VARCHAR(100)
);
--Populate table variable
INSERT INTO #table ([TABLE_NAME], [COLUMN_NAME])
SELECT [TABLE_NAME], [COLUMN_NAME]
FROM MyDatabase.INFORMATION_SCHEMA.COLUMNS
WHERE [TABLE_SCHEMA] = 'dbo'
AND [IS_NULLABLE] = 'YES';
--Initialize counting variables
DECLARE #counter BIGINT = 1;
DECLARE #max BIGINT = (SELECT COUNT(1) FROM #table);
--Iterate through each pair
WHILE #counter <= #max
BEGIN
--Assign the current pair values to variables
DECLARE #TableName VARCHAR(100), #ColumnName VARCHAR(100);
SELECT #TableName = [TABLE_NAME], #ColumnName = [COLUMN_NAME]
FROM #table
WHERE [Key] = #counter;
--Execute dynamic SQL
EXEC
(
'UPDATE [' + #TableName + ']' +
'SET [' + #ColumnName + '] = NULL' +
'WHERE RTRIM([' + #ColumnName + ']) = '''';'
);
--Increment the counter
SET #counter = #counter + 1;
END
I guess it depends on how you define whitespace. Will the following work?
update mytable set mycolumn = null where len(rtrim(ltrim(mycolumn))) = 0