sql >> Base de Datos >  >> RDS >> Database

5 consejos sencillos para usar la instrucción SQL UPDATE con JOIN

"¡Ups! Culpa mía." ¿Cuántas veces dijiste esto después de que una ACTUALIZACIÓN de SQL salió mal? La cuestión es que, si no tiene cuidado, una actualización de la tabla puede tener graves consecuencias en forma de declaración DELETE. Podría empeorar aún más si lo complica usando UPDATE con JOIN. Es por eso que debe pensarlo bien antes de presionar Ejecutar o presionar CTRL-E.

Entonces, hoy aprenderá a codificar su ACTUALIZACIÓN SQL con JOIN sin problemas y nunca dirá "¡Ups! Mi culpa” otra vez.

Pero antes de empezar a practicar, comencemos con la sintaxis. También hará que nuestros novatos se sientan como en casa con la ACTUALIZACIÓN de SQL Server con JOIN. A continuación, prepararemos algunos datos y algunos ejemplos. Y finalmente, examine los consejos de seguridad.

[ID de formulario de envío de pulso =”12968″]

SQL Sintaxis de UPDATE JOIN

UPDATE table1
SET column1 = <expression1 | value1> [, column2 = <expression2 | value2>, <columnN = expression3 | value3>]
FROM table1
[INNER | OUTER LEFT | OUTER RIGHT] JOIN table2 on table1.keycolumn = table2.keycolumn
[WHERE condition]

Necesitamos detallar algunos puntos de esto.

  1. Podemos actualizar una tabla a la vez para al menos 1 columna o algunas columnas.
  2. Necesitamos la cláusula FROM para agregar un JOIN. El objeto en la cláusula FROM puede o no ser el mismo que el objeto que se está actualizando.
  3. Podemos usar INNER o OUTER JOIN (ver ejemplos más adelante).
  4. Solo podemos actualizar un subconjunto de los datos mediante la cláusula WHERE.

Antes de tener nuestros ejemplos, preparemos los datos.

Nuestros datos de prueba

Por amor a las películas, creemos una base de datos de títulos de películas con calificaciones de los usuarios.

CREATE DATABASE [Movies]
GO

USE [Movies]
GO

CREATE TABLE [dbo].[Titles](
	[TitleID] [int] IDENTITY(1,1) NOT NULL,
	[Title] [varchar](50) NOT NULL,
	[ReleaseDate] [date] NOT NULL,
	[OverallUserRating] [varchar](10) NULL,
 CONSTRAINT [PK_Titles] PRIMARY KEY CLUSTERED 
(
	[TitleID] ASC
))
GO

CREATE TABLE [dbo].[UserRatings](
	[UserRatingID] [int] IDENTITY(1,1) NOT NULL,
	[TitleID] [int] NOT NULL,
	[User] [varchar](50) NOT NULL,
	[Rating] [tinyint] NOT NULL,
 CONSTRAINT [PK_UserRatings] PRIMARY KEY CLUSTERED 
(
	[UserRatingID] ASC
))
GO

ALTER TABLE [dbo].[UserRatings]  WITH CHECK ADD  CONSTRAINT [FK_UserRatings_Titles] FOREIGN KEY([TitleID])
REFERENCES [dbo].[Titles] ([TitleID])
GO

ALTER TABLE [dbo].[UserRatings] CHECK CONSTRAINT [FK_UserRatings_Titles]
GO

ALTER TABLE [dbo].[UserRatings]  WITH CHECK ADD  CONSTRAINT [CK_UserRatings_Rating] CHECK  (([Rating]>=(1) AND [Rating]<=(5)))
GO

ALTER TABLE [dbo].[UserRatings] CHECK CONSTRAINT [CK_UserRatings_Rating]
GO

Ahora que tenemos la base de datos y las tablas, insertemos algunos datos:

INSERT INTO Titles
(Title, ReleaseDate)
VALUES 
('The Avengers', '05/04/2012'),
('Avengers: Age of Ultron','5/1/2015'),
('Avengers: Infinity War','4/27/2018'),
('Avengers: Endgame','4/26/2019'),
('Captain America: Civil War','5/6/2016')
GO

