SOẠN LAB ONLINE VỚI GOOGLE DOCS

Sau khi có hình lab và đã publish lên FTP webdoanhnghiep.org , công đoạn tiếp theo là mở Microsoft Word lên để tạo file .doc hoặc mở Dreamweaver lên để tạo trang web HTML kèm những hình đó. Trong 1 buổi không có lớp dạy, tôi đã mượn 1 máy tính Nhất Nghệ để soạn lab, nhưng máy tính này không có cài MS Word hay Dreamweaver, với google docs đã giúp tôi có thể soạn xong 1 bài lab với hơn 100 tấm hình 1 cách nhanh chóng. Bài lab soạn xong có thể save dưới dạng word hoặc HTML tùy ý.

Chuẩn bị:
- 1 máy tính có kết nối Internet
- 1 account gmail
- download bộ gõ tiếng Việt tại http://thuc.webdoanhnghiep.org/softs/UniKey4.zip
- download WSFTP tại http://thuc.webdoanhnghiep.org/softs/wsftp701.zip (nên xài cái này trong trường hợp không nhớ đường dẫn hình đã upload trên FTP server)

LazyDroid - Xài nhiều chức năng của ĐT Android trên máy tính

LazyDroid là một phần mềm có tính năng tương tự với Remote Desktop đã được giới thiệu trước đây. Cả hai đều hay ở chỗ là miễn phí và bạn có thể tải về từ Market, ngoài phần mềm cài trên điện thoại Android thì bạn không cần phải cài thêm bất kì phần mềm nào khác vào máy tính của mình. Để có thể kết nối điện thoại Android với máy tính thì yêu cầu duy nhất là cả hai phải cùng kết nối vào một mạng wifi, chúng ta sẽ dùng trình duyệt trên máy tính để truy cập và sử dụng các tính năng của điện thoại. Khác...

Function và Trigger

Các bài viết trong: Quan hệ C# và Database
Phần 1: Kết nối C# với Database
Phần 2: Sql Command
Phần 3: SqlDataReader & Dataset
Phần 4: Stored Procedure
Phần 5: Function và Trigger

Trong bài trước mình đã giới thiệu về Stored ProcedureTrong bài tiếp theo của loạt bài Quan hệ C# và Database mình sẽ giới thiệu về  Function và Trigger - một phần cũng không kém phần quan trong trong lập trình với cơ sở dữ liệu

1. Hàm - Functions


Cũng giống như Stored Procedure Hàm là một đối tượng trong cơ sở dữ liệu bao gồm một tập nhiều câu lệnh SQL được nhóm lại với nhau thành một nhóm. Điểm khác biệt giữa hàm và thủ tục là hàm trả về một giá trị thông qua tên hàm. Điều này cho phép ta sử dụng hàm như là một thành phần của một biểu thức chẳng hạn như trong các câu lệnh truy vấn hay các câu lệnh thực hiện cập nhật dữ liệu

Trong SQL có rất nhiều các hàm được định nghĩa sẵn (Được chia theo nhóm - Trong 1 Database bạn chọn Programmability/Functions/System Functions) như các hàm về chuỗi (String Functions), các hàm về ngày tháng (Date and Time Functions), Các hàm toán học (Mathematical Function), ... Ngoài những hàm do hệ quản trị cơ sở dữ liệu cung cấp sẵn, bạn có thể tự xây dựng các hàm nhằm phục vụ cho mục đích riêng của mình - Các hàm do người dùng định nghĩa. Các hàm do người dùng định nghĩa thường có 2 loại: Loại 1 là Hàm với giá trị trả về là "dữ liệu kiểu bảng" - Table-valued Functions; Loại 2 là Hàm với giá trị trả về là một giá trị - Scalar-valued Functions và các hàm này cũng sẽ được Hệ quản trị phân thành 2 nhóm.

Các hàm sẵn có của SQL bạn tự tìm hiểu và sử dụng, trong bài viết này mình giới thiệu qua về những hàm "Do người dùng định nghĩa".

