Kỹ thuật phân trang trong trang asp.net dùng Store

Trên website này có viết bài "Kỹ thuật phân trang bằng Store Procedure" nhưng nhiều bạn chưa áp dụng được nó để phân trang trong asp.net và có nhiều bạn hỏi cách làm. Bài này mình sẽ hướng dẫn các bạn sử dụng thuật toán của bài viết đó vào trang asp.net.

Trong bài viết "Kỹ thuật phân trang bằng Store Procedure" thì dữ liệu hiển thị sẽ được chia làm 2 phần, một phần là dữ liệu truy vấn, một phần là chuỗi html hiển thị phân trang. Vậy để sử dụng nó bạn cần đổ dữ liệu từ Store vào DataSet. Khi đó phần dữ liệu truy vấn sẽ là Table[0] và phần hiển thị phân trang là Table[1]. Vậy bạn cần viết hàm để thực hiện nó. Để thực hiện mình lấy database Northwind, truy vấn dữ liệu từ bảng Customrs. Với Stored Procedure spPhanTrangSQL bạn xem ở bài viết  "Kỹ thuật phân trang bằng Store Procedure"  còn Store truy vẫn dữ liệu mình viết như sau:

CREATE PROCEDURE [dbo].[spCustomers_PhanTrang]
@currPage int,
@recodperpage int,
@Pagesize int
AS
Begin
    Begin
    WITH s AS
    (
        SELECT ROW_NUMBER() 
            OVER(ORDER BY CustomerID, 
            CompanyName) AS RowNum, 
            CustomerID, 
            CompanyName, 
            ContactName, City 
        FROM Customers  
    )
    Select * From s 
    Where RowNum Between 
        (@currPage - 1)*@recodperpage+1 
            AND @currPage*@recodperpage
    END
    -- Tính tổng số bản ghi
    DECLARE @Tolal int
    SELECT @Tolal=Count(*) FROM Customers

    EXEC spPhanTrangSQL 
            @Tolal, 
            @currPage, 
            @Pagesize, 
            @recodperpage
END

Trong trang aspx bạn cần một Gridview và một Literal để hiển thị dữ liệu như sau:

<table cellpadding="0" cellspacing="0" width="620">
    <tr>
        <td style="width: 100%">
        <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" BackColor="White"
        BorderColor="#CC9966" BorderStyle="None" BorderWidth="1px" CellPadding="4" Width="620px">
        <RowStyle BackColor="White" ForeColor="#330099" />
        <Columns>
            <asp:BoundField DataField="RowNum" HeaderText="RowNum" SortExpression="RowNum" />
            <asp:BoundField DataField="CustomerID" HeaderText="CustomerID" SortExpression="CustomerID" />
            <asp:BoundField DataField="CompanyName" HeaderText="CompanyName" SortExpression="CompanyName" />
            <asp:BoundField DataField="ContactName" HeaderText="ContactName" SortExpression="ContactName" />
            <asp:BoundField DataField="City" HeaderText="City" SortExpression="City" />
        </Columns>
        <FooterStyle BackColor="#FFFFCC" ForeColor="#330099" />
        <PagerStyle BackColor="#FFFFCC" ForeColor="#330099" HorizontalAlign="Center" />
        <SelectedRowStyle BackColor="#FFCC66" Font-Bold="True" ForeColor="#663399" />
        <HeaderStyle BackColor="#990000" Font-Bold="True" ForeColor="#FFFFCC" />
    </asp:GridView>
        </td>
    </tr>
    <tr>
        <td style="width: 100%">
            <asp:Literal ID="ltlPhanTrang" runat="server"/></td>
    </tr>
</table>

Khi chạy dữ liệu được hiển thị như hình minh họa sau:

 

Giờ chúng ta sẽ viết  Các hàm cần thực hiện hiển thị dữ liệu:
1. Hàm thực thi một Store đổ dữ liệu vào DataSet

private static DataSet ThucThiStore_DataSet(
        string StoredProcedure, 
        params SqlParameter[] Parameters)
    {
    //Khai báo cuỗi kết nối
        string ConnectionString = 
        @"Server =.\SQL2005;Initial Catalog=Northwind;User ID=sa;Password=sa"; 
        SqlConnection Conn = new SqlConnection(ConnectionString);
        SqlCommand Command = new SqlCommand(StoredProcedure, Conn);
        if (Parameters != null)
        {
            Command.Parameters.Clear();
            Command.Parameters.AddRange(Parameters);
        }
        DataSet ds = new DataSet(); SqlDataAdapter da = 
            new SqlDataAdapter(StoredProcedure, Conn);
        Command.CommandType = CommandType.StoredProcedure;
        da.SelectCommand = Command;
        try
        {
    //Mở kết nối
            Conn.Open();
            da.Fill(ds);
        }
        finally
        {
    // Đóng kết nối
            if (Conn.State == ConnectionState.Open)
                Conn.Close();
            Conn.Dispose();
        }
        return ds;
    }

