DropColumn conditionally in a migration - sql

I want to perform a column dropping in my Up migration. I am using EF 5.
DropColumn("dbo.MyObjects", "AttributeId");
The issue is that in some way part of database instances do not have that column (long story how). I am thinking of dropping it with Sql and searching in sys.columns, or wrapping DropColumn in try ... catch.
But maybe there is some known way to do it with Entity Framework migrations?

There was also a default constraint on my column, so ended up with the following:
public override void Up()
{
Sql(#"IF EXISTS(
SELECT 1 FROM sys.columns c
INNER JOIN sys.tables t ON t.object_id = c.object_id
WHERE c.name = 'AttributeId' AND t.name = 'MyObjects')
BEGIN
DECLARE #AttributeIdDefConstraint nvarchar(128)
SELECT #AttributeIdDefConstraint = name
FROM sys.default_constraints
WHERE parent_object_id = object_id(N'dbo.MyObjects')
AND col_name(parent_object_id, parent_column_id) = 'AttributeId';
IF #AttributeIdDefConstraint IS NOT NULL
BEGIN
EXECUTE('ALTER TABLE [dbo].[MyObjects] DROP CONSTRAINT ' + #AttributeIdDefConstraint)
END
ALTER TABLE [dbo].[MyObjects] DROP COLUMN [AttributeId]
END");
}
Hope that will save one's time.

Related

Stored procedure in SQL that checks if index not exist then create

My SQL script is running perfectly when I execute it.
Now the problem is when I check on table indexes this script does not create index.
Below is the script
CREATE PROCEDURE test
#TableName nvarchar(128)
AS
BEGIN
DECLARE #sql nvarchar(max)
SET #Sql = N'IF NOT EXISTS (SELECT name FROM sys.indexes WHERE name = Index_type)
CREATE UNIQUE CLUSTERED INDEX Index_type
ON ' + QUOTENAME(#TableName) + N'(CustomerId) ON "PRIMARY"';
END
EXECUTE test customer
This is possible (link to very simple demo at the end of the answer but this doesn't account for all scenarios by any means), but I really wouldn't bother doing this dynamically. If you are creating any index dynamically it is a bit of a code smell, let alone a clustered one.
The issue is, that you can work out if a table has a CustomerID column, and if it contains unique values, and if there is no clustered index already, but none of this tells you if a clustered index is appropriate. Consider the following table
CREATE TABLE Orders
(
OrderID INT IDENTITY(1, 1) NOT NULL,
CustomerID INT NOT NULL,
OrderDate DATE NOT NULL,
....
);
This has no clustered index, has a table called CustomerID, CustomerID may be unique if it is empty or has only a few orders in, so is a candidate for a clustered index to be created on CustomerID. But CustomerID is clearly not the appropriate clustering key here. There is no script in the world that can tell you how you intend your tables to be used, you could get very very close using best design practices to try and work it out, or even standard conventions such as column ordinals, but nothing will ever do this perfectly.
A better approach would be to do this as a one off task, you could identify all the tables with a customerID column, and even give yourself a template create index script as follows:
SELECT t.name,
CreateIndexScript = CONCAT('CREATE UNIQUE CLUSTERED INDEX UQ_',
NULLIF(s.name, 'dbo') + '_', t.name, '_CustomerID ON ',
QUOTENAME(s.name), '.', QUOTENAME(t.name), ' (CustomerID);')
FROM sys.tables AS t
INNER JOIN sys.schemas AS s
ON s.schema_id = t.schema_id
WHERE EXISTS (SELECT 1 FROM sys.columns AS c WHERE c.object_id = t.object_id AND c.name = 'CustomerID')
AND NOT EXISTS (SELECT 1 FROM sys.indexes AS i WHERE i.object_id = t.object_id AND i.type = 1)
You can then review each table thrown up by this script to see if creating a clustered index on CustomerID is appropriate, and then run the script(s) manually. Once the index is created it shouldn't be dropped, so you don't have to worry about this being run on a regular basis.
Demo on DB<>Fiddle

SQL Server - Check column if exists >> rename and change type

SQL Server:
Check column if exists when
If True : (Change/Modify) column_name and dataType
If False : Create
Schema name : Setup
Code:
IF EXISTS (SELECT 1 FROM sys.columns
WHERE Name = N'bitIntialBalance'
AND Object_ID = Object_ID(N'Setup.LeaveVacationsSubType'))
BEGIN
ALTER TABLE [Setup].[LeaveVacationsSubType]
ALTER COLUMN intIntialBalance INT NULL;
EXEC sp_RENAME 'Setup.LeaveVacationsSubType.bitIntialBalance', 'intIntialBalance', 'COLUMN';
--ALTER TABLE [Setup].[LeaveVacationsSubType] MODIFY [intIntialBalance] INT; not working
END
GO
IF NOT EXISTS(SELECT 1 FROM sys.columns
WHERE Name = N'intIntialBalance'
AND Object_ID = Object_ID(N'Setup.LeaveVacationsSubType'))
BEGIN
ALTER TABLE [Setup].[LeaveVacationsSubType]
ADD intIntialBalance INT NULL;
END
GO
If I guess correctly, the problem is that query plan is made for the whole script, and SQL Server also checks that it can actually perform all the operations, even if it is inside an if statement. That's why you'll get an error, even if in the reality that statement would never be executed.
One way to get around this issue is to make all those statements dynamic, something like this:
execute ('ALTER TABLE [Setup].[LeaveVacationsSubType] MODIFY [intIntialBalance] INT')

tSQLt How can I tell if a table has been faked

I am just starting creating some unit tests for my database.
If I have faked a table,
EXEC tSQLt.FakeTable
#TableName = 'dbo.[My Table]',
#Identity = 0,
#ComputedColumns = 0,
#Defaults = 0
Can I check if it has been faked?
Note that documentation on the FakeTable SP can be found here.
Motivation
I want to be able to do this as I imagine creating several stored procedures which populate these faked tables so I can perform tests.
However I do not want to handle faking the tables in the stored procedures (so I can call them multiple times entering different info each time).
I don't want to have the possibility that I forget to fake the table before adding the data (as would almost certainly cause me to fail my test).
tSQLt adds an extended property to a fake table to track the table it fakes. This is easily tested using the function tSQLt.Private_GetOriginalTableName:
SELECT tSQLt.Private_GetOriginalTableName('dbo','[My Table]')
This will return NULL if the table isn't faked.
If you want to do something more complex, you can query sys.extended_properties directly. See the contents of the tSQLt.class.sql script (in the tSQLt distribution) for the definition of tSQLt.Private_GetOriginalTableName.
You can check for the existence of the table at the beginning of your stored procedure(s).
If Not Exists ( Select 1 From Sys.Objects Where [Name] = 'YOURTABLENAME' And [Type] = 'U')
Begin
-- Your Create Table Statement Here
ENd
Based on your comments, the tool has to be doing something like this, using schema:
Create table dbo.MisterPositive ( test int )
Create table developers.MisterPositive (test Int )
-- Both statements below work
Select * From dbo.MisterPositive
Select * From developers.MisterPositive
-- Use this to look for existence prior
Select 1 from sys.objects
Inner join sys.schemas ON sys.objects.schema_id = sys.schemas.schema_id
where sys.objects.[Name] = 'MisterPositive' And sys.schemas.name = 'dbo'
Select 1 from sys.objects
Inner join sys.schemas ON sys.objects.schema_id = sys.schemas.schema_id
where sys.objects.[Name] = 'MisterPositive' And sys.schemas.name = 'Developers'
So yours would be
If Not Exists ( Select 1 from sys.objects
Inner join sys.schemas ON sys.objects.schema_id = sys.schemas.schema_id
where sys.objects.[Name] = 'YOURTABLE' And sys.schemas.[Name] = 'tSQLt' )
Begin
-- create table here
End

Why OBJECT_ID used while checking if a table exists or not

I need to check if a table in SQL exist or not.
If not it must create one automatically.
Now I researched and found this code:
IF NOT EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[YourTable]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[YourTable](
....
....
....
)
END
Can anyone explain why it says where object_id = OBJECT_ID and what should I put in its place?
I like this syntax:
if(object_id(N'[dbo].[YourTable]', 'U') is not null)
...
Where object_id takes the 2 char type of object as the second parameter. You can find the list of Object types listed below in the sys.objects documentation:
AF = Aggregate function (CLR)
C = CHECK constraint
D = DEFAULT (constraint or stand-alone)
F = FOREIGN KEY constraint
FN = SQL scalar function
FS = Assembly (CLR) scalar-function
FT = Assembly (CLR) table-valued function
IF = SQL inline table-valued function
IT = Internal table
P = SQL Stored Procedure
PC = Assembly (CLR) stored-procedure
PG = Plan guide
PK = PRIMARY KEY constraint
R = Rule (old-style, stand-alone)
RF = Replication-filter-procedure
S = System base table
SN = Synonym
SO = Sequence object
SQ = Service queue
TA = Assembly (CLR) DML trigger
TF = SQL table-valued-function
TR = SQL DML trigger
TT = Table type
U = Table (user-defined)
UQ = UNIQUE constraint
V = View
X = Extended stored procedure
The ISO SQL way to check existence of a table level object is the INFORMATION_SCHEMA.TABLES view
There's nothing wrong with looking at sys.objects but.... INFORMATION_SCHEMA.TABLES is a bit more declarative -- and it's cross platform (which often doesn't matter at all but meh still nice.)
I think this is probably more readable for a new coder though:
DECLARE #tableName SYSNAME = 'tbfoo'
DECLARE #schemaNAme SYSNAME = 'fooSchema'
IF EXISTS ( SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #tableName AND TABLE_SCHEMA = #schemaName )
BEGIN
RAISERROR('%s exists in schema: %s', 0, 1, #tableName, #schemaName)
END
ELSE
BEGIN
RAISERROR('%s DOES NOT EXIST in schema: %s', 0, 1, #tableName, #schemaName)
END
Don't worry about the RAISERROR command -- its just a nice way of printing formatted messages.
You can query the INFORMATION_SCHEMA view to get a sense of what's in it.
SELECT TOP 5 * FROM INFORMATION_SCHEMA.TABLES
As you can see -- you can reference schemas and catalogs by name rather than looking up their ID with OBJECT_ID()
object_id = OBJECT_ID(N'[dbo].[YourTable]')
object_id is the column name in sys.objects
OBJECT_ID is a function that returns the ID for the object you specify, i.e. YourTable.
You are comparing the object_id of YourTable with the object_id column in the sys.objects table. You need to replace YourTable with the table name you want to check already exists.
OBJECT_ID() is a function which returns the Object ID. See the documentation:
Returns the database object identification number of a schema-scoped
object.
http://msdn.microsoft.com/en-us/library/ms190328.aspx
By passing it certain parameters (ie. your table details), it will return an ID. You can then compare this with the IDs in the sys.objects table to check whether it currently exists.

T-SQL Scripts to copy all table constraints

I have created many tables on my local database and moved them to production database.
Now I am working on fine tuning the database and created many constraints on my local database tables such as PK, FK, Default Values, Indexes etc. etc.
Now I would like to copy only these constraints to production database. Is there a way to do it?
Please note that my production database tables already populated with some data. So I can’t drop and recreate them.
If you don't want to buy any tools (which are totally worth their price, BTW), you can always interrogate the system catalog views, and extract the info from there to create scripts you could execute on your new database.
In the case of e.g. the default constraints, this query shows you a list of all the default constraints in your database:
SELECT
dc.name 'Constraint Name',
OBJECT_NAME(parent_object_id) 'Table Name',
c.name 'Column Name',
definition
FROM
sys.default_constraints dc
INNER JOIN
sys.columns c ON dc.parent_object_id = c.object_id
AND dc.parent_column_id = c.column_id
ORDER BY
OBJECT_NAME(parent_object_id), c.name
and based on that, you could of course create a query which would emit T-SQL statements to recreate those default constraints on your target server:
SELECT
'ALTER TABLE ' + OBJECT_SCHEMA_NAME(dc.parent_object_id) + '.' + OBJECT_NAME(dc.parent_object_id) +
' ADD CONSTRAINT ' + dc.name + ' DEFAULT(' + definition
+ ') FOR ' + c.name
FROM
sys.default_constraints dc
INNER JOIN
sys.columns c ON dc.parent_object_id = c.object_id
AND dc.parent_column_id = c.column_id
You'd get something like this (for the AdventureWorks sample DB):
ALTER TABLE dbo.Store ADD CONSTRAINT DF_Store_rowguid DEFAULT((newid())) FOR rowguid
ALTER TABLE dbo.Store ADD CONSTRAINT DF_Store_ModifiedDate DEFAULT((getdate())) FOR ModifiedDate
ALTER TABLE dbo.ProductPhoto ADD CONSTRAINT DF_ProductPhoto_ModifiedDate DEFAULT((getdate())) FOR ModifiedDate
ALTER TABLE dbo.ProductProductPhoto ADD CONSTRAINT DF_ProductProductPhoto_Primary DEFAULT(((0))) FOR Primary
ALTER TABLE dbo.ProductProductPhoto ADD CONSTRAINT DF_ProductProductPhoto_ModifiedDate DEFAULT((getdate())) FOR ModifiedDate
ALTER TABLE dbo.StoreContact ADD CONSTRAINT DF_StoreContact_rowguid DEFAULT((newid())) FOR rowguid
ALTER TABLE dbo.StoreContact ADD CONSTRAINT DF_StoreContact_ModifiedDate DEFAULT((getdate())) FOR ModifiedDate
ALTER TABLE dbo.Address ADD CONSTRAINT DF_Address_rowguid DEFAULT((newid())) FOR rowguid
Of course, you could tweak the resulting T-SQL being output to your liking - but basically, copy&paste those results from the query to your new database, and off you go.
Of course, there are similar system catalog views for foreign key relationships (sys.foreign_keys), check constraints (sys.check_constraints), indexes (sys.indexes and sys.index_columns) and many more.
It's a bit of work - but it can be done on your own time, and you'll learn a lot about SQL Server in the process.
So it's a traditional "make or buy" decision all over again :-)
Marc
The best way would be to store all your DDL code in a source control. Then deploy it to production using tools like dbGhost (my favorite) or SQL Compare
Red Gate's SQL Compare is a popular, non-free way to do this.
Sure this is an old post, but none of the scripts in all the above answers put out the table schemas as well. So it didn't work out the box for my database.
This one does, so it did:
-- ===========================================================
-- Default Constraints
-- How to script out Default Constraints in SQL Server 2005+
-- ===========================================================
-- view results in text, to make copying and pasting easier
-- drop default constraints
SELECT
'ALTER TABLE ' +
QuoteName(OBJECT_SCHEMA_NAME(sc.id)) + '.' + QUOTENAME(OBJECT_NAME(sc.id)) +
CHAR(10) +
' DROP CONSTRAINT ' +
QuoteName(OBJECT_NAME(sc.cdefault))
FROM
syscolumns sc
INNER JOIN
sysobjects as so on sc.cdefault = so.id
INNER JOIN
syscomments as sm on sc.cdefault = sm.id
WHERE
OBJECTPROPERTY(so.id, N'IsDefaultCnst') = 1
-- create default constraints
SELECT
'ALTER TABLE ' +
QuoteName(OBJECT_SCHEMA_NAME(sc.id)) + '.' + QuoteName(OBJECT_NAME(sc.id)) +
' ADD CONSTRAINT ' +
QuoteName(OBJECT_NAME(sc.cdefault))+
' DEFAULT ' +
sm.text +
' FOR ' + QuoteName(sc.name)
+ CHAR(13)+CHAR(10)
FROM
syscolumns sc
INNER JOIN
sysobjects as so on sc.cdefault = so.id
INNER JOIN
syscomments as sm on sc.cdefault = sm.id
WHERE
OBJECTPROPERTY(so.id, N'IsDefaultCnst') = 1
I adapted it from Donabel Santos's blog here.
EDIT and NB: Be sure to run both parts of the query and save the second result set (i.e. ADD CONSTRAINTs) before dropping your default constraints, else you won't be able to re-create them again (no I didn't do that :)
A good and free Microsoft tool. You can export the schema and the data.
Microsoft SQL Server Database Publishing Wizard
Try DBSourceTools. http://dbsourcetools.codeplex.com
It has schema compare function that will help you create an update script.
Bear in mind though, that you should be using source-code control on your entire database.
This is what DBSourceTools was designed to do - help developers bring their databases under source control.