How do I set a value for existing rows when creating a new timestamp column? - sql

I have the following table:
Study id
Pepsi 1
Coke 2
Sprite 3
I need to add a new column timestamp in the above table. i.e, study creation time and date will be stored in this column. What value should I have set for existing rows? Or should the "Timestamp" column have a value only for newly created rows?
I have used the following query to add the new column:
alter table Study add Timestamp datetime

There is no way to tell you what value you should set for existing rows - that is up to you to decide. If you can somehow retrieve the creation time by piecing together other information, then perhaps you can do this one by one, or you could just leave the existing rows to NULL.
Setting a default like GETDATE() for the column, and setting it to NOT NULL, forces all of the existing rows to inherit the current date and time - and you won't be able to set those back to NULL. I'm quite opposed to using garbage token values like 1900-01-01 to represent unknown, and I also don't believe in modifying the code to say something like "if the date is October 8, 2013 then that's because we just didn't know." So I would suggest adding a NULLable column with a default:
ALTER TABLE dbo.Study ADD CreationTime DATETIME NULL DEFAULT CURRENT_TIMESTAMP;
GO
Note that if you leave the column nullable, then the DEFAULT constraint is only useful if DML never sets it to NULL. If an INSERT statement, for example, explicitly places NULL there, the default is ignored. A way around this is to use a trigger (just like you would handle an update):
CREATE TRIGGER dbo.StudyCreationTime
ON dbo.Study
FOR INSERT
AS
BEGIN
SET NOCOUNT ON;
UPDATE s
SET s.CreationTime = CURRENT_TIMESTAMP
FROM dbo.Study AS s
INNER JOIN inserted AS i
ON s.StudyID = i.StudyID;
END
GO

what value should i have to set for previous studies
This would have to be defined by your business. This doesn't require a technical answer, so no one here can tell you what is right.
I have used below query to adding new column:
alter table Study add Timestamp datetime
This will work just fine, though this will allow nulls. I might suggest making this column non-null, adding a default, and changing the name of the column slightly since timestamp is a reserved word in SQL Server (a datatype that has not much to do with dates or times):
alter table Study add CreateDate datetime not null default current_timestamp;
Note that this will set all rows to the current date and time, so you may want to update them if you have more accurate data. Alternatively, simply create the column as nullable and existing rows won't get the default value, but rather null instead.
Another choice you might have to make is whether to use local time or UTC time (e.g. default getutcdate()). You might want to use the same time that your servers use or that other "CreateDate" columns use.

Related

How to add timestamp column into existing table

I have a Postgres table with certain data, let's say I have 3 columns at the beginning:
name
age
gender
Name1
31
F
Name2
18
M
Name3
22
F
Later on I want to add a new field created_date to record when a user is created and meet 2 sceanrios:
For the existing users, leave the fields empty
For the new users, the field created_date is required and can't be NULL.
Now I can't find a way to how to define "empty" since it can't be null if I add created_date NOT NULL like below query, but the same time I don't want to add DEFAULT xxx since the time is inaccurate.
ALTER TABLE `users`
ADD `created_Date` DATETIME NOT NULL
DEFAULT '2023-02-03 00:00:00'
Can anyone help to define the "empty" in this case?
There are only three options:
Make the new column not nullable
Make the column nullable and use a default date for all existing entries like 01.01.2000. You can set a default value on the column or do an update after adding the column. In the second case the not null needs to be added (with alter table statement) to the column after the update statement.
Create a complete new Table and use it to insert new entries. To read all values together (old entries without date column and new columns with date column) you can make a View which combines the two tables with a union all. This case requires adjustments in your Application and a good thinking about to not have duplicate entries in both tables. And of course the sequences needs to be adjusted aswell. I would not go this way.
Unfortunately there is no other option if the column needs to be not null.
I'd recommend instead:
ALTER TABLE users
ADD COLUMN created TIMESTAMP WITH TIME ZONE NOT NULL
DEFAULT '1970-01-01 00:00:00 UTC';
ALTER TABLE users
ALTER COLUMN created SET DEFAULT now();
That's because:
The column name "current_date" is misleading. This is a timestamp, not just a date.
You should always use "timestamp with time zone" for timestamps, or you'll otherwise have various bugs, like values going backwards, being duplicated, jumping forward, being interpreted differently depending on the client's time zone etc.
This will fill currently existing rows with the timestamp '1970-01-01 00:00:00 UTC', which is immediately recognized as a timestamp "0", so called "epoch time", making it obviously fake, but still older than any newly created.
After that, changing the default to now() will make new rows fill the timestamp automatically and correctly, when the client will either skip the column or will use DEFAULT as value.

DB2 Automatic Initialization and Updating for TIMESTAMP

I would like to append two timestamp columns to an existing table.
CREATED_TSTMP would be populated with the current timestamp when a record is inserted and LAST_UPD_TSTMP would be updated automatically when a record in the table gets updated.
I would like to do this without having to modify existing queries.
I have the following DDL statements:
ALTER TABLE XXX ADD CREATED_TSTMP TIMESTAMP NOT NULL WITH DEFAULT CURRENT TIMESTAMP ;
alter table XXX add column LAST_UPD_TSTMP timestamp not null generated by default for each row on update as row change timestamp ;
However once the columns are appended, this will cause an existing query with the following syntax:
INSERT INTO XXX VALUES(?,?,?,?,?,?,?,?,?,?)
to fail with
The number of values assigned is not the same as the number of
specified or implied columns or variables..
Is there any way around this problem without having to inspect all of the existing queries (there are hundreds of them...)
If you add the IMPLICITLY HIDDEN option when creating the columns, they will be ignored by the SQL statements unless you explicitly mention these columns.
PS. I'm assuming you are on a sufficiently recent version of DB2, because of your using row change timestamp. To avoid ambiguity, you should indicate the DB2 version and platform in question.

