SQL Server: How to recreate a computed column *only once* - sql-server

I need to change the computation logic for an existing computed column in SQL Server.
And I want to keep the column name unchanged.
I wrote a script to do this job. And the script would
Drop the computed column
Recreate the computed column with new computation logic with same name.
The issue is this script may run multiple times in the future, however I don't want this script to drop and create the column every time.
Is there some way to avoid this column to be deleted and created more than once?
One way I could think of is to create a dummy check constraint.
So before drop and recreate the computed column, first check if that dummy check constraint exists or not.
If it does not exists, then create the dummy constraint and drop/create the computed column.
Not sure if there is any other easy way to solve this.

you may rely upon the computed_columns system table:
SELECT t.name
,SCHEMA_NAME(t.schema_id) AS [schema_name]
,c.name
,c.definition
FROM sys.computed_columns c
JOIN sys.tables t ON t.object_id = c.object_id;
I think it will require some trial and error to get the correct code but you should be able to build a check script to verify the current formula and trigger the DROP/CREATE only when needed.
possible issue: if an outdated version of the script is run it may revert the formula to a previous version so a timestamp check must be implemented.

If you don't mind using hardcoded value (not good), this script might help.
You will have to use a hard coded column_id or better figure out a way to retrieve it (count columns for this table maybe?).
CREATE TABLE dbo.Table_1(ID int identity(0, 1), V1 int NULL, V2 int NULL, V3 AS V1*V2) ON [PRIMARY]
select top 10 * from sys.computed_columns where OBJECT_ID('table_1') = object_id
If Exists(Select 1 From sys.computed_columns where OBJECT_ID('table_1') = object_id and name = 'v3' and column_id<=
4)
Begin
Print 'Update Computed Column'
Alter TAble Table_1 Drop column V3;
Alter Table Table_1 Add V3 as V1*V2
End
Else
Begin
Print 'No Update'
End
select top 10 * from sys.computed_columns where OBJECT_ID('table_1') = object_id

Related

T-SQL table creation in source

