Temporarily changing a Timestamp column so parked rows can be added - sql

for an erroneous situation we've had to park a bunch of DB rows, which we now want to put back into a table. The problem with this is one of the columns, RowVersion, which is of type Timestamp. Inserting the old values does not seem to be possible, so it seems like these would need to be re-generated. I am still researching what the impact would be (seems none/low).
However, I was thinking of a workaround that does keep the original row values intact. My idea was as follows, in 1 locked transaction:
-- Sample table with only UNIQUEIDENTIFIER Id & TIMESTAMP RowVersion
ALTER TABLE Test
ALTER COLUMN RowVersion VARBINARY(8);
INSERT INTO Test (
Id,
RowVersion
)
VALUES (
'd261ff28-6279-4e81-93f4-825c9ca689bd',
0x000000000000CF0A
);
ALTER TABLE Test
ALTER COLUMN RowVersion TIMESTAMP;
Which results in the error Cannot alter column 'RowVersion' because it is 'timestamp'.. Is there any workaround for this? Or to avoid the XY-problem: Is there any ways of inserting rows with a specific row AND keeping the column as a timestamp?

Related

SQL Server Add Default datetime column for existing table

I want to have a new column in the table that will show the date and time of the inserts, but without modifying the queries to include the column itself.
I have added the new column in the following way:
ALTER TABLE DBO.HOURLYMODULETIMES
ADD CreateTime datetime DEFAULT NOT NULL getdate()
This adds the values to previous entries, but when I try to INSERT INTO the table without including the new column
INSERT INTO DBO.HOURLYMODULETIMES VAlUES
(99999999,11111,2222,'JA')
Table has 5 columns ID, AVGMODULETIME, SUMHOURS, USERNAME, CreateTime(newly added). I get the following error:
Column name or number of supplied values does not match table definition.
Is it possible to create such a column without modifying the queries?
You have to specify the columns now when you want to omit one of them when doing INSERT:
INSERT INTO DBO.HOURLYMODULETIMES (ID, AVGMODULETIME, SUMHOURS, USERNAME)
VALUES (99999999,11111,2222,'TEST')
It's good programming practice to always do this, since table definitions may change over time - as you have noticed!

Add timestamp to existing table

I have a SQL Server table with just 3 columns, one of which is of type varbinary. The data in this column is actually a Json document which among other properties contains information about when the data was last modified. Unfortunately the SQL table itself does not contain information about when its rows were modified.
Now when doing sorting and filtering of the data I of course don't want fetch all rows in order to find e.g. the latest 100 entries.
So my question is: does SQL Server somehow remember when a row was added/modified? I have tried adding a timestamp and this is applied to all existing rows but this is applied randomly I think, because the sorting doesn't work. I don't need a datetime or anything, I just want to be able sort the records based on when they were last modified.
Thanks
For those looking to insert a tamestamp column of type DateTime into an existing DB table, you can do this like so:
ALTER TABLE TestTable
ADD DateInserted DATETIME NOT NULL DEFAULT (GETDATE());
The existing records will automatically get the value equal to the date/time of the moment when column is added.
New records will get up-to-date value upon insertion.
SQL Server will not track historically when a row was inserted or modified so you need to rely on the JSON data to figure that out yourself. You are going to need a new column to make this efficient to query. Once you have your new column you have some options:
Loop through all your records populating the new column with the relevant value from the JSON data.
If your version of SQL Server is recent enough, you can query the JSON data directly. Populate this column using a query like this:
UPDATE MyTable
SET MyNewColumn = JSON_VALUE(JsonDataColumn, '$.Customer.DateCreated')
The downside of this method is that you need to maintain this
Make SQL Server compute the value from the JSON automatically, for example:
ALTER TABLE MyTable
ADD MyNewColumn AS JSON_VALUE(JsonDataColumn, '$.Customer.DateCreated')
And, create an index to make it efficient:
CREATE INDEX IX_MyTable_MyNewColumn
ON MyTable(MyNewColumn)
Use a new column CreatedDate and store datetime every time you make an Insert.
You could use GetDate() for inserting date in the column.
A UpdatedDate column can be used for updates.
in order to find e.g. the latest 100 entries.
Timestamp is indeed what you need.
It's ever-increasing value, it's updated automatically, so you are always able to find all last modified/inserted rows.
Here is an example:
create table dbo.test1 (id int);
insert into dbo.test1 values(1), (2), (3);
alter table dbo.test1 add ts timestamp;
update dbo.test1
set id = 10
where id = 2
select top 1 *
from dbo.test1
order by ts desc;
--id ts
--10 0x000000001FCFABD2
insert into dbo.test1 (id)
values (100);
select top 1 *
from dbo.test1
order by ts desc;
--id ts
--100 0x000000001FCFABD3
As you see, you always get the last modified/inserted row.
For your purpose just use
select top 100 *
...
order by ts desc;
Thanks. Apparently I didn't look hard enough before I posted this question. The question has been asked a couple of times before and the answer is: Nope! There is no easy solution to this.
SQL Server does not keep track of when a record was created or modified, which was somehow what I was looking for. So I will go for the next best solution, which is probably to create a datetime column, retrieve the modified date from the Json document and then update the record. Or rather, the 1,4 million records:-(

Alter column of type timestamp

I want to update an empty table , which has a column of type timestamp to varbinary(8)
I used the following command
ALTER TABLE Notification ALTER COLUMN RowRevisionID varbinary(8)
and I get and an error
Cannot alter column 'RowRevisionID' because it is 'timestamp'.
How can I change a timestamp column type?
I do not wish to drop the column an add a new one , because that will create a column at the end , and I wish to preserve column order to use this table in an INSERT INTO
You unfortunately cannot make a change to a timestamp column, as the error implies; you are stuck with what you have. Also, each table can only have one timestamp column, so you cannot duplicate the column in any solution.
Your best bet (depending on the size of the table) might be to copy the data into a staging table (using SELECT * INTO MyTempTable FROM OriginalTable syntax to preserve the timestamp values), then drop and recreate the original table with the desired columns in the desired order and reinsert the data, or you could add a new VARBINARY(8) column to the existing table and drop the timestamp column, preserving the original table. There are probably other solutions along the same lines as these, but all will require a new column, rather than an ALTER COLUMN script.
You are looking for:
ALTER TABLE Notification DROP RowRevisionID;
and
ALTER TABLE Notification ADD RowRevisionID varbinary(8) AFTER myOtherColumn;

SQL IDENTITY COLUMN

I have an sql table which is basically a statement.
Now lets say the records I have in my table have a date and an identity column which is autonumbered and defines the order which the transactions are displayed in the front end to the client.
The issue is during an insert some of the data have gone missing and some transactions between two dates are missing.
I need to insert the data into the table, but I need to insert them between the dates and not at the end of the table. If I do a a normal insert, the data will appear at the end of the table and not at the date I specify, because the identity column is autonumbered, and cannot be updated.
Using SET IDENTITY_INSERT (table) ON, you force SQL Server to let you insert any arbitrary value into an IDENTITY column - but there's no way to update an IDENTITY column.
What's the big problem with a few gaps anyway?? Yes, it might be a bit of a "cosmetic" problem - but how much hassle and effort do you really want to spend on cosmetic problems?? The order of the entries is still a given - even with gaps.
So again: what's the big deal?? IDENTITY columns are guaranteed to be ever increasing - that's all they guarantee. And for 99% of the cases, that's more than good enough....
Why not just display the records in the user interface sorted by the date, rather than by the primary key?
OK, if you really want to do this (personally, I think changing the sort date in the UI is going to be easier than updating the primary key values in the database, but anyway...). This should work, assuming you're not using the primary key values in any foreign key constraints (if you are, then you'll need to make sure those constraints have ON UPDATE CASCADE set)
SET IDENTITY_INSERT tablename ON
UPDATE tablename SET
primary_key = primay_key + 1
WHERE
primary_key >= <the primary key where you want to insert the new date>
INSERT INTO tablename
(primary_key, date, ...)
VALUES
(<the primary key to insert>, <the date to insert>, ...)
SET IDENTITY_INSERT tablename OFF
However, I strongly, strongly suggest you backup your database before attempting this.
Just out of curiosity, is it one ID per date? Your answers imply this a little, so if so, replace the Identity column with a computed column that is defined as the date difference in days from an arbitrary starting point?
DECLARE #EXAMPLE TABLE
(
[Date] DATE,
ID AS DATEDIFF(Day, '1 Jan 2010', [Date])
)
INSERT INTO #EXAMPLE([Date])
VALUES (GETDATE()), (GETDATE()+1), (GETDATE()+2)
SELECT * FROM #EXAMPLE

