How can I detect renaming operations on tables and columns? - sql-server

I need to detect renaming operation on columns and tables.
I can see alter, drop and create operation in this query:
DECLARE #filename nvarchar(1000);
SELECT #filename = cast(value as nvarchar(1000))
FROM ::fn_trace_getinfo(default)
WHERE traceid = 1 and property = 2;
SELECT *
FROM fn_trace_gettable(#filename, default) AS ftg
INNER JOIN sys.trace_events AS te ON ftg.EventClass = te.trace_event_id
left outer join sys.all_objects o on o.name = ftg.ObjectName
order by EventSequence DESC;
But when someone renames a table or column on MSSQL Management Studio, I cannot detect on this query. Is there another way to do it?
Regards.

You can create a database trigger.
List of available events: [sys].[events] (there are ALTER_COLUMN event)
Example from msdn:
CREATE TRIGGER safety
ON DATABASE
FOR DROP_SYNONYM
AS
RAISERROR ('You must disable Trigger "safety" to drop synonyms!',10, 1)
ROLLBACK
GO
DROP TRIGGER safety
ON DATABASE;
GO
another example: https://www.mssqltips.com/sqlservertip/2085/sql-server-ddl-triggers-to-track-all-database-changes/

Related

Is there a way to check if an object is a table or a view in SQL Server?

I have a view that I am changing to a table in SQL Server.
I am dropping the view and then the next part of my code I am establishing the table.
My code works the first time I run it (when the object is a view), but when I need to run it multiple times, I get this error:
Cannot use DROP VIEW with 'engineer.Well' because 'engineer.Well' is a table. Use DROP TABLE.
I've been looking online but cannot find a way to check if an object is a table or a view, and the subsequently drop the object.
Any advice would be greatly appreciated.
Right now it looks like this,
IF OBJECT_ID('engineer.well') IS NOT NULL
BEGIN
DROP TABLE [engineer].[Well]
PRINT '<<< DROPPED TABLE Vendor >>>'
END
I am playing around with a way to check if the object is a table and then drop it, or check if it is a view then drop it.
You can query the system views.
DECLARE #Type varchar(2)
SELECT #Type = type
FROM sys.objects o
JOIN sys.schemas s ON o.schema_id = s.schema_id
WHERE o.name = 'well'
AND s.name = 'engineer'
IF #Type = 'U'
BEGIN
DROP TABLE [engineer].[Well]
PRINT '<<< DROPPED TABLE Vendor >>>'
END
IF #Type = 'V'
BEGIN
DROP VIEW [engineer].[Well]
PRINT '<<< DROPPED VIEW Vendor >>>'
END
OBJECTPROPERTY(OBJECT_ID('name'), 'IsView')
https://learn.microsoft.com/en-us/sql/t-sql/functions/objectproperty-transact-sql?view=sql-server-2017
or
SELECT id, type FROM sysobjects where id=OBJECT_ID('objectName')
You can query sys.objects table:
select type_desc, * from sys.objects where object_id = object_id('[dbo].[DimDates]')

'IF' does not prevent error, but only if it has already executed

I'm trying to move a column from one table to another (here's the post for that),
However this runs as a task, and may run after it has already completed so I need a clause that prevents the logic from running again. I thought I could achieve this with an IF:
IF EXISTS (
SELECT *
FROM sys.columns
WHERE object_id = OBJECT_ID(N'Table_A')
AND name = 'internalID'
)
BEGIN
UPDATE Table_B
SET b.internalID = a.internal_ID
FROM Table_B b INNER JOIN
Table_A a
ON a.id = b.FK_toTableA;
ALTER TABLE Table_A DROP COLUMN internalID;
END
However, I get an error at
SET b.internalID = a.internal_ID
The error is:
Invalid column name 'internalID'.
But Only If a.internalID doesn't exist anymore.
I had to use EXEC sp_executesql:
IF EXISTS (
SELECT *
FROM sys.columns
WHERE object_id = OBJECT_ID(N'Table_A')
AND name = 'internalID'
)
BEGIN
EXEC sp_executesql N'UPDATE Table_B
SET b.internalID = a.internal_ID
FROM Table_B b INNER JOIN
Table_A a
ON a.id = b.FK_toTableA';
ALTER TABLE Table_A DROP COLUMN internalID;
END
I guess because SQL Server complies the whole thing - even that stuff bypassed via conditional logic - before running the script.
Here's a good article about what sql_executesql does, compared to EXEC, but it basically boils down to more injection prevention.