Cú pháp của hàm như sau:

CREATE  FUNCTION dbo.fuGetCurrYear ()  
RETURNS int
AS  
BEGIN 
    RETURN   YEAR(getdate())
END

Xem ví dụ trên bạn sẽ thấy nó rất đơn giản nhưng qua đây bạn cũng đã biết được việc viết hàm trong SQL như thế nào.

Ví dụ 2: Tiếp theo mình sẽ viết một ví dụ nữa để bạn hiểu và có thể viết cho mình các hàm tự định nghĩa:
Ví dụ này sẽ có 2 tham sô trong Danh_Sach_Cac_Tham_So. Hàm sẽ trả về số ngày của tháng, năm do bạn truyền vào; Bạn biết khi lập trình với Pascal bạn đã quen với bài toán tính số ngày của thàng - Với năm nhuận thì tháng 2 có 29 ngày, các năm khác có 28 ngày. (Qua hàm này bạn cũng sẽ hiểu hơn về điều khiển IF (Xem thêm bài viết Kỹ thuật phân trang bằng Store Procedure để hiểu hơn về cách sử dụng IF trong SQL) và sử dụng Case - (Xem bài viết về Hàm Case trong SQL để hiểu hơn về Case)

CREATE  FUNCTION dbo.fuDaysInMonth (
    @Thang  Int,
    @Nam    Int
)  
RETURNS int
AS  
BEGIN   
    DECLARE @Ngay   Int
    IF @Thang = 2 
        BEGIN
            IF ((@Nam % 4 = 0 AND @Nam % 100 <> 0) 
                OR (@Nam % 400 = 0))
                SET @Ngay = 29
            ELSE
                SET @Ngay = 28
        END
    ELSE
        SELECT @Ngay =  
            CASE @Thang
                WHEN 1 THEN 31
                WHEN 3 THEN 31
                WHEN 5 THEN 31
                WHEN 7 THEN 31
                WHEN 8 THEN 31
                WHEN 10 THEN 31
                WHEN 12 THEN 31
                WHEN 4 THEN 30
                WHEN 6 THEN 30
                WHEN 9 THEN 30
                WHEN 11 THEN 30
            END
    RETURN @Ngay
END

Ví dụ 3: Bạn xem tiếp ví dụ sau để xác định thứ trong tuần của một giá trị kiểu ngày

CREATE FUNCTION fuThu
(
    @ngay DATETIME
)
RETURNS NVARCHAR(10)
AS
     BEGIN
          DECLARE @KetQua NVARCHAR(10)
          SELECT @KetQua=CASE DATEPART(DW,@ngay)
                        WHEN 1 THEN N'Chủ nhật'
                        WHEN 2 THEN N'Thứ hai'
                        WHEN 3 THEN N'Thứ ba'
                        WHEN 4 THEN N'Thứ tư'
                        WHEN 5 THEN N'Thứ năm'
                        WHEN 6 THEN N'Thứ sáu'
                        ELSE N'Thứ bảy'
                      END    
          RETURN (@KetQua)  /* Trị trả về của hàm */
END

 

Một hàm khi đã được định nghĩa có thể được sử dụng như các hàm do hệ quản trị cơ sở dữ liệu cung cấp (thông thường trước tên hàm ta phải chỉ định thêm tên của người sở hữu hàm bằng dbo.) như ví dụ dưới đây:

SELECT e.FirstName, e.LastName,
dbo.fuThu(e.BirthDate) AS ThuOfBirth
FROM Employees e
Bạn có thể tham khảo thêm bài viết Xử lý từ khóa tìm kiếm cho bài viết

Tiếp theo mình sẽ nói về Hàm với giá trị trả về là "dữ liệu kiểu bảng"

Nếu đã biết về SQL chắc hẳn bạn đã biết cách tạo View từ các bảng trong CSDL, Nhưng với View bạn không thể truyền các tham số được, điều này phần nào đó làm giảm tính linh hoạt trong việc sử dụng View. Vậy nên khi bạn cần sử dụng dữ liệu dạng View mà có các tham số thì việc sử dụng hàm là một giải pháp hợp lý nhất.
Ví dụ 4: Giả sử Mình tạo 1 View như sau:
CREATE VIEW vProducts
 as
 SELECT     
    Categories.CategoryID, 
    Categories.CategoryName, 
    Products.ProductName, 
    Products.QuantityPerUnit, 
    Products.UnitPrice
FROM         
    Categories INNER JOIN
    Products ON Categories.CategoryID = Products.CategoryID
 WHERE Categories.CategoryID=1
 Bạn xem ví dụ bạn thấy rằng mình tạo ra 1 View vProducts có  Categories.CategoryID=1 và bạn muốn truy vấn các trường của bảng Products ừng với CategoryID=1 và bạn chỉ cần câu lệnh Select * from vProducts là bạn đã có kết quả như ý. Nhưng với những CategoryID khác thì View vProducts  không làm được trừ phi bạn. Vậy bạn thử sử dụng hàm sau để làm minh họa nhé

Ví dụ 5:
Tạo một hàm trả về dữ liệu dạng bảng tùy theo giá trị của biến @CategoryID truyền vào:
CREATE FUNCTION fuGetProducts
(
    @CategoryID int
)RETURNS TABLE
 AS 
    RETURN
    (
        SELECT     
        Categories.CategoryID, 
        Categories.CategoryName, 
        Products.ProductName, 
        Products.QuantityPerUnit, 
        Products.UnitPrice
    FROM         
        Categories INNER JOIN
        Products ON Categories.CategoryID = Products.CategoryID
     WHERE Categories.CategoryID=@CategoryID)
 Bạn chạy thử hàm trên(Chú ý là khi hàm trả về dạng bảng bạn cũng coi đó như 1 table hoặc 1 View và bạn có thể truy vấn theo 1 hay nhiều trường của hàm) như sau:  

SELECT CategoryID, 
CategoryName,
ProductName,
QuantityPerUnit,
UnitPrice 
FROM  dbo.fuGetProducts(1)
Sẽ tra về dữ liệu chính là Select * from vProducts ở trên. Nếu muốn lầy Theo CategoryID=2 bạn dùng câu lệnh Select * from dbo.fuGetProducts(2)...

Trên đây chỉ là một ví dụ nhỏ về hàm trả lại dữ liệu kiểu bảng hy vọng bạn sẽ hiểu phần nào về loại hàm này. Trong thực tế chúng ta sẽ cần nó để thực hiện các yêu cầu phức tạp hơn tùy vào dữ liệu thiết kế, quan hệ dữ liệu và yêu cầu mà bạn viết hàm để sử dụng

2. Trigger

Cũng tương tự như thủ tục lưu trữ (Stored Prodedure), một trigger là một đối tượng chứa một tập các câu lệnh SQL và tập các câu lệnh này sẽ được thực thi khi trigger được gọi. Điểm khác biệt giữa thủ tục lưu trữ và trigger là: Các thủ tục lưu trữ được thực thi khi người sử dụng có lời gọi đến chúng còn các trigger lại được "gọi" tự động khi xảy ra những giao tác làm thay đổi dữ liệu trong các bảng.

Mỗi một trigger được tạo ra được gắn liền với một bảng nào đó trong cơ sở dữ liệu của bạn. Khi dữ liệu trong bảng bị thay đổi (Là khi xảy ra các sự kiện INSERT, UPDATE hay DELETE) thì trigger sẽ được tự đông kích hoạt. Để xem các Trigger của một bảng trong SQL 2005 bạn chọn bảng đó, chọn Triggers

Sử dụng trigger một cách hợp lý trong cơ sở dữ liệu sẽ có tác động rất lớn trong việc tăng hiệu năng của cơ sở dữ liệu. Các  trigger thực sự hữu dụng với những khả năng sau:

    * Một trigger có thể nhận biết, ngăn chặn và huỷ bỏ được những thao tác làm thay đổi trái phép dữ liệu trong cơ sở dữ liệu.
    * Các thao tác trên dữ liệu (xoá, cập nhật và bổ sung) có thể được trigger phát hiện ra và tự động thực hiện một loạt các thao tác khác trên cơ sở dữ liệu nhằm đảm bảo tính hợp lệ của dữ liệu.
    * Thông qua trigger, ta có thể tạo và kiểm tra được những mối quan hệ phức tạp hơn giữa các bảng trong cơ sở dữ liệu mà bản thân các ràng buộc không thể thực hiện được.

Khi xảy ra ra một sự kiện thao tác dữ liệu một bản ghi trong CSDL nó sẽ lưu ra một bản ghi trong Trigger nó có tên là inserted đối với các thao tác Insert hay Update và deleted đối với Delete

Cú pháp chung để tạo một Trigger như sau:

CREATE TRIGGER Ten_Trigger
ON Ten_Bang
FOR {[INSERT] | [UPDATE] | [DELETE]}
AS
BEGIN
    Cac_Cau_Lenh_Cua_Trigger
END

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, có thể dùng cặp Begin ... End hoặc không). Bạn có thể tham khảo thêm bài viết về Trigger Xây dựng cơ sở dữ liệu - TRIGGER

