How to create a column that can never be updated? - sql-server

In SQL Server 2008, is it possible to create a column that can have a value inserted into it, but can never be updated? In other words, the column can have an initial value inserted into it, but once it contains a non-null value, it can never be changed. If possible, I would prefer to do it without using a trigger.
Thanks - Randy

You can't define a column as Read Only but can you not achieve your goal by setting permission against the column so it can be inserted but not updated by all the relevant users/roles/groups in your database?
Edit:
I misread a bit of your spec, if you need to allow someone to "insert" a null and it only goes read only when a null value is entered then you probably would need a trigger - either to block the updates or to set the deny permissions for the column after a non null value is entered.

You'll have to use a trigger.
You can GRANT INSERT and DENY UPDATE on the column itself, but that would stop you from being able to UPDATE from NULL to something after the initial INSERT.

Related

SQL Change tracking SYS_CHANGE_COLUMNS

We are running SQL 2008 R2 and have started exploring change tracking as our method for identifying changes to export to our data warehouse. We are only interested in specific columns.
We are identifying the changes on a replicated copy of the source database. If we query the change table on the source server, any specific column update is available and the SYS_CHANGE_COLUMNS is populated.
However on the replicated copy the changes are being tracked but the SYS_CHANGE_COLUMNS field is always NULL for an update change.
Track columns updated is set to true on the subscriber.
Is this due to the way replication works and it is performing whole row updates and therefore you cannot get column level changes on a subscriber?
Any help or alternative approaches would be much appreciated.
Thanks
I realize this is an old question, but since I've happened across it I figure I may as well provide an answer for others who come later.
SYS_CHANGE_COLUMNS is null when every column is "updated". "Updated" here doesn't nessarily mean the value changed, it just means the column was touched by the DML statement. So, "update t set c = c" would mean column c was "updated".
Inserts and deletes will therefore always have a SYS_COLUMNS_CHANGED value of "null", since the whole row is affected by an insert or a delete. But most replication technologies do an update by setting every column value to the value of the column on the replication source. Therefore, a replication "update" will touch every column, and so the SYS_CHANGE_COLUMNS value will always be null.

data capture using CDC

To capture the deleted user name I had added a new column in my CDC table (eg:- cdc.dbo_testCDC_CT) to set the logged SQL user name.
ie; ALTER TABLE cdc.dbo_testCDC_CT ADD username VARCHAR(20) DEFAULT(SUSER_SNAME())).
The value coming in that column is always "sa", but I am logged as windows authentication. Why this happing?
First of all, you should never be modifying the system tables generated by cdc. This table was generated when you enabled cdc on your dbo.testCDC table and will include the columns of your source table, plus 5 additional columns, whose meaning is described here: http://msdn.microsoft.com/en-us/library/bb500305(v=sql.110).aspx. It will be deleted automatically when you disable cdc from your table.
I recommend reading up on cdc and the intended usage patterns first. A good start could be this article:
http://technet.microsoft.com/en-us/magazine/2008.11.sql.aspx
To answer your question why sa was always assigned to your column: all rows in the *_CT tables are filled by the log reader process which happens to run under the sa account in your case. This is not the way to add auditing to your system. The previously mentioned article can give you some pointers on better ways to implement auditing too.
Your solution should be capturing the 'Changed By' or 'Inserted By' Logged User Name and persisting that to the underlying data table subject of the capture instance itself. In that way your CDC Instance will also capture the logged User Name for you.
As already mentioned, you should NEVER change the system-generated tables, for two simple reasons:
1. When they are restored for any reason, your changes will be lost
2. Changing system tables can provide you with quite unintended consequences.
Hope this might assist.
Rather than changing the CDC table change the base table (Main source table for default value set DEFAULT(SUSER_SNAME())) COLUMN you will get the user who deleted , or inserted and updated

SQl-SERVER accidental update

Hi I have accidently updated a row in SQL-SERVER that I should not have is there anyway to get the previous value of the row using this query:
UPDATE Documents
SET Name = 'Files'
WHERE Id = 950
Is there any way to recover the previous value?
Yes, it is possible, but only under certain circumstances.
If you had wrapped the UPDATE in a transaction, you could ROLLBACK. This would undo the UPDATE.
Assuming you didn't put it in a transaction, you need to reset the database to a previous point in time. This is only possible if you have some form of back-up on the database. How to do this is shown in this MSDN page
Not that both of these options will UNDO the update, not just tell you the previous values.

