Bài 6: Data Modification
Cẩn thận với Production Data
Luôn backup và test trên development database trước khi chạy INSERT/UPDATE/DELETE trên production!
1. INSERT Statement
1.1 Basic INSERT
SQL
1-- INSERT single row với tất cả columns2INSERT INTO HumanResources.Department (Name, GroupName, ModifiedDate)3VALUES ('Data Science', 'Research and Development', GETDATE());45-- INSERT với specified columns6INSERT INTO HumanResources.Department (Name, GroupName)7VALUES ('Analytics', 'Information Technology');8-- ModifiedDate sẽ dùng default value1.2 INSERT Multiple Rows
SQL
1INSERT INTO HumanResources.Department (Name, GroupName, ModifiedDate)2VALUES 3 ('Machine Learning', 'Research and Development', GETDATE()),4 ('Business Intelligence', 'Information Technology', GETDATE()),5 ('Data Engineering', 'Information Technology', GETDATE());1.3 INSERT INTO SELECT
SQL
1-- Copy data từ query result2INSERT INTO Sales.SalesTerritory_Archive (TerritoryID, Name, CountryRegionCode, [Group])3SELECT TerritoryID, Name, CountryRegionCode, [Group]4FROM Sales.SalesTerritory5WHERE CountryRegionCode = 'US';1.4 SELECT INTO (Create new table)
SQL
1-- Tạo table mới từ query2SELECT 3 p.ProductID,4 p.Name,5 p.ListPrice,6 pc.Name AS Category7INTO Production.Product_Backup8FROM Production.Product p9JOIN Production.ProductSubcategory ps ON p.ProductSubcategoryID = ps.ProductSubcategoryID10JOIN Production.ProductCategory pc ON ps.ProductCategoryID = pc.ProductCategoryID11WHERE p.ListPrice > 1000;1.5 INSERT với OUTPUT
SQL
1-- Capture inserted values2DECLARE @InsertedDepts TABLE (DeptID INT, DeptName NVARCHAR(50));34INSERT INTO HumanResources.Department (Name, GroupName, ModifiedDate)5OUTPUT inserted.DepartmentID, inserted.Name INTO @InsertedDepts6VALUES ('New Department', 'Executive General and Administration', GETDATE());78SELECT * FROM @InsertedDepts;2. UPDATE Statement
2.1 Basic UPDATE
SQL
1-- UPDATE với WHERE2UPDATE Production.Product3SET ListPrice = ListPrice * 1.14WHERE ProductID = 1;56-- UPDATE multiple columns7UPDATE Production.Product8SET 9 ListPrice = ListPrice * 1.1,10 ModifiedDate = GETDATE()11WHERE ProductSubcategoryID = 1;NEVER forget WHERE clause!
UPDATE table SET column = value không có WHERE sẽ update TẤT CẢ rows!
2.2 UPDATE với JOIN
SQL
1-- Increase price của Bikes by 5%2UPDATE p3SET 4 p.ListPrice = p.ListPrice * 1.05,5 p.ModifiedDate = GETDATE()6FROM Production.Product p7JOIN Production.ProductSubcategory ps ON p.ProductSubcategoryID = ps.ProductSubcategoryID8JOIN Production.ProductCategory pc ON ps.ProductCategoryID = pc.ProductCategoryID9WHERE pc.Name = 'Bikes';2.3 UPDATE với Subquery
SQL
1-- Update products với price thấp hơn category average2UPDATE Production.Product3SET ListPrice = ListPrice * 1.154WHERE ListPrice < (5 SELECT AVG(ListPrice) 6 FROM Production.Product 7 WHERE ListPrice > 08);2.4 UPDATE với OUTPUT
SQL
1-- Track changes2DECLARE @UpdatedProducts TABLE (3 ProductID INT,4 OldPrice MONEY,5 NewPrice MONEY6);78UPDATE Production.Product9SET ListPrice = ListPrice * 1.110OUTPUT 11 deleted.ProductID,12 deleted.ListPrice AS OldPrice,13 inserted.ListPrice AS NewPrice14INTO @UpdatedProducts15WHERE ProductSubcategoryID = 1;1617SELECT * FROM @UpdatedProducts;3. DELETE Statement
3.1 Basic DELETE
SQL
1-- DELETE với WHERE2DELETE FROM Sales.SalesOrderDetail3WHERE SalesOrderID = 12345;45-- DELETE với JOIN6DELETE sod7FROM Sales.SalesOrderDetail sod8JOIN Sales.SalesOrderHeader soh ON sod.SalesOrderID = soh.SalesOrderID9WHERE soh.OrderDate < '2011-01-01';NEVER forget WHERE clause!
DELETE FROM table không có WHERE sẽ xóa TẤT CẢ rows!
3.2 DELETE với Subquery
SQL
1-- Delete products không có sales2DELETE FROM Production.Product_Archive3WHERE ProductID NOT IN (4 SELECT DISTINCT ProductID 5 FROM Sales.SalesOrderDetail6);3.3 DELETE với OUTPUT
SQL
1-- Track deleted records2DECLARE @DeletedOrders TABLE (3 OrderID INT,4 CustomerID INT,5 OrderDate DATETIME6);78DELETE FROM Sales.SalesOrderHeader_Archive9OUTPUT 10 deleted.SalesOrderID,11 deleted.CustomerID,12 deleted.OrderDate13INTO @DeletedOrders14WHERE OrderDate < '2011-01-01';1516SELECT * FROM @DeletedOrders;3.4 TRUNCATE vs DELETE
SQL
1-- DELETE: row-by-row, logged, triggers execute, can use WHERE2DELETE FROM Sales.SalesOrderHeader_Archive;34-- TRUNCATE: deallocate pages, minimal logging, faster, no WHERE5TRUNCATE TABLE Sales.SalesOrderHeader_Archive;| Feature | DELETE | TRUNCATE |
|---|---|---|
| WHERE clause | ✅ Yes | ❌ No |
| Triggers | ✅ Execute | ❌ Don't execute |
| Transaction log | Full logged | Minimal |
| Identity reset | ❌ No | ✅ Yes |
| Speed | Slower | Faster |
4. MERGE Statement
4.1 Concept
MERGE (Upsert) performs INSERT, UPDATE, DELETE in single statement based on source/target comparison.
SQL
1MERGE target_table AS target2USING source_table AS source3ON target.key = source.key4WHEN MATCHED THEN5 UPDATE SET ...6WHEN NOT MATCHED BY TARGET THEN7 INSERT (columns) VALUES (values)8WHEN NOT MATCHED BY SOURCE THEN9 DELETE;4.2 Example: Sync Product Prices
SQL
1-- Create staging table with new prices2CREATE TABLE #ProductPriceUpdates (3 ProductID INT,4 NewListPrice MONEY5);67INSERT INTO #ProductPriceUpdates VALUES8 (1, 150.00),9 (2, 200.00),10 (999, 50.00); -- Might not exist1112-- MERGE to update13MERGE Production.Product AS target14USING #ProductPriceUpdates AS source15ON target.ProductID = source.ProductID16WHEN MATCHED THEN17 UPDATE SET 18 target.ListPrice = source.NewListPrice,19 target.ModifiedDate = GETDATE()20WHEN NOT MATCHED BY TARGET THEN21 INSERT (Name, ProductNumber, ListPrice, ModifiedDate)22 VALUES ('New Product', 'NEW-' + CAST(source.ProductID AS VARCHAR), source.NewListPrice, GETDATE());2324DROP TABLE #ProductPriceUpdates;4.3 MERGE với OUTPUT
SQL
1DECLARE @MergeOutput TABLE (2 Action NVARCHAR(10),3 ProductID INT,4 OldPrice MONEY,5 NewPrice MONEY6);78MERGE Production.Product AS target9USING #ProductPriceUpdates AS source10ON target.ProductID = source.ProductID11WHEN MATCHED THEN12 UPDATE SET target.ListPrice = source.NewListPrice13WHEN NOT MATCHED BY TARGET THEN14 INSERT (Name, ProductNumber, ListPrice)15 VALUES ('New', 'NEW-' + CAST(source.ProductID AS VARCHAR), source.NewListPrice)16OUTPUT 17 $action,18 COALESCE(inserted.ProductID, deleted.ProductID),19 deleted.ListPrice,20 inserted.ListPrice21INTO @MergeOutput;2223SELECT * FROM @MergeOutput;5. Transaction Management
5.1 Basic Transaction
SQL
1BEGIN TRANSACTION;23UPDATE Production.Product4SET ListPrice = ListPrice * 1.15WHERE ProductSubcategoryID = 1;67-- Check if update is correct8IF @@ROWCOUNT > 1009BEGIN10 ROLLBACK TRANSACTION;11 PRINT 'Too many rows affected. Transaction rolled back.';12END13ELSE14BEGIN15 COMMIT TRANSACTION;16 PRINT 'Update successful.';17END5.2 TRY...CATCH với Transaction
SQL
1BEGIN TRY2 BEGIN TRANSACTION;3 4 -- Update 15 UPDATE Production.Product6 SET ListPrice = ListPrice * 1.17 WHERE ProductSubcategoryID = 1;8 9 -- Update 210 UPDATE Production.Product11 SET StandardCost = StandardCost * 1.112 WHERE ProductSubcategoryID = 1;13 14 COMMIT TRANSACTION;15 PRINT 'All updates successful.';16END TRY17BEGIN CATCH18 IF @@TRANCOUNT > 019 ROLLBACK TRANSACTION;20 21 PRINT 'Error occurred: ' + ERROR_MESSAGE();22END CATCH;5.3 SAVE TRANSACTION
SQL
1BEGIN TRANSACTION;23-- Step 1: Update prices4UPDATE Production.Product5SET ListPrice = ListPrice * 1.16WHERE ProductSubcategoryID = 1;78SAVE TRANSACTION SavePoint1;910-- Step 2: Update costs11UPDATE Production.Product12SET StandardCost = StandardCost * 1.113WHERE ProductSubcategoryID = 1;1415-- Rollback only step 2 if needed16IF @@ROWCOUNT > 5017BEGIN18 ROLLBACK TRANSACTION SavePoint1;19 PRINT 'Cost update rolled back.';20END2122COMMIT TRANSACTION;6. Best Practices
6.1 Always Test First
SQL
1-- Step 1: SELECT to verify rows2SELECT *3FROM Production.Product4WHERE ProductSubcategoryID = 1;56-- Step 2: Run UPDATE only after verifying7UPDATE Production.Product8SET ListPrice = ListPrice * 1.19WHERE ProductSubcategoryID = 1;6.2 Use TOP for Batched Updates
SQL
1-- Update in batches of 10002WHILE 1 = 13BEGIN4 UPDATE TOP (1000) Production.Product5 SET ListPrice = ListPrice * 1.16 WHERE ListPrice < 100 AND ModifiedDate < '2020-01-01';7 8 IF @@ROWCOUNT = 0 BREAK;9END6.3 Backup Before Major Changes
SQL
1-- Create backup table2SELECT * INTO Production.Product_Backup_202401153FROM Production.Product;45-- Perform updates6UPDATE Production.Product7SET ListPrice = ListPrice * 1.1;89-- If something goes wrong, restore10-- UPDATE Production.Product11-- SET ListPrice = (SELECT ListPrice FROM Production.Product_Backup_20240115 b WHERE b.ProductID = Production.Product.ProductID);7. Thực hành
Exercises
Lưu ý: Tạo backup tables trước khi thực hành!
Exercise 1: INSERT
SQL
1-- Tạo bảng backup và insert departments thuộc 'Manufacturing'2-- Step 1: CREATE TABLE Department_Backup (DepartmentID, Name, GroupName)3-- Step 2: INSERT departments WHERE GroupName = 'Manufacturing'45-- YOUR CODE HERE💡 Xem đáp án
SQL
1-- Create backup table2SELECT 3 DepartmentID,4 Name,5 GroupName6INTO HumanResources.Department_Backup7FROM HumanResources.Department8WHERE GroupName = 'Manufacturing';910-- Verify11SELECT * FROM HumanResources.Department_Backup;Exercise 2: UPDATE với JOIN
SQL
1-- Giảm giá 10% cho tất cả Accessories2-- Output: ProductID, OldPrice, NewPrice34-- YOUR CODE HERE💡 Xem đáp án
SQL
1-- First backup2SELECT ProductID, Name, ListPrice3INTO Production.Product_Price_Backup4FROM Production.Product5WHERE ProductSubcategoryID IN (6 SELECT ps.ProductSubcategoryID7 FROM Production.ProductSubcategory ps8 JOIN Production.ProductCategory pc ON ps.ProductCategoryID = pc.ProductCategoryID9 WHERE pc.Name = 'Accessories'10);1112-- Update with output13DECLARE @PriceChanges TABLE (ProductID INT, OldPrice MONEY, NewPrice MONEY);1415UPDATE p16SET p.ListPrice = p.ListPrice * 0.917OUTPUT deleted.ProductID, deleted.ListPrice, inserted.ListPrice INTO @PriceChanges18FROM Production.Product p19JOIN Production.ProductSubcategory ps ON p.ProductSubcategoryID = ps.ProductSubcategoryID20JOIN Production.ProductCategory pc ON ps.ProductCategoryID = pc.ProductCategoryID21WHERE pc.Name = 'Accessories';2223SELECT * FROM @PriceChanges;Exercise 3: DELETE với Subquery
SQL
1-- Xóa records trong backup table với products không có sales2-- (Sử dụng NOT EXISTS)34-- YOUR CODE HERE💡 Xem đáp án
SQL
1-- Delete products with no sales from backup2DELETE FROM Production.Product_Price_Backup3WHERE NOT EXISTS (4 SELECT 1 5 FROM Sales.SalesOrderDetail sod6 WHERE sod.ProductID = Production.Product_Price_Backup.ProductID7);Exercise 4: Transaction
SQL
1-- Viết transaction:2-- 1. Tăng giá Bikes 5%3-- 2. Nếu > 100 rows affected, rollback4-- 3. Print kết quả56-- YOUR CODE HERE💡 Xem đáp án
SQL
1BEGIN TRY2 BEGIN TRANSACTION;3 4 DECLARE @RowsAffected INT;5 6 UPDATE p7 SET p.ListPrice = p.ListPrice * 1.058 FROM Production.Product p9 JOIN Production.ProductSubcategory ps ON p.ProductSubcategoryID = ps.ProductSubcategoryID10 JOIN Production.ProductCategory pc ON ps.ProductCategoryID = pc.ProductCategoryID11 WHERE pc.Name = 'Bikes';12 13 SET @RowsAffected = @@ROWCOUNT;14 15 IF @RowsAffected > 10016 BEGIN17 ROLLBACK TRANSACTION;18 PRINT 'Rolled back. Too many rows: ' + CAST(@RowsAffected AS VARCHAR);19 END20 ELSE21 BEGIN22 COMMIT TRANSACTION;23 PRINT 'Success. Updated ' + CAST(@RowsAffected AS VARCHAR) + ' rows.';24 END25END TRY26BEGIN CATCH27 IF @@TRANCOUNT > 028 ROLLBACK TRANSACTION;29 PRINT 'Error: ' + ERROR_MESSAGE();30END CATCH;8. Tổng kết
| Statement | Purpose | Key Points |
|---|---|---|
| INSERT | Add new rows | Use OUTPUT to capture inserted IDs |
| UPDATE | Modify existing rows | Always use WHERE! |
| DELETE | Remove rows | Test with SELECT first! |
| MERGE | Upsert (sync) | Combine INSERT/UPDATE/DELETE |
| TRUNCATE | Fast delete all | Resets identity, minimal logging |
Safety Checklist:
- ☑️ Backup important tables
- ☑️ Test với SELECT trước
- ☑️ Use transactions cho multiple operations
- ☑️ Check @@ROWCOUNT sau mỗi operation
- ☑️ Use OUTPUT để track changes
Bài tiếp theo: String và DateTime Functions
