SQL Server: How to list changed columns with change tracking? - sql-server

I use SQL Server 2012 Standard edition, and I activated Change Tracking function on a table.
When I list changes on a table with the CHANGETABLE function, I have a SYS_CHANGE_COLUMNS property with binary data
0x0000000045000000460000004700000048000000
How do I know which columns have changed ?

Because the column is a bitmask made up of the column IDs of all the columns which were changed, it's difficult to know what it's made up of. In fact, MSDN says not to interrogate SYS_CHANGE_COLUMNS directly here: https://msdn.microsoft.com/en-us/library/bb934145.aspx
This binary value should not be interpreted directly.
However, when you are detecting changes for notification purposes, usually the notification consumer has a good idea of which columns they are interested in changing.
For this use-case, use the CHANGE_TRACKING_IS_COLUMN_IN_MASK function.
-- Get the column ID of my column
declare #MyColumnId int
set #MyColumnId = columnproperty(object_id('MyTable'), 'MyColumn', 'ColumnId')
-- Check if it's changed
declare #MyColumnHasChanged bit
set #MyColumnHasChanged = CHANGE_TRACKING_IS_COLUMN_IN_MASK (MyColumnId, #change_columns_bitmask);
If CHANGE_TRACKING_IS_COLUMN_IN_MASK tell me if a column has changed,
how can I write a script that tell me which columns have changed ? I
have around 50 attributes for each table.
I'm afraid you'll need to loop through all of the columns you may be interested in... If this is too restrictive, you may have to use another change-notification approach, like Change Data Capture (CDC), or Triggers

Related

MS Access Write Conflict - SQL Server - Me = Dirty

I'm getting the error message:
This record has been changed by another user since you started editing it. If you save the record, you will overwrite the changes the other user made...
I know this is a common question and I've spent hours researching and testing but to no avail. A few notes:
There are no bit fields anywhere in my database
All tables have a primary key, data type = identity
All tables have a create/modified timestamp trigger on updates and insert
I'm fairly certain that the problem has to do when the form (and multiple subforms) are creating the identity fields and/or the timestamp triggers. Specifically, I only get this error on the "Individual Fish" table when I go back to edit an old 'fish' (as shown on the screenshot). If I just tab through the form and don't make any edits, it works fine. But if I need to edit anything on a previous 'fish' - after the identity / trigger fires - then it gives me the error.
I've gone through and added If Me.Dirty Then Me.Dirty = False End If to every form for the following events:
On Current, On Load, On Click, After Update, Before Update, Before Insert, On Dirty.
I also added DoCmd.RunCommand acCmdSaveRecord to On Deactivate. I will admit that I am not great at VBA, so there could be something silly I did here. Code attached. I've also messed around with Record Locks = Edited Record.
So far nothing seems to work. Please let me know if you think I'm missing something. Also, if you have any random comments or suggestions about my database design or anything else, I always welcome feedback.
Thanks!
UPDATE:
Albert's answers got me to the right place. The short version is to add a rowversion (aka timestamp) field to all tables. This was mentioned on several other posts, but i didn't realize the [awfully named] "timestamp" didn't actually have to do with date or time. Thanks for the help!
]3
Ok, lots of things here to check. First of all, placing a me.Dirty = false in on-current, or events like before update will cause the "same" event to try and trigger again. You really (but really really) don't want to do this. (so wild random tossing in of me.dirty in those events will only make this issue much worse and often cause the very same event to trigger again.
next up:
All tables have a create/modified timestamp trigger on updates and insert
Ok, now the above is confusing. Do you have an actual trigger - as that is a separate issue and will often trigger the record been modified by someone else.
Also when we speak of a timestamp column, do keep in mind that such columns have ZERO ZERO ZERO to do with datetime. Over the years Microsoft has attempted to "change" the name used. The correct name is ROWVERSION, and this column is NOT a datetime data type column, but is called timestamp. Do NOT in ANY WAY confuse this rowversion system/column with that of a datetime column.
So, the assumptions are:
You have a timestamp column - this is of data type timestamp. This column is NOT touched by your code or trigger in ANY WAY. This column is NOT of datetime, nor of datetime2, but is of data type timestamp.
If you don't have a actual timestamp column here (it does not need to be on the form), then you will get constant "dirty" warnings. You also get this with any real data type columns - especially if they are set by server code. (access will round differnt).
Bottom line:
You need a actual rowversion column (of type timestamp) in that table. Behind the scenes if access does NOT find this column, then it will do a column by column compare, and without question with a trigger to set some LastUpdated column with GETDATE() on the server side trigger, then this will cause nothing but problems. Introduction of a timestamp column will STOP access from doing the column by column compare after a update, and it will ONLY look at the timestamp column. So, your trigger can now update the LastUpdated, and the timestamp column should not change from access points of view.
So, you need to be sure:
A PK column is seen by access - all sql tables need a PK.
All tables should have a rowversion column.
If you do add a timestamp (rowverison) column to the problem table, then make sure you re-link on the access client side. In fact after ANY table change or modifications server side, then you should re-link the client side.
I would remove any stray me.Dirty = False code in that form.
You can place a "save" button on the form if you wish, and simply have it go
if me.dirty = true then me.Dirty = False
Edit
With the above setup, you should be able to re-introduce your server side trigger that sets the LastUpdated. However, you not want any code in the form that "touches" or uses that column. You can however should be able to drop in that LastUpdated column into the form and see it update after you save.
Bottom line as I have run across this error on an upgrade of SQL Server to 2016, due not assume "timestamp" is of data type "datetime". It is not. The data type that Access requires is of type "timestamp". Add a column with that data type to any table that is editable through Access and that will clear the "Write conflict with grayed out save button" message.

