Introduction
Most applications out there need to work with databases. Ask any big company's programmers and you'll hear them speak about how important databases are in the computing world. A programmer who can work with databases will be in a position to create a great many really useful applications.
You might have a database such as Microsoft Access on your computer. Alternatively, you could install Microsoft SQL Server Express Edition, which is a really nice way to learn about the SQL Server database, used in many of the largest companies around the globe. SQL Server Express is available as part of the Visual Basic Express installation, so you may already have it installed.
The System.Data classes in the .NET Framework allow you to work with databases. A database is quite different to things like pictures and word processor documents, which are often called unstructured. A database is more structured. It most often contains many rows of the same type of data, grouped into blocks called tables. The table contains one or more columns and each column holds a particular piece of information for that row.
Rows are sometimes called records and columns are sometimes called fields. |
Here is a representation of a database table that holds information about planets. The columns in this case are PlanetName, DistanceFromSun and Inhabitants.
PLANET | | |
PlanetName | DistanceFromSun | Inhabitants |
Mercury | 57909 | Mercurians |
Venus | 108200 | Venusians |
Earth | 149600 | Earthlings |
Mars | 227940 | Martians |
Jupiter | 778400 | Jupiterians |
Znock | 7208100 | Znockers |
Saturn | 1423600 | Saturnians |
Uranus | 2867000 | Uranians |
Neptune | 4488400 | Neptunians |
Pluto | 5909600 | Plutonians |
You can see, for example, that the planet Venus is 108 200 thousand kilometers from the sun and that the creatures living there are called Venusians.
Here's another table, this time showing how many creatures were found living on the planets each year.
This is top secret information never revealed before. It was retrieved from an alien craft that crashed in a remote part of the Gobi desert. You should feel privileged to have a copy. Apparently, they too use SQL Server Express databases, which made it easier for us to bundle a copy with the code samples.
POPULATION | | |
PlanetName | Year | Population |
Mercury | 2000 | 40000 |
Venus | 2000 | 25 |
Earth | 2000 | 6000000000 |
Mars | 2000 | 325000 |
Jupiter | 2000 | 8426300200 |
Znock | 2000 | 550000 |
Saturn | 2000 | 1000000 |
Uranus | 2000 | 753425370 |
Neptune | 2000 | <NULL> |
Pluto | 2000 | <NULL> |
Mercury | 2001 | 35000 |
Venus | 2001 | 3 |
Earth | 2001 | 6500000000 |
Mars | 2001 | 326000 |
Jupiter | 2001 | 8426300202 |
Znock | 2001 | 8700 |
Saturn | 2001 | 75000 |
Uranus | 2001 | 844360002 |
Neptune | 2001 | <NULL> |
Pluto | 2001 | <NULL> |
Looking at all the rows that refer to Venus, you'll notice there are two. You can see that in the year 2000 there were 25 Venusians on Venus, but in 2001 there were only 3 of them left. I guess the volcanoes wiped them out.
Don't confuse database tables with spreadsheets. While it's true that spreadsheets can show data in a way that looks like the tables above, the way they work with the data is quite different. |
The SQL Language
There are many different databases around the world: Microsoft Access, Oracle, DB2, Microsoft SQL Server, Informix, mySQL and so the list goes on. So how do you talk to a database from Visual Basic? Will they all understand what we're asking for?
The simple answer is that you use a language like Visual Basic to wrap up and send some "database language" to the database, and it decides how to fetch and send back the columns and rows that you asked for.
(To tell the truth, there is another layer in-between called ADO.NET, but we won't talk too much about that here)
Many years ago, because of all the different databases, some people got together and agreed on "one database language" that could speak to most of the databases around. That language is called Structured Query Language (SQL for short). Don’t confuse the SQL language with Microsoft’s product named SQL Server – most databases support the SQL language.
Before we talk about how to work with databases in Visual Basic, let's get familiar with the basics of the SQL language. Here follow some examples of statements written in SQL and what happens when you run them.
The three main types of actions are SELECT to view some data, INSERT to insert new data, and UPDATE to change existing data - we'll give examples of each of these.
Usually the way you write Select statements is the following:
SELECT <the Columns you want to see>
FROM <the appropriate Database Tables>
WHERE <some condition is true>
Brings back all rows and all columns from the table called PLANET.
(The star * means all columns)
PlanetName | DistanceFromSun | Inhabitants |
|---|
Mercury | 57909 | Mercurians |
Venus | 108200 | Venusians |
Earth | 149600 | Earthlings |
Mars | 227940 | Martians |
Jupiter | 778400 | Jupiterians |
Znock | 7208100 | Znockers |
Saturn | 1423600 | Saturnians |
Uranus | 2867000 | Uranians |
Neptune | 4488400 | Neptunians |
Pluto | 5909600 | Plutonians |
SELECT PlanetName, Inhabitants
FROM PLANET |
Brings back just the "PlanetName" and "Inhabitants" columns for all rows in the PLANET table.
PlanetName | Inhabitants |
|---|
Mercury | Mercurians |
Venus | Venusians |
Earth | Earthlings |
Mars | Martians |
Jupiter | Jupiterians |
Znock | Znockers |
Saturn | Saturnians |
Uranus | Uranians |
Neptune | Neptunians |
Pluto | Plutonians |
SELECT PlanetName, Inhabitants
FROM PLANET
WHERE PlanetName='Venus' |
Brings back just the "PlanetName" and "Inhabitants" columns for only those rows in the PLANET table which have a PlanetName of "Venus".
PlanetName | Inhabitants |
|---|
Venus | Venusians |
SELECT PlanetName
FROM POPULATION
WHERE Population<100000 |
Brings back the PlanetName and Population, from the POPULATION table, wherever the population column has a value less than 100000.
PlanetName | Population |
|---|
Mercury | 40000 |
Venus | 25 |
Neptune | <NULL> |
Pluto | <NULL> |
Mercury | 35000 |
Venus | 3 |
Saturn | 75000 |
Neptune | <NULL> |
Pluto | <NULL> |
Usually the way you write Insert statements is the following:
INSERT INTO <the Database Table you want to add rows to>
(<the Columns you want to add values into>)
INSERT INTO PLANET
(PlanetName, DistanceFromSun, Inhabitants)
VALUES
('Fluff', 23500000, 'Fluffies') |
Adds a new row to the PLANET table. This is actually a "silent" action - it doesn't bring back any rows to your Visual Basic program - but we show the table here so you get a picture of what's happened.
PLANET | | |
PlanetName | DistanceFromSun | Inhabitants |
Mercury | 57909 | Mercurians |
Venus | 108200 | Venusians |
Earth | 149600 | Earthlings |
Mars | 227940 | Martians |
Jupiter | 778400 | Jupiterians |
Znock | 7208100 | Znockers |
Saturn | 1423600 | Saturnians |
Uranus | 2867000 | Uranians |
Neptune | 4488400 | Neptunians |
Pluto | 5909600 | Plutonians |
Fluff | 23500000 | Fluffies |
Usually the way you write Update statements is the following:
UPDATE <the Database Table you want to change>
SET <Columns you want to change> = <new values>
UPDATE PLANET
SET PlanetName='Stuff', Inhabitants='Stuffies'
WHERE PlanetName='Fluff' |
Changes some of the values in the row which has a PlanetName "Fluff". We show the resulting table here, but in reality this is a "silent" action and will not bring back any rows to your Visual Basic program.
PLANET | | |
PlanetName | DistanceFromSun | Inhabitants |
Mercury | 57909 | Mercurians |
Venus | 108200 | Venusians |
Earth | 149600 | Earthlings |
Mars | 227940 | Martians |
Jupiter | 778400 | Jupiterians |
Znock | 7208100 | Znockers |
Saturn | 1423600 | Saturnians |
Uranus | 2867000 | Uranians |
Neptune | 4488400 | Neptunians |
Pluto | 5909600 | Plutonians |
Stuff | 23500000 | Stuffies |
Relationships and Joining
If you think about it, you will notice there is a relationship between the two tables PLANET and POPULATION above. They both have a column called "PlanetName." We say that the two tables are related on the column "PlanetName" - and that allows us to collect all the information for a particular planet
We could take, for example, all the rows that have to do with Venus, from both tables ...
PLANET | PlanetName | DistanceFromSun | Inhabitants | Venus | 108200 | Venusians |
| POPULATION | PlanetName | Year | Population | Venus | 2000 | 25 | Venus | 2001 | 3 |
|
and join them together into what appears to be one big table ...
SELECT *
FROM PLANET INNER JOIN POPULATION ON PLANET.PlanetName=POPULATION.planetName
WHERE PlanetName='Venus' |
PLANETS_AND_POPULATION | PlanetName | DistanceFromSun | Inhabitants | PlanetName | Year | Population | Venus | 108200 | Venusians | Venus | 2000 | 25 | Venus | 108200 | Venusians | Venus | 2001 | 3 |
|
Which Database are you Using?
There are reasons why programmers may want to do special things for special databases or situations. In the .NET environment, for example, there are several different ways to work with data. If you know you're using a Microsoft SQL Server database, for example, you can use special objects to send your SQL queries and because of that it will work really fast. But if you're talking to Microsoft Access, you can't use that special object.
The code differs slightly then, depending on whether you’re using SQL Server or not, and we’re not sure whether you are. So here’s what we’ve done:
The three database example programs in this section (12a, 13a and 14a) are written assuming you have got SQL Server Express installed (or one of the other SQL Server versions).
But in case you haven’t, we’ve also included, with the disk samples, a version of each that uses Microsoft Access. These are programs 12b, 13b and 14b, and they will run without needing any database setup at all.
We encourage you to install SQL Server Express at some stage though – it’s a much better database to program against. Additionally, SQL Server skills are more valued in the business world – so the sooner you get to know SQL Server the better. You can download it free from http://msdn.microsoft.com/vstudio/express/sql/download/.
If you have Microsoft SQL Server Express installed and working, use examples 12a, 13a and 14a. If you don’t, or if you have trouble getting them working, you can fall back to examples 12b, 13b and 14b instead, which do the same thing without needing a database installed. |
Talking to a Database from Visual Basic
In the following Visual Basic examples, we'll use the SqlConnection and SqlCommand classes to communicate with the SQL Server Express sample database named "Planets". These are the special classes for talking to any version of Microsoft SQL Server. We'll work with the data further in two different ways
Using the SqlDataReader class.
The SqlDataReader class allows you a lot of programming control since you can step through each data row yourself and choose what to do with the values you get back.
Using the SqlDataAdapter and DataSet classes.
Datasets are useful if you wish to have the data rows automatically displayed in a forms control such as a datagrid. This approach requires quite a few lines of code to get the data from the database, but saves a lot of trouble in displaying that same data - because smart controls like the DataGridView understand how to hook themselves up to a dataset.
Example Program 12
The following program connects to a SQL Server Express database and sends it a SQL query. It then gets the results back, steps through each row and writes each PlanetName value on a new line in a Label control.
Code for program 12a (SQL Express Version – see disk example 12b for Microsoft Access version) |
| Imports System
Imports System.Windows.Forms
Imports System.Data
Imports System.Data.SqlClient ' Namespace for working with SQL Server data
Class SimpleDataAccess
Inherits Form
Public Sub New()
' Set the window title
Me.Text = "A simple databasing program"
' Determine the physical path to the PLANETS sample database
Dim dbLocation As String = _
System.IO.Path.GetFullPath("../../../database/SqlServer/planets.mdf")
' Add a label that fills the form
Dim label1 As Label = New Label()
label1.Dock = DockStyle.Fill
Me.Controls.Add(label1)
' Connect to the SQL Server database
Dim connection1 As SqlConnection = New SqlConnection( _
"data source=.\SQLEXPRESS;" _
& "User Instance=true;Integrated Security=SSPI;AttachDBFilename=" _
& dbLocation)
connection1.Open()
' Talk to the database - ask it for data on planets
Dim sql As String = "SELECT * FROM PLANET"
Dim command1 As SqlCommand = New SqlCommand(sql, connection1)
Dim dataReader1 As SqlDataReader = command1.ExecuteReader()
' Loop through the records returned and add each planet name to the label
While dataReader1.Read()
label1.Text = _
label1.Text & dataReader1("PlanetName").ToString() & Environment.NewLine
End While
' Clean up
dataReader1.Close()
connection1.Close()
End Sub
Shared Sub Main()
' Start a new instance of a forms application, using our class above
Application.Run(New SimpleDataAccess())
End Sub
End Class |
|
Example Program 13
In this next program we want to display several columns of data, which would be too messy in a Label - so we use a DataGridView.
We execute the same query as the previous program but this time put the results into a DataSet. We then hook the DataGridView to the DataSet and it automatically displays all the data.
Hooking up some invisible back-end data to a visual control is referred to as data binding.
Code for program 13a (SQL Express version – see disk example 13b for Microsoft Access version) |
| Imports System.Windows.Forms
Imports System.Data
Imports System.Data.SqlClient ' Namespace for working with SQL Server databases
Class DataInGrid
Inherits Form
Public Sub New()
' Set the window title
Me.Text = "One-Way Database Grid Binding"
' Determine the physical path to the PLANETS sample database
Dim dbLocation As String = _
System.IO.Path.GetFullPath("../../../database/SqlServer/planets.mdf")
' Add a DataGridView to the form
Dim DataGridView1 As DataGridView = New DataGridView()
DataGridView1.Width = Me.Width
DataGridView1.Height = 250
DataGridView1.DataMember = "Table"
DataGridView1.Dock = DockStyle.Fill
Me.Controls.Add(DataGridView1)
' Connect to the SQL Server database
Dim connection1 As SqlConnection = New SqlConnection( _
"data source=.\SQLEXPRESS;" _
& "User Instance=true;Integrated Security=SSPI;AttachDBFilename=" _
& dbLocation)
connection1.Open()
' The DataSet will hold the data in memory (in structures called DataTables)
Dim dataSet1 As DataSet = New DataSet()
' The DataAdapter will be the bridge between the database and the dataset
Dim sqlDataAdapter1 As SqlDataAdapter = New SqlDataAdapter()
' Tell the DataAdapter what we want it to fetch, and where from
sqlDataAdapter1.SelectCommand = New SqlCommand("SELECT * FROM PLANET", connection1)
' Fill the in-memory DataSet with the data now
sqlDataAdapter1.Fill(dataSet1)
' Hook the DataGridView (the visual grid) to the in-memory data
DataGridView1.DataSource = dataSet1
' Close the database connection
connection1.Close()
End Sub
Shared Sub Main()
' Start a new instance of a forms application, using our class above
Application.Run(New DataInGrid())
End Sub
End Class |
|
Example Program 14
Displaying data in a DataGridView is okay, but you’ll notice that if you change the data, it does not get saved back into the database. So let’s modify the approach to allow "two-way data binding".
We’ll cheat a little bit here (hey, it’s called "increasing our productivity") by not writing our own UPDATE and INSERT SQL commands – the System.Data namespace has a smart little class called CommandBuilder that can figure out how to write them itself and handles them behind the scenes.
Code for program 14a (SQL Express version – see disk example 14b for Microsoft Access version) |
| Imports System.Windows.Forms
Imports System.Data
Imports System.Data.SqlClient ' Namespace for working with SQL Server databases
Class PlanetsForm
Inherits Form
' Declare some objects we'll be talking to from different methods
Private dg As DataGridView
Private da As SqlDataAdapter
Public Sub New()
' This is the "constructor" method for the PlanetsForm class
' Set the window title
Me.Text = "Two-way Database Grid Binding"
' Determine the physical path to the PLANETS sample database
Dim dbLocation As String = _
System.IO.Path.GetFullPath("../../../database/SqlServer/planets.mdf")
' Prepare to connect to the SQL Server database
Dim connectionString As String = "data source=.\SQLEXPRESS;" _
& "User Instance=true;Integrated Security=SSPI;AttachDBFilename=" _
& dbLocation
' Add a "Save" button to the form
Dim btnSave As Button = New Button()
btnSave.Text = "Save"
AddHandler btnSave.Click, AddressOf BtnSave_Click
btnSave.Dock = DockStyle.Top
Me.Controls.Add(btnSave)
' Add a DataGridView to the form
dg = New DataGridView()
dg.Width = Me.Width
dg.Height = 250
dg.Dock = DockStyle.Fill
Me.Controls.Add(dg)
' Instantiate a few objects that are smart with data and use them
' together to "bind" the DataGridView to the back-end data we want
' DataAdapter will act as bridge between database and in-memory DataTable
da = New SqlDataAdapter("SELECT * FROM PLANET", connectionString)
' CommandBuilder will handle UPDATE and INSERT automatically
Dim cb As SqlCommandBuilder = New SqlCommandBuilder(da)
' DataTable will keep track, in memory, of changes
Dim dt As DataTable = New DataTable()
' Fill the DataTable with the data now
da.Fill(dt)
' Link the DataTable to the DataGridView now
dg.DataSource = dt
End Sub
Shared Sub Main()
' Start a new instance of a forms application, using our class above
Application.Run(New PlanetsForm())
End Sub
Private Sub BtnSave_Click(ByVal sender As Object, ByVal e As System.EventArgs)
' Since the save button was clicked, update the database with
' any changes made to the DataGridView's underlying source, which
' in this case is a DataTable.
da.Update(DirectCast(dg.DataSource, DataTable))
MessageBox.Show("Data has been saved", "For your information", MessageBoxButtons.OK)
End Sub
End Class |
|
Try modifying some values and entering new ones. Click "Save" and then close the form. If you re-run the program you will see that the data really has been updated/inserted in the database.
What If I’m Not Using SQL Server Express?
Those of you who do not have a version of Microsoft SQL Server installed, and have a different database you wish to talk to, will need to make a few small adjustments.
First of all, the connection string describing the database location, type, etc. must change.
A connection string for SQL Server Express may look like this:
| Dim connectionString As String = _
"Integrated Security=SSPI;Persist Security Info=False; Initial
Catalog=Northwind;Data Source=localhost" |
or like this, if you’re connecting directly to the database file (as in this book’s examples):
| Dim connectionString As String = _
"data source=.\SQLEXPRESS;Integrated Security=SSPI;
AttachDBFilename=c:\Visual
Basic4#KIDS\examples\database\SqlServer\planets.mdf;
User Instance=true" |
(Because of space we’re wrapping this around to a few lines, but in this format the portion in quotes actually needs to be on one line)
A connection string for Microsoft Access could look like this:
| Dim connectionString As String = _
"Provider=Microsoft.Jet.OLEDB.4.0; Data Source c:\Visual
Basic4#KIDS\examples\database\Access\planets.mdb;" |
(as explained higher above, you would need to write this on one line)
Other databases will each have a particular format. You may find examples in the Visual Basic Express help documentation or in the documentation that came with your database.
Beyond changing the connection string, you then also change the "Sql" classes to "OleDb" classes.
Start by including System.Data.OleDb namespace instead of the System.Data. SqlClient namespace. This contains classes that can work with a variety of databases.
Imports System.Data.OleDb
Then, swap the classes you use to work with data, as follows:
SQL Server | General Databases |
SqlCommand | OleDbCommand |
SqlCommandBuilder | OleDbCommandBuilder |
SqlDataAdapter | OleDbDataAdapter |
SqlConnection | OleDbConnection |