Phần 5: Tạo báo cáo với C#


Chức năng báo cáo (Report) là chức năng yêu cầu cần có ở hầu hết các ứng dụng lập trình với cơ sở dữ liệu. Ưu điểm của báo cáo để cho người sử dụng có tổng kết nhanh tình hình kinh doanh, có thể in ấn và xuất ra các định dạng dữ liệu khác một cách dễ dàng. Các loại báo cáo thường gặp như báo cáo doanh thu theo tháng, theo quý hoặc theo năm, báo cáo hàng tồn kho …
Trong bài này chúng ta sẽ tiếp tục phần tạo báo cáo với Cơ sở dữ liệu đã trình bày ở phần 4.


Yêu cầu bài toán

Cho cơ sở dữ liệu QLBanHang có các bảng:

tblMatHang( MaSP nchar(5), TenSP nvarchar(30), NgaySX Date, NgayHH Date, DonVi nvarchar(10), DonGia float , GhiChu nvarchar(200))

tblNhaCC(MaNhaCC nchar(5), TenNhaCC nvarchar(50), DiaChi nvarchar(200), MaSoThue nvarchar(15), TaiKhoan nvarchar(15), DienThoai nvarchar(11))

tblHangNhap(MaSP nchar(5), MaNhaCC nchar(5), SoLuong int, DonGia float, SoHD nvarchar(10), NgayGH Date)

Tạo các báo cáo làm các chức năng sau:

  • Báo cáo thống kê các mặt hàng nhập trong ngày
  • Báo cáo danh sách các mặt hàng theo từng hoá đơn, cho phép nhập vào số hoá đơn.
  • Báo cáo 10 nhà cung cấp có số tiền nhập cao nhất

>Xem hướng dẫn tạo Database trong VisualStudio


Hướng dẫn tạo báo cáo

> Xem hướng dẫn chi tiết.
Trong hướng dẫn ở trên, chúng ta chỉ tạo báo cáo cho một bảng. Với báo cáo lấy dữ liệu từ nhiều bảng, để đơn giản chúng ta có thể tạo Stored Procedure rồi sau đó gọi tên Procedure đó trong code.
Ví dụ báo cáo thống kê các mặt hàng nhập trong ngày câu lệnh sẽ như sau:


CREATE PROCEDURE dbo.BCMHNhapTheoNgay
	@NgayGH DateTime
AS
	SELECT A.MaSP, TenSP, TenNhaCC,DiaChi,SoLuong,A.DonGia,SoHD
	FROM tblHangNhap as A, tblMatHang as B, tblNhaCC C
	WHERE A.MaSP = B.MaSP and A.MaNhaCC = C.MaNhaCC and NgayGH = @NgayGH

  • Hướng dẫn cài đặt control ReportViewer trong Visual Studio 2019

  • Xem video hướng dẫn tạo Stored Procedure

  • Xem video hướng dẫn tạo mẫu báo cáo

  • Xem video hướng dẫn code hiển thị báo cáo


Tham khảo thêm

Có thể bạn sẽ thích…

