🎯 Mục tiêu bài học
Sau bài học này, bạn sẽ:
- ✅ Hiểu View là gì và tại sao cần sử dụng Views
- ✅ Tạo được các loại Views: Simple, JOINs, Indexed, Updatable
- ✅ Sử dụng View Options: SCHEMABINDING, ENCRYPTION, CHECK OPTION
- ✅ Biết cách ALTER và DROP Views
- ✅ Áp dụng best practices khi thiết kế Views
🔍 View là gì?
View là một "virtual table" được định nghĩa bởi một SELECT query. View không lưu trữ data mà lưu trữ query definition.
Tại sao sử dụng Views?
Benefits of Views
| Benefit | Description |
|---|---|
| Simplification | Ẩn các JOINs phức tạp |
| Security | Giới hạn columns/rows users có thể thấy |
| Abstraction | Thay đổi base tables mà không ảnh hưởng app |
| Reusability | Tái sử dụng queries |
Checkpoint
View là gì? Liệt kê ít nhất 3 lợi ích của việc sử dụng Views.
📝 CREATE VIEW - Basic Syntax
Simple View
1-- Basic view syntax2CREATE VIEW ViewName3AS4SELECT columns5FROM tables6WHERE conditions;7GO89-- Example: View khách hàng active10CREATE VIEW Customer.vw_ActiveCustomers11AS12SELECT 13 CustomerID,14 FirstName,15 LastName,16 Email,17 Phone18FROM Customer.Customer19WHERE IsActive = 1;20GO2122-- Use the view23SELECT * FROM Customer.vw_ActiveCustomers;View với JOINs
1-- View ẩn complex JOINs2CREATE VIEW Sales.vw_OrderSummary3AS4SELECT 5 o.OrderID,6 o.OrderNumber,7 o.OrderDate,8 c.FirstName + ' ' + c.LastName AS CustomerName,9 c.Email AS CustomerEmail,10 o.Status,11 o.TotalAmount,12 COUNT(od.OrderDetailID) AS ItemCount13FROM Sales.[Order] o14JOIN Customer.Customer c ON o.CustomerID = c.CustomerID15LEFT JOIN Sales.OrderDetail od ON o.OrderID = od.OrderID16GROUP BY 17 o.OrderID, o.OrderNumber, o.OrderDate,18 c.FirstName, c.LastName, c.Email,19 o.Status, o.TotalAmount;20GO2122-- Simple query từ view23SELECT * 24FROM Sales.vw_OrderSummary25WHERE Status = 'Delivered'26ORDER BY OrderDate DESC;AdventureWorks Views
1-- AdventureWorks có nhiều Views sẵn2-- Xem danh sách Views3SELECT 4 SCHEMA_NAME(schema_id) AS SchemaName,5 name AS ViewName6FROM sys.views7ORDER BY SchemaName, ViewName;89-- Ví dụ: Sales.vSalesPerson10SELECT * FROM Sales.vSalesPerson;1112-- Ví dụ: HumanResources.vEmployee13SELECT 14 FirstName,15 LastName,16 JobTitle,17 Department,18 HireDate19FROM HumanResources.vEmployee20WHERE Department = 'Sales';Checkpoint
Viết cú pháp CREATE VIEW cơ bản. Tại sao View với JOINs giúp đơn giản hóa việc query?
⚙️ View Options
WITH SCHEMABINDING
1-- SCHEMABINDING ngăn base tables bị modify/drop2CREATE VIEW Sales.vw_ProductSales3WITH SCHEMABINDING -- Lock base tables4AS5SELECT 6 p.ProductID,7 p.ProductName,8 SUM(od.Quantity) AS TotalQuantitySold,9 SUM(od.LineTotal) AS TotalRevenue10FROM Inventory.Product p11JOIN Sales.OrderDetail od ON p.ProductID = od.ProductID12GROUP BY p.ProductID, p.ProductName;13GO1415-- Giờ không thể:16-- DROP TABLE Inventory.Product -- Error!17-- ALTER TABLE Inventory.Product DROP COLUMN ProductName -- Error!⚠️ Lưu ý: SCHEMABINDING yêu cầu phải dùng two-part names (Schema.Table)
WITH ENCRYPTION
1-- Ẩn view definition2CREATE VIEW Sales.vw_SensitiveData3WITH ENCRYPTION4AS5SELECT 6 CustomerID,7 Email,8 Phone9FROM Customer.Customer;10GO1112-- Không thể xem definition13SELECT OBJECT_DEFINITION(OBJECT_ID('Sales.vw_SensitiveData'));14-- Returns NULLWITH CHECK OPTION
1-- Ngăn INSERT/UPDATE vi phạm WHERE clause2CREATE VIEW Customer.vw_ActiveCustomers_Protected3AS4SELECT 5 CustomerID,6 FirstName,7 LastName,8 Email,9 IsActive10FROM Customer.Customer11WHERE IsActive = 112WITH CHECK OPTION;13GO1415-- Không thể insert inactive customer qua view này16INSERT INTO Customer.vw_ActiveCustomers_Protected 17 (FirstName, LastName, Email, IsActive)18VALUES ('Test', 'User', 'test@email.com', 0); -- Error!Checkpoint
So sánh SCHEMABINDING, ENCRYPTION và CHECK OPTION. Khi nào dùng từng option?
📊 Indexed Views (Materialized Views)
Tạo Indexed View
1-- Indexed View lưu trữ data (materialized)2-- Yêu cầu: SCHEMABINDING, deterministic functions34CREATE VIEW Sales.vw_ProductSalesIndexed5WITH SCHEMABINDING6AS7SELECT 8 p.ProductID,9 p.ProductName,10 COUNT_BIG(*) AS SaleCount,11 SUM(od.Quantity) AS TotalQuantity,12 SUM(od.LineTotal) AS TotalRevenue13FROM Inventory.Product p14JOIN Sales.OrderDetail od ON p.ProductID = od.ProductID15GROUP BY p.ProductID, p.ProductName;16GO1718-- Tạo unique clustered index19CREATE UNIQUE CLUSTERED INDEX IX_vw_ProductSalesIndexed20ON Sales.vw_ProductSalesIndexed(ProductID);21GOKhi nào dùng Indexed Views?
| Use Indexed View | Don't Use |
|---|---|
| Complex aggregations | Frequently updated tables |
| Infrequently updated data | Simple queries |
| Reporting queries | OLTP workloads |
| Heavy read workloads | Small result sets |
1-- Indexed View tự động được dùng với Enterprise Edition2-- Standard Edition cần hint3SELECT * FROM Sales.vw_ProductSalesIndexed WITH (NOEXPAND);Checkpoint
Indexed View khác gì View thường? Liệt kê điều kiện để tạo Indexed View.
✏️ Updatable Views
Rules for Updatable Views
View có thể UPDATE/INSERT/DELETE nếu:
- Không có aggregate functions (SUM, COUNT, AVG...)
- Không có GROUP BY, HAVING
- Không có DISTINCT
- Không có TOP với ORDER BY
- Không có UNION
- Modify chỉ 1 base table
1-- Updatable View2CREATE VIEW Customer.vw_CustomerBasic3AS4SELECT 5 CustomerID,6 FirstName,7 LastName,8 Email,9 Phone,10 IsActive11FROM Customer.Customer;12GO1314-- Có thể UPDATE qua View15UPDATE Customer.vw_CustomerBasic16SET Phone = '0123456789'17WHERE CustomerID = 1;1819-- Có thể INSERT qua View20INSERT INTO Customer.vw_CustomerBasic (FirstName, LastName, Email)21VALUES ('New', 'Customer', 'new@email.com');INSTEAD OF Triggers
1-- View với JOINs không updatable by default2-- Dùng INSTEAD OF trigger để enable34CREATE VIEW Sales.vw_OrderWithCustomer5AS6SELECT 7 o.OrderID,8 o.OrderNumber,9 o.OrderDate,10 o.CustomerID,11 c.FirstName AS CustomerFirstName,12 c.LastName AS CustomerLastName13FROM Sales.[Order] o14JOIN Customer.Customer c ON o.CustomerID = c.CustomerID;15GO1617-- INSTEAD OF INSERT trigger18CREATE TRIGGER trg_vw_OrderWithCustomer_Insert19ON Sales.vw_OrderWithCustomer20INSTEAD OF INSERT21AS22BEGIN23 INSERT INTO Sales.[Order] (OrderNumber, OrderDate, CustomerID)24 SELECT OrderNumber, OrderDate, CustomerID25 FROM inserted;26END;27GOCheckpoint
Điều kiện nào để một View là updatable? INSTEAD OF Trigger dùng để giải quyết vấn đề gì?
🔧 ALTER & DROP View
ALTER VIEW
1-- Modify existing view2ALTER VIEW Customer.vw_ActiveCustomers3AS4SELECT 5 CustomerID,6 FirstName,7 LastName,8 Email,9 Phone,10 CreatedDate -- Added column11FROM Customer.Customer12WHERE IsActive = 1;13GO1415-- Best practice: CREATE OR ALTER (SQL Server 2016+)16CREATE OR ALTER VIEW Customer.vw_ActiveCustomers17AS18SELECT 19 CustomerID,20 FirstName,21 LastName,22 Email,23 Phone,24 CreatedDate25FROM Customer.Customer26WHERE IsActive = 1;27GODROP VIEW
1-- Drop single view2DROP VIEW Customer.vw_ActiveCustomers;34-- Drop if exists5DROP VIEW IF EXISTS Customer.vw_ActiveCustomers;67-- Drop multiple views8DROP VIEW 9 Customer.vw_View1,10 Customer.vw_View2;Checkpoint
CREATE OR ALTER khác gì ALTER VIEW thông thường? Khi nào nên dùng?
💡 View Best Practices
Naming Conventions
1-- Prefix với vw_ hoặc V_2vw_ActiveCustomers3vw_OrderSummary4vw_ProductSales56-- Hoặc không prefix nhưng dùng schema7Reporting.CustomerSummary8Reporting.SalesAnalysisPerformance Considerations
1-- ❌ KHÔNG nên: Nested views (view gọi view)2CREATE VIEW vw_Level1 AS SELECT * FROM Table1;3CREATE VIEW vw_Level2 AS SELECT * FROM vw_Level1;4CREATE VIEW vw_Level3 AS SELECT * FROM vw_Level2;5-- Query optimizer khó optimize67-- ✅ NÊN: Flat views8CREATE VIEW vw_Final AS 9SELECT * FROM Table110JOIN Table2...1112-- ❌ KHÔNG nên: SELECT *13CREATE VIEW vw_Bad AS SELECT * FROM BigTable;1415-- ✅ NÊN: Explicit columns16CREATE VIEW vw_Good AS 17SELECT Col1, Col2, Col3 FROM BigTable;Security Views
1-- Row-level security2CREATE VIEW HR.vw_MyDepartmentEmployees3AS4SELECT 5 EmployeeID,6 FirstName,7 LastName,8 Salary9FROM HR.Employee10WHERE DepartmentID = (11 SELECT DepartmentID 12 FROM HR.Employee 13 WHERE Email = SUSER_SNAME()14);15GO1617-- Column-level security (hide sensitive data)18CREATE VIEW Customer.vw_CustomerPublic19AS20SELECT 21 CustomerID,22 FirstName,23 LastName,24 -- Email masked25 LEFT(Email, 3) + '***@***' AS MaskedEmail,26 -- Phone hidden27 'XXX-XXX-' + RIGHT(Phone, 4) AS MaskedPhone28FROM Customer.Customer;29GOCheckpoint
Tại sao không nên tạo nested views? Cho ví dụ cách dùng View cho security.
🏢 AdventureWorks Views Analysis
Study Existing Views
1-- Xem definition của View2SELECT OBJECT_DEFINITION(OBJECT_ID('Sales.vSalesPerson')) AS ViewDefinition;34-- Hoặc dùng sp_helptext5EXEC sp_helptext 'Sales.vSalesPerson';67-- Xem View với dependencies8SELECT 9 v.name AS ViewName,10 OBJECT_NAME(d.referenced_id) AS DependsOn,11 d.referenced_entity_name12FROM sys.views v13JOIN sys.sql_expression_dependencies d ON v.object_id = d.referencing_id14WHERE SCHEMA_NAME(v.schema_id) = 'Sales';Common AdventureWorks Views
1-- HumanResources.vEmployee - Employee info với Person data2SELECT TOP 5 * FROM HumanResources.vEmployee;34-- Sales.vSalesPerson - Sales person với quota và YTD sales5SELECT TOP 5 * FROM Sales.vSalesPerson;67-- Production.vProductAndDescription - Product với description8SELECT TOP 5 * FROM Production.vProductAndDescription;910-- Sales.vIndividualCustomer - Customer info11SELECT TOP 5 * FROM Sales.vIndividualCustomer;Checkpoint
Làm sao xem definition của một View trong SQL Server? Cách kiểm tra dependencies của View?
🛠️ Hands-on Exercise
Exercise: Create Views for Reporting
1-- 1. View: Top Products by Revenue2CREATE OR ALTER VIEW Reporting.vw_TopProductsByRevenue3AS4SELECT TOP 1005 p.ProductID,6 p.Name AS ProductName,7 pc.Name AS Category,8 SUM(sod.OrderQty) AS TotalQuantitySold,9 SUM(sod.LineTotal) AS TotalRevenue,10 AVG(sod.UnitPrice) AS AvgSellingPrice11FROM Production.Product p12JOIN Production.ProductSubcategory ps ON p.ProductSubcategoryID = ps.ProductSubcategoryID13JOIN Production.ProductCategory pc ON ps.ProductCategoryID = pc.ProductCategoryID14JOIN Sales.SalesOrderDetail sod ON p.ProductID = sod.ProductID15GROUP BY p.ProductID, p.Name, pc.Name16ORDER BY TotalRevenue DESC;17GO1819-- 2. View: Monthly Sales Summary20CREATE OR ALTER VIEW Reporting.vw_MonthlySalesSummary21AS22SELECT 23 YEAR(soh.OrderDate) AS SalesYear,24 MONTH(soh.OrderDate) AS SalesMonth,25 COUNT(DISTINCT soh.SalesOrderID) AS OrderCount,26 COUNT(DISTINCT soh.CustomerID) AS CustomerCount,27 SUM(soh.SubTotal) AS TotalSubTotal,28 SUM(soh.TaxAmt) AS TotalTax,29 SUM(soh.Freight) AS TotalFreight,30 SUM(soh.TotalDue) AS TotalRevenue31FROM Sales.SalesOrderHeader soh32GROUP BY YEAR(soh.OrderDate), MONTH(soh.OrderDate);33GO3435-- 3. View: Customer Purchase History36CREATE OR ALTER VIEW Customer.vw_CustomerPurchaseHistory37AS38SELECT 39 c.CustomerID,40 p.FirstName + ' ' + p.LastName AS CustomerName,41 COALESCE(p.EmailPromotion, 0) AS EmailPromotion,42 COUNT(DISTINCT soh.SalesOrderID) AS TotalOrders,43 MIN(soh.OrderDate) AS FirstOrderDate,44 MAX(soh.OrderDate) AS LastOrderDate,45 SUM(soh.TotalDue) AS LifetimeValue,46 AVG(soh.TotalDue) AS AvgOrderValue47FROM Sales.Customer c48LEFT JOIN Person.Person p ON c.PersonID = p.BusinessEntityID49LEFT JOIN Sales.SalesOrderHeader soh ON c.CustomerID = soh.CustomerID50GROUP BY c.CustomerID, p.FirstName, p.LastName, p.EmailPromotion;51GO5253-- Test views54SELECT * FROM Reporting.vw_TopProductsByRevenue;55SELECT * FROM Reporting.vw_MonthlySalesSummary ORDER BY SalesYear DESC, SalesMonth DESC;56SELECT TOP 10 * FROM Customer.vw_CustomerPurchaseHistory ORDER BY LifetimeValue DESC;Checkpoint
Viết một View để tổng hợp doanh thu theo tháng. Cần JOIN những bảng nào?
📋 Summary Checklist
When to Create Views
- ✅ Complex JOINs used repeatedly
- ✅ Security requirements (hide columns/rows)
- ✅ Reporting and analytics
- ✅ Data abstraction layer
- ✅ Backward compatibility
Best Practices
- ✅ Use SCHEMABINDING for indexed views
- ✅ Avoid nested views
- ✅ Select specific columns, not SELECT *
- ✅ Use WITH CHECK OPTION for updatable views
- ✅ Document view purpose
Performance Tips
- Use indexed views for heavy aggregations
- Avoid views on views
- Check execution plans
- Consider materialized tables for complex reports
Checkpoint
Tóm tắt khi nào nên tạo View và khi nào nên dùng Indexed View.
🚀 Bài tiếp theo
Bài tiếp theo: Stored Procedures với T-SQL
Học cách tạo và sử dụng Stored Procedures để encapsulate business logic!