Giờ ta sẽ tìm hiều ví dụ để hiều hơn về Trigger nhé.
Vẫn với CSDL Northwind giả sử trong bảng Employees mình thêm 1 trường là Age - là tuổi của Employees. Giờ mình sẽ viết 1 trigger gắn với bảng Employees để khi thay đổi BirthDay thì trường Age sẽ tự động được cập nhật. Bạn xem ví dụ sau.

CREATE TRIGGER trigCalcAge
   ON Employees
  FOR  UPDATE, Insert 
AS 
BEGIN
    DECLARE @age int
    DECLARE @EmployeeID int 
    SELECT 
        @age=YEAR(GETDATE())-year(BirthDate),
        @EmployeeID=EmployeeID 
    FROM inserted
    IF UPDATE (BirthDate)
    UPDATE Employees 
        SET Age = @age 
    WHERE EmployeeID=@EmployeeID
END

Xem ví dụ trên bạn thấy khi có thay đổi nó sẽ tạo 1 bản ghi inserted và chúng ta có thể lấy các giá trị của bản ghi đó. Khi thay đổi dữ liệu bạn sẽ không cần cập nhật trường Age.
Tương tự như vậy với trường hợp Xóa dữ liệu.
Một điều chú ý là với Trigger nó chỉ thực hiện với sự thay đổi dữ liệu của từng bản ghi. Với trường hợp cập nhật dữ liệu theo bó thì khi đó bạn cần các kỹ thuật xử lý phức tạp hơn.
Ví dụ câu lệnh sau: Update Employees Set BirthDate='12/12/1990 12:00:00 AM' Câu lệnh này sẽ update toàn bộ dữ liệu (Cập nhật theo bó) của bảng Employees  Nhưng khi đó Trigger của ta chỉ update trường Age ở bản ghi đầu tiên. Để xử lý trường hợp này có nhiều cách chẳng hạn như dùng vòng lặp (dùng con trỏ).

