Lesson 29 · Database Introduction

Database Introduction

Connect VB 2026 to a real database using ADO.NET — understand the architecture, write your first SQL queries, load data into a DataGridView, and build a complete read/search application with SQLite or Microsoft Access.

Key Takeaway: VB 2026 accesses databases through ADO.NET — a layered stack of Connection → Command → DataAdapter → DataSet/DataTable. You write a SQL SELECT statement, fill a DataTable via a DataAdapter, and bind it to a DataGridView with one line: dgv.DataSource = dt. Always open the connection inside a Using block so it is closed and disposed even if an exception occurs. For school projects and simple apps, SQLite (zero installation, single file) is the easiest choice; Microsoft Access (.accdb) works well if students already have Office.
Connection
ADO.NET layer 1
Opens a channel to the database. SQLiteConnection / OleDbConnection. Use Using block.
Command
ADO.NET layer 2
Holds the SQL text and a reference to the Connection. ExecuteNonQuery / ExecuteScalar / ExecuteReader.
DataAdapter
ADO.NET layer 3
Bridges Command and DataTable. Fill(dt) runs SELECT and populates the table.
DataSet / DataTable
In-memory cache
Disconnected copy of query results. Rows and Columns collections. Bind to controls.
DataGridView
Control
Grid control. DataSource = dt auto-generates columns. SelectedRows, CellClick events.
SELECT
SQL
SELECT col1, col2 FROM table WHERE condition ORDER BY col. Read rows.
INSERT / UPDATE / DELETE
SQL
Modify data. Use parameterised queries (cmd.Parameters.AddWithValue) to prevent SQL injection.
Parameters
Security
cmd.Parameters.AddWithValue("@Name", txtName.Text) — never concatenate user input into SQL.

29.1 ADO.NET Architecture

ADO.NET uses a layered architecture: a Connection object opens the database, a Command object carries the SQL statement, a DataAdapter executes the query and fills an in-memory DataTable, and the DataTable is bound directly to the UI (DataGridView, ComboBox, etc.).

ADO.NET Data Access Architecture
VB Form / UI
DataGridView
DataSource = dt
DataTable / DataSet
In-memory cache
← Disconnected layer (no live DB connection needed)
DataAdapter
Fill(dt) / Update(dt)
↑↓
Command
SQL string
↑↓
Connection
Open / Close
Database
SQLite / Access / SQL Server

Choosing a Database for VB 2026 Projects

DatabaseConnection typeBest forNotes
SQLite (.db file)SQLiteConnection (NuGet)School / portable appsZero install, single file, .NET provider via System.Data.SQLite or Microsoft.Data.Sqlite
Microsoft Access (.accdb)OleDbConnectionOffice-integrated projectsBuilt-in OleDb provider, 64-bit requires ACE driver
SQL Server LocalDBSqlConnectionReal-world production appsInstalled with Visual Studio; full T-SQL, stored procs
SQL Server ExpressSqlConnectionTeam / networked projectsFree edition, up to 10 GB, network capable

29.2 SQL Primer — SELECT, WHERE, ORDER BY

SQL (Structured Query Language) is the language used to read and modify database tables. You only need five statements to build most VB applications: SELECT, INSERT, UPDATE, DELETE, and CREATE TABLE.

SQL — core statements
-- Read all columns from Students table
SELECT * FROM Students;

-- Read specific columns, filtered and sorted
SELECT StudentID, Name, Grade
FROM   Students
WHERE  Grade >= 70
ORDER BY Name ASC;

-- Pattern search with LIKE (% = wildcard)
SELECT * FROM Students WHERE Name LIKE 'A%';

-- Aggregate: count, average, max
SELECT COUNT(*), AVG(Grade), MAX(Grade) FROM Students;

-- Insert a new row
INSERT INTO Students (Name, Grade, Class)
VALUES ('Alice Wong', 88, '4A');

-- Update an existing row
UPDATE Students SET Grade = 92 WHERE StudentID = 5;

-- Delete a row
DELETE FROM Students WHERE StudentID = 5;

-- Create a table
CREATE TABLE IF NOT EXISTS Students (
    StudentID INTEGER PRIMARY KEY AUTOINCREMENT,
    Name      TEXT    NOT NULL,
    Grade     REAL    DEFAULT 0,
    Class     TEXT
);
Try It — Simulation 29.1: SQL Sandbox