I'm starting a new SQL Server Azure DB from scratch. I want to establish a strong source control so I want to be careful in how I create all my tables.
What is the best method to check if the table exists and only execute my CREATE TABLE statement if it does not exist yet? I'm working with passing dynamic SQL to a stored procedure that checks for the tables existence, but that is so limiting. There has to be a preferred way to do this out there. I mean I could preface every query with:
IF NOT EXISTS (SELECT *
FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].j[myTable]') AND type in(N'U'))
BEGIN
CREATE TABLE myTable(....)
END
But that's pretty repetitive.
More simply:
IF NOT EXISTS (SELECT * FROM sys.tables WHERE Name = N'myTable')
CREATE TABLE myTable .......
Idea: use the more focused sys.tables catalog view, instead of the all-encompassing sys.objects - then you don't have to remember those rather unintuitive type values..... - but yes, that is the safest way to do this kind of code.
As of SQL Server 2016, you can also use the
DROP TABLE IF EXISTS myTable;
CREATE TABLE myTable .......
command (which is new - see here: https://blogs.msdn.microsoft.com/sqlserverstorageengine/2015/11/03/drop-if-exists-new-thing-in-sql-server-2016/)

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

SQL Server unique column ID's

I am working on an application that we will be using to maintain our database dictionary (description of each column in each table) and would like to build a "Refresh" function that can go to the database and get the updated column list/names.
What I'm having issues with is when a column is renamed/moved, I would like to track if there is way to update the database with the new details. I tried using the Column_ID from syscolumns, but this changes when the column is moved so seems to be an arbitrary number.
Is there any unique ID that SQL Server generates for a column that doesn't change?
My alternative is to add the new column and mark the old one as removed.
Thanks
Why don't you store the description in the column's description property. Then when the column is updated the developer would be responsible for updating this.
I wrote a web font-end to this over 10 years ago.... I could send you the code if you want? Looked like:
You can try this... its a composite of two queries:
This will get you the list of coumns inside a table:
SELECT c.object_id, OBJECT_NAME(c.object_id) AS OBJECT_NAME
,c.NAME AS COLUMN_NAME
,t.NAME AS DATA_TYPE
,c.max_length AS MAX_LENGTH
FROM sys.all_columns c
INNER JOIN sys.types t ON t.system_type_id = c.system_type_id
WHERE object_name(c.object_id) = 'TableName'
You can generate a HashCode of the column name and combine it with object_id... which should be unique unless the column name changed
Hope this will help.
I would add a new column and mark the old one as removed.
[edit]
I thought that what SQL server deleted the table and create it in the first place, and after your question I went to investigate:
If you mark the option of "Prevent saving changes that require table re creation" (in Options -> Designers -> Table and database designers) and then create the following table:
CREATE TABLE [dbo].[testTbl](
[colA] [nchar](10) NULL,
[colB] [smallint] NOT NULL
) ON [PRIMARY]
and even insert some values:
INSERT INTO dbo.testTbl
VALUES ('text1', 12)
,('text2', 22)
,('text3', 32)
,('text4', 42)
,('text5', 52)
And then run the code from above you will get:
SELECT c.object_id, OBJECT_NAME(c.object_id) AS OBJECT_NAME
,c.NAME AS COLUMN_NAME
,t.NAME AS DATA_TYPE
,c.max_length AS MAX_LENGTH
FROM sys.all_columns c
INNER JOIN sys.types t ON t.system_type_id = c.system_type_id
WHERE object_name(c.object_id) = 'testTbl'
You will get the following values:
If you try to do now the following changes (in the table designer)you will find out that all require drop and re create from the database:
Change a NULLABLE column to non-nullable
change non-nullable column to a nullable
(which is to be expected; those changes require the columns to move around the page)
change the datatype of a column (nchar(10) to more or less, smallint to tinyint or int)
but if you want to change the column from B to C, SQL can manage it, and the results would be:
The object id refers to the table id, and therefore the table stayed with the same object id.

SQL Server 2008 insert records then update fields based on another table

In my searches I have found many threads that deal with my issue in round about ways but I have not been able to find one answer that clearly defines the answer.
Yes I'm a noob to SQL Server.
Here is what I am trying to do And I need to know the best way to do it. For the purposes of this question I will keep it simple. Remember it is the functionality I want
I have table A
ClientName,
manager name,
Location
Table B
Manager name
Manager location
Table C
Random columns,
Client name,
Manager name
I want to
take the rows returned from SELECT [client name], [manager name] from table c
Insert those rows into table a with #clientname and #manager name parameters already defined.
Once the row is inserted it will make
tableA.location = Select [manager location]
from tableb
where #manager = tableb.[manager name]
These needs to happen in a batch for multiple rows in one call
This is point in time data so no lookups and no blanket update queries
Use functions?
Stored procedures?
Triggers may not be the answer since I will be inputting 500+ rows on each call
Thank you in advance
It is hard to believe you could not find something very closely related to your question. All you should have to do is
insert TableA
select C.ClientName, C.ManagerName, B.Location
from TableC C
join TableB B on B.ManagerName = C.ManagerName
where C.ManagerName = #ManagerName and C.ClientName = #clientname
Your stated usage case makes this look like you should wrap this inside of a stored proc. In fact, the statement above is the entire body of a stored proc
create proc sotest(#clientname varchar(40), #managername varchar(40)) as
begin
-- code above
end
It is not possible to avoid lookup of location from TableB as your have defined the problem (assuming you want to populated the Location column)

What is the best way to figure out if an UPDATE, DELETE or INSERT occur for a trigger

I want to create a single trigger in MS SQL for INSERT, UPDATE and DELETE but I want to be able to simple extract the data without 3 IF statements
I want to do something like this:
DECLARE #PK int
SELECT #PK = COALESCE(i.PK, d.PK)
FROM inserted i, deleted d
This did not work but would like to know if I can do it in one query.
What are some alternatives?
You can do the switch logic found here: SQL Server Trigger switching Insert,Delete,Update
Or you can create 3 different triggers.
Those are you options.
I ended up using a FULL OUTER JOIN
DECLARE #DataIWanted as varchar(255)
SELECT #DataIWanted = COALESCE(i.TheData,d.TheData)
FROM inserted i
FULL OUTER JOIN deleted d on d.PK=i.PK
The query above will be from the deleted table or the inserted/updated table. With the assumption that TheData is defined as NON NULL in the DB.

Resources