Trong bài viết này mình chỉ giới thiệu qua về Trigger như  vậy để bạn hiểu và ứng dụng nó vào database và yêu cầu của Project của bạn theo yêu cẩu cụ thể


Một số tài liệu cho bạn tham khảo:

1. Lập trình cơ sở dữ liệu SQL server

GTSQLServer.doc (861,00 kb)

2. Câu lệnh truy vấn SQL server

Cau lenh truy van SQL.pdf (379,56 kb)

3. Giáo trình thực hành SQL server

Giao trinh thuc hanh SQL.pdf (430,24 kb)

SqlDataReader & Dataset

Các bài viết trong: Quan hệ C# và Database
Phần 1: Kết nối C# với Database
Phần 2: Sql Command
Phần 3: SqlDataReader & Dataset
Phần 4: Stored Procedure
Phần 5: Function và Trigger

Trong bài tiếp theo này sẽ tập trung trình bày về DataReaders và Dataset, ưu và khuyết điềm của chúng.

1.Data Readers

Khi bạn kết nối tới cơ sở dữ liệu và thi hành các query, dữ liệu thu nhận được cần phải có một ai đó xử lý, chứ không lẽ kết nối đến SQL thêm, xóa, sửa.. xong rồi lại không biết được kết quả đúng hay sai, có thực hiện được hay không, ít ra chúng ta phải xuất kết quả ra để theo dõi đúng không. Như vậy cần phải có một đối tượng thu nhận các thông tin, đó chính là DataReaders