An in-browser SQL sandbox with a pre-loaded Students table. Type or choose a query, click Execute, and see results instantly. Shows the equivalent VB ADO.NET code for each query.

SQL Sandbox — Students Table
Quick query:
SQL:

29.3 Connecting to SQLite in VB 2026

SQLite is the recommended beginner database — a single .db file, no server required. Add the NuGet package Microsoft.Data.Sqlite (or System.Data.SQLite), then use SqliteConnection.

Adding SQLite to a VB 2026 Project

In Visual Studio 2026: right-click project → Manage NuGet Packages → search Microsoft.Data.Sqlite → Install. Then add Imports Microsoft.Data.Sqlite at the top of your form file. The database file is created automatically the first time you open the connection.

SQLiteConnect.vb — Visual Basic 2026
Imports Microsoft.Data.Sqlite   ' NuGet: Microsoft.Data.Sqlite

' Connection string — database file in same folder as the exe
Private Const CONN_STR = "Data Source=students.db"

' --- Create the table if it doesn't exist yet ---
Private Sub CreateTable()
    Using conn As New SqliteConnection(CONN_STR)
        conn.Open()
        Using cmd As New SqliteCommand(
            "CREATE TABLE IF NOT EXISTS Students (" &
            "  StudentID INTEGER PRIMARY KEY AUTOINCREMENT," &
            "  Name TEXT NOT NULL," &
            "  Grade REAL DEFAULT 0," &
            "  Class TEXT" &
            ")", conn)
            cmd.ExecuteNonQuery()
        End Using
    End Using
End Sub

' --- Load all students into DataGridView ---
Private Sub LoadStudents()
    Dim dt As New DataTable()
    Using conn As New SqliteConnection(CONN_STR)
        conn.Open()
        Using da As New SqliteDataAdapter("SELECT * FROM Students ORDER BY Name", conn)
            da.Fill(dt)
        End Using
    End Using
    dgvStudents.DataSource = dt        ' auto-generates columns
    lblCount.Text = $"{dt.Rows.Count} records"
End Sub

' --- Search by name ---
Private Sub SearchStudents(nameFilter As String)
    Dim dt As New DataTable()
    Using conn As New SqliteConnection(CONN_STR)
        conn.Open()
        Dim sql = "SELECT * FROM Students WHERE Name LIKE @Filter ORDER BY Name"
        Using da As New SqliteDataAdapter(sql, conn)
            da.SelectCommand.Parameters.AddWithValue("@Filter", $"%{nameFilter}%")
            da.Fill(dt)
        End Using
    End Using
    dgvStudents.DataSource = dt
End Sub

' --- Form_Load: create table, load data ---
Private Sub Form_Load(...) Handles MyBase.Load
    CreateTable()
    LoadStudents()
End Sub
Never Concatenate User Input into SQL

Writing "SELECT * FROM Students WHERE Name = '" & txtName.Text & "'" is a SQL injection vulnerability. A user could type ' OR '1'='1 to retrieve all records, or worse. Always use parameterised queries: cmd.Parameters.AddWithValue("@Name", txtName.Text). The @Name placeholder is replaced safely — special characters are never interpreted as SQL.

29.4 Connecting to Microsoft Access (.accdb)

If your school uses Microsoft Office, connecting to an Access database file requires the OleDb provider. Note that on 64-bit Windows you must install the Microsoft Access Database Engine 2016 Redistributable (free from Microsoft) and compile your project as x86.

AccessConnect.vb — Visual Basic 2026
Imports System.Data.OleDb

' Connection string for .accdb (Access 2007–2016 format)
Private connStr As String =
    "Provider=Microsoft.ACE.OLEDB.12.0;" &
    "Data Source=C:\School\students.accdb"

' --- Load all records into DataGridView ---
Private Sub LoadData()
    Dim dt As New DataTable()
    Using conn As New OleDbConnection(connStr)
        conn.Open()
        Using da As New OleDbDataAdapter("SELECT * FROM Students", conn)
            da.Fill(dt)
        End Using
    End Using
    DataGridView1.DataSource = dt
End Sub