how can I write a query (SQL Server) that, based on a table, it can return all the tables that it references?

I have a massive procedure which does a ton of deletes. The issue is that:
There are missing tables from this delete (that need to be deleted)
I'm having a hard time with the order of the deletes.
I'm wondering if there is a way to build a query in which I give it a table name and then it shows me all the tables I need to delete from before deleting this table. Anyone can give me a hand?
The following query returns all the tables which have foreign keys pointing into your table in #tablename.
declare #tablename sysname = 'your table name';
select OBJECT_NAME(parent_object_id) parent_object_name
from sys.foreign_keys
where OBJECT_NAME(referenced_object_id) = #tablename;
All these would have to be dropped before dropping your table.
(Tested in SQL Server 2014)
And as an additional thought - if you need to go to thenext level down, and find all foreign keys that reference this list of tables, you can use a common table expression as such:
declare #tablename sysname = 'your table name';
;with cte as (
select OBJECT_NAME(parent_object_id) parent_object_name, OBJECT_NAME(referenced_object_id) referenced_object_name
from sys.foreign_keys
where OBJECT_NAME(referenced_object_id) = #tablename
union all
select OBJECT_NAME(parent_object_id) parent_object_name, OBJECT_NAME(referenced_object_id)
from sys.foreign_keys fk
inner join cte on OBJECT_NAME(fk.referenced_object_id) = cte.parent_object_name
)
select * from cte
shows me all the tables I need to delete from before deleting this table
Look at foreign key relationships. But if there are none defined, you're just going to have to roll up your sleeves and analyze the stored procedure.
One quick solution is to use the (deprecated) sp_depends stored procedure to return the dependencies of an object, eg:
exec sp_depends 'mySprocName';
A better solution is to use the sys.dm_sql_referenced_entities table-valued function to returned all referenced entities. This offers more details than sp_depends. Like all table-valued functions, it can be part of a larger query:
select distinct referenced_entity_name
into #tables
from sys.dm_sql_referenced_entities ( 'mySproc','OBJECT')
This article shows how you can use such methods to create a PlantUML graph from such system functions and views.

Keeping the design of 2 tables in sync

