views:

147

answers:

6

What is the easiest way to automatically fill a datetime column in an SQL data table with the date the row was created? Using SQL Server 2005 if that matters.

EDIT: I've tried using GETDATE() for default and that works for an INSERT Query but not when the TableAdapter adds the row.

+1  A: 

use the getdate() function,

so something like: insert data1, data2, getdate() into table1

David
+4  A: 
ALTER TABLE table
ADD column NOT NULL DEFAULT (GETDATE())
David M
This is what i tried initially but it didn't work, I now suspect because VB.NET inserts 0 instead of NULL when nothing is specified.
MatsT
@MatsT: Null can't signal the database to insert default value for column. if you want to specify default value on insert, you must do this: `INSERT INTO member(lastname,firstname,membership_date) values('lennon','john',default)`
Michael Buen
unfortunately you cannot do: `INSERT INTO member(lastname,firstname,membership_date) values('lennon','john',coalesce(@paramFromYourVb,default))`
Michael Buen
Correct, you don't include the column you want defaulted.
David M
since you cannot make null participate in functions or expressions. try make a UDF in SQL Server, like what i've advised here: http://stackoverflow.com/questions/2672571/conditionally-set-a-column-to-its-default-value-in-postgres/2672668#2672668
Michael Buen
Default values are a bad idea for this - they can be easily bypassed by users. All you have to do is specify a real value for the column (when either inserting or updating later) and your scheme falls apart.
paxdiablo
another approach is just make your query dynamic, that is if it detected null value from vb.net, then change the nullable parameter to DEFAULT keyword. another approach is, remove the column if the value from your program is null, omiting the column makes the insert receives DEFAULT value
Michael Buen
It would not be possible for the user to input anything, the data input is very controlled. Is there any way to get VB.NET to not send any value at all instead of sending DBNull? Easiest solution might just be to let VB.NET send the current date I guess.
MatsT
another approach is bypass the default value altogether, it doesn't hurt to re-specify the getdate() in your insert statement. i.e. `insert into member(lastname, firstname,membership_date) values('lennon', 'john', coalesce(@paramFromYourVb,getdate())`
Michael Buen
@MatsT, why are you so certain that a malicious app couldn't connect to your database? One where the input isn't so well controlled.
paxdiablo
@MatsT: unfortunately that isn't feasible, SqlCommand's parameter is strongly typed, you cannot make it not send anything(say empty string) that will make it be translated to DEFAULT on the other side. DateTime can only hold NULL (DBNull) or a datetime, date
Michael Buen
@Michael: Ok, then I think the best solution is to set the default value in VB.NET to today's date. Only problem is if the user doesn't close the program over the night.@paxdiablo: A malicious app typically wouldn't have access to the database or even the network. Also, if someone managed to do that they could just alter the tables or drop them or whatever. No reason to think that his major purpose would be to bypass the 24 hour time limit for deleting entries.
MatsT
@MatsT, I'm no expert on SQLServer but I'm pretty certain that Microsoft's flagship DBMS would have levels of authority :-) Normal users would not have the power to alter or drop tables. But they would have the right to insert and update records since they need that for their job. You're not protecting from outsiders but rather people with those rights from suborning your column. They have the password to access your database and they could write/get another app which doesn't follow your rules. That's why the DBMS should enforce its own rules.
paxdiablo
@MatsT: Did you craft your own InsertCommand? if you didn't obtain the InsertCommand from SqlCommandBuilder, just include the coalesce in your InsertCommand's string, that way, even if the user doesn't close the program overnight, it will still receive the current date, and also another advantage is the user cannot won't be able to change the date of server, unlike in workstation. If you crafted your own InsertCommand, use this: `insert into member(lastname, firstname,membership_date) values(@lastname, @firstname, coalesce(@membership_date,getdate())`
Michael Buen
@Michael: I've tried that but I'm still getting SQLExceptions thrown back at me from trying to "insert a null value". This is getting a bit annoying :(
MatsT
hmm.. that's impossible, maybe you are still using InsertCommand constructed from SqlCommandBuilder. try to manually write the query and assign it to InsertCommand, include the coalesce in the date field expression
Michael Buen
paxdiablo, in a well-designed designed database (for security I mean) users do not have the rights to directly affect tables. They only have rights to execute stored procs which control the only actions they can take. Giving users direct table rights is a recipe for fraud.
HLGEM
+1  A: 

set a default value for it:

CREATE TABLE dbo.YourTable
    (
    columns....,
    YourDateTime datetime NOT NULL
    )  ON [PRIMARY]
GO
ALTER TABLE dbo.YourTable ADD CONSTRAINT
    DF_YourTable_YourDateTime DEFAULT GETDATE() FOR YourDateTime

when you INSERT don't list that column:

INSERT INTO dbo.YourTable (columns...) VALUES (values...)

or when you INSERT list that column, but use DEFAULT keyword:

INSERT INTO dbo.YourTable (columns...,YourDateTime) VALUES (values...,DEFAULT)

or when you INSERT list that column, and use GETDATE() or some other datetime:

INSERT INTO dbo.YourTable (columns...,YourDateTime) VALUES (values...,'1/1/2010 12:34:56')
KM
+2  A: 

You should create the column and have:

  • an insert(after) trigger which sets it to the current date (or date/time).
  • an update(instead-of) trigger which does not allow the column to change.

Any default-value solution is not secure since it can be bypassed by either specifically setting the column when inserting or changing the column afterwards. The trigger-based solution puts the control with the DBMS where it belongs.

paxdiablo
Why the downvote? This is the correct answer in my opinion since it suffers from none of the auditability problems of default values. I'd be interested in hearing the rationale as to why you think it's incorrect.
paxdiablo
I didn't down vote, but the user doesn't control the value in an application.
Marcus Adams
I don't understand your comment, @Marcus. If you want a row to hold its creation date, the user _shouldn't_ have control, the DBMS should.
paxdiablo
@Marcus: paxdiablo's solution never is within the application, but on the DBMS-side of the medal.
Will Marcouiller
@paxdiablo, a default value should suffice since the user won't be able to set the value within an application. The programmer is in control, not the user. The trigger is overkill. If you replaced "should create" with "could create" and "can be bypassed" with "may be bypassed", I'd probably up vote you.
Marcus Adams
No, I'm afraid I have to disagree vehemently with that comment, @Marcus. You _never_ rely on the good behaviour of client code, _ever_. That's because you never know if someone may slip in a malicious client which doesn't follow your rules. The DBMS should maintain its own integrity. Classic example is with JDBC. Any monkey can write a Java JDBC app which can connect to your database and break the rules. If the rules are enforced at the DBMS, this is not possible. Another classic is the OS - you don't leave it insecure just because you've told apps not to do something bad.
paxdiablo
@paxdiablo, you've convinced me, though only from the perspective of a DBA. If the DBA is charged with ensuring the integrity of the data, then your answer definitely makes sense. Where I work, we have no DBAs, only programmers, but no monkey programmers. :) +1
Marcus Adams
@Marcus Adams, these are things that should be handled by the database no matter what. If you have no dbas then someone needs to have responsibility for seeing to it that data integrity is enforced. Databases outlive the applications they are orginally attached to usually. It is foolish in the extreme to not enforce required data integrity rules in the database. @paxdiable, your solution is correct only if you let people have direct access to tables, which you should not. Generally, defaults are faster than triggers, so a default is usually the better choice unless your rules are complex.
HLGEM
A: 

Be careful if you're using replication. I'm not familiar with the internal details of SQL Server replication, but I know that with some forms of MySQL replication, if you use NOW() as the default value or use a trigger using the same function, the dates will differ between the publisher and subscriber databases.

If you set the date in the application, then the date always replicates fine. Of course, the caveat is that the date or time should be set from the application (or web) server, since the date and time on the end user's machine can't be trusted.

If you're using replication, you might want to test this.

Marcus Adams
+1  A: 

As you can see, you have multiple solutions ahead. In short,

  1. You can write your method (from within VBNET) that sets the appropriate field to System.DateTime.Now (not best practice);

    Public Function InsertObject(o As Object) As Integer
        cmd.CommandText = String.Format("INSERT INTO table_name (Id, CreationDate) VALUES (1, {0})", System.DateTime.Now)
        Return cmd.ExecuteNonQuery()
    End Function
    
  2. You can write an INSTEAD OF INSERT trigger and replace the the CreationDate inserted value with GETDATE() (recommended way);

    CREATE TRIGGER trg_bi_PersistObject
    ON objectToPersistTable
    INSTEAD OF INSERT AS
        insert into objectToPersistTable (Id, CreationDate) (
            select Id, GETDATE()
                from inserted
        )
    

    (Please consider that the code is up the top of my head. Some fixes may be needed for syntax purposes or so. This at least gives you the main idea.)

  3. You can set a default value (but it doesn't seem to have worked for you for some reasons).

    (See David M's or KM's answer for code).

As stated in paxdiablo's comment of his own answer, such feature shall be handled on the DBMS side in order to avoid data corruption on the client-side of the application. Then, I bet your better choice might be 2., creating a BEFORE INSERT trigger and setting the value wihtin it with GETDATE() function.

Will Marcouiller