' --- Parameterised INSERT (OleDb uses ? placeholders, not @name) ---
Private Sub InsertStudent(name As String, grade As Double, cls As String)
    Using conn As New OleDbConnection(connStr)
        conn.Open()
        Dim sql = "INSERT INTO Students (Name, Grade, Class) VALUES (?, ?, ?)"
        Using cmd As New OleDbCommand(sql, conn)
            cmd.Parameters.AddWithValue("@Name",  name)
            cmd.Parameters.AddWithValue("@Grade", grade)
            cmd.Parameters.AddWithValue("@Class", cls)
            cmd.ExecuteNonQuery()
        End Using
    End Using
    LoadData()   ' refresh the grid
End Sub

' --- Get a single scalar value ---
Private Function GetStudentCount() As Integer
    Using conn As New OleDbConnection(connStr)
        conn.Open()
        Using cmd As New OleDbCommand("SELECT COUNT(*) FROM Students", conn)
            Return CInt(cmd.ExecuteScalar())
        End Using
    End Using
End Function
Try It — Simulation 29.2: DataGridView Data Binding

Simulates loading a DataTable from a database query and binding it to a DataGridView. Shows how da.Fill(dt)dgv.DataSource = dt populates the grid, and how filtering with WHERE narrows results.

DataGridView — dgvStudents.DataSource = dt
Search name:
Min grade:
Sort by:
0 records

29.5 Reading Rows with ExecuteReader

For large result sets where you only need to read through rows once (not bind to a grid), use a DataReader — it is faster and uses less memory than filling a DataTable, because it fetches one row at a time without loading everything into RAM.

DataReader.vb — Visual Basic 2026
' --- DataReader: fast forward-only read ---
Private Sub ListTopStudents()
    Using conn As New SqliteConnection(CONN_STR)
        conn.Open()
        Dim sql = "SELECT Name, Grade FROM Students WHERE Grade >= @Min ORDER BY Grade DESC"
        Using cmd As New SqliteCommand(sql, conn)
            cmd.Parameters.AddWithValue("@Min", 80)
            Using reader = cmd.ExecuteReader()
                lstResults.Items.Clear()
                While reader.Read()
                    Dim name  = reader.GetString(0)     ' column 0 = Name
                    Dim grade = reader.GetDouble(1)    ' column 1 = Grade
                    lstResults.Items.Add($"{name}: {grade}")
                End While
            End Using
        End Using
    End Using
End Sub

' --- Column access alternatives ---
reader.GetString(0)           ' by index (fast)
reader("Name").ToString()     ' by column name (convenient)
reader.IsDBNull(2)            ' check for NULL before reading

' --- ExecuteScalar: single value ---
Dim avg = CDbl(cmd.ExecuteScalar())    ' returns first column of first row

' --- ExecuteNonQuery: INSERT / UPDATE / DELETE ---
Dim rowsAffected = cmd.ExecuteNonQuery()   ' returns count of rows changed
Try It — Simulation 29.3: Connection → Command → Reader Pipeline

Step through the ADO.NET pipeline manually: open connection, run command, read rows one by one. The console shows each step, like an actual database session.

ADO.NET Pipeline — Step-by-Step
Database:
SQL:

29.6 INSERT, UPDATE, and DELETE

Data modification uses the same pattern as reading, but calls ExecuteNonQuery() instead of Fill(). Always use @parameter placeholders (SQLite / SQL Server) or ? (OleDb/Access) — never string-concatenate user input.

CRUD.vb — Visual Basic 2026
' --- INSERT ---
Private Sub AddStudent(name As String, grade As Double, cls As String)
    Using conn As New SqliteConnection(CONN_STR)
        conn.Open()
        Dim sql = "INSERT INTO Students (Name, Grade, Class) VALUES (@Name, @Grade, @Class)"
        Using cmd As New SqliteCommand(sql, conn)
            cmd.Parameters.AddWithValue("@Name",  name)
            cmd.Parameters.AddWithValue("@Grade", grade)
            cmd.Parameters.AddWithValue("@Class", cls)
            cmd.ExecuteNonQuery()
        End Using
    End Using
    LoadStudents()   ' refresh DataGridView
End Sub

' --- UPDATE ---
Private Sub UpdateGrade(studentId As Integer, newGrade As Double)
    Using conn As New SqliteConnection(CONN_STR)
        conn.Open()
        Dim sql = "UPDATE Students SET Grade = @Grade WHERE StudentID = @ID"
        Using cmd As New SqliteCommand(sql, conn)
            cmd.Parameters.AddWithValue("@Grade", newGrade)
            cmd.Parameters.AddWithValue("@ID",    studentId)
            Dim rows = cmd.ExecuteNonQuery()
            MessageBox.Show($"{rows} row(s) updated.")
        End Using
    End Using
    LoadStudents()