Trong bài trước mình có trình bày 1 ví dụ về có liên quan đến DataReaders nội dung ví dụ 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;
using System.Data.SqlClient;

public partial class Database : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string strHTML = "";
        strHTML+="<table border=\"0\" width=\"550\" cellspacing=\"1\" cellpadding=\"0\" bgcolor=\"#999966\" id=\"table2\">";
        strHTML+="  <tr>";
        strHTML += "        <td bgcolor=\"#CC3300\" align=\"center\"><font color=\"#FFFFFF\">CustomerID</font></td>";
        strHTML += "        <td bgcolor=\"#CC3300\" align=\"center\"><font color=\"#FFFFFF\">CompanyName</font></td>";
        strHTML += "        <td bgcolor=\"#CC3300\" align=\"center\"><font color=\"#FFFFFF\">ContactName</font></td>";
        strHTML += "        <td bgcolor=\"#CC3300\" align=\"center\"><font color=\"#FFFFFF\">Country</font></td>";
        strHTML+="  </tr>";
        // Khai báo chuỗi kết nối
        string connectString = @"Server =.\SQL2005;Initial Catalog=Northwind;User ID=sa;Password=******";
        // Khai báo câu truy vấn
        string sql = @"SELECT TOP 10 CustomerID, CompanyName, ContactName, Country FROM Customers ";
        // Tạo một connection tới máy chủ
        SqlConnection conn = new SqlConnection(connectString);
        try
        {
            conn.Open();
            SqlCommand cmd = new SqlCommand(sql, conn);
            SqlDataReader reader = cmd.ExecuteReader();
            while (reader.Read())
            {
                strHTML += "    <tr>";
                strHTML += "        <td bgcolor=\"#FFFFFF\">" + reader[0]+ "</td>";
                strHTML += "        <td bgcolor=\"#FFFFFF\">" + reader[1] + "</td>";
                strHTML += "        <td bgcolor=\"#FFFFFF\">" + reader[2] + "</td>";
                strHTML += "        <td bgcolor=\"#FFFFFF\">" + reader[3] + "</td>";
                strHTML += "    </tr>";
            }
            reader.Close();//Đóng SqlDataReader
        }

        catch (SqlException ex)
        {
            Console.WriteLine("Error: " + ex);
        }
        finally
        {
            conn.Close();
        }
        strHTML += "</table>";
        Literal1.Text = strHTML;
    }
}

Kết quả thu được là các thông tin của trong câu lệnh

SELECT TOP 10 CustomerID, CompanyName, ContactName, Country FROM Customers

 Với câu lệnh truy vấn trên nó sẽ trả về 10 bản ghi trong bảng Customers bạn có thể thấy muốn lấy dữ liệu là cột  CustomerID bạn dùng reader[0];  0 ở đây là vị trí Index theo thứ tự (Index được bắt đầu từ 0) trong thứ tự các cột của câu truy vấn, Trong ví dụ của bài trước bạn thấy muốn lấy dữ liệu của cột CustomerID  Mình dùng reader[0].GetValue(0) Ngoài cách dùng index để hiển thị dữ liệu bạn cũng có thể dùng trực tiếp tên cột như sau: reader["CustomerID"]. Tới đây chắc các bạn cũng đã nắm được vị trí index trong reader rồi nhỉ. Bạn sẽ thấy rằng hai cách hiển thị sẽ cho kết qua như nhau nhưng theo mình bạn nên dùng trực tiếp tên cột vì đôi khi trong lập trình bạn thay đổi thứ tự hiển thị trong câu truy vấn SQL.

 Lưu ý:
