MySQL C# tutorial
About this tutorial
This is a C# tutorial for the MySQL database. It covers the basics of MySQL programming with C#. In this tutorial, we use the Connector/Net driver. This driver is based on the ADO.NET specification. The examples were created and tested on Ubuntu Linux.
If you need to refresh your knowledge of the C# language, there is a full C# tutorial on Fastread.aitechtonic.
About MySQL database
MySQL is a leading open source database management system. It is a multi user, multithreaded database management system. MySQL is especially popular on the web. It is one part of the very popular LAMP platform consisting of Linux, Apache, MySQL, and PHP. Currently MySQL is owned by Oracle. MySQL database is available on most important OS platforms. It runs on BSD Unix, Linux, Windows or Mac OS. Wikipedia and YouTube use MySQL. These sites manage millions of queries each day. MySQL comes in two versions: MySQL server system and MySQL embedded system.
Before we start
On Linux, we need to install several packages to execute the examples in this tutorial: libmysql6.1-cil, mysql-server, mysql-client. We also need to install C# compiler from the Mono project, either from a package or from sources.
The libmysql6.1-cil is the MySQL database connector for CLI. It is written in C# and is available for all CLI languages: C#, Visual Basic, Boo, and others.
$ ls /usr/lib/cli/MySql.Data-6.1/MySql.Data.dll
/usr/lib/cli/MySql.Data-6.1/MySql.Data.dll
From the technical point of view, we need a DLL. On an Ubuntu Linux, it was located under the above path. We need to know the path to the DLL library. To compile our examples.
If you do not already have MySQL installed, we must install it.
$ sudo apt-get install mysql-server
This command installs the MySQL server and various other packages. While installing the package, we are prompted to enter a password for the MySQL root account. For installing MySQL from sources, have a look at MySQL installation page.
$ service mysql status
mysql start/running, process 1238
We check if the MySQL server is running. If not, we need to start the server.
$ sudo -b /usr/local/mysql/bin/mysqld_safe
The above command starts MySQL server using the MySQL server startup script. The way how we start a MySQL server might be different. It depends whether we have installed MySQL from sources or from packages and also on the Linux distro. For further information consult MySQL first steps or your Linux distro information.
Next, we are going to create a new database user and a new database. We use the mysql client.
$ mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 30
Server version: 5.0.67-0ubuntu6 (Ubuntu)
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
+--------------------+
2 rows in set (0.00 sec)
We use the mysql monitor client application to connect to the server. We connect to the database using the root account. We show all available databases with the SHOW DATABASES statement.
mysql> CREATE DATABASE mydb;
Query OK, 1 row affected (0.02 sec)
We create a new mydb database. We will use this database throughout the tutorial.
mysql> CREATE USER user12@localhost IDENTIFIED BY '34klq*';
Query OK, 0 rows affected (0.00 sec)
mysql> USE mydb;
Database changed
mysql> GRANT ALL ON mydb.* to user12@localhost;
Query OK, 0 rows affected (0.00 sec)
mysql> quit;
Bye
We create a new database user. We grant all privileges to this user for all tables of the mydb database.
Definitions
ADO.NET is an important part of the .NET framework. It is a specification that unifies access to relational databases, XML files and other application data. A MySQL Connector/Net is an implementation of the ADO.NET specification for the MySQL database. It is a driver written in C# language and is available for all .NET languages.
The Connection, Command, DataReader, DataSet, and DataProvider are the core elements of the .NET data provider model. The Connection creates a connection to a specific data source. The Command object executes an SQL statement against a data source. The DataReader reads streams of data from a data source. The DataSet object is used for offline work with a mass of data. It is a disconnected data representation that can hold data from a variety of different sources. Both DataReader and DataSet are used to work with data; they are used under different circumstances. If we only need to read the results of a query, the DataReader is the better choice. If we need more extensive processing of data, or we want to bind a Winforms control to a database table, the DataSet is preferred.
MySQL version
If the following program runs OK, then we have everything installed OK. We check the version of the MySQL server.
using System;
using MySql.Data.MySqlClient;
public class Example
{
static void Main()
{
string cs = @"server=localhost;userid=user12;
password=34klq*;database=mydb";
MySqlConnection conn = null;
try
{
conn = new MySqlConnection(cs);
conn.Open();
Console.WriteLine("MySQL version : {0}", conn.ServerVersion);
} catch (MySqlException ex)
{
Console.WriteLine("Error: {0}", ex.ToString());
} finally
{
if (conn != null)
{
conn.Close();
}
}
}
}
We connect to the database and get some info about the MySQL server.
using MySql.Data.MySqlClient;
We import the elements of the MySQL data provider.
string cs = @"server=localhost;userid=user12;
password=34klq*;database=mydb";
This is the connection string. It is used by the data provider to establish a connection to the database. We specify the host name, user name, password and a database name.
conn = new MySqlConnection(cs);
A MySQLConnection object is created. This object is used to open a connection to a database.
conn.Open();
This line opens the database connection.
Console.WriteLine("MySQL version : {0}", conn.ServerVersion);
Here we print the version of MySQL using the ServerVersion property of the connection object.
} catch (MySqlException ex)
{
Console.WriteLine("Error: {0}", ex.ToString());
In case of an exception, we print the error message to the console.
} finally
{
if (conn != null)
{
conn.Close();
}
}
At the final step, we close the connection object.
$ dmcs -r:/usr/lib/cli/MySql.Data-6.1/MySql.Data.dll version.cs
We compile our example. A path to the MySQL connector DLL is provided.
$ ./version.exe
MySQL version : 5.5.9
This is the output of the program on my system.
A more complex program follows.
using System;
using MySql.Data.MySqlClient;
public class Example
{
static void Main()
{
string cs = @"server=localhost;userid=user12;
password=34klq*;database=mydb";
MySqlConnection conn = null;
try
{
conn = new MySqlConnection(cs);
conn.Open();
string stm = "SELECT VERSION()";
MySqlCommand cmd = new MySqlCommand(stm, conn);
string version = Convert.ToString(cmd.ExecuteScalar());
Console.WriteLine("MySQL version : {0}", version);
} catch (MySqlException ex)
{
Console.WriteLine("Error: {0}", ex.ToString());
} finally
{
if (conn != null)
{
conn.Close();
}
}
}
}
We check for the version of the MySQL database. This time using an SQL query.
string stm = "SELECT VERSION()";
This is the SQL SELECT statement. It returns the version of the database. The VERSION() is a built-in MySQL function.
MySqlCommand cmd = new MySqlCommand(stm, conn);
The MySqlCommand is an object, which is used to execute a query on the database. The parameters are the SQL statement and the connection object.
string version = Convert.ToString(cmd.ExecuteScalar());
There are queries which return only a scalar value. In our case, we want a simple string specifying the version of the database. The ExecuteScalar() is used in such situations. We avoid the overhead of using more complex objects.
$ ./version2.exe
MySQL version : 5.5.9
Same result as in the previous example.
Creating and populating tables
Next we are going to create database tables and fill them with data. These tables will be used throughout this tutorial.
DROP TABLE IF EXISTS Books, Authors;
CREATE TABLE IF NOT EXISTS Authors(Id INT PRIMARY KEY AUTO_INCREMENT,
Name VARCHAR(25)) ENGINE=INNODB;
INSERT INTO Authors(Id, Name) VALUES(1, 'Jack London');
INSERT INTO Authors(Id, Name) VALUES(2, 'Honore de Balzac');
INSERT INTO Authors(Id, Name) VALUES(3, 'Lion Feuchtwanger');
INSERT INTO Authors(Id, Name) VALUES(4, 'Emile Zola');
INSERT INTO Authors(Id, Name) VALUES(5, 'Truman Capote');
CREATE TABLE IF NOT EXISTS Books(Id INT PRIMARY KEY AUTO_INCREMENT,
AuthorId INT, Title VARCHAR(100),
FOREIGN KEY(AuthorId) REFERENCES Authors(Id) ON DELETE CASCADE)
ENGINE=INNODB;
INSERT INTO Books(Id, AuthorId, Title) VALUES(1, 1, 'Call of the Wild');
INSERT INTO Books(Id, AuthorId, Title) VALUES(2, 1, 'Martin Eden');
INSERT INTO Books(Id, AuthorId, Title) VALUES(3, 2, 'Old Goriot');
INSERT INTO Books(Id, AuthorId, Title) VALUES(4, 2, 'Cousin Bette');
INSERT INTO Books(Id, AuthorId, Title) VALUES(5, 3, 'Jew Suess');
INSERT INTO Books(Id, AuthorId, Title) VALUES(6, 4, 'Nana');
INSERT INTO Books(Id, AuthorId, Title) VALUES(7, 4, 'The Belly of Paris');
INSERT INTO Books(Id, AuthorId, Title) VALUES(8, 5, 'In Cold blood');
INSERT INTO Books(Id, AuthorId, Title) VALUES(9, 5, 'Breakfast at Tiffany');
We have a books.sql file. It creates two database tables: Authors and Books. The tables are of InnoDB type. InnoDB databases support foreign key constraints and transactions. We place a foreign key constraint on the AuthorId column of the Books table. We fill the tables with initial data.
mysql> source books.sql
Query OK, 0 rows affected (0.07 sec)
Query OK, 0 rows affected (0.12 sec)
Query OK, 1 row affected (0.04 sec)
...
We use the source command to execute the books.sql script.
Prepared statements
Now we will concern ourselves with prepared statements. When we write prepared statements, we use placeholders instead of directly writing the values into the statements. Prepared statements increase security and performance.
using System;
using MySql.Data.MySqlClient;
public class Example
{
static void Main()
{
string cs = @"server=localhost;userid=user12;
password=34klq*;database=mydb";
MySqlConnection conn = null;
try
{
conn = new MySqlConnection(cs);
conn.Open();
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = conn;
cmd.CommandText = "INSERT INTO Authors(Name) VALUES(@Name)";
cmd.Prepare();
cmd.Parameters.AddWithValue("@Name", "Trygve Gulbranssen");
cmd.ExecuteNonQuery();
} catch (MySqlException ex)
{
Console.WriteLine("Error: {0}", ex.ToString());
} finally
{
if (conn != null) {
conn.Close();
}
}
}
}
We add a new author to the Authors table. We use a parameterized command.
cmd.CommandText = "INSERT INTO Authors(Name) VALUES(@Name)";
cmd.Prepare();
Here we create a prepared statement. When we write prepared statements, we use placeholders instead of directly writing the values into the statements. Prepared statements are faster and guard against SQL injection attacks. The @Name is a placeholder, which is going to be filled later.
cmd.Parameters.AddWithValue("@Name", "Trygve Gulbranssen");
A value is bound to the placeholder.
cmd.ExecuteNonQuery();
The prepared statement is executed. We use the ExecuteNonQuery() method of the MySQLCommand object when we don't expect any data to be returned. This is when we create databases or execute INSERT, UPDATE, and DELETE statements.
$ ./prepared.exe
mysql> SELECT * FROM Authors;
+----+--------------------+
| Id | Name |
+----+--------------------+
| 1 | Jack London |
| 2 | Honore de Balzac |
| 3 | Lion Feuchtwanger |
| 4 | Emile Zola |
| 5 | Truman Capote |
| 6 | Trygve Gulbranssen |
+----+--------------------+
6 rows in set (0.00 sec)
We have a new author inserted into the table.
Retrieving data with MySqlDataReader
The MySqlDataReader is an object used to retrieve data from the database. It provides fast, forward-only, read-only access to query results. It is the most efficient way to retrieve data from tables.
using System;
using MySql.Data.MySqlClient;
public class Example
{
static void Main()
{
string cs = @"server=localhost;userid=user12;
password=34klq*;database=mydb";
MySqlConnection conn = null;
MySqlDataReader rdr = null;
try
{
conn = new MySqlConnection(cs);
conn.Open();
string stm = "SELECT * FROM Authors";
MySqlCommand cmd = new MySqlCommand(stm, conn);
rdr = cmd.ExecuteReader();
while (rdr.Read())
{
Console.WriteLine(rdr.GetInt32(0) + ": "
+ rdr.GetString(1));
}
} catch (MySqlException ex)
{
Console.WriteLine("Error: {0}", ex.ToString());
} finally
{
if (rdr != null)
{
rdr.Close();
}
if (conn != null)
{
conn.Close();
}
}
}
}
We get all authors from the Authors table and print them to the console.
reader = cmd.ExecuteReader();
To create a MySQLDataReader, we must call the ExecuteReader() method of the MySqlCommand object.
while (reader.Read())
{
Console.WriteLine(reader.GetInt32(0) + ": "
+ reader.GetString(1));
}
The Read() method advances the data reader to the next record. It returns true if there are more rows; otherwise false. We can retrieve the value using the array index notation, or use a specific method to access column values in their native data types. The latter is more efficient.
if (rdr != null)
{
rdr.Close();
}
Always call the Close() method of the reader when done reading.
$ ./retrieve.exe
1: Jack London
2: Honore de Balzac
3: Lion Feuchtwanger
4: Emile Zola
5: Truman Capote
6: Trygve Gulbranssen
This is the output of the example.
Column headers
Next we will show, how to print column headers with the data from the database table.
using System;
using MySql.Data.MySqlClient;
public class Example
{
static void Main()
{
string cs = @"server=localhost;userid=user12;
password=34klq*;database=mydb";
MySqlConnection conn = null;
MySqlDataReader rdr = null;
try
{
conn = new MySqlConnection(cs);
conn.Open();
string stm = @"SELECT Name, Title From Authors,
Books WHERE Authors.Id=Books.AuthorId";
MySqlCommand cmd = new MySqlCommand(stm, conn);
rdr = cmd.ExecuteReader();
Console.WriteLine("{0} {1}", rdr.GetName(0),
rdr.GetName(1).PadLeft(18));
while (rdr.Read())
{
Console.WriteLine(rdr.GetString(0).PadRight(18) +
rdr.GetString(1));
}
} catch (MySqlException ex)
{
Console.WriteLine("Error: {0}", ex.ToString());
} finally
{
if (rdr != null)
{
rdr.Close();
}
if (conn != null)
{
conn.Close();
}
}
}
}
In this program, we select authors from the Authors table and their books from the Books table.
string stm = @"SELECT Name, Title From Authors,
Books WHERE Authors.Id=Books.AuthorId";
This is the SQL statement which joins authors with their books.
reader = cmd.ExecuteReader();
We create a MySqlDataReader object.
Console.WriteLine("{0} {1}", reader.GetName(0),
reader.GetName(1).PadLeft(18));
We get the names of the columns with the GetName() method of the reader. The PadLeft() method returns a new string of a specified length in which the beginning of the current string is padded with spaces. We use this method to align strings properly.
while (reader.Read())
{
Console.WriteLine(reader.GetString(0).PadRight(18) +
reader.GetString(1));
}
We print the data that was returned by the SQL statement to the terminal.
$ ./headers.exe
Name Title
Jack London Call of the Wild
Jack London Martin Eden
Honore de Balzac Old Goriot
Honore de Balzac Cousin Bette
Lion Feuchtwanger Jew Suess
Emile Zola Nana
Emile Zola The Belly of Paris
Truman Capote In Cold blood
Truman Capote Breakfast at Tiffany
Ouput of the program.
DataSet & MySqlDataAdapter
A DataSet is a copy of the data and the relations among the data from the database tables. It is created in memory and used when extensive processing on data is needed or when we bind data tables to a Winforms control. When the processing is done, the changes are written to the data source. A MySqlDataAdapter is an intermediary between the DataSet and the data source. It populates a DataSet and resolves updates with the data source.
using System;
using System.Data;
using MySql.Data.MySqlClient;
public class Example
{
static void Main()
{
string cs = @"server=localhost;userid=user12;
password=34klq*;database=mydb";
MySqlConnection conn = null;
try
{
conn = new MySqlConnection(cs);
conn.Open();
string stm = "SELECT * FROM Authors";
MySqlDataAdapter da = new MySqlDataAdapter(stm, conn);
DataSet ds = new DataSet();
da.Fill(ds, "Authors");
DataTable dt = ds.Tables["Authors"];
dt.WriteXml("authors.xml");
foreach (DataRow row in dt.Rows)
{
foreach (DataColumn col in dt.Columns)
{
Console.WriteLine(row[col]);
}
Console.WriteLine("".PadLeft(20, '='));
}
} catch (MySqlException ex)
{
Console.WriteLine("Error: {0}", ex.ToString());
} finally
{
if (conn != null)
{
conn.Close();
}
}
}
}
We print the authors from the Authors table. We also save them in an XML file. This time, we use the MySqlDataAdapter and DataSet objects.
MySqlDataAdapter da = new MySqlDataAdapter(stm, conn);
A MySqlDataAdapter object is created. It takes an SQL statement and a connection as parameters.
DataSet ds = new DataSet();
da.Fill(ds, "Authors");
We create and fill the DataSet.
DataTable dt = ds.Tables["Authors"];
We get the table called "Authors". We have given a DataSet only one table, but it can contain multiple tables.
dt.WriteXml("authors.xml");
We write the data to an XML file.
foreach (DataRow row in dt.Rows)
{
foreach (DataColumn col in dt.Columns)
{
Console.WriteLine(row[col]);
}
Console.WriteLine("".PadLeft(20, '='));
}
We display the contents of the Authors table to the terminal. To traverse the data, we utilize the rows and columns of the DataTable object.
In the next example, we are going to bind a table to a Winforms DataGrid control.
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Data;
using MySql.Data.MySqlClient;
class MForm : Form
{
private DataGrid dg = null;
private MySqlConnection conn = null;
private MySqlDataAdapter da = null;
private DataSet ds = null;
public MForm()
{
this.Text = "DataGrid";
this.Size = new Size(350, 300);
this.InitUI();
this.InitData();
this.CenterToScreen();
}
void InitUI()
{
dg = new DataGrid();
dg.CaptionBackColor = System.Drawing.Color.White;
dg.CaptionForeColor = System.Drawing.Color.Black;
dg.CaptionText = "Authors";
dg.Location = new Point(8, 0);
dg.Size = new Size(350, 300);
dg.TabIndex = 0;
dg.Parent = this;
}
void InitData()
{
string cs = @"server=localhost;userid=user12;
password=34klq*;database=mydb";
string stm = "SELECT * FROM Authors";
try
{
conn = new MySqlConnection(cs);
conn.Open();
ds = new DataSet();
da = new MySqlDataAdapter(stm, conn);
da.Fill(ds, "Authors");
dg.DataSource = ds.Tables["Authors"];
} catch (MySqlException ex)
{
Console.WriteLine("Error: " + ex.ToString());
} finally
{
if (conn != null)
{
conn.Close();
}
}
}
}
class MApplication
{
public static void Main()
{
Application.Run(new MForm());
}
}
In this example, we bind a Authors table to a Winforms DataGrid control.
using System.Windows.Forms;
using System.Drawing;
These two namespaces are for the GUI.
this.InitUI();
this.InitData();
Inside the InitUI() method, we build the user interface. In the InitData() method, we connect to the database, retrieve the data into the DataSet and bind it to the DataGrid control.
dg = new DataGrid();
The DataGrid control is created.
string stm = "SELECT * FROM Authors";
We will display the data from the Authors table in the DataGrid control.
dg.DataSource = ds.Tables["Authors"];
We bind the DataSource property of the DataGrid control to the chosen table.
$ dmcs -r:/usr/lib/cli/MySql.Data-6.1/MySql.Data.dll -r:System.Windows.Forms.dll
-r:System.Drawing.dll -r:System.Data.dll dataadapter2.cs
To compile the example, we must include additional DLLs: the DLL for MySQL connector, for the Winforms, Drawing, and for the Data.
Figure: DataGrid
Transaction support
A transaction is an atomic unit of database operations against the data in one or more databases. The effects of all the SQL statements in a transaction can be either all committed to the database or all rolled back.
The MySQL database has different types of storage engines. The most common are the MyISAM and the InnoDB engines. There is a trade-off between data security and database speed. The MyISAM tables are faster to process and they do not support transactions. On the other hand, the InnoDB tables are more safe against the data loss. They support transactions. They are slower to process.
using System;
using MySql.Data.MySqlClient;
public class Example
{
static void Main()
{
string cs = @"server=localhost;userid=user12;
password=34klq*;database=mydb";
MySqlConnection conn = null;
MySqlTransaction tr = null;
try
{
conn = new MySqlConnection(cs);
conn.Open();
tr = conn.BeginTransaction();
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = conn;
cmd.Transaction = tr;
cmd.CommandText = "UPDATE Authors SET Name='Leo Tolstoy' WHERE Id=1";
cmd.ExecuteNonQuery();
cmd.CommandText = "UPDATE Books SET Title='War and Peace' WHERE Id=1";
cmd.ExecuteNonQuery();
cmd.CommandText = "UPDATE Books SET Titl='Anna Karenina' WHERE Id=2";
cmd.ExecuteNonQuery();
tr.Commit();
} catch (MySqlException ex)
{
try
{
tr.Rollback();
} catch (MySqlException ex1)
{
Console.WriteLine("Error: {0}", ex1.ToString());
}
Console.WriteLine("Error: {0}", ex.ToString());
} finally
{
if (conn != null)
{
conn.Close();
}
}
}
}
In this program, we want to change the name of the author on the first row of the Authors table. We must also change the books associated with this author. A good example where a transaction is necessary. If we change the author and do not change the author's books, the data is corrupted.
MySqlTransaction tr = null;
The MySqlTransaction is an object for working with transactions.
tr = conn.BeginTransaction();
We begin a transaction.
cmd.CommandText = "UPDATE Books SET Titl='Anna Karenina' WHERE Id=2";
cmd.ExecuteNonQuery();
The third SQL statement has an error. There is no 'Titl' column in the table.
tr.Commit();
If there is no exception, the transaction is committed.
try
{
tr.Rollback();
} catch (MySqlException ex1)
{
Console.WriteLine("Error: {0}", ex1.ToString());
}
In case of an exception, the transaction is rolled back. No changes are committed to the database. During the rollback there could be an Exception. We handle this in a separate try/catch statement.
$ ./transaction.exe
Error: MySql.Data.MySqlClient.MySqlException: Unknown column 'Titl' in 'field list'
at MySql.Data.MySqlClient.MySqlStream.ReadPacket () [0x00000]
at MySql.Data.MySqlClient.NativeDriver.ReadResult () [0x00000]
mysql> SELECT Name, Title From Authors, Books WHERE Authors.Id=Books.AuthorId;
+-------------------+----------------------+
| Name | Title |
+-------------------+----------------------+
| Jack London | Call of the Wild |
| Jack London | Martin Eden |
| Honore de Balzac | Old Goriot |
| Honore de Balzac | Cousin Bette |
| Lion Feuchtwanger | Jew Suess |
| Emile Zola | Nana |
| Emile Zola | The Belly of Paris |
| Truman Capote | In Cold blood |
| Truman Capote | Breakfast at Tiffany |
+-------------------+----------------------+
9 rows in set (0.00 sec)
An exception was thrown. The transaction was rolled back and no changes took place.
However, without a transaction, the data is not safe.
using System;
using MySql.Data.MySqlClient;
public class Example
{
static void Main()
{
string cs = @"server=localhost;userid=user12;
password=34klq*;database=mydb";
MySqlConnection conn = null;
try
{
conn = new MySqlConnection(cs);
conn.Open();
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = conn;
cmd.CommandText = "UPDATE Authors SET Name='Leo Tolstoy' WHERE Id=1";
cmd.ExecuteNonQuery();
cmd.CommandText = "UPDATE Books SET Title='War and Peace' WHERE Id=1";
cmd.ExecuteNonQuery();
cmd.CommandText = "UPDATE Books SET Titl='Anna Karenina' WHERE Id=2";
cmd.ExecuteNonQuery();
} catch (MySqlException ex)
{
Console.WriteLine("Error: {0}", ex.ToString());
} finally {
if (conn != null)
{
conn.Close();
}
}
}
}
We have the same example. This time, without the transaction support.
$ ./notransaction.exe
Error: MySql.Data.MySqlClient.MySqlException: Unknown column 'Titl' in 'field list'
at MySql.Data.MySqlClient.MySqlStream.ReadPacket () [0x00000]
at MySql.Data.MySqlClient.NativeDriver.ReadResult () [0x00000]
mysql> SELECT Name, Title From Authors, Books WHERE Authors.Id=Books.AuthorId;
+-------------------+----------------------+
| Name | Title |
+-------------------+----------------------+
| Leo Tolstoy | War and Peace |
| Leo Tolstoy | Martin Eden |
| Honore de Balzac | Old Goriot |
| Honore de Balzac | Cousin Bette |
| Lion Feuchtwanger | Jew Suess |
| Emile Zola | Nana |
| Emile Zola | The Belly of Paris |
| Truman Capote | In Cold blood |
| Truman Capote | Breakfast at Tiffany |
+-------------------+----------------------+
9 rows in set (0.00 sec)
An exception is thrown again. Leo Tolstoy did not write Martin Eden. The data is corrupted.