End Sub

' --- DELETE ---
Private Sub DeleteStudent(studentId As Integer)
    If MessageBox.Show("Delete this student?", "Confirm",
            MessageBoxButtons.YesNo) <> DialogResult.Yes Then Return
    Using conn As New SqliteConnection(CONN_STR)
        conn.Open()
        Using cmd As New SqliteCommand("DELETE FROM Students WHERE StudentID = @ID", conn)
            cmd.Parameters.AddWithValue("@ID", studentId)
            cmd.ExecuteNonQuery()
        End Using
    End Using
    LoadStudents()
End Sub

' --- Read selected row from DataGridView ---
Private Sub dgvStudents_CellClick(...) Handles dgvStudents.CellClick
    If dgvStudents.SelectedRows.Count = 0 Then Return
    Dim row   = dgvStudents.SelectedRows(0)
    Dim id    = CInt(row.Cells("StudentID").Value)
    Dim name  = row.Cells("Name").Value.ToString()
    Dim grade = CDbl(row.Cells("Grade").Value)
    txtName.Text  = name
    nudGrade.Value = grade
    _selectedId   = id   ' store for UPDATE/DELETE
End Sub
Try It — Simulation 29.4: Full CRUD Application

A complete student record manager — Add, Edit, Delete, and Search. Click a row to populate the form fields for editing. Shows the exact VB ADO.NET code for every operation.

Student Record Manager — Full CRUD
Name:
Grade:
Class:
Search:
IDNameGradeClass
Click a row to select for editing.

29.7 GitHub Copilot — Auto-complete Connection Setup

GitHub Copilot — SQLite Setup + DataGridView
You typed: ' Set up a SQLite database for a library app: Books table with BookID, Title, Author, Year, Available (boolean). Load into DataGridView, support search by title, mark a book as borrowed (Available = False).'
' Library app — Books table with borrow tracking.
Imports Microsoft.Data.Sqlite

Private Const DB = "Data Source=library.db"
Private _selectedBookId As Integer = -1

Private Sub EnsureSchema()
    Using conn As New SqliteConnection(DB)
        conn.Open()
        Using cmd As New SqliteCommand(
            "CREATE TABLE IF NOT EXISTS Books (" &
            "  BookID    INTEGER PRIMARY KEY AUTOINCREMENT," &
            "  Title     TEXT NOT NULL," &
            "  Author    TEXT," &
            "  Year      INTEGER," &
            "  Available INTEGER DEFAULT 1)", conn)   ' 1=Yes, 0=No
            cmd.ExecuteNonQuery()
        End Using
    End Using
End Sub

Private Sub LoadBooks(Optional filter As String = "")
    Dim dt  As New DataTable()
    Dim sql = "SELECT BookID, Title, Author, Year," &
              "  CASE Available WHEN 1 THEN 'Yes' ELSE 'Borrowed' END AS Available" &
              " FROM Books WHERE Title LIKE @F ORDER BY Title"
    Using conn As New SqliteConnection(DB)
        conn.Open()
        Using da As New SqliteDataAdapter(sql, conn)
            da.SelectCommand.Parameters.AddWithValue("@F", $"%{filter}%")
            da.Fill(dt)
        End Using
    End Using
    dgvBooks.DataSource = dt
    lblCount.Text = $"{dt.Rows.Count} books"
End Sub

Private Sub BorrowBook()
    If _selectedBookId < 0 Then Return
    Using conn As New SqliteConnection(DB)
        conn.Open()
        Using cmd As New SqliteCommand(
            "UPDATE Books SET Available = 0 WHERE BookID = @ID AND Available = 1", conn)
            cmd.Parameters.AddWithValue("@ID", _selectedBookId)
            Dim rows = cmd.ExecuteNonQuery()
            If rows = 0 Then MessageBox.Show("Book already borrowed.")
        End Using
    End Using
    LoadBooks(txtSearch.Text)
End Sub

Private Sub dgvBooks_CellClick(...) Handles dgvBooks.CellClick
    If dgvBooks.SelectedRows.Count > 0 Then
        _selectedBookId = CInt(dgvBooks.SelectedRows(0).Cells("BookID").Value)
    End If
End Sub