- Thông thường data reader mà phải xử lý thông qua phương thức ‘ExecuteReader’ của một đối tượng command.
- Một DataReader có thể gọi là một stream đã kết nối tới cơ sở dữ liệu đọc dữ liệu hiệu quả, theo một chiều và thu nhận dữ liệu theo từng dòng (row). Quy tắc chung khi sử dụng data reader dơn giản là chỉ thu nhận và trình bày kết quả thu được. Đây là điểm khác biệt lớn nhất giữa dataReader và DataSet.

2.DataSet và DataAdapter

2.1.Dataset
Trong lúc làm việc ta không thể nào kết nối liên tục đến DataSource việc làm này rất tốn tài nguyên của máy, chính vì lẽ đó mà .Net cung cấp cho bạn DataSet mục đích là lưu trữ dữ liệu và chỉnh sửa cục bộ. Tức là toàn bộ thông tin từ DataSource vẫn còn được lưu trữ trên DataSet khi ta đã ngắt kết nối. Bạn hoàn toàn có thể thao tác trên DataSet như xem, chỉnh sửa sau đó update dữ liệu lại cho DataSource. Nếu khi bạn không update thì việc thao tác trên Dataset sẽ không ảnh hưởng gì đến DataSource cả!
DataSet được lưu trữ dưới dạng tập hợp các Tables và bạn cần xử lý thông qua các lớp của Tables là DataRow, DataColumn.
Có nhiều cách xử lý DataSet nhưng nổi bậc hơn hẳn là 2 cách sử dụng:
- Sử dụng DataAdapter
- Sử dụng XML (cái này sẽ được trình bày trong bài viết sau)

2.2. DataAdapter:
DataAdapter được hiểu nôm na, như là cầu nối và  truyền tải dữ liệu giữa dataSet và dataSource. Mối quan hệ này được biểu diễn như sau: DataSource ~ DataAdapter ~ DataSet

Bạn xem ví dụ sau:
Trong trang aspx bạn cần tạo 1 gridview để hiển thị dữ liệu:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="DataSetDemo.aspx.cs" Inherits="DataSetDemo" %>
<!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>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:GridView ID="GridView1" runat="server" BackColor="White" BorderColor="#CCCCCC"
            BorderStyle="None" BorderWidth="1px" CellPadding="3">
            <RowStyle ForeColor="#000066" />
            <FooterStyle BackColor="White" ForeColor="#000066" />
            <PagerStyle BackColor="White" ForeColor="#000066" HorizontalAlign="Left" />
            <SelectedRowStyle BackColor="#669999" Font-Bold="True" ForeColor="White" />
            <HeaderStyle BackColor="#006699" Font-Bold="True" ForeColor="White" />
        </asp:GridView>

    </div>
    </form>
</body>
</html>

Trong CodeFile (Code behind) bạn viết 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;
using System.Data.SqlClient;
public partial class DataSetDemo : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        DataTable dtb = new DataTable();
        dtb = DemoDataSet();
        GridView1.DataSource = dtb;
        GridView1.DataBind();
    }
    private DataTable DemoDataSet()
    {
        DataTable dtbTmp = new DataTable();
        // Tạo connection string
        string connString = @"Server =.\SQL2005;Initial Catalog=Northwind;User ID=sa;Password=******";
        // Tạo SQL query
        string sql = @"SELECT TOP 8 CustomerID, CompanyName, ContactName, Country FROM Customers ";
        // Tạo connection
        SqlConnection conn = new SqlConnection(connString);
        try
        {
            // Mở kết nối
            conn.Open();
            // Tạo một Adapter
            SqlDataAdapter da = new SqlDataAdapter(sql, conn);
            // Tạo DataSet
            DataSet ds = new DataSet();
            // Đổ dữ liệu DataSet
            da.Fill(ds, "Customers");
            // Tạo DataTable từ dataSet
            dtbTmp = ds.Tables[0];
        }
        catch (Exception e)
        {
            // Bắt lỗi
            Console.WriteLine(e.Message);
        }
        finally
        {
            // Đóng kết nối
            conn.Close();
        }
        return dtbTmp;
    }
}