2. Hàm lấy dữ liệu từ Store trả về một DataSet

private DataSet StoreToDataSet(
        int currPage, 
        int recodperpage, 
        int Pagesize)
    {
        DataSet dts = new DataSet();
        SqlParameter[] arrParam = {
                new SqlParameter("@currPage", SqlDbType.Int),
                new SqlParameter("@recodperpage", SqlDbType.Int),
                new SqlParameter("@Pagesize", SqlDbType.Int)
                };
        arrParam[0].Value = currPage;
        arrParam[1].Value = recodperpage;
        arrParam[2].Value = Pagesize;
        return ThucThiStore_DataSet("spCustomers_PhanTrang", arrParam);
    }

3. Hàm nạp dữ liệu để hiển thị

private void NapDuLieu(int currPage, int recodperpage, int Pagesize)
   {
       DataSet ds = StoreToDataSet(currPage, recodperpage, Pagesize);
       DataTable dtbData = ds.Tables[0];
       DataTable dtbPhanTrang = ds.Tables[1];
       if (dtbData.Rows.Count > 0)
       {
           GridView1.DataSource = dtbData;
           GridView1.DataBind();
           if (dtbPhanTrang.Rows.Count > 0)
           {
               ltlPhanTrang.Text = dtbPhanTrang.Rows[0]["PhanTrang"] + "";
           }
       }
   }

Trong hàm này bạn thấy dữ liệu được chia làm 2 phần như đã nói ở trên và Phân dữ liệu truy vấn là Table[0] được bind vào Gridview1 còn phần dữ liệu phân trang là Table[1] (Chỉ là 1 bản ghi dữ liệu dang html được gán vào ltlPhanTrang).
 
Trong hàm Page_Load bạn cần khai báo 1 biến page và hiển thị dữ liệu như sau:

protected void Page_Load(object sender, EventArgs e)
    {
        int page = int.Parse("0" + Request.QueryString["page"]);
    if (page == 0) page = 1;
        if (!IsPostBack)
        {
            NapDuLieu(page, 10, 5);
        }
    }

Xem hàm Page_Load bạn thấy mình truyền giá trị 10 là số bản ghi hiển thị cho mỗi trang (rowperpage) và giá trị 5 là số trang hiển thị mỗi phân đoạn (@PageSize)
 
Bạn có thể download mã nguồn trang asp mình viết về tham khảo tại đây.

PhanTrangaspx.rar (1,92 kb)

Tham khảo thêm bài viết về phân trang trong Gridview

Chúc bạn thành công

Ví dụ về phân trang trong gridview

Trong bài này mình sẽ hướng dẫn các bạn viết phân trang đơn giản trong gridview. Với cách chọn trang hiển thị từ DropDownList. Trong ví dụ này hiển thị 10 bản ghi mỗi trang. Khi chọn trang từ DropDownList thì dữ liệu sẽ hiển thị theo trang được chọn. Bạn có thể xem demo tại đây

Dữ liệu minh họa mình lấy từ Access. Để làm phân trang như môt tả sau:

Để làm được điều đó bạn cần tạo gridview và tạo PageTemplate trong gridview.
Code trang asp.net như sau:

<%@ Page Language="C#" AutoEventWireup="true" 
CodeFile="Default.aspx.cs" Inherits="Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>hmweb.com.vn</title>
    <%--create by <a href="mailto:hungbv@hmweb.com.vn--%%3E%3C/head%3E%3Cbody%3E">hungbv@hmweb.com.vn--%>
