SQL Server: Drop Table with FK - sql-server

On table "A" depend about 30 other tables via FK to "A.Id".
For integration testing I have to drop the table and recreate it to create a defined state. Because of the dependent objects their seem to be no way to delete and recreate the table. The error message is:
Could not drop object 'dbo.A'
because it is referenced by a FOREIGN
KEY constraint
Question(s):
How can I drop and recreate table "A"?
(or) is there any way turn the schema dependencies off globally?
(or) is there any way to backup (all!) dependencies before deleting and restoring table "A" and restore all dependencies afterward?

Explore the sys.foreign_key_columns system table. Here's an example that I had laying around that will, given a table, tells you which of it's columns are keyed to another table:
DECLARE #tableName VARCHAR(255)
SET #tableName = 'YourTableName'
SELECT OBJECT_NAME(fkc.constraint_object_id) AS 'FKName', OBJECT_NAME(fkc.[referenced_object_id]) AS 'FKTable', c2.[name] AS 'FKTableColumn', #tableName AS 'Table', c1.[name] AS 'TableColumn'
FROM sys.foreign_key_columns as fkc
JOIN sys.columns AS c1 ON c1.[object_id] = fkc.[parent_object_id] AND c1.[column_id] = fkc.[parent_column_id]
JOIN sys.columns AS c2 ON c2.[object_id] = fkc.[referenced_object_id] AND c2.[column_id] = fkc.[referenced_column_id]
WHERE fkc.[parent_object_id] = OBJECT_ID(#tableName)
ORDER BY OBJECT_NAME(fkc.constraint_object_id)
With this, or some variation there-of, you could find out the foreign keys, drop them, do your stuff, and then re-create the foreign keys.
I should add that I know this works on SQL2005 and SQL2008. I don't really know if it will work on SQL2000/MSDE.

In Management Studio, you can right-click on the table and script the CREATE and the DROP which will include all of the foreign keys.
To be more specific, this will give you all constraints on which your Table depends. However, it does not give you the list of foreign keys that depend on this table. So, in addition to the scripts you would generate by right-clicking on the table in SMS, you need to find and script all the foreign keys. To get a list of them, you can run a query like so:
select FKConstraint.TABLE_NAME, FKConstraint.CONSTRAINT_NAME
from INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
Join INFORMATION_SCHEMA.TABLE_CONSTRAINTS As UniqueConstraint
On UniqueConstraint.CONSTRAINT_NAME = INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS.UNIQUE_CONSTRAINT_NAME
Join INFORMATION_SCHEMA.TABLE_CONSTRAINTS As FKConstraint
On FKConstraint.CONSTRAINT_NAME = INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS.CONSTRAINT_NAME
Where UniqueConstraint.TABLE_NAME = 'TableA'
For each one of these, you'll need to script the create and drop. You would append the drops to the top of your drop script and the creates at the end of your create script.

Go to the database in SSMS and right click. Choose tasks, generate scripts. Then go through the options and set them the way you want (Probaly to only choose foreign keys inthe table and create dependant objects and drop and recreate, dont;hve the options in front of me but you will see them. THen choose the tables you want to script the FKs for and script them to a file. Open the file and separate the drop statements into one file and the create statments into another. Now you have tweo files you can run do autmatically do what you want when ever you run run a test. I would suggest recreating the files before running the first test (in case they have changed since the last time tests were run) but not for each individual test.

Expand the table in Sql Server Management Studio, Expand the Constraints folder.
Write down any constraints that you have so you can re-create them. Delete the constraints and drop the table. Rebuild the table and re-create your constraints.

Use transaction.
At the end of test - rollback it.

Perhaps consider maintaining a virtual server with your database in its initialize test setup. Boot the VM, perform your testing, then throw away the changed VM.

Related

Script out objects from DB with identity columns replaced by variables to copy object to other environment

I have multiple environments for an application, like DEV, TEST, UAT, PROD.
I would need to copy some objects from the database created in UAT environment into PROD environment. The object is stored in the DB spreaded to multiple tables. Most of the tables have PK as IDENTITY (autogenerated). I don't have access to PROD db data (it is sensitive data in general).
What I need is to generate SQL script for inserting the object that does not preserve the values of Ids but uses the Ids assigned in target environment to related records.
Example: let's say object Order composed of [Order] and list of [OrderItem] rows. I would need to select one specific row in [Order] table, specify that also related rows from [OrderItem] should be included and generate script that would insert new row for [Order], get the value of assigned Order.Id, keep it in a variable and use it for inserting [OrderItem] rows. This is trivial example, my object is spreaded to many more tables, but the concept is the same.
Is there any tool for doing this? All scripting out utilities I tried preserve values of Identity columns.
I think you would need to write custom code to achieve the need of first loading to parent table, followed by loading to child table based on SCOPE_IDENTITY() value.
Instead, if you don't have huge number of rows, I would suggest you to follow below steps:
Load data from Production to another environment using SSIS package or another means
Add the foreign key constraint in the child table to have UPDATE CASCADE
ALTER TABLE ChildTable ADD CONSTRAINT FK_OrderDetail_Order FOREIGN KEY([OrderID])
REFERENCES [dbo].[Order] ([OrderID])
ON UPDATE CASCADE
Now, Update the identity value to another value using some logic
set identity_insert Order ON
UPDATE Order SET OrderID = OrderID + 1000000 -- Have some other logic for random generation
set identity_Insert Order OFF
Now, automatically the child table will get updated with new OrderID.
UPDATE
This SO link talks about automating the code generation for INSERT scripts: What is the best way to auto-generate INSERT statements for a SQL Server table?
Also, I would suggest you to first script out parent tables, followed by child tables.
You can identify parent,child tables using the below script. You need to generate scripts now as per the parent, child depency.
select object_name(parent_object_id) as childTable, object_name(referenced_object_id) as parentTable
from sys.foreign_keys
WHERE object_name(parent_object_id) IN 'Your comma separated list of tables'

How can we know what kind of a constraint was dropped in Sql Server without maintaining our own copy of sys.objects?

So, I created the following DDL trigger:
CREATE TRIGGER [BeforeAlterTable] ON DATABASE FOR ALTER_TABLE
AS
BEGIN
SELECT * FROM sys.objects WHERE Name = 'PK_MyTable'
END
The problem is that when PK_MyTable primary key is dropped, the trigger outputs empty result set - the information about the primary key is already gone from the sys.objects view.
This could be resolved by maintaining our own copy of sys.objects (a subset of type = 'PK' would suffice), but I would like to avoid it.
Anyway to make it work?
EDIT 1
Note that the question is about deducing the kind of the constraint, not its name or the table it belongs too. These are found in the EVENTDATA() function result. What is not found there is the kind of the constraint.

Remove record from sys.objects?

I ran a ALTER SCHEMA ... on a table (SQL Server 2012 SP1) but the sys.objects record is still there. When I run the ETL I get an error that the table doesn't exist anymore because it's trying to remove all constraints. This one is of type PK Primary Key Constraint. How can I safely remove the record from the sys.objects table?
I managed to fix this issue by scripting the database to a new one, ALTER SCHEMA ... again on that table to bring it back to dbo., then DROP and recreate the table in the different table schema.

How To change the column order of An Existing Table in SQL Server 2008

I have situation where I need to change the order of the columns/adding new columns for existing Table in SQL Server 2008.
Existing column
MemberName
MemberAddress
Member_ID(pk)
and I want this order
Member_ID(pk)
MemberName
MemberAddress
I got the answer for the same ,
Go on SQL Server → Tools → Options → Designers → Table and Database Designers and unselect Prevent saving changes that require table re-creation
2- Open table design view and that scroll your column up and down and save your changes.
It is not possible with ALTER statement. If you wish to have the columns in a specific order, you will have to create a newtable, use INSERT INTO newtable (col-x,col-a,col-b) SELECT col-x,col-a,col-b FROM oldtable to transfer the data from the oldtable to the newtable, delete the oldtable and rename the newtable to the oldtable name.
This is not necessarily recommended because it does not matter which order the columns are in the database table. When you use a SELECT statement, you can name the columns and have them returned to you in the order that you desire.
If your table doesn't have any records you can just drop then create your table.
If it has records you can do it using your SQL Server Management Studio.
Just click your table > right click > click Design then you can now arrange the order of the columns by dragging the fields on the order that you want then click save.
Best Regards
I tried this and dont see any way of doing it.
here is my approach for it.
Right click on table and Script table for Create and have this on
one of the SQL Query window,
EXEC sp_rename 'Employee', 'Employee1' -- Original table name is Employee
Execute the Employee create script, make sure you arrange the columns in the way you need.
INSERT INTO TABLE2 SELECT * FROM TABLE1.
-- Insert into Employee select Name, Company from Employee1
DROP table Employee1.
Relying on column order is generally a bad idea in SQL. SQL is based on Relational theory where order is never guaranteed - by design. You should treat all your columns and rows as having no order and then change your queries to provide the correct results:
For Columns:
Try not to use SELECT *, but instead specify the order of columns in the select list as in: SELECT Member_ID, MemberName, MemberAddress from TableName. This will guarantee order and will ease maintenance if columns get added.
For Rows:
Row order in your result set is only guaranteed if you specify the ORDER BY clause.
If no ORDER BY clause is specified the result set may differ as the Query Plan might differ or the database pages might have changed.
Hope this helps...
This can be an issue when using Source Control and automated deployments to a shared development environment. Where I work we have a very large sample DB on our development tier to work with (a subset of our production data).
Recently I did some work to remove one column from a table and then add some extra ones on the end. I then had to undo my column removal so I re-added it on the end which means the table and all references are correct in the environment but the Source Control automated deployment will no longer work because it complains about the table definition changing.
The real problem here is that the table + indexes are ~120GB and the environment only has ~60GB free so I'll need to either:
a) Rename the existing columns which are in the wrong order, add new columns in the right order, update the data then drop the old columns
OR
b) Rename the table, create a new table with the correct order, insert to the new table from the old and delete from the old as I go along
The SSMS/TFS Schema compare option of using a temp table won't work because there isn't enough room on disc to do it.
I'm not trying to say this is the best way to go about things or that column order really matters, just that I have a scenario where it is an issue and I'm sharing the options I've thought of to fix the issue
SQL query to change the id column into first:
ALTER TABLE `student` CHANGE `id` `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT FIRST;
or by using:
ALTER TABLE `student` CHANGE `id` `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT AFTER 'column_name'