Trong ví dụ trên mình dùng chỉ số index (ds.Tables[0]) để bạn hiểu rằng DataSet là một tập hợp nhiều DataTable. Khi bạn dùng da.Fill(ds, "Customers") Như vậy ngoài cách sử dụng index bạn cũng có thể sử dụng tên đã khai báo như sau: dtbTmp = ds.Tables["Customers"];

Chạy chương trình xong ta thấy, kết quả xuất ra như sau:  (Bạn có thể nhấn vào dưới đây để down mã nguồn ví dụ trên để tham khảo)

 

DataSetDemo.rar (1,93 kb)

Như vậy kết quả không khác mấy khi dùng SqlDataReader, nên nhớ rằng khi bạn thực hiện với mục đích xuất thông tin ra thôi thì nên dùng SqlDataReader để thay thế cho DataSet, vì sẽ tiết kiệm được thời gian và tài nguyên. Ví dụ trên nhằm mình họa một trong những tính năng của DataSet. Như vậy DataSet có điểm gì nổi bậc hơn SqlDataReader, điểm nổi bậc ở đây chính là khả năng tùy biến cao, có thể chỉnh sửa CSDL và lưu lại vào DataSource.
Bạn hãy xem ví dụ sau:

private void DataSetInsertAndView()
{
    // Tạo connection strin
    string connString = @"Server =.\SQL2005;Initial Catalog=Northwind;User ID=sa;Password=******";
    // Tạo SQL query        
    string ins = @"INSERT INTO employees (firstname, lastname, titleofcourtesy, city, country) VALUES
                       (@firstname, @lastname, @titleofcourtesy, @city, @country) ";
    // Tạo connection
    SqlConnection conn = new SqlConnection(connString);
    try
    {
        // Tạo Adapter
        SqlDataAdapter da = new SqlDataAdapter(qry, conn);
        // Tạo và lấp đầy DataSet
        DataSet ds = new DataSet();
        da.Fill(ds, "employees");
        // Lấy thông tin Table vào DataTable
        DataTable dt = ds.Tables["employees"];
        // Tạo thêm row mới
        DataRow newRow = dt.NewRow();
        newRow["firstname"] = "Bui";
        newRow["lastname"] = "Hung";
        newRow["titleofcourtesy"] = "AND";
        newRow["city"] = "HaNoi";
        newRow["country"] = "Viet Nam";
        dt.Rows.Add(newRow);
        // Hiển thị thông tin các rows trong DataSet sau khi thêm vào
        foreach (DataRow row in dt.Rows)
        {
            Literal1.Text = row["firstname"].ToString().PadRight(15) + "|";
            Literal1.Text += row["lastname"].ToString().PadLeft(15) + "|";
            Literal1.Text += row["city"];
        }
        // Làm việc với Insert
        SqlCommand cmd = new SqlCommand(ins, conn);
        cmd.Parameters.Add("@firstname", SqlDbType.NVarChar, 10, "firstname");
        cmd.Parameters.Add("@lastname", SqlDbType.NVarChar, 20, "lastname");
        cmd.Parameters.Add("@titleofcourtesy", SqlDbType.NVarChar, 25, "titleofcourtesy");
        cmd.Parameters.Add("@city", SqlDbType.NVarChar, 15, "city");
        cmd.Parameters.Add("@country", SqlDbType.NVarChar, 15, "country");
        // Tiến hành insert vào database Source
        da.InsertCommand = cmd;
        da.Update(ds, "employees");
    }
    catch (Exception e)
    {
        // Bắt lỗi
        Console.WriteLine(e.Message);
    }
    finally
    {
        // Đóng kết nối
        conn.Close();
    }
}