</head>
<body>
</a>    <form id="form1" runat="server">
    <div style="text-align: center">
        <asp:Panel ID="Panel1" runat="server" 
        GroupingText="Ví dụ về phân trang Gridview"
            Width="610px">
        <asp:GridView id="CustomersGridView" 
        DataSourceID="AccessDataSource1"   
        autogeneratecolumns="False"
        allowpaging="True"
        ondatabound="CustomersGridView_DataBound"  
        runat="server" DataKeyNames="CustomerID" 
        BackColor="White" BorderColor="#CC9966" 
        BorderStyle="None" BorderWidth="1px" 
        CellPadding="4" Width="100%">
        <PagerStyle forecolor="#330099"
          backcolor="#FFFFCC" HorizontalAlign="Center"/>
        <PagerTemplate>
          <table width="100%">                    
            <tr>                        
              <td style="width:70%">

                <asp:Label id="MessageLabel"
                  forecolor="Blue"
                  text="Chọn trang:" 
                  runat="server"/>
                <asp:DropDownList id="PageDropDownList"
                  autopostback="true"
                  onselectedindexchanged = "PageDropDownList_SelectedIndexChanged" 
                  runat="server"/>
              </td>   
              <td style="width:70%; text-align:right">
                <asp:Label id="CurrentPageLabel"
                  forecolor="Blue"
                  runat="server"/>
              </td>
            </tr>                    
          </table>
        </PagerTemplate> 
          <Columns>
              <asp:BoundField DataField="CustomerID" 
              HeaderText="CustomerID" ReadOnly="True" 
              SortExpression="CustomerID" />
              <asp:BoundField DataField="CompanyName" 
              HeaderText="CompanyName" SortExpression="CompanyName" />
              <asp:BoundField DataField="Address" 
              HeaderText="Address" SortExpression="Address" />
              <asp:BoundField DataField="City" 
              HeaderText="City" SortExpression="City" />
          </Columns>
            <RowStyle BackColor="White" ForeColor="#330099" />
            <FooterStyle BackColor="#FFFFCC" ForeColor="#330099" />
            <SelectedRowStyle BackColor="#FFCC66" Font-Bold="True" ForeColor="#663399" />
            <HeaderStyle BackColor="#990000" Font-Bold="True" ForeColor="#FFFFCC" />
      </asp:GridView>
        </asp:Panel>
        <asp:AccessDataSource ID="AccessDataSource1" 
        runat="server" DataFile="~/SP.mdb" 
        SelectCommand="SELECT [CustomerID], [CompanyName], [ContactName], [Address], [City] FROM [Customers]">
        </asp:AccessDataSource>
    </div>
    </form>
</body>
</html>

Trong code behind bạn làm như sau:

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }
    protected void PageDropDownList_SelectedIndexChanged(Object sender, EventArgs e)
    {
        GridViewRow pagerRow = CustomersGridView.BottomPagerRow;
        DropDownList pageList = (DropDownList)pagerRow.Cells[0].FindControl("PageDropDownList");
        // Thiết lập thuộc tính PageIndex Để hiển thị trang đuợc chọn.
        CustomersGridView.PageIndex = pageList.SelectedIndex;
    }

    protected void CustomersGridView_DataBound(Object sender, EventArgs e)
    {
        GridViewRow pagerRow = CustomersGridView.BottomPagerRow;
        DropDownList pageList = (DropDownList)pagerRow.Cells[0].FindControl("PageDropDownList");
        Label pageLabel = (Label)pagerRow.Cells[0].FindControl("CurrentPageLabel");

        if (pageList != null)
        {
            for (int i = 0; i < CustomersGridView.PageCount; i++)
            {
                int pageNumber = i + 1;
                ListItem item = new ListItem(pageNumber.ToString());
                if (i == CustomersGridView.PageIndex)
                {
                    item.Selected = true;
                }
                pageList.Items.Add(item);
            }
        }

        if (pageLabel != null)
        {

            int currentPage = CustomersGridView.PageIndex + 1;
            // Cập nhật thông tin lable hiển thị trang
            pageLabel.Text = "Page " + currentPage.ToString() +
              " of " + CustomersGridView.PageCount.ToString();
        }
    }
}

Bạn có thể download mã nguồn về tham khảo

 

phantrang.rar (18,48 kb)

Kỹ thuật phân trang bằng Store Procedure

Bài này mình giới thiệu thuật toán trang bằng SQL với những tùy chọn hiển thị khác nhau.

Ở bài viết Phân trang trong SQL mình đã giới thiệu cách dùng Store để phân trang, Ở bài này mình sẽ giới thiệu thuật toán phân trang tối ưu hơn.
Giả sử bạn muốn trang web của bạn phân trang theo cách hiển thị như sau
Trang đầu Trang trước 2 3 [4] 5 6 Trang sau Trang cuối (Với số trang hiển thị =5 và đang hiển thị nội dung trang thứ 4, và mỗi trang 10 bản ghi)

1. Store trả về chuỗi hiển thị phân trang.
 Trước tiên bạn cần viết 1 hàm trả về chuỗi hiển thị phân trang. Hàm này có các biến truyền vào là:
