web-dev-qa-db-fra.com

Besoin d'un champ datetime dans MS SQL qui se met à jour automatiquement lorsque l'enregistrement est modifié

J'ai besoin de créer un nouveau champ DATETIME dans MS-SQL qui contiendra toujours la date de création de l'enregistrement, puis il doit se mettre à jour automatiquement chaque fois que l'enregistrement est modifié. J'ai entendu des gens dire que j'avais besoin d'un déclencheur, ce qui est bien, mais je ne sais pas comment l'écrire. Quelqu'un pourrait-il aider avec la syntaxe d'un déclencheur pour accomplir cela?

En termes MySQL, il devrait faire exactement la même chose que cette déclaration MySQL:

ADD `modstamp` timestamp NULL DEFAULT CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP

Voici quelques exigences:

  • Je ne peux pas modifier mes instructions UPDATE pour définir le champ lorsque la ligne est modifiée, car je ne contrôle pas la logique d'application qui écrit dans les enregistrements.
  • Idéalement, je n'aurais pas besoin de connaître les noms des autres colonnes de la table (comme la clé primaire)
  • Il doit être court et efficace, car cela se produira très souvent.
20
Jesse Barnum

MSSQL, en fait, n'a aucun moyen de définir une valeur par défaut pour UPDATE.

Vous devez donc ajouter une colonne avec une valeur par défaut pour l'insertion:

ADD modstamp DATETIME2 NULL DEFAULT GETDATE()

Et ajoutez un déclencheur sur la table:

CREATE TRIGGER tgr_modstamp
ON **TABLENAME**
AFTER UPDATE AS
  UPDATE **TABLENAME**
  SET ModStamp = GETDATE()
  WHERE **ID** IN (SELECT DISTINCT **ID** FROM Inserted)

Et oui, vous devez spécifier une colonne d'identité pour chaque déclencheur.

ATTENTION: faites attention lors de l'insertion de colonnes sur des tables dont vous ne connaissez pas le code de l'application. Si votre application a la commande INSERT VALUES sans définition de champ, elle générera des erreurs même avec la valeur par défaut sur les nouvelles colonnes.

29
rkawano

D'accord, j'aime toujours garder une trace non seulement quand quelque chose s'est passé, mais qui l'a fait!

Permet de créer une table de test dans [tempdb] nommée [nains]. Lors d'un emploi antérieur, une institution financière, nous gardons une trace de la date insérée (créer) et mise à jour (modifier).

-- just playing
use tempdb;
go

-- drop table
if object_id('dwarfs') > 0
drop table dwarfs
go

-- create table
create table dwarfs
(
asigned_id int identity(1,1),
full_name varchar(16),
ins_date datetime,
ins_name sysname,
upd_date datetime,
upd_name sysname,
);
go

-- insert/update dates
alter table dwarfs
    add constraint [df_ins_date] default (getdate()) for ins_date;
alter table dwarfs
    add constraint [df_upd_date] default (getdate()) for upd_date;

-- insert/update names
alter table dwarfs
    add constraint [df_ins_name] default (coalesce(suser_sname(),'?')) for ins_name;

alter table dwarfs
    add constraint [df_upd_name] default (coalesce(suser_sname(),'?')) for upd_name;
go

Pour les mises à jour, mais les tables insérées et supprimées existent. Je choisis de participer à l'inséré pour la mise à jour.

-- create the update trigger
create trigger trg_changed_info on dbo.dwarfs
for update
as
begin

    -- nothing to do?
    if (@@rowcount = 0)
      return;

    update d
    set 
       upd_date = getdate(),
       upd_name = (coalesce(suser_sname(),'?'))
    from
       dwarfs d join inserted i 
    on 
       d.asigned_id = i.asigned_id;

end
go

Last but not least, permet de tester le code. Tout le monde peut taper une instruction TSQL non testée. Cependant, je soumets toujours mon équipe à des tests de stress!

-- remove data
truncate table dwarfs;
go

-- add data
insert into dwarfs (full_name) values
('bilbo baggins'),
('gandalf the grey');
go

-- show the data
select * from dwarfs;

-- update data
update dwarfs 
set full_name = 'gandalf'
where asigned_id = 2;

-- show the data
select * from dwarfs;

Le résultat. J'ai seulement attendu 10 secondes entre l'insertion et la suppression. Ce qui est bien, c'est que qui et quand sont tous les deux capturés.

enter image description here

12
CRAFTY DBA
Create trigger tr_somename 
On table_name
For update
As 
Begin
    Set nocount on; 
       Update t
        Set t.field_name = getdate()
        From table_name t inner join inserted I 
        On t.pk_column = I.pk_column
End
7
M.Ali

Cela est possible depuis SQL Server 2016 en utilisant PERIOD FOR SYSTEM_TIME.

C'est quelque chose qui a été introduit pour les tables temporelles mais vous n'avez pas besoin d'utiliser des tables temporelles pour l'utiliser.

Un exemple est ci-dessous

CREATE TABLE dbo.YourTable
(  
    FooId INT PRIMARY KEY CLUSTERED,   
    FooName VARCHAR(50) NOT NULL,
    modstamp DATETIME2 GENERATED ALWAYS AS ROW START NOT NULL,   
    MaxDateTime2 DATETIME2 GENERATED ALWAYS AS ROW END HIDDEN NOT NULL,     
    PERIOD FOR SYSTEM_TIME (modstamp,MaxDateTime2)    
)  

INSERT INTO dbo.YourTable (FooId, FooName)
VALUES      (1,'abc');

SELECT *
FROM   dbo.YourTable;

WAITFOR DELAY '00:00:05'

UPDATE dbo.YourTable
SET    FooName = 'xyz'
WHERE  FooId = 1;

SELECT *
FROM   dbo.YourTable;

DROP TABLE dbo.YourTable; 

enter image description here

Il a certaines limites.

  • L'heure stockée sera mise à jour par le système et sera toujours UTC.
  • Il est nécessaire de déclarer une deuxième colonne (MaxDateTime2 ci-dessus) qui est complètement superflu pour ce cas d'utilisation. Mais il peut être marqué comme caché, ce qui le rend plus facile à ignorer.
6
Martin Smith
ALTER TRIGGER [trg_table_name_Modified]
ON [table_name]
AFTER UPDATE 
AS
Begin
    UPDATE table_name
    SET modified_dt_tm = GETDATE()  -- or use SYSDATETIME() for 2008 and newer
    FROM Inserted i
    WHERE i.ID = table_name.id

    end
2
Rolwin C