41 phản hồi

  1. nguyenthituoi viết:

    thầy ơi trong nhóm bọn em có 3 người chia phần mỗi bạn làm 1 form con làm thế nào để ghép bài ạ bọn e ghép toàn báo lỗi thôi ạ

    • Phan Tiến viết:

      Em gửi cho mình xem thông báo lỗi gì?
      Chú ý khi ghép bài, vì mỗi bạn làm một form khác nhau trong đó có kết nối tới CSDL thì khi copy code sang, các em phải chỉnh sửa lại chuỗi kết nối tới CSDL.
      Để copy ít bị lỗi hơn, em mở 2 project đồng thời rồi ở cửa sổ Solution Explorer, em copy form từ project này rồi paste tới project kia.

  2. nguyenthituoi viết:

    thầy ơi thầy cho em hỏi là trong phần from thống kế báo cáo của e có 2 lỗi thầy xem hộ e với ạ

    namespace BaiTapLon
    {
    public partial class frmBCMHnhap : Form
    {
    public frmBCMHnhap()
    {
    InitializeComponent();
    }

    private void frmBCMHnhap_Load(object sender, EventArgs e)
    {

    this.rpvBCMHNhap.RefreshReport();
    }

    private void button1_Click(object sender, EventArgs e)
    {
    SqlConnection con = new SqlConnection();
    con.ConnectionString = Properties.Settings.Default.QuanlibanhangConnectionString;
    SqlCommand cmd = new SqlCommand();
    cmd.CommandText = “BCMMHnhapTheoThang”;
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Connection = con;
    cmd.Parameters.Add(new SqlParameter(“@NgayGH”,dtpNgayGH.Value.Date));
    //khai báo dataset để lấy dữ liệu
    DataSet ds = new DataSet();
    SqlDataAdapter dap = new SqlDataAdapter(cmd);
    dap.Fill(ds);
    //thiết lập báo cáo
    rpvBCMHNhap.ProcessingMode = ProcessingMode.Local;
    rpvBCMHNhap.LocalReport.reportPath = “rptBCMHNhap.rdlc”;

    if (ds.Tables[0].Rows.Count > 0)

    {
    ReportDataSource rds = new ReportDataSource();
    rds.Name = “dsMaHangNhap”;
    rds.Value = ds.Tables[0];
    //gắn lên mẫu báo cáo
    rpvBCMHNhap.LocalReport.DataSources.Clear();
    rpvBCMHNhap.LocalReport.DataSources.Add(rds);
    rpvBCMHNhap.RefreshReport();

    }

    }
    }
    }

  3. nguyenthituoi viết:

    Error 1 The name ‘dtpNgayGH’ does not exist in the current context D:\SV\BaiTapLon\BaiTapLon\frmBCMHnhap.cs 37 59 BaiTapLon

    Error 2 ‘Microsoft.Reporting.WinForms.LocalReport’ does not contain a definition for ‘reportPath’ and no extension method ‘reportPath’ accepting a first argument of type ‘Microsoft.Reporting.WinForms.LocalReport’ could be found (are you missing a using directive or an assembly reference?) D:\SV\BaiTapLon\BaiTapLon\frmBCMHnhap.cs 44 37 BaiTapLon

    • Phan Tiến viết:

      Lỗi 1: dtpNgayGH là control DateTimePicker để nhập vào ngày tháng. E phải kéo vào form rồi đặt tên thuộc tính Name la dtpNgayGH
      Lỗi 2: em sử dụng thuộc tính không đúng. Tên thuộc tính là ReportPath

  4. duongminhtu viết:

    e thưa thầy e có làm một form đổi mật khẩu với code như sau:
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Data.SqlClient;

    namespace BTLQuanLyKho.View
    {
    public partial class frmDoiMK : Form
    {
    public static string pass;
    // chuỗi kết nối
    string strConnection = @”Data Source=USERNAM-VJEJT5S\DUONGMINHTU;Initial Catalog=QLKho;Integrated Security=True”;
    SqlConnection conn;
    SqlCommand cmd;
    //string sql = “SELECT * FROM [QLKho].[dbo].[DangNhap]”;

    public frmDoiMK()
    {
    InitializeComponent();
    }

    private void textBox2_TextChanged(object sender, EventArgs e)
    {

    }

    private Boolean kiemtrarong()
    {
    Boolean kq = false;
    if (txtMkcu.Text == “”)
    {
    MessageBox.Show(“Bạn chưa nhập mật khẩu cũ!”);
    txtMkcu.Focus();
    kq = true;
    }
    else if (txtMkmoi.Text == “”)
    {
    MessageBox.Show(“Bạn chưa nhập mật khẩu mới!”);
    txtMkmoi.Focus();
    kq = true;
    }
    else if (txtNhaplai.Text == “”)
    {
    MessageBox.Show(“Bạn chưa nhập lại mật khẩu mới!”);
    txtNhaplai.Focus();
    kq = true;
    }
    return kq;
    }

    private void btnDoimk_Click(object sender, EventArgs e)
    {
    if (kiemtrarong() == false)
    {
    if (txtMkcu.Text == frmDoiMK.pass)
    {
    if (txtMkmoi.Text == txtNhaplai.Text)
    {
    string matkhau = txtMkmoi.Text;
    string capnhat = “UPDATE DangNhap SET Password='” + matkhau + “‘ WHERE Username=’admin’;”;
    conn = new SqlConnection(strConnection);
    cmd = new SqlCommand(capnhat, conn);
    conn.Open();
    cmd.ExecuteNonQuery();
    MessageBox.Show(“Đổi mật khẩu thành công”, “Thông báo”);
    conn.Close();
    }
    else
    {
    MessageBox.Show(“Mật khẩu xác nhận không đúng vui lòng kiểm tra lại!”, “Thông báo”, MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);
    txtMkmoi.Text = “”;
    txtNhaplai.Text = “”;
    }
    }
    else
    {
    MessageBox.Show(“Mật khẩu hiện tại không đúng vui lòng kiểm tra lại!”);
    txtMkcu.Text = “”;
    txtMkmoi.Text = “”;
    txtNhaplai.Text = “”;
    }
    }
    }

    private void btnThoat_Click(object sender, EventArgs e)
    {
    this.Close();
    }

    private void frmDoiMK_Load(object sender, EventArgs e)
    {

    }

    }
    }
    nhưng e không biết cách làm để đổ data của pass cũ để so sánh với pass cũ mình nhập có giống nhau không nên nó toàn thông báo “mật khẩu hiện tại không đúng”
    thầy giúp e sửa code với ạ
    em xin cảm ơn

    • Phan Tiến viết:

      Theo code bạn gửi thì bạn thiết kế Form có 3 TextBox:
      – 1 TextBox cho nhập mật khẩu cũ
      – 1 TextBox cho nhập mật khẩu mới
      – 1 TextBox bắt người sử dụng nhập lại mật khẩu mới
      Nhưng câu lệnh if (txtMkcu.Text == frmDoiMK.pass) => Biến pass này em lấy dữ liệu ở đâu? Làm sao để so sánh được chứ nên luôn luôn nó nhày đến thông báo lỗi như của em thôi.

  5. Nguyễn Văn Đạo viết:

    ở chỗ đáp.Fill(đs); nó báo lỗi này em phải khắc phục như thế nào thưa thầy?
    An unhandled exception of type ‘System.Data.SqlClient.SqlException’ occurred in System.Data.dll

    Additional information: Procedure or function ‘BCPNTheoNgay’ expects parameter ‘@NgayNhap’, which was not supplied.

  6. Trần Anh Sơn viết:

    thầy cho em hỏi em có một database đã tạo bằng sql server thì làm thế nào để sử dụng nó như tỏng phần 4.4 của thầy

    • Phan Tiến viết:

      Em vào cửa sổ Server Explorer, kích vào biểu tượng có hình Database có phích cắm (Connect to Database), sau đó có cửa sổ Choose Data Source hiển thị ra => Chọn Microsoft SQL Server =>Rồi em điền các thông số như tên Server, User, Pass và tên Database của em vào là sẽ làm được giống như phần 4.4.

  7. Tiến Thành viết:

    thầy cho em hỏi, trong bảng lọc thống kê của em, em muốn thêm một thuộc tính lọc theo ngày (vd: tìm các hóa đơn từ ngày X đến ngày Y), em có viết một Store để lọc, nhưng khi truyền dữ liệu từ C# vào SQL thì không được nhận do type date của winform C# và SQL server không giống nhau. Thầy có thể hướng dẫn em cách chỉnh sửa không ạ

    • Phan Tiến viết:

      Gửi cho mình code store và phần code c# em truyền vào store.

      • Tiến Thành viết:

        Dạ đây là store
        create proc spTongSoHoaDon
        (
        @Ngay1 date,
        @Ngay2 date
        )
        as
        Begin
        select COUNT(MaHD)
        from HoaDon
        where Ngay between @Ngay1 and @Ngay2
        End

        còn phần này là code C#:
        public DataTable DemHoaDon_Ngay(string Ngay1, string Ngay2)
        {
        return db.ExecuteQueryDataSet(“exec spTongSoHoaDon ” + Ngay1 + “,” + Ngay2 + “”, CommandType.Text, null);
        }
        public DataTable ExecuteQueryDataSet(string strSQL, CommandType ct, params SqlParameter[] p)
        {
        if (conn.State == ConnectionState.Open)
        conn.Close();
        conn.Open();
        comm.CommandText = strSQL;
        comm.CommandType = ct;
        da = new SqlDataAdapter(comm);
        DataTable ds = new DataTable();
        da.Fill(ds);
        return ds;
        }

        • Phan Tiến viết:

          Đoạn code em viết rối quá.
          Em chỉ cần viết như sau:
          public DataTable ExecuteQueryDataSet(string sql, string Ngay1, string Ngay2)
          {
          if (conn.State == ConnectionState.Open)
          conn.Close();
          conn.Open();
          comm.CommandText = sql;
          comm.CommandType = CommandType.StoreProcedured;
          comm.Parameters.AddWithValue(“@Ngay1”,Ngay1);
          comm.Parameters.AddWithValue(“@Ngay2”,Ngay2);
          da = new SqlDataAdapter(comm);
          DataTable ds = new DataTable();
          da.Fill(ds);
          return ds;
          }

  8. Cuong viết:

    thầy ơi
    Cho em hỏi làm sao để thêm sản phẩm vào hoá đơn cùng lúc 2 bảng hoá đơn với chi tiết hoá đơn vậy thầy
    Code như thế nào v thầy
    em muốn chọn sảnphẩm sau đó thêm vào bên chi tiết hoá đơn gồm có MaSP,TenSP,Giá,SL,Thành Tiền.
    khi load lại hoá đơn có sao để k hiện lại hoá đơn cũ trên girlConTrol
    Em cảm ơn thầy

    • Phan Tiến viết:

      – Để thêm dữ liệu vào 2 bảng cùng một lúc, em viết 2 câu lệnh SQL insert. Khi kích nút thêm dữ liệu thì sẽ thêm truyền từng câu lệnh SQL vào và thực hiện ExcuteNonQuery() với từng câu lệnh SQL đó. Chú ý nếu 2 bảng em Insert có quan hệ cha – con (ví dụ Hoá Đơn và Hoá Đơn chi tiết) thì em Insert vào bảng Hoá Đơn trước rồi sau đó mới Insert vào bảng hoá đơn chi tiết.
      Ví dụ viết vào sự kiện btnThem_Click(….)
      SqlCommand cmd = new SqlCommand();
      cmd.CommandText = sql1;//Câu lệnh insert vào Hoá đơn
      cmd.Connection = con;//Đối tượng Connection
      cmd.ExecuteNonQuery();
      cmd.CommandText = sql2;//Câu lệnh insert vào Hoá đơn chi tiết
      cmd.Connection = con;//Đối tượng Connection
      cmd.ExecuteNonQuery();

  9. Phạm Tươi viết:

    thầy ơi, phần tblHangNhap thì của e chia làm tblChitietHDN và tblHoadonnhap thì phải làm sao thầy?

  10. Thắng Nguyễn viết:

    Thầy cho em hỏi là làm thế nào để form cho người dùng chọn tháng, năm và in ra báo cáo từng ngày trong tháng đó ạ? vì êm thấy datetimepicker chỉ cho phép chọn ngày, tháng, năm chứ ko cho chọn riêng tháng,năm!

  11. Trang viết:

    thầy ơi cho em hỏi muốn làm báo cáo mặt hàng bán chạy nhất trong tháng thì làm như thế nào ah, và em code như video hướng dẫn nhưng lúc chọn ngày thì không lọc ra ngày mình đã chọn mà sổ ra tất cả các ngày nhập hàng ah lỗi này sửa như thế nào ah.

  12. Dung viết:

    Thưa thầy khi đã đưa ra được báo cáo giống vậy mà em muốn xuất ra excel để in ra thì làm như thế nào à

  13. than viết:

    thầy ơi. e có 5 file pdf khác nhau, trong form e có danh sách 5 người. khi click vào người nào thì sẽ mở file pdf tương ứng người đó lên có được k thầy?

  14. trang mi viết:

    Thầy ơi làm sao để báo cáo danh sách mà dạng tìm theo năm ạ theo kiểu datetimepicker ạ

  15. Hiếu viết:

    Thưa thầy cho e hỏi e muốn tạo 1 stored procedures báo cáo hàng tồn kho từ ngày đến ngày khác gồm 2 bảng hàng nhập(MaMH, NgayNhap, ….) và hàng xuất (MaMH, NgayXuat, …) e có viết 1 stored nhưng ko hiện mong thầy giúp e chỉnh sửa.
    create proc dbo.HangTonKho
    @NgayNhap date,@NgayXuat date
    as
    begin
    select dbo.HangNhap.MaMH, dbo.HangNhap.TenMH,(dbo.HangNhap.SoLuong-dbo.HangXuat.SoLuong) as SLHangTon,GiaNhap
    from dbo.HangNhap, dbo.HangXuat
    where dbo.HangNhap.MaMH = dbo.HangXuat.MaMH and NgayNhap = @NgayXuat
    end

    • Phan Tiến viết:

      Của em báo lỗi gì? Mình thấy đang câu lệnh có vẻ đang bị nhầm phần điều kiện where. Em thử store này xem:

      
      create proc dbo.HangTonKho
      @TuNgay date,@DenNgay date
      as
      begin
      select dbo.HangNhap.MaMH, dbo.HangNhap.TenMH,(dbo.HangNhap.SoLuong-dbo.HangXuat.SoLuong) as SLHangTon,GiaNhap
      from dbo.HangNhap, dbo.HangXuat
      where dbo.HangNhap.MaMH = dbo.HangXuat.MaMH and dbo.HangXuat.NgayXuat between @TuNgay and @DenNgay
      end
      
      

      Nhưng em cần kiểm tra lại, vì phần tính tồn kho của em như thế này không đúng đâu nhé.

  16. Hiếu viết:

    Thưa thầy e xin lỗi làm phiền thầy. Em đã viết lại csdl nhưng khi chạy trên sql vs vs đều mất dữ liệu, mong thầy sửa giúp e.

    create proc dbo.HangTonKho
    @TuNgay date,@DenNgay date
    as
    begin

    SELECT A.MaMH,dbo.HangNhap.TenMH,dbo.NhaCC.TenNCC ,(TSLNhap – ISNULL(TSLXuat,0)) as SLHangTon,dbo.HangNhap.GiaNhap from
    (SELECT dbo.HangNhap.MaMH,SUM(SoLuong) as TSLNhap FROM dbo.HangNhap GROUP BY MaMH) as A full join
    (SELECT MaMH, SUM(SoLuong) as TSLXuat FROM DBO.HangXuat GROUP BY MaMH) as B
    on A.MaMH=b.MaMH left join dbo.HangNhap on B.MaMH = dbo.HangNhap.MaMH left join dbo.NhaCC on dbo.HangNhap.MaNCC = dbo.NhaCC.MaNCC
    left join dbo.HangXuat on dbo.HangNhap.MaMH = dbo.HangXuat.MaMH
    where dbo.HangXuat.NgayXuat between @TuNgay and @DenNgay
    end

    • Phan Tiến viết:

      Vì em chưa có kinh nghiệm nên không thể viết phát chạy được ngay đâu. Em nên mở dạng cửa sổ query ra và chạy thử xem ra kết quả hay không. Ví dụ em chạy:
      SELECT A.MaMH,dbo.HangNhap.TenMH,dbo.NhaCC.TenNCC ,(TSLNhap – ISNULL(TSLXuat,0)) as SLHangTon,dbo.HangNhap.GiaNhap from
      (SELECT dbo.HangNhap.MaMH,SUM(SoLuong) as TSLNhap FROM dbo.HangNhap GROUP BY MaMH) as A full join
      (SELECT MaMH, SUM(SoLuong) as TSLXuat FROM DBO.HangXuat GROUP BY MaMH) as B
      on A.MaMH=b.MaMH left join dbo.HangNhap on B.MaMH = dbo.HangNhap.MaMH left join dbo.NhaCC on dbo.HangNhap.MaNCC = dbo.NhaCC.MaNCC
      left join dbo.HangXuat on dbo.HangNhap.MaMH = dbo.HangXuat.MaMH
      where dbo.HangXuat.NgayXuat between “01/01/2020” and “06/01/2020”

      Khi chạy OK thì em mới thay vào phần Store Procedured của em.

      Có thể em cần chạy từng đoạn để test chắc chắn kết quả chạy đúng.

  17. Dương viết:

    thầy ơi em gặp lỗi này thì sửa sao dc ạ.
    an error occurred during local report processing. the report definition for report ‘BCTN’ has not been specified

  18. Phan Tiến viết:

    Store em vẫn khai báo: @NgayGH DateTime, cái control DateTimePiker cho chọn ngày tháng, em chọn Format => Long

  19. minh ho viết:

    thầy ơi e using Microsoft.Reporting.WinForms; mà cứ bị lỗi ở chỗ WinForms là sao thầy

      • minh hà viết:

        private void btReport_Click(object sender, EventArgs e)
        {
        SqlConnection con = new SqlConnection();
        con.ConnectionString = Properties.Settings.Default.TTConnectionString;
        SqlCommand cmd = new SqlCommand();
        cmd.CommandText = “ticket”;
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Connection = con;
        cmd.Parameters.Add(new SqlParameter(“@date”,dateTimePicker1.Value.Date));
        TTDataSet1 ds = new TTDataSet1();
        SqlDataAdapter dap = new SqlDataAdapter(cmd);
        dap.Fill(ds);
        reportViewer1.ProcessingMode = ProcessingMode.Local;
        reportViewer1.LocalReport.ReportPath = “Report1.rdlc”;
        if( ds.Tables[0].Rows.Count > 0)
        {
        ReportDataSource rsd = new ReportDataSource();
        rsd.Name = “DataSet1”;
        rsd.Value = ds.Tables[0];
        reportViewer1.LocalReport.DataSources.Clear();
        reportViewer1.LocalReport.DataSources.Add(rsd);
        reportViewer1.RefreshReport();
        }
        thầy ơi em làm như thầy nhưng với table k phải store thì nó báo lỗi tại Fill h mình sửa đâu ạ

  20. hmnam viết:

    Em chào a, e cài report như a hướng dẫn mà ko đc.
    vào project có phần cài report mà ở phần add lại ko tìm thấy.
    link ảnh của e, mong a hướng dẫn: https://www.mediafire.com/file/xnas64kpfcj6szy/b.png/file
    e dùng visual 2022

Trả lời

EnglishVietnamese