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 đọc số bằng chữ bằng C#

Khi lập trình có liên quan đến số liệu phải tính toán và yêu cầu phải đọc được số thành chữ như đọc tiền bằng chữ, Bài này sẽ hướng dẫn bạn viết hàm bằng C#

Để thực hiện đọc số thành chữ bạn cần viết hàm đọc số có 3 chữ số trước, Bạn hãy thực hiện theo các hàm dưới đây.
Bạn đọc và hiểu được thuật toán thì không khó khăn để chuyển đổi bằng các ngôn ngữ lập trình khác nhau. Bài sau mình sẽ giới thiệu hàm "đọc tiền bằng chữ bằng javascript" dùng thuật toán trên

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;

namespace hmweb.Services
{
    public class cCommonFunction
    {
        private string[] ChuSo = new string[10] { " không", " một", " hai", " ba", " bốn", " năm", " sáu", " bẩy", " tám", " chín" };
        private string[] Tien = new string[6] { "", " nghìn", " triệu", " tỷ", " nghìn tỷ", " triệu tỷ" };
        // Hàm đọc số thành chữ
        public string DocTienBangChu(long SoTien, string strTail)
        {
            int lan, i;
            long so;
            string KetQua = "", tmp = "";
            int[] ViTri = new int[6];
            if (SoTien < 0) return "Số tiền âm !";
            if (SoTien == 0) return "Không đồng !";
            if (SoTien > 0)
            {
                so = SoTien;
            }
            else
            {
                so = -SoTien;
            }
            //Kiểm tra số quá lớn
             if (SoTien > 8999999999999999)
            {
                SoTien = 0;
             return "";
            }
            ViTri[5] = (int)(so / 1000000000000000);
            so = so - long.Parse(ViTri[5].ToString()) * 1000000000000000;
            ViTri[4] = (int)(so / 1000000000000);
            so = so - long.Parse(ViTri[4].ToString()) * +1000000000000;
            ViTri[3] = (int)(so / 1000000000);
            so = so - long.Parse(ViTri[3].ToString()) * 1000000000;
            ViTri[2] = (int)(so / 1000000);
            ViTri[1] = (int)((so % 1000000) / 1000);
            ViTri[0] = (int)(so % 1000);
            if (ViTri[5] > 0)
            {
                lan = 5;
            }
            else if (ViTri[4] > 0)
            {
                lan = 4;
            }
            else if (ViTri[3] > 0)
            {
                lan = 3;
            }
            else if (ViTri[2] > 0)
            {
                lan = 2;
            }
            else if (ViTri[1] > 0)
            {
                lan = 1;
            }
            else
            {
                lan = 0;
            }
            for (i = lan; i >= 0; i--)
            {
                tmp = DocSo3ChuSo(ViTri[i]);
                KetQua += tmp;
                if (ViTri[i] != 0) KetQua += Tien[i];
                if ((i > 0) && (!string.IsNullOrEmpty(tmp))) KetQua += ",";//&& (!string.IsNullOrEmpty(tmp))
            }
            if (KetQua.Substring(KetQua.Length - 1, 1) == ",") KetQua = KetQua.Substring(0, KetQua.Length - 1);
            KetQua = KetQua.Trim() + strTail;
            return KetQua.Substring(0, 1).ToUpper() + KetQua.Substring(1);
        }

        // Hàm đọc số có 3 chữ số
        private string DocSo3ChuSo(int baso)
        {
            int tram, chuc, donvi;
            string KetQua = "";
            tram = (int)(baso / 100);
            chuc = (int)((baso % 100) / 10);
            donvi = baso % 10;
            if ((tram == 0) && (chuc == 0) && (donvi == 0)) return "";
            if (tram != 0)
            {
                KetQua += ChuSo[tram] + " trăm";
                if ((chuc == 0) && (donvi != 0)) KetQua += " linh";
            }
            if ((chuc != 0) && (chuc != 1))
            {
                KetQua += ChuSo[chuc] + " mươi";
                if ((chuc == 0) && (donvi != 0)) KetQua = KetQua + " linh";
            }
            if (chuc == 1) KetQua += " mười";
            switch (donvi)|
            {
                case 1:
                    if ((chuc != 0) && (chuc != 1))
                    {
                        KetQua += " mốt";
                    }
                    else
                    {
                        KetQua += ChuSo[donvi];
                    }
                    break;
                case 5:
                    if (chuc == 0)
                    {
                        KetQua += ChuSo[donvi];
                    }
                    else
                    {
                        KetQua += " lăm";
                    }
                    break;
                default:
                    if (donvi != 0)
                    {
                        KetQua += ChuSo[donvi];
                    }
                    break;
            }
            return KetQua;
        }
    }
}

 