INSERT INTO UserRatings(TitleID, [User], Rating) 
VALUES 
(1,'Natasha',5),
(1,'Bruce',3),
(1,'Tony',4),
(1,'Bucky',5),
(2,'Steve',4),
(2,'Wanda',3),
(2,'Pietro',2),
(2,'Clint',5),
(3,'Hope',5),
(3,'Sam',5),
(3,'Nick',3),
(3,'James',5),
(4,'Scott',5),
(4,'Wong',5),
(4,'Peter',5),
(4,'Carol',4),
(4,'Shuri',5)
GO

ACTUALIZACIÓN de SQL Server con ÚNETE Ejemplo

Examinaremos diferentes ejemplos que tienen el mismo objetivo de actualizar la OverallUserRating en los Títulos mesa. Las calificaciones pueden ser del 1 al 5. Calificación general del usuario es el promedio de todas las calificaciones para el título de una película.

Este es el estado inicial de la tabla:

ACTUALIZAR LEFT JOIN Ejemplo

-- SQL UPDATE with LEFT OUTER JOIN
SELECT
 a.TitleID
,CAST(CAST(AVG(CAST(b.Rating AS DECIMAL(3,2))) AS DECIMAL(3,2)) AS varchar(9)) AS AverageRating
INTO #ComputedRatings
FROM titles a
INNER JOIN UserRatings b ON a.TitleID = b.TitleID
GROUP BY a.TitleID

-- mark 'No Rating' if there are no existing ratings
UPDATE Titles
SET OverallUserRating = ISNULL(b.AverageRating,'No Rating') 
FROM Titles a
LEFT JOIN #ComputedRatings b ON a.TitleID = b.TitleID

La primera instrucción SELECT calcula la calificación promedio por título de película en función de las UserRatings. mesa. El resultado se vuelca en una tabla temporal llamada #ComputedRatings . Como usamos INNER JOIN, los títulos de películas sin calificaciones se descartan.

En la instrucción UPDATE, los Títulos la tabla se actualizó usando LEFT JOIN de #ComputedRatings mesa temporal. Si la calificación promedio es null , el valor se convierte en Sin calificación . En nuestra muestra, Capitán América:Civil War aún no tiene calificaciones de los usuarios; consulte la Figura 2.

Ejemplo de SQL UPDATE INNER JOIN

Así es como funciona:

-- SQL UPDATE with INNER JOIN
SELECT
 a.TitleID
,ISNULL(CAST(CAST(AVG(CAST(b.Rating AS DECIMAL(3,2))) AS DECIMAL(3,2)) AS varchar(9)),'No Rating')  AS AverageRating
INTO #ComputedRatings
FROM titles a
LEFT JOIN UserRatings b ON a.TitleID = b.TitleID
GROUP BY a.TitleID


UPDATE Titles
SET OverallUserRating = b.AverageRating
FROM Titles a
INNER JOIN #ComputedRatings b ON a.TitleID = b.TitleID

Cuando ejecute el código anterior, el resultado será el mismo que en la Figura 2. Pero, ¿cuál es la diferencia entre los dos códigos?

  • La primera declaración SELECT considera la calificación NULL del usuario a diferencia de nuestro ejemplo LEFT JOIN anterior. No descarta el título de la película sin calificaciones de los usuarios. Entonces, esta vez, Sin calificación valor para Capitán América:Civil War ya está considerado.
  • Un INNER JOIN con la asignación directa de la AverageRating el valor es más apropiado ya que todos los TitleIDs se contabilizan.

Ahora, en lugar de una tabla temporal, también se puede usar la expresión de tabla común (CTE). Aquí está el código modificado:

-- SQL UPDATE with JOIN using INNER JOIN from a CTE
;WITH ComputedRatings AS
(SELECT
 a.TitleID
,ISNULL(CAST(CAST(AVG(CAST(b.Rating AS DECIMAL(3,2))) AS DECIMAL(3,2)) AS varchar(9)),'No Rating')  AS AverageRating
FROM Titles a
LEFT JOIN UserRatings b ON a.TitleID = b.TitleID
GROUP BY a.TitleID)
UPDATE Titles
SET OverallUserRating = b.AverageRating
FROM Titles t
INNER JOIN ComputedRatings cr ON t.TitleID = cr.TitleID

Más información

  • Tu guía definitiva para SQL JOIN:INNER JOIN - Parte 1
  • Su guía definitiva para SQL JOIN:OUTER JOIN - Parte 2

Uso de Actualizar Comando con Unirse Con seguridad (5 consejos)