The problem:
I have 2 tables in a database:
TableA TableB
X, Y, Z X, Y, Z
If I add column W to table A I want to copy it automatically to Table B with the same name and data type (without writing it explicitly).
Constraints on deployment:
The tables are updated using update scripts (so must be able to be called / executed in tsql).
There are multiple tables that could be updated (however I can hand code a mapping if needed)
Example update script:
IF NOT EXISTS (SELECT 1 FROM SYS.COLUMNS C INNER JOIN SYS.TABLES T ON C.OBJECT_ID = T.OBJECT_ID
WHERE C.NAME = 'W' AND T.NAME = 'TableA')
BEGIN
ALTER TABLE TableA ADD W [Int] NULL
END
GO
At the end of this script I want to add my ‘SyncMyTables’ SQL
So far from my research I have found 3 possible ways to tackle this:
Call a function at the end of the script which syncs the table designs
Some form of table trigger (but I don’t think triggers are that clever)
Some inline sql that builds up an update string and then runs it against the database.
Option 1 seems the most sensible to me.
What help I need:
Some guidance on how best to tackle this.
An example to point me in the right direction.
Cheers
please note, I don't want to keep the content of the columns in sync, I need to keep the table DESIGN
The column duplication can be achieved with a DDL trigger:
CREATE TRIGGER DDL_TableA_TableB
ON database
FOR ALTER_TABLE
AS
BEGIN
Declare #CommandText nvarchar(1000)
SELECT #CommandText = EVENTDATA().value('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]','SYSNAME');
if left(#CommandText, 26) = 'ALTER TABLE dbo.TableA ADD'
begin
set #CommandText = replace( #CommandText, 'TableA', 'TableB')
exec sp_executesql #CommandText
end
end

How do you truncate all tables except lookup tables in a database using TSQL?

What is the best way to remove all the data from all the tables except look-up tables data using TSQL in SQL Server 2012 and downwards?
I would like the TSQL identify and exclude look-up tables then create truncate table statements for the other tables.
** There is almost a similar question but it truncates all the tables.
Both Lookup Tables and non Lookup tables are similar in technical characteristics. Only functionally they are different. Hence there won't be specific criteria to differentiate both of them.
Unless you set yourself up to be able to do this from the design standpoint, e.g. putting all "Lookup" tables in a "lkup" schema, or something of that nature, I don't think there's a way to do this. As someone already mentioned, a lookup table is a table like any other.
I would automate using DELETE by deleting in the right order first of all (dependencies)
1) pass the table name
2) disable your foreign keys
3) empty DELETE ALL the table
4) re-enable they keys.
this way you can control passing the table names you want "truncated" with a conditional.
or will it matter then:
ALTER PROCEDURE
up_ResetEntireDatabase
#IncludeIdentReseed BIT,
#IncludeDataReseed BIT
AS
EXEC sp_MSForEachTable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'
EXEC sp_MSForEachTable 'DELETE FROM ?'
IF #IncludeIdentReseed = 1
BEGIN
EXEC sp_MSForEachTable 'DBCC CHECKIDENT (''?'' , RESEED, 1)'
END
EXEC sp_MSForEachTable 'ALTER TABLE ? CHECK CONSTRAINT ALL'
IF #IncludeDataReseed = 1
BEGIN
-- Populate Core Data Table Here
END
GO
And then once ready the execution is really simple:
EXEC up_ResetEntireDatabase 1, 1
I'm not sure if you mean lookup tables like one that could drive this.
build a simple table that has names of each of the database tables and create columns you could modify if necessary before you execute the script.
the columns could just be flags that tell the script whether or not to truncate that table or other.
that way you (script) will know dependencies as it reads table names. an index is not needed if you keep table order static in record number order.
just another maintenance script.
So you want to truncate the tables that have foreign keys but keep the references tables alone. This should do it.
WITH CTE_fks
AS
(
SELECT obj.name AS FK_NAME,
sch1.name AS [table_schema],
tab1.name AS [table_name],
col1.name AS [column],
sch2.name AS [ref_table_schema],
tab2.name AS [referenced_table],
col2.name AS [referenced_column]
FROM sys.objects obj
INNER JOIN sys.foreign_key_columns fkc
ON obj.object_id = fkc.constraint_object_id
INNER JOIN sys.tables tab1
ON tab1.object_id = fkc.parent_object_id
INNER JOIN sys.schemas sch1
ON tab1.schema_id = sch1.schema_id
INNER JOIN sys.columns col1
ON col1.column_id = parent_column_id AND col1.object_id = tab1.object_id
INNER JOIN sys.tables tab2
ON tab2.object_id = fkc.referenced_object_id
INNER JOIN sys.schemas sch2
ON tab2.schema_id = sch2.schema_id
INNER JOIN sys.columns col2
ON col2.column_id = referenced_column_id AND col2.object_id = tab2.object_id
)
SELECT 'TRUNCATE TABLE ' + QUOTENAME(A.TABLE_SCHEMA) + '.' + QUOTENAME(A.table_name) + ';'
FROM INFORMATION_SCHEMA.TABLES A
LEFT JOIN CTE_fks B
ON A.TABLE_NAME = B.referenced_table
AND A.TABLE_SCHEMA = B.ref_table_schema
WHERE A.TABLE_TYPE = 'BASE TABLE'
AND A.TABLE_NAME != 'sysdiagrams'
AND B.table_name IS NULL

Resources