Select dữ liệu từ database ra SiteMapNode

Trong bài viết Xây dựng Menu đa cấp với Multi Sitemap mình có hướng dẫn cách tạo menu đa cấp với MultiSitemap với dữ liệu từ file.sitemap có sẵn. Bài này mình sẽ viết hàm trong SQL để lấy dữ liệu từ database dưới dạng SiteMapNode để sử dụng tạo menu đa cấp

Thường việc này bạn có thể dùng code asp.net (C# hay VB) để thực hiện bằng vòng lặp hoặc sử dụng System.Xml - XmlTextWriter để thực hiện. Nhưng bạn cũng có thể sử dụng vòng lặp trong SQL để thực hiện việc này. Ở đây mình viết hàm trong SQL kết quả trả về là nội dung của file MenuTop.sitemap. Hàm này lấy đến menu cấp 3. nếu dự án của bạn có nhiều hơn 3 cấp bạn hãy sửa lại hàm cho phù hợp

Bạn chạy hàm sau trong SQL

-- =============================================
-- Author:        webmaster@hmweb.com.vn
-- Description:   <Get Sitemap content>
-- =============================================
ALTER   FUNCTION [dbo].[fuGetTreeNodeMap]
(
)
RETURNS NVarChar(4000)
AS 
BEGIN
DECLARE @SQL nvarchar(4000)
SET @SQL=''
-- Khai báo mở sitemap
SET @SQL=@SQL+N'<?xml version="1.0" encoding="utf-8" ?>
<siteMap>'
-- Duyệt cây cấp 1 (Có Decen=0)
DECLARE @CateID int, @CateName nvarchar(200), @Link nvarchar(200)
Declare CursorCategory Cursor For SELECT CateID, CateName, Link FROM TBCategory WHERE Decen=0
Open CursorCategory
Fetch next from CursorCategory INTO @CateID, @CateName , @Link
while @@FetchStatus =0
BEGIN
-- Nếu cây cấp 1 không có con thì đóng luôn TreeNode
IF dbo.fuCountSubMenu(@CateID)=0
BEGIN
      SET @SQL=@SQL+ '
      <siteMapNode url="'+dbo.ufDomainName() + @Link + '" title="' + @CateName + '" />'
END
-- Nếu cây cấp 1 có con thì mở TreeNote và tiếp tục duyệt cây cấp 2
      ELSE
      BEGIN
            SET @SQL=@SQL+ '
            <siteMapNode url="'+dbo.ufDomainName() + @Link + '" title="' + @CateName + '" >'
            -- Duyệt cây cấp 2
                  DECLARE @CateID2 int, @CateName2 nvarchar(200), @Link2 nvarchar(200)
                  Declare CursorCategory2 Cursor For SELECT CateID, CateName, Link FROM TBCategory WHERE Decen=1 AND  ParentID=@CateID
                  Open CursorCategory2
                  Fetch next from CursorCategory2 INTO @CateID2, @CateName2 , @Link2
                  WHILE @@FETCHSTATUS=0
                  BEGIN
                        -- Nếu cây cấp 2 không có con thì đóng luôn TreeNode
                        IF dbo.fuCountSubMenu(@CateID2)=0
                              BEGIN
                                    SET @SQL=@SQL+ '<siteMapNode url="'+dbo.ufDomainName() + @Link2 + '" title="' + @CateName2 + '" />'
                              END
                        -- Nếu cây cấp 2 có con thì mở TreeNode và tiếp tục duyệt cây cấp 3
                        ELSE
                              BEGIN
                              SET @SQL=@SQL+ '
                                    <siteMapNode url="'+dbo.ufDomainName() + @Link2 + '" title="' + @CateName2 + '" >'
                                    -- Duyệt cây cấp 3
                                    DECLARE @CateID3 int, @CateName3 nvarchar(200), @Link3 nvarchar(200)
                                    Declare CursorCategory3 Cursor For SELECT CateID, CateName, Link FROM TBCategory WHERE Decen=2 AND ParentID=@CateID2
                                    Open CursorCategory3
                                    Fetch next from CursorCategory3 INTO @CateID3, @CateName3 , @Link3
                                    WHILE @@FETCHSTATUS=0
                                          BEGIN
                                                SET @SQL=@SQL+ '
                                                <siteMapNode url="'+dbo.ufDomainName() + @Link3 + '" title="' + @CateName3 + '" />'
                                          FETCH NEXT FROM CursorCategory3 INTO @CateID3, @CateName3 , @Link3
                                          END
                                    Close CursorCategory3
                                    DEALLOCATE CursorCategory3
                                    SET @SQL=@SQL+ '</siteMapNode>'
                              END
                        FETCH NEXT FROM CursorCategory2 INTO @CateID2, @CateName2 , @Link2
                  END
                  Close CursorCategory2
                  DEALLOCATE CursorCategory2
                  SET @SQL=@SQL+ '</siteMapNode>'
            END
FETCH NEXT FROM CursorCategory INTO @CateID, @CateName , @Link
END
Close CursorCategory
DEALLOCATE CursorCategory
--Đóng sitemap
SET @SQL=@SQL+'
</siteMap>'
-- Print @SQL
RETURN  @SQL
END

Nếu bảng TBCategory của bạn có nhiều bản ghi thì độ dài chuỗi @SQL có thể quá 4000. Khi đó bạn cần phân tách và ghép chuỗi lại

Kết quả hàm trên (hoặc bạn thử bằng lệnh Print @SQL) như sau (Bạn có thể copy vào visual studio để xem đúng cấu trúc)

<?xml version="1.0" encoding="utf-8" ?>
<siteMap>
  <siteMapNode url="http://www.hmweb.com.vn" title="Thể thao" >
    <siteMapNode url="http://www.hmweb.com.vn" title="Pháp luật" />
    <siteMapNode url="http://www.hmweb.com.vn" title="Môi trường" />
  </siteMapNode>
  <siteMapNode url="http://www.hmweb.com.vn" title="Thể thao" >
    <siteMapNode url="http://www.hmweb.com.vn" title="Bóng đá" >
      <siteMapNode url="http://www.hmweb.com.vn" title="Bóng đá trong nước" />
      <siteMapNode url="http://www.hmweb.com.vn" title="Bóng đá Anh" />
      <siteMapNode url="http://www.hmweb.com.vn" title="Bóng đá TBN" />
      <siteMapNode url="http://www.hmweb.com.vn" title="Bóng đá Ý" />
    </siteMapNode>
    <siteMapNode url="http://www.hmweb.com.vn" title="Quần vợt" />
  </siteMapNode>
  <siteMapNode url="http://www.hmweb.com.vn" title="Sức khỏe" />
  <siteMapNode url="http://www.hmweb.com.vn" title="Kinh doanh" >
    <siteMapNode url="http://www.hmweb.com.vn" title="Chứng khoán" />
    <siteMapNode url="http://www.hmweb.com.vn" title="Thị trường" />
    <siteMapNode url="http://www.hmweb.com.vn" title="Doanh nghiệp" />
  </siteMapNode>
  <siteMapNode url="http://www.hmweb.com.vn" title="Giải trí" />
</siteMap>

Khi đã có dữ liệu hàm trên rồi bạn có thể chạy thử bằng câu lệnh SELECT dbo.fuGetTreeNode_Map() để xem kết quả.
Từ kết quả trên để đưa ra file.sitemap bạn chỉ cần viết 1 hàm (C#) write ra file với phát biểu Select là xong.

Có thời gian mình sẽ hướng dẫn dùng asp.net để bind dữ liệu thàng sitemap

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