Linked SQL Server's table shows all fields as #Deleted, but when converted to local, all information is there

My company has a really old Access 2003 .ADP front-end connected to an on-premise SQL Server. I was trying to update the front-end to MS Access 2016, which is what we're transitioning to, but when linking the tables I get all the fields in this specific table as #Deleted. I've looked around and tried to change some of the settings, but I'm really not that into SQL Server to know what I'm doing, hence asking for help.
When converting the table to local, all the info is correctly displayed, so it begs the question. Also, skipping to the last record will reveal the info on that record, or sorting/filtering reveals some of the records, but most of the table stays "#Deleted"...
Since I know you're going to ask: Yes, I need to edit the records.. Although the snapshot method would work for people trying to view the info, some of us need to edit it.
I'm hoping someone can shed some light on this,
Thanks in advance, Rafael.
There are 3 common reasons for this:
You have bit fields in SQL server, but they are null. They should be assigned a default of 0.
The table in question does NOT have a PK (primary key).
Last but not least you need (want) to add a timestamp column. Keep in mind that this is really what we call a “row version” column (so it not a date/time column, but a timestamp column). Adding this column will help access determine if a record been changed, and this is especially the case for any table/form in Access that allows editing of “real” number data types (single, double). If access does not find a timestamp column, then it reverts to a column by column comparison to determine table changes, and due to how computers handle “real” numbers (with rounding), then such comparisons often fail.
So, check for the above 3 issues. You likely should re-run the linked table manager have making any changes.

What is the conventional way to hard-code values in a database?

My application has a database table that is used to record the attendance of employees. And the column attedance_status has only three possible values - "present", "absent", "on_leave", and NULL as default.
How do I add it to the database? So far I have come up with two possible ways.
Create another table attendance_status with status_id and status_value and add the above values to it. And then use the id in the application for all SQL queries.
Probably the bad way. Hardcode the values (maybe in a config file) and use it throughout the app's SQL queries.
Am I missing the right way? How should this be approached?
Either will work, but Option 1 will give you flexibility in the event that the requirements change and is the standard data model. I would, however, name my columns a little differently. I would have id, value, name. Then the references become attendance_status.id and attendance_status.value. The third column is available for use in displays or reports or whatever. value is on_leave and name is On leave.
Option 2 works provided the data input point is totally closed. If someone codes new functionality there is the risk that he or she will invent something different to mean the same thing like onLeave.

Python+peewee: retrieve a field after Model.save() (and trigger execution in Sqlite database)

In my application I work with Sqlite. In one of the tables inside database I've implemented a trigger (basically, after an insert event on the table TAB, it has to update a column named codecolumn which depends on the ID PK field)
In my code I create and object from a PeeweeModel previously setted
objfromModel = Model(params....)
After the execution of line:
objfromModel.save()
We hoped to get appart from the _id field generated -in fact objfromModel.id is retrieved from DB-, but also the codecolumn new field generated by the trigger execution on insert event. But objfromModel.codecolumn is None
Question: is there a trick to make on Peewee in order to recover this new field generated in database by trigger.
Unfortunately SQLite does not support the concept of INSERT ... RETURNING. What you could do is a couple of things:
A. After creation simply re-fetch the codecolumn. e.g. self.codecolumn = MyModel.select(MyModel.codecolumn).where(MyModel.id == self.id).scalar(convert=True) (the use of "scalar" says return just one value, the "convert=True" says convert the underyling database type to a Python type. This is really only necessary if the database type is a date or datetime
B. Create a post-insert trigger that calls a user-defined function. Register a handler for the user-defined function on your database instance, and have your callback receive the new codecolumn value and set it as a database attribute in the callback. Hopefully this makes sense?
C. Move the codecolumn trigger out of SQL and into Python, making it easier to know ahead-of-time what its value will be. This depends obviously on what that column contains.
Hope these ideas help.

What does it mean setting "SET collapse_empty_table_version to false or true in netezza

can you please explain me what it does if we set
"SET collapse_empty_table_version = false/true"
in netezza environment.
This parameter controls the behavior when multiple table versions are created (e.g. when you alter a table to add a column) but no data was added to the table in one of the interim (i.e. not most recent) versions.
Each version of a table has its own data store. If this parameter is set to TRUE then the data store for interim version where no data was added are discarded, or collapsed.
For example:
TABLE_A has some number of rows, and let's say we call its data store
TABLE_A.00 (the data stores are under the covers and have no names we
would normally reference).
Then we alter TABLE_A to add a new column. This would have another
data store that we can call TABLE_A.01.
Then we alter TABLE_A again to add another column before any rows are
added after our last alteration. This would have another data store
that we can call TABLE_A.02.
At this point the data store TABLE_A.01 serves no purpose as it holds
no data, and can never receive any data. If this parameter is set to
TRUE that data store will be reclaimed before a GROOM is performed.
Disclaimer: this parameter is something to fiddle with only when directed by IBM support as it is not publicly documented, and my description here is just my understanding of it, and is probably not complete or 100% accurate.

Resources