@Total=Tổng số bản ghi truy vấn
@currPage=Trang hiện hàng
@PageSize=Số trang muốn hiển thị
@rowperpage = Số bản ghi trên 1 trang

Kết quả trả về của store này là chuỗi (html) hiển thị phân trang có dạng như sau:

EXEC [spPhanTrangSQL] 56,4,5, 10 (Demo với @Total=56, @currPage=4, @Pagesize=5, @rowperpage=10)
<a href="?page=1">Trang đầu</a> 
<a href="?page=3">Trang trước</a>
<a href="?page=2">2</a> 
<a href="?page=3">3</a> 
[4] 
<a href="?page=5">5</a> 
<a href="?page=6">6</a> 
<a href="?page=5">Trang sau</a> 
<a href="?page=6">Trang cuối</a>

 Store được viết như sau:

CREATE PROCEDURE [dbo].[spPhanTrangSQL]
@Total int,
@currPage int ,
@PageSize int,
@rowperpage int  
AS
BEGIN
DECLARE  @PageNumber int SET @PageNumber=1
DECLARE @i int
SET @i=1
DECLARE @TotalPage int
IF @Total%@rowperpage>0
SET @TotalPage=(@Total/@rowperpage)+1
ELSE
SET @TotalPage=@Total/@rowperpage 
DECLARE @Start int SET @Start=0
DECLARE @SQL nvarchar(4000)
SET @SQL=''
IF @currPage<=@TotalPage
BEGIN
    -- Xử lý trường hợp @currPage=1
    IF @currPage=1
    BEGIN
        SET @SQL=@SQL+ N'Trang '
        SET @PageNumber=@PageSize
        IF @PageNumber>@TotalPage SET @PageNumber=@TotalPage
        SET @Start=1
    END
    ELSE
    BEGIN
        SET @SQL=@SQL+ N' <a href="?page=1">Trang đầu</a>'
        SET @SQL=@SQL+ ' <a href="?page='+ 
            Cast((@currPage-1) AS nvarchar(4))+N'">Trang trước</a>'
        -- Xử lý trường hợp (@TotalPage-@currPage)<@PageSize/2
        IF(@TotalPage-@currPage)<@PageSize/2
           BEGIN
              SET @Start=(@TotalPage-@PageSize)+1
              IF @Start<0 SET @Start=1 
              SET @PageNumber = @TotalPage
           END
        ELSE
        BEGIN
            IF (@currPage-(@PageSize/2))=0
            BEGIN
                SET @Start=1
                SET @PageNumber=@currPage+(@PageSize/2)+1
                IF @TotalPage<@PageNumber
                    SET @PageNumber=@TotalPage
            END
            ELSE
               BEGIN
                  SET @Start=@currPage-(@PageSize/2)
                  IF @Start<0 SET @Start=1 
                  SET @PageNumber=@currPage+(@PageSize/2)
                  IF @TotalPage<@PageNumber
                      SET @PageNumber=@TotalPage
                  ELSE
                  IF @PageNumber <@PageSize 
                      SET @PageNumber=@PageSize
               END
        END
     END    

    SET @i=@Start
    WHILE @i<=@PageNumber
    BEGIN
        IF @i=@currPage
            SET @SQL=@SQL+'
             ['+Cast(Cast(@i AS int) AS nvarchar(4))+'] '
        ELSE
            SET @SQL=@SQL+'
             <a href="?page='+Cast(@i AS nvarchar(4))+'">'
                +Cast(@i AS nvarchar(4))+'</a> '
        SET @i=@i+1 
    END
    IF @currPage<@TotalPage
    BEGIN
        SET @SQL=@SQL+ N'
         <a href="?page='+Cast((@currPage+1) 
            AS nvarchar(4))+N'">Trang sau</a>'
         SET @SQL=@SQL+ N' 
             <a href="?page='+cast(@TotalPage AS nvarchar(6))+
              N'">Trang cuối</a>'
    END
    SELECT @SQL AS PhanTrang    
    -- PRINT @SQL
END
END

2. Store phân trang

Tiếp theo chúng ta sẻ dùng thủ tục trên vào việc phân trang dữ liệu. Thủ tục này  có các biến truyền vào:
@currpage=Trang hiện hành
@recodeperpage=Số bản ghi trên mỗi trang

@Pagesize=Số trang hiển thị phân trang
Thủ tục sẽ trả về dữ liệu là 2 bảng (Dùng dataset).