Change huge table PK column data type

Now that we've ran out of int capacity on a PK column (which is an IDENTITY) I'd like to do this to bigint, but simple ALTER TABLE seems to be unable to handle that big of a table. So my question is: how do I change the type of a PK column with keeping actual values in place and do I need to alter referencing tables as well?
In addition to KLE's suggestion, the following queries might help:
To disable all constraints on the tables that reference oldTable try to execute the output of the following query:
SELECT 'ALTER TABLE ' + OBJECT_NAME(fk.parent_object_id) + ' NOCHECK CONSTRAINT ' + fk.name
FROM sys.foreign_keys fk
INNER JOIN sys.foreign_key_columns AS fkc ON fk.OBJECT_ID = fkc.constraint_object_id
WHERE OBJECT_NAME (fk.referenced_object_id) = 'oldTable'
To move all data into the new table, with alteration of the field try this:
INSERT INTO newTable
SELECT CONVERT(BIGINT, ID) AS ID, COL1, COL2, ..., COLN
FROM oldTable
To drop the old table:
DROP TABLE oldTable
To rename the new table to the old name:
sp_rename newTable, oldTable
To reenable all the constraints on the tables that reference oldTable, try to execute the output of the following query:
SELECT 'ALTER TABLE ' + OBJECT_NAME(fk.parent_object_id) + ' CHECK CONSTRAINT ' + fk.name
FROM sys.foreign_keys fk
INNER JOIN sys.foreign_key_columns AS fkc ON fk.OBJECT_ID = fkc.constraint_object_id
WHERE OBJECT_NAME (fk.referenced_object_id) = 'oldTable'
Hope it helps...
What we would do is:
save your table
create a new table with the correct structure
disable all constraints on these tables, and the ones that reference them
move all data into the new table, with alteration of the field ; it can be done by batches
delete the old table when it's empty
rename the new table to the old name
enable all constraints on all tables (some FK column and constraints probably need fixing too... But they are not PK, so they are modifiable)
6 edited (thanks to Alexey)
This is clean, doable in batches, well understood.
You will need to also alter the child tables. After all you will now be trying to insert a big int into them as well. I would change over the child tables first
This is not an easy or short process. I would suggest to you that you tell your users the the database is going to be down for maintenance (you can gauge how long by how long it takes to do dev) on a set date and reset the databse to single user mode while you make these changes. You don't want to lose data that is added (or changed)by users to one table while you are switching to the other one. if for somereason you can't havea maintence window (and I strongly suggest for the sake of data integrity that you do), then you must change the child tables over first to avoid insert errors if your a really close to the limit and will be seeing the large numbers almost immediately.
Make sure to script the entire datbase structure including defaults, triggers, check constrants indexes etc as you will want to recreate everything.
Make sure to do all this through scripts on dev. That will make it much easier to do one prod once you have tested the process out.
I'm think you can only create a new database with changed PK datatype, and then export/import data, or bulk insert into new, then rename new database. Of course this actual if you have many referenced tables and your new PK datatype not compatible with previous.

Resources