La seguridad se refiere a la actualización de los registros previstos. Además, se trata de NO tocar los registros que no pretendemos actualizar. Aquí, nos ocuparemos de 5 escenarios:

Ver los registros primero con la instrucción SELECT

Este consejo es perfecto para algunos discos. Entonces, antes de afectar los registros para la actualización, intente esto:

Eso es fácil de hacer. Y si se ve bien, descomente las cláusulas UPDATE y SET. Marque la cláusula SELECT como un comentario. Entonces, estás listo para irte. De esta manera, minimiza el control de daños por ser proactivo.

Realice una ejecución de prueba usando tablas temporales

¿No está seguro de si puede ocurrir un error? Luego intente volcar los registros de la tabla que desea actualizar en una tabla temporal. Después de eso, haz una prueba desde allí.

¿No hay errores de tiempo de ejecución? ¿Se ven bien los resultados? Luego, reemplace la tabla temporal con la tabla original. De esta manera, sabrá que no habrá errores de tiempo de ejecución en el camino.

Además, tenga en cuenta que las tablas temporales son una de las formas de almacenar las copias de las tablas originales. También puede usar tablas optimizadas para memoria o una copia de seguridad de la base de datos. Con las copias de seguridad de la base de datos, tiene más libertad para jugar con los registros que necesita actualizar. Pero la desventaja de esto es el espacio de almacenamiento.

Más información

  • CREAR TABLA (Transact-SQL):tablas temporales

Intente agregar la cláusula OUTPUT en UPDATE

¿Quiere volver atrás en el tiempo antes de ejecutar la actualización? Si es tan escéptico, puede usar la cláusula OUTPUT y ver el pasado y el presente. En el siguiente ejemplo, una variable de tabla sirve para volcar los valores anterior y actual después de la actualización. A continuación, puede SELECCIONAR la variable de la tabla para ver los resultados:

DECLARE @tvTitles AS TABLE(TitleID INT NOT NULL,
                           OldOverallRatings VARCHAR(10) NULL,
			    NewOverAllRatings varchar(10) NOT NULL)

;WITH ComputedRatings AS
(
	SELECT
	a.TitleID
	,ISNULL(CAST(CAST(AVG(CAST(b.Rating AS DECIMAL(3,2))) AS DECIMAL(3,2)) AS varchar(9)),'No Rating')  AS AverageRating
	FROM titles a
	LEFT JOIN UserRatings b ON a.TitleID = b.TitleID
	GROUP BY a.TitleID
)
UPDATE #tmpTitles
SET OverallUserRating = cr.AverageRating
OUTPUT INSERTED.TitleID, DELETED.OverallUserRating, INSERTED.OverallUserRating
INTO @tvTitles
FROM #tmpTitles t
INNER JOIN ComputedRatings cr ON t.TitleID = cr.TitleID

Algunos puntos a tener en cuenta sobre este código:

  • La variable de tabla funciona como un contenedor de los valores anteriores y actuales.
  • La actualización habitual con CTE está en orden. Todavía usamos la mesa temporal para jugar de forma segura.
  • La cláusula OUTPUT se aplica para volcar los valores anterior y actual en la variable de la tabla. INSERTADO contiene valores nuevos, mientras que ELIMINADO contiene valores antiguos.

Si emite un SELECT desde la variable de la tabla, esto es lo que puede esperar:

El resultado es como la Figura 3, pero mira hacia el futuro. Este mira hacia el pasado. Si es diferente, algo salió mal en el medio.

La buena noticia es que puede usar valores antiguos en la variable de la tabla para restaurarla a su estado anterior. Pero si es lo mismo, de nuevo, estás listo para continuar. Puede marcar la cláusula OUTPUT como un comentario o eliminarla y luego reemplazar la tabla temporal con la tabla original.

Más información

  • Cláusula OUTPUT (Transact-SQL)

Utilice TRY…CATCH para manejar errores futuros

Los 3 consejos anteriores son útiles cuando está elaborando y luego probando su código. Pero todos sabemos que no podemos anticipar todo. Por lo tanto, necesitamos agregar más redes de seguridad al código.

Hablando de redes de seguridad, T-SQL tiene los bloques de manejo de errores TRY…CATCH como C# y C++.

Echemos un vistazo al código modificado del ejemplo anterior:

