SQL Select: Actualizar si existe, Insertar si no, ¿con comparación de parte de fecha?
-
19-08-2019 - |
Pregunta
Necesito actualizar un registro en una base de datos con los siguientes campos
[ID] int (AutoIncr. PK)
[ScorerID] int
[Score] int
[DateCreated] smalldatetime
Si existe un registro para la fecha de hoy (solo se debe verificar la parte de la fecha, no la hora) y un anotador dado, me gustaría actualizar el valor de puntaje para este tipo y este día. Si el anotador no tiene un registro para hoy, me gustaría crear uno nuevo.
Tengo canas tratando de imaginar cómo poner esto en una sola declaración SQL (¿es esto posible?). Por cierto, estoy usando una base de datos MSSQl y el método ExecuteNonQuery ()
para emitir la consulta.
Solución
IF EXISTS (SELECT NULL FROM MyTable WHERE ScorerID = @Blah AND CONVERT(VARCHAR, DateCreated, 101) = CONVERT(VARCHAR, GETDATE(), 101))
UPDATE MyTable SET blah blah blah
ELSE
INSERT INTO MyTable blah blah blah
Otros consejos
Los otros chicos han cubierto 2005 (y anteriores) T-SQL / apprroaches compatibles. Solo quería agregar que si tiene la suerte de trabajar con SQL Server 2008, podría aprovechar la nueva declaración Merge (a veces denominada Upsert).
Tuve problemas para encontrar una entrada de blog o un artículo que lo explique más, pero encontré esto más bien (1) entrada útil . La entrada oficial de MSDN es (2) aquí.
(1) [ http: // www.sqlservercurry.com/2008/05/sql-server-2008-merge-statement.html]
(2) [ http://msdn.microsoft.com/ es-es / biblioteca / bb510625.aspx]
CREATE PROCEDURE InsertOrUpdateScorer(@ScorerID INT, @Score INT)
AS
BEGIN
IF EXISTS (
SELECT 1
FROM Scorer
WHERE ScorerID = @ScorerID AND DATEDIFF(dd, GETDATE(), DateCreated) = 0
)
BEGIN
UPDATE
Scorer
SET
Score = @Score
WHERE
ScorerID = @ScorerID
RETURN @ScorerID
END
ELSE
BEGIN
INSERT
Scorer
(ScorerID, Score, DateCreated)
VALUES
(@ScorerID, @Score, GETDATE())
RETURN SCOPE_IDENTITY()
END
END
Utilice el valor de retorno del procedimiento para obtener el nuevo ScorerId.
SqlCommand UpdateScorer = New SqlCommand("InsertOrUpdateScorer", DbConn);
UpdateScorer.CommandType = CommandType.StoredProcedure;
SqlParameter RetValue = UpdateScorer.Parameters.Add("RetValue", SqlDbType.Int);
RetValue.Direction = ParameterDirection.ReturnValue;
SqlParameter Score = UpdateScorer.Parameters.Add("@Score", SqlDbType.Int);
Score.Direction = ParameterDirection.Input;
SqlParameter ScorerId = UpdateScorer.Parameters.Add("@ScorerID", SqlDbType.Int);
ScorerId.Direction = ParameterDirection.Input;
Score.Value = 15; // whatever
ScorerId.Value = 15; // whatever
UpdateScorer.ExecuteNonQuery();
Console.WriteLine(RetValue.Value);
Para el caso en que se desea actualizar o insertar todos los valores a la vez, no solo para un registro, utilicé este fragmento
Primero ejecuta el script de actualización
UPDATE Table1 SET OPIS = T1.OPIS FROM Table1 AS T INNER JOIN Table2 AS T1 ON T.col = T1.col;
Luego haz el script de inserción
INSERT INTO Table1 SELECT * FROM ( SELECT T1.* Table2 AS T1 LEFT JOIN Table1 AS T2 ON (T2.col = T1.col) WHERE T2.col IS NULL ) AS T;
Espero que alguien encuentre esto útil.
El equivalente de esto en MySql (en algunos casos) es algo como esto:
INSERT INTO table (a,b,c) VALUES (1,2,3) ON DUPLICATE KEY UPDATE c=c+1;
Alguien puede descubrir que esto está relacionado con el artículo en Soluciones para INSERTAR O ACTUALIZAR en SQL Server
Versión actualizada con MERGE (Transact-SQL) :
DECLARE @USER_ID AS INT=76;
DECLARE @TYPE AS NVARCHAR(MAX)='set.global';
DECLARE @FKEY AS NVARCHAR(MAX)='21';
DECLARE @DATA AS NVARCHAR(MAX)='test';
begin tran
MERGE UserData
USING (SELECT @USER_ID, @TYPE, @FKEY, @DATA) AS Source([UserId], [Type], [FKey], [Data])
ON (UserData.[UserId] = Source.[UserId] AND UserData.[Type] = Source.[Type] AND (UserData.[FKey] = Source.[FKey] OR (Source.[FKey] IS NULL AND UserData.[FKey] IS NULL)))
WHEN MATCHED
THEN
UPDATE SET [Data] = Source.[Data]
WHEN NOT MATCHED BY TARGET THEN
INSERT
([UserId]
,[Type]
,[FKey]
,[Data])
VALUES
( Source.[UserId]
,Source.[Type]
,Source.[FKey]
,Source.[Data]);
commit tran