--CREATE BY webmaster@hmweb.com.vn
CREATE PROCEDURE [dbo].[spTB_TableName_PhanTrang]
@currPage int,
@recodperpage int,
@Pagesize int
AS
Begin
    Begin
    WITH s AS
    (
        SELECT ROW_NUMBER() 
            OVER(ORDER BY MaTruong,TenTruong) AS RowNum, 
            MaTruong, TenTruong
        FROM dbo.TB_TableName  
    )
    Select * From s 
    Where RowNum Between 
        (@currPage - 1)*@recodperpage+1 
            AND @currPage*@recodperpage
    END
    -- Tính tổng số bản ghi
    DECLARE @Tolal int
    SELECT @Tolal=Count(*) FROM TB_TableName

    EXEC spPhanTrangSQL @Tolal, @currPage,@Pagesize, 10
End

Sử dụng Store này bạn cần bind dữ liệu và Fill vào 1 DataSet, Sau đó thực hiện hiển thị dữ liệu phân trang. Bạn đọc bài viết Kỹ thuật phân trang bằng Store trong asp.net sẽ hướng dẫn bạn cách sử dụng Store trên

 

Hàm chuyển chuỗi có dấu (Unicode) thành không dấu

Bài này mình viết hàm chuyển một chuỗi ký tự có dấu (Unicode), chuyển thành chuỗi không dấu

Ứng dụng của hàm này bạn có thể tạo thêm một trường trong table của TB_BaiViet, để khi cần tạo liên kết  dạng http://blog.thuvienit.com/post/2010/11/19/Tao-mot-ham-ma-hoa-voi-T-SQL.aspx

CREATE FUNCTION [dbo].[fuChuyenCoDauThanhKhongDau]
(
      @strInput NVARCHAR(4000)
)
RETURNS NVARCHAR(4000)
AS
BEGIN    
    IF @strInput IS NULL RETURN @strInput
    IF @strInput = '' RETURN @strInput
    DECLARE @RT NVARCHAR(4000)
    DECLARE @SIGN_CHARS NCHAR(136)
    DECLARE @UNSIGN_CHARS NCHAR (136)
    SET @SIGN_CHARS = N'ăâđêôơưàảãạáằẳẵặắầẩẫậấèẻẽẹéềểễệế
                  ìỉĩịíòỏõọóồổỗộốờởỡợớùủũụúừửữựứỳỷỹỵý
                  ĂÂĐÊÔƠƯÀẢÃẠÁẰẲẴẶẮẦẨẪẬẤÈẺẼẸÉỀỂỄỆẾÌỈĨỊÍ
                  ÒỎÕỌÓỒỔỖỘỐỜỞỠỢỚÙỦŨỤÚỪỬỮỰỨỲỶỸỴÝ'
                  +NCHAR(272)+ NCHAR(208)
    SET @UNSIGN_CHARS = N'aadeoouaaaaaaaaaaaaaaaeeeeeeeeee
                  iiiiiooooooooooooooouuuuuuuuuuyyyyy
                  AADEOOUAAAAAAAAAAAAAAAEEEEEEEEEEIIIII
                  OOOOOOOOOOOOOOOUUUUUUUUUUYYYYYDD'
    DECLARE @COUNTER int
    DECLARE @COUNTER1 int
    SET @COUNTER = 1
    WHILE (@COUNTER <=LEN(@strInput))
    BEGIN  
      SET @COUNTER1 = 1
      --Tìm trong chuỗi mẫu
       WHILE (@COUNTER1 <=LEN(@SIGN_CHARS)+1)
       BEGIN
     IF UNICODE(SUBSTRING(@SIGN_CHARS, @COUNTER1,1))
            = UNICODE(SUBSTRING(@strInput,@COUNTER ,1) )
     BEGIN          
          IF @COUNTER=1
              SET @strInput = SUBSTRING(@UNSIGN_CHARS, @COUNTER1,1)
              + SUBSTRING(@strInput, @COUNTER+1,LEN(@strInput)-1)  
          ELSE
              SET @strInput = SUBSTRING(@strInput, 1, @COUNTER-1)
              +SUBSTRING(@UNSIGN_CHARS, @COUNTER1,1)
              + SUBSTRING(@strInput, @COUNTER+1,LEN(@strInput)- @COUNTER)
              BREAK
               END
             SET @COUNTER1 = @COUNTER1 +1
       END
      --Tìm tiếp
       SET @COUNTER = @COUNTER +1
    END
    SET @strInput = replace(@strInput,' ','-')
    RETURN @strInput
END

Ví dụ:

SELECT dbo.fuChuyenCoDauThanhKhongDau (N'Tạo một hàm mã hoá với T-SQL')

 --Sẽ trả về kết quả là