Change each record in a table with no primary key?

I have a table in a database that represents dates textually (i.e. "2008-11-09") and I would like to replace them with the UNIX timestamp. However, I don't think that MySQL is capable of doing the conversion on its own, so I'd like to write a little script to do the conversion. The way I can think to do it involves getting all the records in the table, iterating through them, and updating the database records. However, with no primary key, I can't easily get the exact record I need to update.
Is there a way to get MySQL to assign temporary IDs to records during a SELECT so that I refer back to them when doing UPDATEs?
Does this not do it?
UPDATE
MyTable
SET
MyTimeStamp = UNIX_TIMESTAMP(MyDateTime);
If for some reason you do have to iterate (the other answers cover the situation where you don't), I can think of two ways to do it (these aren't MySQL-specific):
Add a column to the table that's an auto-assigned number. Use that as the PK for your updates, then drop the column afterwards (or just keep it around for future use).
In a table with no defined PK, as long as there are no exact duplicate rows, you can use the entire row as a composite PK; just use every column in the row as your distinguishing characteristic. i.e., if the table has 3 columns, "name", "address", and "updated", do the following:
UPDATE mytable SET updated = [timestamp value] WHERE name = [name] AND address = [address] AND timestamp = [old timestamp]
Many data access frameworks use this exact strategy to implement optimistic concurrency.
No, you should be able to do this with a single update statement. If all of the dates are yyyy-mm-dd and they are just stored in some sort of text column instead of DATETIME, you can just move the data over. SQL would be like:
ALTER TABLE t ADD COLUMN dates DATETIME;
UPDATE t set t.dates=t.olddate;
This shouldn't be dependent on a PK because MySQL can scan through each row in the table. The only time PK's become an issue is if you need to update a single row, but the row may not be unique.
You can generate values during a SELECT using the MySQL user variables feature, but these values do not refer to the row; they're temporary parts of the result set only. You can't use them in UPDATE statements.
SET #v := 0;
SELECT #v:=#v+1, * FROM mytable;
Here's how I'd solve the problem. You're going to have to create another column for your UNIX timestamps anyway, so you can add it first. Then convert the values in the old datetime column to the UNIX timestamp and place it in the new column. Then drop the old textual datetime column.
ALTER TABLE mytable ADD COLUMN unix_timestamp INT UNSIGNED NOT NULL DEFAULT 0;
UPDATE mytable
SET unix_timestamp = UNIX_TIMESTAMP( STR_TO_DATE( text_timestamp, '%Y-%m-%d' ) );
ALTER TABLE mytable DROP COLUMN text_timestamp;
Of course you should confirm that the conversion has been done correctly before you drop the old column!
See UNIX_TIMESTAMP() and STR_TO_DATE()