Private Sub txtSearch_TextChanged(...) Handles txtSearch.TextChanged
    LoadBooks(txtSearch.Text)    ' live search as user types
End Sub
Copilot Chat Prompts for This Lesson

Try these in the Copilot Chat panel:

  • "Write a helper function that returns a DataTable from any SELECT query — takes the SQL string and a Dictionary of parameter names/values, returns a populated DataTable"
  • "Generate INSERT statements to seed the Students table with 10 sample rows: realistic Malaysian student names, grades between 55 and 98, classes 4A through 5B"
  • "Add error handling to the LoadStudents Sub: wrap in Try-Catch, show a MessageBox with the SQLite error message, and log the exception to a text file"
  • "Write a backup function that copies the SQLite .db file to a timestamped filename in an /backups subfolder each time the application closes"

Lesson Summary

  • ADO.NET follows a layered pattern: Connection → Command → DataAdapter → DataTable → DataGridView. Fill the DataTable offline, then bind it with dgv.DataSource = dt.
  • Always wrap connections in Using blocks — they guarantee the connection is closed and disposed even if an exception is thrown.
  • SQLite (zero install, single .db file) is the easiest choice for school projects. Add the Microsoft.Data.Sqlite NuGet package and use SqliteConnection.
  • For Microsoft Access (.accdb) use OleDbConnection with the ACE provider. OleDb uses ? positional placeholders, not @name named parameters.
  • Never concatenate user input into SQL strings — use cmd.Parameters.AddWithValue("@Name", value) for every value that comes from a user control.
  • Use da.Fill(dt) + dgv.DataSource = dt to bind a query result to a grid. Use cmd.ExecuteNonQuery() for INSERT/UPDATE/DELETE. Use cmd.ExecuteScalar() for a single aggregate value (COUNT, AVG, MAX).
  • A DataReader (cmd.ExecuteReader()) reads rows one at a time — faster and more memory-efficient for large result sets you only need to scan once.

Exercises

Exercise 29.1 — Student Grade Viewer

  • Create a SQLite database school.db with a Students table (StudentID, Name, Grade, Class).
  • Seed it with 10 rows using INSERT statements. On Form_Load, call CreateTable() then LoadStudents() to populate a DataGridView.
  • Add a TextBox for name search and a NumericUpDown for minimum grade — filter on button click using parameterised WHERE Name LIKE @F AND Grade >= @Min.
  • Show a label: "X records found" after each load.
  • Copilot challenge: "Add a bar chart below the DataGridView that shows the grade distribution in 10-point buckets (0–9, 10–19, … 90–100) drawn with GDI+"

Exercise 29.2 — Contact Book

  • Database: contacts.db — Contacts table (ContactID, FullName, Phone, Email, Group: Friend/Work/Family).
  • DataGridView showing all contacts. Form fields (TextBox + ComboBox for Group) to Add, Update, and Delete.
  • Clicking a row fills the form for editing. Confirm delete with a MessageBox.
  • A ComboBox to filter by Group (show only "Work" contacts, etc.).
  • Copilot challenge: "Export the current DataGridView content to a CSV file: iterate dgv.Rows, build comma-separated strings, write to a .csv with StreamWriter"

Exercise 29.3 — Inventory Tracker

  • Database: inventory.db — Products table (ProductID, Name, Category, Quantity, Price).
  • DataGridView with search by Name and filter by Category (ComboBox auto-populated from SELECT DISTINCT Category).
  • Show total inventory value (SUM(Quantity * Price)) in a label using ExecuteScalar().
  • Highlight rows where Quantity < 5 in red using dgv.RowPrePaint event.
  • Copilot challenge: "Add a ReOrder button that generates a text report of all items with Quantity < 5, formatted as a purchase order with today's date, and saves it to reorder.txt"

Next: Lesson 30 — Connecting to a Database

Build a complete data-bound application — BindingSource, BindingNavigator, runtime column customisation, and live DataGridView editing with auto-save.

Continue »

Related Resources


Featured Books

Visual Basic 2022 Made Easy

Visual Basic 2022 Made Easy

by Dr. Liew Voon Kiong

Database chapters covering ADO.NET, DataGridView, and CRUD with Access and SQL Server.

View on Amazon →
VB Programming With Code Examples

VB Programming With Code Examples

by Dr. Liew Voon Kiong

Worked database programs: student records, inventory, and contact management.

View on Amazon →