Tao-mot-ham-ma-hoa-voi-T-SQL

Triggers And Views

Trong bài này chúng ta sẽ tìm hiểu ứng dụng của một loại stored procedure đặc biệt gọi là Triggers và dùng Views để thể hiện data trong một hay nhiều table như thế nào.

Triggers


Trigger là một loại stored procedure đặc biệt được execute (thực thi) một cách tự động khi có một data modification event xảy ra như Update, Insert hay Delete. Trigger được dùng để đảm bảo Data Integrity hay thực hiện các business rules nào đó.

 Khi nào ta cần sử dụng Trigger:

  • Ta chỉ sử dụng trigger khi mà các biện pháp bảo đảm data intergrity khác như Constraints không thể thỏa mãn yêu cầu của ứng dụng. Nên nhớ Constraint thuộc loại Declarative Data Integrity cho nên sẽ kiểm tra data trước khi cho phép nhập vào table trong khi Trigger thuộc loại Procedural Data Integrity nên việc insert, update, delete đã xảy ra rồi mới kích hoạt trigger. Chính vì vậy mà ta cần cân nhắc trước khi quyết định dùng loại nào trong việc đảm bảo Data Integrity.
  • Khi một database được denormalized (ngược lại quá trình normalization, là một quá trình thiết kế database schema sao cho database chứa data không thừa không thiếu) sẽ có một số data thừa (redundant ) được chứa trong nhiều tables. Nghĩa là sẽ có một số data được chứa cùng một lúc ở hai hay nhiều nơi khác nhau. Khi đó để đảm bảo tính chính xác thì khi data được update ở một table này thì cũng phải được update một cách tự động ở các table còn lại bằng cách dùng Trigger.

      Ví dụ: ta có table Item trong đó có field Barcode dùng để xác định một mặt hàng nào đó. Item table có vai trò như một cuốn catalog chứa những thông tin cần thiết mô tả từng mặt hàng. Ta có một table khác là Stock dùng để phản ánh món hàng có thực trong kho như được nhập về này nào được cung cấp bởi đại lý nào, số lượng bao nhiêu (tức là những thông tin về món hàng mà không thể chứa trong Item table được)...table này cũng có field Barcode để xác định món hàng trong kho. Như vậy thông tin về Barcode được chứa ở hai nơi khác nhau do đó ta cần dùng trigger để đảm bảo là Barcode ở hai nơi luôn được synchonize (đồng bộ).

  • Ðôi khi ta có nhu cầu thay đổi dây chuyền (cascade) ta có thể dùng Trigger để bảo đảm chuyện đó. Nghĩa là khi có sự thay đổi nào đó ở table này thì một số table khác cũng được thay đổi theo để đảm bảo tính chính xác. Ví dụ như khi một món hàng được bán đi thì số lượng hàng trong table Item giảm đi một món đồng thời tổng số hàng trong kho (Stock table) cũng phải giảm theo một cách tự động. Như vậy ta có thể tạo một trigger trên Item table để mỗi khi một món được bán đi thì trigger sẽ được kích hoạt và giảm tổng số hàng trong Stock table.


 Ðặc điểm của Trigger:

  • Một trigger có thể làm nhiều công việc (actions) khác nhau và có thể được kích hoạt bởi nhiều hơn một event. Ví dụ ta có thể viết một trigger được kích hoạt bởi bất kỳ event nào như Update, Insert hay Delete và bên trong trigger ta sẽ viết code để giải quyết cho từng trường hợp.      
  • Trigger không thể được tạo ra trên temporary hay system table.      
  • Trigger chỉ có thể được kích hoạt một cách tự động bởi một trong các event Insert, Update, Delete mà không thể chạy manually được.      
  • Có thể áp dụng trigger cho View.      
  • Khi một trigger được kích hoạt thì data mới vừa được insert hay mới vừa được thay đổi sẽ được chứa trong Inserted table còn data mới vừa được delete được chứa trong Deleted table. Ðây là 2 table tạm chỉ chứa trên memory và chỉ có giá trị bên trong trigger mà thôi (nghĩa là chỉ nhìn thấy và được query trong trigger mà thôi). Ta có thể dùng thông tin trong 2 table này để so sánh data cũ và mới hoặc kiểm tra xem data mới vừa thay đổi có hợp lệ trước khi commit hay roll back. (Xem thêm ví dụ bên dưới).      
  • Có 2 loại triggers (class) : INSTEAD OF và AFTER. Loại INSTEAD OF sẽ bỏ qua (bybass) action đã kích hoạt trigger mà thay vào đó sẽ thực hiện các dòng lệnh SQL bên trong Trigger. Ví dụ ta có một Update trigger trên một table với câu INSTEAD OF thì khi table được update thay vì update SQL Server sẽ thực hiện các lệnh đã được viết sẵn bên trong trigger. Ngược lại loại AFTER (loại default tương đương với keyword FOR) sẽ thực hiện các câu lệnh bên trong trigger sau khi các action tạo nên trigger đã xảy ra rồi.