Qua ví dụ trên bạn có thể nhận thấy DataSet khá linh hoạt, thông qua việc đổ dữ liệu từ dataSet đến các Tables, chúng ta có thể xem thông tin, chỉnh sửa thông tin và khi chỉnh sửa xong có thể update cho DataSource. Nếu các bạn nào tinh ý sẽ thấy quá trình trên thêm vào thực hiện quá dài cứ lập đi lặp lại việc cmd.Parameters.Add,để giải quyết vấn để này .NET cung cấp cho ta SqlCommandBuilder
Bạn xem ví dụ sau:

private void DataSetInsertAndView()
{
    // Tạo connection string
    string connString = @"Server =.\SQL2005;Initial Catalog=Northwind;User ID=sa;Password=******";
    // Tạo SQL query        
    string ins = @"INSERT INTO employees (firstname, lastname, titleofcourtesy, city, country) VALUES
                       (@firstname, @lastname, @titleofcourtesy, @city, @country) ";
    // Tạo connection
    SqlConnection conn = new SqlConnection(connString);
    try
    {
        // Tạo Adapter
        SqlDataAdapter da = new SqlDataAdapter(qry, conn);
        // Tạo commandbuider
        SqlCommandBuilder cb = new SqlCommandBuilder(da);
        // Tạo và lấp đầy DataSet
        DataSet ds = new DataSet();
        da.Fill(ds, "employees");
        // Lấy thông tin Table vào DataTable
        DataTable dt = ds.Tables["employees"];
        // Tạo thêm row mới
        DataRow newRow = dt.NewRow();
        newRow["firstname"] = "Pi";
        newRow["lastname"] = "Pi_Pi";
        newRow["titleofcourtesy"] = "AND";
        newRow["city"] = "UITS";
        newRow["country"] = "Viet Nam";
        dt.Rows.Add(newRow);
        // Hiển thị thông tin các rows trong DataSet sau khi thêm vào
        foreach (DataRow row in dt.Rows)
        {
            Literal1.Text = row["firstname"].ToString().PadRight(15) + "|";
            Literal1.Text += row["lastname"].ToString().PadLeft(15) + "|";
            Literal1.Text += row["city"];
        }
        //Nhờ sử dụng SqlCommandBuilder mà ta update rất nhanh
        da.Update(ds, "employees");
    }
    catch (Exception e)
    {
        // Bắt lỗi
        Console.WriteLine(e.Message);
    }
    finally
    {
        // Đóng kết nối
        conn.Close();
    }
}

Trong hai ví dụ trên mình có tạo 1 <asp:Literal ID="Literal1" runat="server" /> để khi chạy sẽ hiển thị dữ liệu. Để chạy thử hàm trong ví dụ trên bạn chỉ cần đưa nó lên hàm Page_Load: DataSetInsertAndView();


3.Tổng kết và So sánh giữa DataSet và DataReader

3.1. DataReader:

-Ưu điểm: Thực thi nhanh, ít tốn tài nguyên hơn so với DataSet rất nhiều
-Khuyết Điểm: Tùy biến không cao, thường dùng để lấy dữ liệu và trình bày, hạn chế chỉnh sửa CSDL

3.2. DataSet

- Ưu điểm: Tùy biến cao, có thể chỉnh sửa CSDL rất tiện lợi
-Khuyết điểm: Như đã trình bày, hơi tốn tài nguyên

Lời khuyên: Tùy vào từng trườn hợp, từng mục đích mà ta có thể quyết định chọn cái nào, tránh lạm dụng quá khả năng một cái, phải biết kết hợp hài hòa. Thông thường khi bạn xuất CSDL viết dưới dạng XML, sau đó thao tác trên XML thì bạn nên sử dụng DataReader để phát huy sức mạnh của nó.