Can you tell me when data was inserted into a table

I am using MS SQL Server 2008 and I have an sql table with some data that is inserted daily at 6 am by an sql job. The problem I have is that some data has been inserted separately into the job and I need to know when this data was added.
Is there a query I can run that will show me this?
I think the short answer is NO, there's no magic, ad hoc SQL query that will let you go back after the fact and find out when a row was inserted.
If you want to know when a row is inserted, the easiest thing would be to simply add a date or timestamp field with a default value (like getDate()) that automatically fills in the date/time when the row is inserted.
There are, of course, SQL logs available that will let you track when rows are inserted, updated, deleted, etc., but those require set up and maintenance.
Third option would be to have the program that's inserting the data perform some logging.
Add a date field to the table. You can give it a default value of GETDATE()
Then ORDER BY that field.
SELECT Column1, Column2, NewDateColumn
FROM YourTable
ORDER BY NewDateColumn
what i would do is :
/* add new column to keep inserted row date */
ALTER TABLE [schemaName].[tableName] ADD [RecTime] DATETIME;
/* update the existing rows with the current date since there is no way to guess their insertion date */
UPDATE [schemaName].[tableName] SET [RecTime] = GETDATE();
/* and set a constraint to the RecTime column to set current date on every new row added */
ALTER TABLE [schemaName].[tableName] ADD CONSTRAINT [DF_tableName_RecTime] DEFAULT (GETDATE()) FOR [RecTime]
then you can get those rows like :
SELECT *
FROM [schemaName].[tableName]
WHERE NOT(DATEPART(hh, RecTime) = 6 AND DATEPART(mi, RecTime) <= 20)
you can 'play' with '20' if you know how long sql job run
you probably need to look at SQL CREATE TRIGGER to add the logic to know when the data is being added and log that info in another table for further actions. Without further details I am not sure we can say more than that.
As you're referring to data which has already been inserted, the answer is No, unless you already have a datetime column which has a default value of GETDATE(). The best you can manage after the event has occurred is to look at the sequence of rows and determine that it was between two known times.

Intervals: How can I make sure there is just one row with a null value in a timstamp column in table?

I have a table with a column which contains a 'valid until' Date and I want to make sure that this can only be set to null in a single row within the table. Is there an easy way to do this?
My table looks like this (postgres):
CREATE TABLE 123.myTable(
some_id integer NOT NULL,
valid_from timestamp without time zone NOT NULL DEFAULT now(),
valid_until timestamp without time zone,
someString character varying)
some_id and valid_from is my PK. I want nobody to enter a line with a null value in column valid_until if there is already a line with null for this PK.
Thank you
In PostgreSQL, you have two basic approaches.
Use 'infinity' instead of null. Then your unique constraint works as expected. Or if you cannot do that:
CREATE UNIQUE INDEX null_valid_from ON mytable(someid) where valid_until IS NULL
I have used both approaches. I find usually the first approach is cleaner and it allows you to use range types and exclude constraints in newer versions of PostgreSQL better (to ensure no two time ranges overlap based on a given given someid), bt the second approach often is useful where the first cannot be done.
Depending on the database, you can't have null in a primary key (I don't know about all databases, but in sql server you can't). The easiest way around this I can think of is to set the date time to the minimum value, and then add a unique constraint on it, or set it to be the primary key.
I suppose another way would be to set up a trigger to check the other values in the table to see if another entry is null, and if there is one, don't allow the insert.
As Kevin said in his answer, you can set up a database trigger to stop someone from inserting more than one row where the valid until date is NULL.
The SQL statement that checks for this condition is:
SELECT COUNT(*)
FROM TABLE
WHERE valid until IS NULL;
If the count is not equal to 1, then your table has a problem.
The process that adds a row to this table has to perform the following:
Find the row where the valid until value is NULL
Update the valid until value to the current date, or some other meaningful date
Insert the new row with the valid until value set to NULL
I'm assuming you are Storing Effective-dated-records and are also using a valid from date.
If so, You could use CRUD stored procedures to enforce this compliance. E.G the insert closes off any null valid dates before inserting a new record with a null valid date.
You probably need other stored procedure validation to avoid overlapping records and to allow deleting and editing records. It may be more efficient (in terms of where clauses / faster queries) to use a date far in the future rather than using null.
I know only Oracle in sufficient detail, but the same might work in other databases:
create another column which always contains a fixed value (say '0') include this column in your unique key.
Don't use NULL but a specific very high or low value. I many cases this is actually easier to use then a NULL value
Make a function based unique key on a function converting the date including the null value to some other value (e.g. a string representation for dates and 'x' for null)
make a materialized view which gets updated on every change on your main table and put a constraint on that view.
select count(*) cnt from table where valid_until is NULL
might work as the select statement. And a check constraint limiting the cnt value to the values 0 and 1
I would suggest inserting to that table through an SP and putting your constraint in there, as triggers are quite hidden and will likely be forgotten about. If that's not an option, the following trigger will work:
CREATE TABLE dbo.TESTTRIGGER
(
YourDate Date NULL
)
CREATE TRIGGER DupNullDates
ON dbo.TESTTRIGGER
FOR INSERT, UPDATE
AS
DECLARE #nullCount int
SELECT #nullCount = (SELECT COUNT(*) FROM TESTTRIGGER WHERE YourDate IS NULL)
IF(#NullCount > 1)
BEGIN
RAISERROR('Cannot have Multiple Nulls', 16, 1)
ROLLBACK TRAN
END
GO
Well if you use MS SQL you can just add a unique Index on that column. That will allow only one NULL. I guess that if you use other RDBMS, this will still function.

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()