Je voudrais mettre à jour un ensemble de lignes sur la base d'un critère simple et obtenir la liste des PK qui ont été modifiés. Je pensais que je pouvais simplement faire quelque chose comme ça, mais je m'inquiète des éventuels problèmes de concurrence:
SELECT Id FROM Table1 WHERE AlertDate IS NULL;
UPDATE Table1 SET AlertDate = getutcdate() WHERE AlertDate IS NULL;
Si cela est inclus dans une transaction, des problèmes de concurrence peuvent-ils survenir? Ou existe-t-il une meilleure façon de procéder?
Pensez à regarder la clause OUTPUT :
USE AdventureWorks2012;
GO
DECLARE @MyTableVar table(
EmpID int NOT NULL,
OldVacationHours int,
NewVacationHours int,
ModifiedDate datetime);
UPDATE TOP (10) HumanResources.Employee
SET VacationHours = VacationHours * 1.25,
ModifiedDate = GETDATE()
OUTPUT inserted.BusinessEntityID,
deleted.VacationHours,
inserted.VacationHours,
inserted.ModifiedDate
INTO @MyTableVar;
--Display the result set of the table variable.
SELECT EmpID, OldVacationHours, NewVacationHours, ModifiedDate
FROM @MyTableVar;
GO
--Display the result set of the table.
SELECT TOP (10) BusinessEntityID, VacationHours, ModifiedDate
FROM HumanResources.Employee;
GO
Une façon de gérer cela est de le faire dans une transaction et de faire en sorte que votre requête SELECT prenne un verrou de mise à jour sur les lignes sélectionnées jusqu'à ce que la transaction se termine.
BEGIN TRAN
SELECT Id FROM Table1 WITH (UPDLOCK)
WHERE AlertDate IS NULL;
UPDATE Table1 SET AlertDate = getutcdate()
WHERE AlertDate IS NULL;
COMMIT TRAN
Cela élimine la possibilité qu'un client simultané mette à jour les lignes sélectionnées entre votre SELECT et votre UPDATE.
Lorsque vous validez la transaction, les verrous de mise à jour seront libérés.
Une autre façon de gérer cela est de déclarer un curseur pour votre SELECT avec l'option FOR UPDATE. Ensuite, METTEZ À JOUR O C COURANT LE CURSEUR. Ce qui suit n'est pas testé, mais devrait vous donner l'idée de base:
DECLARE cur1 CURSOR FOR
SELECT AlertDate FROM Table1
WHERE AlertDate IS NULL
FOR UPDATE;
DECLARE @UpdateTime DATETIME
SET @UpdateTime = GETUTCDATE()
OPEN cur1;
FETCH NEXT FROM cur1;
WHILE @@FETCH_STATUS = 0
BEGIN
UPDATE Table1 AlertDate = @UpdateTime
WHERE CURRENT OF cur1;
FETCH NEXT FROM cur1;
END
Plusieurs années plus tard...
La réponse acceptée de l'utilisation de la clause OUTPUT est bonne. J'ai dû creuser la syntaxe réelle, alors voici:
DECLARE @UpdatedIDs table (ID int)
UPDATE
Table1
SET
AlertDate = getutcdate()
OUTPUT
inserted.Id
INTO
@UpdatedIDs
WHERE
AlertDate IS NULL;
AJOUTÉ SEP 14, 2015:
"Puis-je utiliser une variable scalaire au lieu d'une variable de table?" on peut demander ... Désolé, mais non, vous ne pouvez pas. Tu devras SELECT @SomeID = ID from @UpdatedIDs
si vous avez besoin d'un seul identifiant.
Il serait plus facile de faire votre mise à jour en premier, puis d'exécuter "SELECT ID FROM INSERTED".
Jetez un œil à SQL Tips pour plus d'informations et d'exemples.
Peut-être quelque chose de plus comme ça?
declare @UpdateTime datetime
set @UpdateTime = getutcdate()
update Table1 set AlertDate = @UpdateTime where AlertDate is null
select ID from Table1 where AlertDate = @UpdateTime
J'ai fait face au même problème; Je dois mettre à jour le montant du crédit et obtenir une modification de l'heure, ainsi que les détails du crédit de DB. C'est fondamentalement
Effectuer de manière synchrone/atomique (MISE À JOUR puis OBTENIR) dans MYSQL
J'ai essayé de nombreuses options et en ai trouvé une qui a résolu mon problème.
1) OPTION_1 CHOISIR POUR LA MISE À JOUR
Cela maintient le verrouillage jusqu'à la mise à jour (SYNC de GET à UPDATE), mais j'ai besoin d'un verrouillage après la mise à jour jusqu'à GET.
2) OPTION_2 Procédure stockée
La procédure stockée ne s'exécutera pas de manière synchrone comme redis lua, nous avons donc également besoin d'un code de synchronisation pour effectuer cela.
3) OPTION_3 Transaction
J'ai utilisé JPA entityManager comme ci-dessous, pensant qu'avant la validation, personne ne peut mettre à jour, et avant la validation, j'obtiendrai l'objet mis à jour avec l'heure modifiée (à partir de la base de données). Mais je n'ai pas eu le dernier objet. N'engagez que j'ai la dernière.
try {
entityManager.getTransaction().begin();
//entityManager.persist(object);
int upsert = entityManager.createNativeQuery(
"update com.bill.Credit c set c.balance = c.balance - ?1
where c.accountId = ?2 and c.balance >= ?1").executeUpdate();
//c.balance >= ? for limit check
Credit newCredit = entityManager.find(Credit.class, "id");
entityManager.refresh(newCredit); //SHOULD GET LATEST BUT NOT
entityManager.getTransaction().commit();
} finally {
entityManager.unwrap(Session.class).close();
}
4) OPTION_4 LOCK a résolu le problème, donc avant la mise à jour, j'ai acquis le verrou; puis après GET j'ai libéré le verrou.
private Object getLock(final EntityManager entityManager, final String Id){
entityManager.getTransaction().begin();
Object obj_acquire = entityManager.createNativeQuery("SELECT GET_LOCK('" + Id + "', 10)").getSingleResult();
entityManager.getTransaction().commit();
return obj_acquire;
}
private Object releaseLock(final EntityManager entityManager, final String Id){
entityManager.getTransaction().begin();
Object obj_release = entityManager.createNativeQuery("SELECT RELEASE_LOCK('" + Id + "')").getSingleResult();
entityManager.getTransaction().commit();
return obj_release;
}
s'il se trouve à l'intérieur de la transaction, le système de verrouillage de la base de données s'occupera des problèmes de concurrence. bien sûr, si vous en utilisez un (la valeur par défaut de mssql est qu'il utilise un verrou, il indique donc si vous ne remplacez pas cela)
dans SQL 2008, une nouvelle instruction TSQL "MERGE" est introduite, qui effectue des opérations d'insertion, de mise à jour ou de suppression sur une table cible en fonction des résultats d'une jointure avec une table source. Vous pouvez synchroniser deux tables en insérant, en mettant à jour ou en supprimant des lignes dans une table en fonction des différences trouvées dans l'autre table.
http://blogs.msdn.com/ajaiman/archive/2008/06/25/tsql-merge-statement-sql-2008.aspxhttp://msdn.Microsoft. com/fr-fr/library/bb510625.aspx
Edit: mon mauvais, vous vouliez que la sélection affiche les résultats après la mise à jour, pas la mise à jour à partir d'une sélection.
Avez-vous essayé une sous-sélection?
update mytable set mydate = sysdate
where mydate in (select mydate from mytable where mydate is null);