Updating column with it's current value

I have a stored proc that should conditionally update a bunch of fields in the same table. Conditionally, because for each field I also pass a "dirty" flag and a field should be updated only if flag is set to 1.
So I'm going to do the following:
create proc update
#field1 nvarchar(1000), #field1Dirty bit, ...other fields...
as
begin
update mytable
set field1 = case when #field1dirty = 1 then #field1 else field1 end,
... same for other fields
end
go
Question - is SQL Server (2008) smart enough to not physically update a field if it's been assigned its own value, like in case if #field1dirty = 0?
Question - is SQL Server (2008) smart enough to not physically update
a field if it's been assigned its own
value, like in case if #field1dirty =
0?
No you should add a where clause that says...where field <> the value you are updating to.
This doesn't seem like a big deal at first, but in truth it can create a massive amount of overhead. One example, think about triggers. If that updates every field in the table, that trigger will fire for every row. YIKES, that's a lot of code execution that's needless, especially if that code is say, moving updates rows to a logging table. I'm sure you get the idea.
Remember, you're updating the field, it just happens to be the same value it was before. It's actually good that this happens, because that means that you can still count the field as modified (think timestamp etc.). If it didn't think updating the field to the same value was modifying the row, you wouldn't know if someone inadvertently (or deliberately) tried to change data.
Update due to comments:
Link to the coalesce function
Example:
For handling null parameter values in your stored procedure
Update Table SET My_Field = COALESCE(#Variable, My_Field)
This doesn't get around what I was talking about before with the field being updated to the same value, but it does allow you to check parameter and conditionally update the field.
SQL doesn't check the value before writing to it. It will overwrite it anyway.
SQL Server will perform the update. The row will be updated as an entire row, so if one column in the row does have FieldxDirty = 1, the update is required anyway. There's no optimization gained in the SET clause.
#Kevin's answer will help more than optimizing the SET clause.
Sorry to come here with an opinion, but I have nowhere else to write :-)
There should at least be a kind of "hint" possibility to tell the UPDATE statement to generally NOT update to the same value.
There are at least 2 reasons I can think of:
1st: the value to update to can be a complicated expression and it is a waste of execution time (not to mention the maintenance of expression changes) to express it again in the WHERE clause. Think also of NULL values!
Ex. UPDATE X SET A = B WHERE ISNULL(A,'') <> ISNULL(B,'')
2nd: we have a synchronized mirroring scenario where the "backup" server is physically placed in another part of the city. This means, that the write to disk is comitted first when the backup-server has performed the write. There is a huge time difference between the write and skip writing. When the developers created the application, they worked in a test environment without mirroring. Most of the UPDATE statements just did not change the values, but it did not matter in the test environment. After deloying the application to production with mirroring, we would really love to have that "only changed value" hint. Reading the original value and checking it does not take time compared to writing

Reason for using ##identity rather than scope_identity

On a SQL Server 2005 database, one of our remote developers just checked in a change to a stored procedure that changed a "select scope_identity" to "select ##identity". Do you know of any reasons why you'd use ##identity over scope_identity?
##IDENTITY will return the last identity value issued by the current session. SCOPE_IDENTITY() returns the last identity value in the current session and same scope. They are usually the same, but assume a trigger is called which inserted something somewhere just before the current statement. ##IDENTITY will return the identity value by the INSERT statement of the trigger, not the insert statement of the block. It's usually a mistake unless he knows what he's doing.
Here is a link that may help differentiate them
looks like:
IDENTITY - last identity on the connection
SCOPE_IDENTITY - last identity you explicitly created (excludes triggers)
IDENT_CURRENT(’tablename’) - Last Identity in table regardless of scope or connection.
I can't think of any, unless there was a trigger then inserted a row (or somesuch) and I really really wanted the id of that trigger-inserted row rather than the row I physically changed.
In other words, no, not really.
DISCLAIMER: Not a T-SQL expert :)
Maybe you should ask the developer their rationale behind making the change.
If you wanted the trigger use you could get another trigger added on is the only reason I can come up with. Even then it's dangerous as another trigger could be added and again you would get the wrong identity. I suspect the developer doesn't know what he is doing. But honestly the best thing to do is to ask him why he made the change. You could change it back, but the developer needs to know not to do that again unless he needs the trigger identity as you may not catch it the next time.

Resources