Tạo Một Trigger Như Thế Nào?

Cú pháp căn bản để tạo ra một trigger có dạng như sau:

CREATE TRIGGER trigger_name
ON table_name or view_name
FOR trigger_class and trigger_type(s)
AS Transact-SQL statements

 

Như vậy khi tạo ra một trigger ta phải chỉ rõ là tạo ra trigger trên table nào và được trigger khi nào (insert, update hay delete. Sau chữ AS là các câu lệnh SQL xử lý công việc.

Ta hãy nghiên cứu một ứng dụng thực tiễn sau. Giả sử ta viết một application cho phép user có thể Insert, Update và Delete những thông tin nằm trong database. User này thường là những người không thông thạo lắm về computer mà chúng tôi thường gọi đùa là "bà tám". Vào một ngày đẹp trời, "bà tám" mặt mày tái xanh đến cầu cứu ta vì đã lỡ tay "delete" những thông tin khá quan trọng và hy vọng ta có thể phục hồi dữ liệu dùm. Nếu chúng ta không phòng xa trước khi viết application thì coi như cũng vô phương cứu chữa vì data đã hoàn toàn bị delete.

Nhưng nếu bạn là một "guru" bạn sẽ gật gù "chuyện này khó lắm!" nhưng sau đó bạn chỉ tốn vài phút đồng hồ để rollback. Muốn làm được chuyện này chúng ta phải dùng một "chiêu" gọi là Audit (kiểm tra hay giám sát). Tức là ngoài các table chính ta sẽ thêm các table phụ gọi là Audit tables. Bất kỳ hoạt động nào đụng chạm vào một số table quan trọng trong database ta đều ghi nhận vào trong Audit table. Ví dụ khi user update hay delete một record trong table nào đó thì trước khi update hay delete ta sẽ âm thầm di chuyển record đó sang Audit table rồi mới update hay delete table chính. Như vậy nếu có chuyện gì xảy ra ta có thể dễ dàng rollback (trả record về chỗ cũ).

Ví dụ: 


Ta có table Orders trong PracticeDB. Ðể audit các hoạt động diễn ra trên table này ta tạo ra một audit table với tên Aud_Orders với các column giống y hệt với Orders table. Ngoài ra ta thêm vào 2 columns:

Audit_Type : với các giá trị có thể là 'I','U','D' để ghi nhận record được Insert, Update hay Delete

Date_Time_Stamp : Data Type thuộc loại DateTime dùng để ghi nhận thời điểm xảy ra sự thay đổi, có vai trò như một con dấu.

 (Nếu trong môi trường nhiều user thì ta thêm một column UserID để ghi nhận user nào thay đổi).

 Sau đó ta sẽ tạo ra 3 trigger dùng cho việc audit  như sau:

--Insert Trigger
CREATE TRIGGER [AuditInsertOrders]
ON [dbo].[Orders]
FOR Insert
AS
insert into aud_orders select *,'I',getdate() From inserted

--Update Trigger
CREATE TRIGGER [AuditUpdateOrders]
ON [dbo].[Orders]
for UPDATE
AS
insert into aud_orders select *,'U',Getdate() from deleted

--Delete Trigger
CREATE TRIGGER [AuditDeleteOrders]
ON [dbo].[Orders]
FOR DELETE
AS
insert into aud_orders select *,'D',getdate() From deleted

Trong ví dụ trên khi user insert một record thì record mới vừa được insert sẽ nằm trong inserted table như đã trình bày ở phần trên. Do đó ta sẽ select tất cả các column trong inserted table cộng thêm Audit Type "I" và dùng hàm GetDate() trong SQL Server để lấy system date time dùng cho Date_Time_Stamp column, sau đó insert vào Aud_Orders table. Tương tự với trường hợp Update và Delete, record đã được update hay delete nằm trong deleted table.

Như vậy trở lại trường hợp thí dụ ở trên nếu "bà tám" yêu cầu ta có thể vào tìm kiếm trong audit table để phục hồi lại record. Ngoài ra ta có thể dùng table này để tìm ra thủ phạm đã xoá hay sửa chữa data khi cần thiết.

Ðể tạo ra hay xem một trigger bằng Enterprise Manager bạn làm như sau: Right-Click lên table mà bạn muốn tạo trigger->All Tasks-> Manage Triggers.

Lưu ý: Ðôi Khi ta chỉ muốn trigger thực sự hoạt động khi một hay vài column nào đó được Update chứ không phải bất kỳ column nào. Khi đó ta có thể dùng hàm Update(Column_Name) để kiểm tra xem column nào đó có bị update hay không.

Ví dụ:

Tạo một trigger cho Customer table. Bên trong Trigger (sau chữ AS) ta có thể kiểm tra xem nếu column First_Name hay Last_Name bị thay đổi thì mới hành động nếu không thì không làm gì cả

IF UPDATE (first_name) OR UPDATE (Last_Name)
BEGIN
    Do some conditional processing
    when either of these columns are updated.
END

Nếu muốn kiểm tra nhiều columns ta có thể dùng hàm khác là Columns_Updated() . Xin xem thêm trong SQL Server Books Online để biết thêm chi tiết về cách sử dụng.

 

Views


Ðịnh nghĩa một cách đơn giản thì view trong SQL Server tương tự như Query trong Access database. View có thể được xem như một table ảo mà data của nó được select từ một stored query. Ðối với programmer thì view không khác chi so với table và có thể đặt ở vị trí của table trong các câu lệnh SQL. Ðặc điểm của View là ta có thể join data từ nhiều table và trả về một recordset đơn. Ngoài ra ta có thể "xào nấu" data (manipulate data) trước khi trả về cho user bằng cách dùng một số logic checking như (if, case...).

Ví dụ:

Create View OrderReport
As   
Select OrderID,
       (case when [Name] is null then 'New Customer'
    else [Name]
    end )As CustomerName,
    ProductName,
    DateProcessed
From Customers Right Outer Join Orders on
    Customers.CustomerID=Orders.CustomerID

 Trong ví dụ trên ta chủ yếu trả về data từ Orders table trong PracticeDB nhưng thay vì display CustomerID vốn không có ý nhiều ý nghĩa đối với user ta sẽ display tên của customer bằng cách join với Customer table. Nếu Customer Name là Null nghĩa là tên của customer đã đặt order không tồn tại trong system. Thay vì để Null ta sẽ display "New Customer" để dễ nhìn hơn cho user.

Nói chung câu lệnh SQL trong View có thể từ rất đơn giản như select toàn bộ data từ một table cho đến rất phức tạp với nhiều tính năng programming của T-SQL.

View Thường Ðược Dùng Vào Việc Gì?


View thường được sử dùng vào một số công việc sau:

 

  • Tập trung vào một số data nhất định : ta thường dùng view để select một số data mà user quan tâm hay chịu trách nhiệm và loại bỏ những data không cần thiết.

    Ví dụ: Giả sử trong table ta có column "Deleted" với giá trị là True hay False để đánh dấu một record bị delete hay không. Việc này đôi khi được dùng cho việc Audit. Nghĩa là trong một ứng dụng nào đó khi user delete một record nào đó, thay vì ta physically delete record ta chỉ logically delete bằng cách đánh dấu record là đã được "Deleted" để đề phòng user yêu cầu roll back. Như vậy chủ yếu ta chỉ quan tâm đến data chưa delete còn data đã được đánh dấu deleted chỉ được để ý khi nào cần roll back hay audit mà thôi. Trong trường hợp này ta có thể tạo ra một view select data mà Deleted=False và làm việc chủ yếu trên view thay vì toàn bộ table.

  • Ðơn giản hóa việc xử lý data: Ðôi khi ta có những query rất phức tạp và sử dụng thường xuyên ta có thể chuyển nó thành View và đối xử nó như một table, như vậy sẽ làm cho việc xử lý data dễ dàng hơn.      
  • Customize data: Ta có thể dùng view để làm cho users thấy data từ những góc độ khác nhau mặc dù họ đang dùng một nguồn data giống nhau. Ví dụ: Ta có thể tạo ra views trong đó những thông tin về customer được thể hiện khác nhau tùy login ID là normal user hay manager.      
  • Export và Import data: Ðôi khi ta muốn export data từ SQL Server sang các ứng dụng khác như Excel chẳng hạn ta có thể dùng view để join nhiều table và export dùng bcp.


Khi sử dụng view ta có thể select,insert, update, delete data bình thường như với một table.

Ví dụ:

Select * From OrderReport
Where DateProcessed <'2003-01-01'