BEGIN TRY						  
  ;WITH ComputedRatings AS
  (
    SELECT
     a.TitleID
    ,ISNULL(CAST(CAST(AVG(CAST(b.Rating AS DECIMAL(3,2))) AS DECIMAL(3,2)) AS varchar(20)),
            'No User Ratings Yet') AS AverageRating
    FROM titles a
    LEFT JOIN UserRatings b ON a.TitleID = b.TitleID
    GROUP BY a.TitleID
  )
  UPDATE Titles
  SET OverallUserRating = cr.AverageRating
  FROM Titles t
  INNER JOIN ComputedRatings cr ON t.TitleID = cr.TitleID

END TRY
BEGIN CATCH
 SELECT  
  ERROR_NUMBER() AS ErrorNumber  
 ,ERROR_SEVERITY() AS ErrorSeverity  
 ,ERROR_STATE() AS ErrorState  
 ,ERROR_PROCEDURE() AS ErrorProcedure  
 ,ERROR_LINE() AS ErrorLine  
 ,ERROR_MESSAGE() AS ErrorMessage;  

END CATCH

Cambié el código anterior para forzar el error de truncamiento de cadena. La Calificación general del usuario columna en los Títulos la tabla puede acomodar hasta 10 caracteres solamente. En el CTE lo cambiamos a 20 caracteres. Eso no encajará. El bloque CATCH se hará cargo del momento en que se produjo el error y proporcionará la información del error.

Aquí está el resultado:

Activamos el error. Si necesita detectar errores imprevistos durante el tiempo de ejecución, esta es una forma de manejarlo.

Más información

  • INTENTAR... ATRAPAR (Transact-SQL)

Usar manejo de transacciones

Finalmente, las transacciones. Se asegura de restaurar todo a su estado anterior antes de que ocurriera el error, incluida la ACTUALIZACIÓN con JOIN y cualquier otra instrucción DML. Esta es una buena adición al consejo n.° 4 anterior.

Cambiemos el código nuevamente para incluir transacciones:

BEGIN TRANSACTION

BEGIN TRY						  
  ;WITH ComputedRatings AS
  (
    SELECT
     a.TitleID
    ,ISNULL(CAST(CAST(AVG(CAST(b.Rating AS DECIMAL(3,2))) AS DECIMAL(3,2)) AS varchar(20)),
            'No User Ratings Yet') AS AverageRating
    FROM titles a
    LEFT JOIN UserRatings b ON a.TitleID = b.TitleID
    GROUP BY a.TitleID
  )
  UPDATE Titles
  SET OverallUserRating = cr.AverageRating
  FROM Titles t
  INNER JOIN ComputedRatings cr ON t.TitleID = cr.TitleID

  COMMIT TRANSACTION
END TRY
BEGIN CATCH
 SELECT  
  ERROR_NUMBER() AS ErrorNumber  
 ,ERROR_SEVERITY() AS ErrorSeverity  
 ,ERROR_STATE() AS ErrorState  
 ,ERROR_PROCEDURE() AS ErrorProcedure  
 ,ERROR_LINE() AS ErrorLine  
 ,ERROR_MESSAGE() AS ErrorMessage;  

 ROLLBACK TRANSACTION
END CATCH

Es lo mismo que en el ejemplo anterior excepto por las transacciones. Por lo tanto, forzará un error de truncamiento de cadena. No pasará de COMMIT TRANSACTION, sino en el bloque CATCH con ROLLBACK TRANSACTION para revertir los valores a sus estados anteriores.

Esta es la forma si queremos jugar seguros con actualizaciones, inserciones y eliminaciones.

Nota :Puede diseñar cualquier consulta visualmente en un diagrama utilizando la función Query Builder de dbForge Studio para SQL Server.

Más información

  • Prácticas recomendadas de T-SQL
  • Cómo escribir consultas T-SQL como un profesional

Conclusión

Has visto la sintaxis de SQL UPDATE con JOIN. Los ejemplos y 5 consejos sencillos lo iluminaron aún más. Los requisitos pueden diferir de lo que presentan los ejemplos, pero entendiste el punto. Todavía puedes cometer errores. Sin embargo, es posible reducirlos a casi cero.

¿Por qué no aplicar estas ideas a su situación?

Si esta publicación fue útil, siéntase libre de correr la voz en sus plataformas de redes sociales favoritas. Y si deseas agregar algunas ideas geniales, eres bienvenido a la sección de Comentarios.