Pääsivulle | TIETOTEKNIIKAN PÄÄSIVULLE
SQLite
Windows-ympäristössä
Vaihtoehtoiset asennustavat Visual Studioon
Vaihtoehtoiset tavat järjestää SQLite-tietokannat käytettäväksi
Visual Studio -ohjelmoinnissa kehityskoneeseen:
a) "Laaja pakettien asennus" eli asennetaan
sqlite-netFx46-setup-bundle-x86-2015-1.0.107.0.exe
Tämä asentaa Ohjelmat-kansioon
tarpeelliset dll-tiedostot
b) Asennetaan vain muutamat kirjastot, eli "Vähimmäisasennus",
esimerkiksi:
sqlite-netFx46-binary-Win32-2015-1.0.107.0.zip
tämä puretaan Visual Studion kansioon
esimerkiksi Projects \ External_105
tämä on suositeltavin tapa useimmissa
tapauksissa
c) Haetaan SQLite-kirjastot NuGet-pakettina:
- Solution Explorer oikeassa
laidassa, valitse hiiren oikealla näppäimellä projektisi nimi
- Tulee valikko, josta valitse
'Manage NuGet Package'
- Avautuu luettelo jossa on
yläreunassa mm. 'Installed'
- Valitse 'Browse' ja aseta
sopiva hakuehto
Sieltä löytyy mm.
System.Data.SQLite
Edellä on mainittu kaksi erilaista asennuspakettia. Niitä löytyy täältä.
"setup-bundle" tarkoittaa laajaa asennuspakettia käyttöjärjestelmän
käytettäväksi
"binary" tarkoittaa vähimmäisasennuksen pakettia
"netFx46" viittaa dotnet-Frameworkin versioon
"x86" ja "Win32" tarkoittavat 32-bittistä käyttöjärjestelmää, "x64"
tarkoittaa 64-bittistä
"1.0.107.0" jne tarkoittaa SQLite:n versionumeroa, normaalisti
valitse uusin
Vähimmäisasennus
1. Lataa edellämainitusta latauspaikasta zip-paketti. Lue huolella
ao.sivulla minkä paketin sieltä lataat. Huomaa, että 32-bittinen
paketti toimii myös 64-bittisessä kehityskoneessa ja loppukäyttäjän
koneessa. Itse olen käyttänyt 32-bittistä pakettia ja dotnet
Framework 4.6 löytyy Windows 8.1- ja Windows 10-koneista (on siellä
uudempikin).
2. Tee Visual Studion Projects-kansioon uusi kansio esim.
Externals_107 lataamasi SQLite-version mukaisesti. Pura zip-paketti
sinne. Käynnistä Visual Studio ja avaa projekti missä tarvitset
SQLitea.
3. Jos projektisi käytössä on aiempi SQLite-versio, niin poista
projektilta sen Reference (katso kuva seuraavassa kohdassa)
4. Lisää Reference lataamaasi SQLite-pakettiin (katso oheinen kuva).
Hiiren oikealla painikkeella klikkaa nimeä References, seuraavaksi
Add reference -> Browse ja mene kansioon johon juuri purit
zip-paketin. Sieltä valitse "System.Data.SQLite.dll"

Lopputuloksena Reference-ryhmässä on viittaus paikoillaan:

Varmista, että Properties-ikkunassa on asetuksena Copy Local = True:

Projektisi ominaisuuksiin pitää asettaa lataamasi paketin tyyppi,
menusta esimerkiksi Project Properties -> Build -> Platform
target = x86
Käynnistä Visual Studio uudestaan ja avaa projektisi. Nyt SQLite on
käytössä. Avaa projektisi bin\ Debug-kansio. Sieltä löytyy
SQLite-paketin tiedostoja. Sinne pitää itse kopioida kaksi tiedostoa
sieltä minne alussa avasit zip-paketin:

Loppukäyttäjän koneeseen
Loppukäyttäjän koneeseen riittää asentaa seuraavat kaksi
dll-tiedostoa samaan kansioon sovellustiedoston ja muiden ehkä
tarvittavien dll-tiedostojen kanssa:
- System.Data.SQLite.dll
- SQLite.Interop.dll
Jos teet sovelluksestasi lopulta asennuspaketin, testaa, että em.
tiedostot todella tulevat mukaan.
TIETOKANNAN KÄSITTELY KOMENTORIVILLÄ
Täältä
löytyvät työkalut, joilla voit tutkia ja käsitellä
tietokantatiedostoa komentokehotteessa (ns. dos-ikkunassa).
Erityisen hyödyllinen on sqlite3.exe, jolla voi testata
ohjelmakoodiin sijoitettavia SQL-lausekkeita.
Tällä hetkellä (11.2.2018) uusin versio työkalupaketista on:
sqlite-tools-win32-x86-3220000.zip
Työkalut ovat komentorivillä käytettäviä sqlite-apusovelluksia,
jotka ovat samaa versiota kuin SQLite-paketit 1.0.107.0
sqlite3.exe
SQLite-tietokannan katselu ja muokkaus SQL-komennoilla. Kopioi
tutkittava tietokanta samaan kansioon sqlite3.exe:n kanssa.
Ohjeet täällä
sqldiff.exe
Kertoo kahden sqlite-tietokannan eroavaisuudet ym. Ohjeet täällä
sqlite3_analyzer
Kertoo lukumääriä ja kokoja tietokantatiedoston sisällöstä varsin
teknisesti
Ohjeet täällä
TIETOKANNAN KÄYTTÄMINEN KOODISSA
Tietokannan avaaminen
Voit vapaasti valita tietokantatiedoston tiedostopäätteen. Olen itse
käyttänyt päätettä 'db'
Yhteys tietokantaan, tietokannan avaaminen ja sulkeminen:
// yhteys
tietokantaan
SQLiteConnection conlite = null;
// polkuz on tietokantatiedoston täysi polku
conlite = new SQLiteConnection($"Data
Source={polkuz};Version=3;");
conlite.Open();
// yhteyden sulkeminen:
conlite.Close();
conlite = null;
Tai jos haluat käyttää connection stringiä erillisenä:
SQLiteConnection
conlite = new SQLiteConnection();
string constr = @"Data
Source=E:\Herbaario\Kuvat\Herbaario_testi.db;Version=3;";
conlite.ConnectionString = constr;
Omissa sovelluksissani olen käyttänyt ini-tiedostoa, jonka sisällön
sovellus lukee aluksi. Siellä on määritelty ympäristökohtaisia
tietoja, kuten juuri tietokantatiedoston nimi ja sijainti. Kun
sovellus siirretään toiselle tietokoneelle, muokataan ini-tiedosto
uutta ympäristöä vastaavaksi.
Tietokantayhteyden avaaminen ja sulkeminen vie paljon aikaa!
Suunnittele sovellus niin, ettei turhaan välillä suljeta!
Näin saat sovelluksen sisällä helposti tietokannan polun, kun
tietokantayhteys on muodostettu:
string kantapolkuz =
conlite.FileName;
Luetaan
tietokannasta tietoja
// Nyt on jo
yhteys conlite avattu
int tulos = 0; // miten lukeminen onnistui
SQLiteCommand kome = null;
SQLiteDataReader rdr = null;
string sqz = "SELECT idKuva, Aihe FROM Kuvat";
try
{
kome = new SQLiteCommand(sqz, conlite);
rdr = kome.ExecuteReader();
while (rdr.Read())
{
int ida = (rdr.IsDBNull(0)) ? -1 :
rdr.GetInt32(0);
string aihez = (rdr.IsDBNull(1)) ?
"" : rdr.GetString(1);
// tee tiedoilla jotain
}
}
catch (Exception ex)
{
string msgz = $"{sqz}\nTietokannan tai lukijan
käsittelyssä tuli ongelma."
string z = $"{msgz}\n{ex.GetType()}:{ex.Message}";
MessageBox.Show(z, "(H03-07)");
tulos = -1;
}
finally
{
if (rdr != null)
rdr.Close();
if (kome != null)
kome.Dispose();
}
if (tulos == -1)
{ // toimenpiteet kun ongelma
}
Joskus allaoleva rakenne on toimiva ja kompakti. Ei tarvitse
huolehtia resurssien sulkemisesta. Virhetilanteen pyydystäminen ei
aina onnistu reader-luupista:
using
(SQLiteCommand kome = new SQLiteCommand(sqz, conlite))
{
using (SQLiteDataReader rdr = kome.ExecuteReader())
{
while (rdr.Read())
{
....
}
}
}
Jos tiedät lukevasi yhden integerin tietokannasta, niin
ExecuteScalar() on usein käytännöllinen, esimerkiksi:
int tulos =
0; // haun tulos
string sqz = "SELECT count(*) AS Kpl FROM Kasvit"; // aina
löytyy tulos
// string sqz = "SELECT iarvo FROM Lajit WHERE ryhma = 7";
// ei tulosta, ryhmää 7 ei ole
SQLiteCommand kome = new SQLiteCommand(conlite, sqz);
object arvo = kome.ExecuteScalar();
if (arvo == DBNull.Value || arvo == null)
tulos = -999; // ei hakutulosta (sovittu arvo
sovelluksessa)
else
tulos = Convert.ToInt32(arvo);
Kirjoitetaan tietokantaan tietoja
Kirjoitetaan tietokantaan uusi rivi (INSERT), tehdään ensin
SQL-lauseke
string sqz =
"INSERT INTO Kuvat (Otsake, Koko, Lati, Longi) ";
sqz += "VALUES('Lomalla', 2239, '62.30987', '22.8765')";
Usein kannattaa käyttää StringBuilderia.
Päivitetään tietokannan aiempaa riviä, tehdään ensin SQL-lauseke
string sqz =
"UPDATE Kuvat SET Paikka = 'Mikkeli', Koko = 1902 WHERE idKuva =
2944";
Ja itse tietokannan päivitys. Tämä kannattaa usein tehdä koodissa
omaksi funktiokseen
SQLiteCommand
kome = null;
int itu = -2; // tulos
try
{
kome = new SQLiteCommand(sqz, conlite);
itu = kome.ExecuteNonQuery();
if (sqz.StartsWith("DROP", true, null) ||
sqz.StartsWith("CREATE", true, null)) // ignore case
itu = 0;
}
catch (Exception ex)
{
MessageBox.Show($"Kysely
epäonnistui:\n{sqz}\n{ex.Message}", "(M09-03)");
itu = -2;
}
finally
{
if (kome != null)
kome.Dispose();
}
if (itu == -2) // tarkoittaa että tapahtui jokin
virhe
{
MessageBox.Show($"Kysely epäonnistui:\n{sqz}",
"(M09-04)");
}
// itu = -2 jokin virhe tapahtui (oma vakio)
// itu = kpl muutetut rivit jos UPDATE, INSERT, DELETE
// itu = 0 jos CREATE ja se onnistui
// itu = -1 muille komennoille
INSERTin jälkeen on helppo selvittää lisätyn rivin avainkentän arvo:
int uusid =
(int)conlite.LastInsertRowId;
Blob-kentän lukeminen ja kirjoittaminen
// luetaan
blob tietokannasta johonkin
string sqz = "SELECT ... length(Bitmap), Bitmap ..." //
neljäs ja viides kenttä SELECT-lausekkeessa
SQLiteCommand kome
= new SQLiteCommand(sqz, conlite);
SQLiteDataReader
rdr = kome.ExecuteReader();
int blobkoko = (rdr.IsDBNull(4)) ? 0 : rdr.GetInt32(4);
if (blobkoko = 0)
{ // virhe...
}
Byte[] blobi = new Byte[blobkoko];
long pyteja = rdr.GetBytes(5, 0, blobi, 0, blobkoko); //
luetaan itse blob
// tehdään blobilla jotain ...
// kirjoita
blobi tietokantaan (conlite):
byte[] bytet = .... // tehty esim. kuvasta
sqz = "INSERT INTO Bitmapit (iKuva, Bitmap) VALUES (999,
@bytet)"; // 999 vain esim
SQLiteCommand kome = conlite.CreateCommand();
kome.CommandText = sqz;
kome.Parameters.Add("@bytet", DbType.Binary,
(int)(bytet.Length)).Value = bytet;
int itu = kome.ExecuteNonQuery(); //
viedään thumbnail tauluun
kome.Dispose();
if (itu != 1)
{ // ... epäonnistui
}
Transaction
Transaktio on hyödyllinen perättäisten SQL-lausekkeiden
toteuttamisessa seuraavanlaisissa tapauksissa:
1) Perättäisiä lausekkeita on paljon, kuten usein ohjelmaloopissa,
tai
2) Jos peräkkäiset lausekkeet saa toteuttaa ehdolla, että
viimeinenkin lauseke onnistuu
Rakenne sovelluksessa on periaatteessa seuraava:
- nimetään transaktiolle joukko sql-lausekkeita (jotka tulee
testatuksi)
- jos joku lauseke epäonnistuu tehdään aikaisempien peruutus eli
Rollback()
- kun kaikki onnistuivat, tehdään varsinainen toteutus eli Commit()
Ohjelmarakenteena transaktio voidaan toteuttaa kahdella
erityyppisellä tavalla: using-rakenne, ja ns. normaalirakenne.
Seuraavassa on esitetty molemmat.
// ensin
rakenne 'using', se hoitaa myös Rollbackin vaikka komentoa ei
tarvitse erikseen antaa
int ida = 234; // esimerkissä käytetty muuttuja
string virhez = "";
bool onok = false; // true = kaikki meni hyvin
using (SQLiteTransaction tr = conlite.BeginTransaction())
{
using (SQLiteCommand kome =
conlite.CreateCommand())
{
kome.Transaction = tr;
kome.CommandText = $"DELETE
FROM Albumit WHERE idAlb = {ida}";
int itu =
kome.ExecuteNonQuery();
if (itu == 1)
{
kome.CommandText = $"DELETE FROM HenkAlbt WHERE iAlbumi = {ida}";
itu =
kome.ExecuteNonQuery();
if (itu
>= 0)
{
kome.CommandText = $"DELETE FROM SisAlbt WHERE
iAlbumi = {ida}";
itu = kome.ExecuteNonQuery();
if (itu >= 0)
onok = true;
else
virhez = "SisAlbt-siivous
epäonnistui";
}
else
virhez = "HenkAlbt-siivous epäonnistui";
}
else
virhez =
"Albumin poisto epäonnistui";
}
if (onok)
tr.Commit(); //
kaikki hyvin, kaikkien toteutus
else
{
MessageBox.Show($"Poistaminen epäonnistui:\n{virhez}", "(D15-07)
Ilmoitus");
}
}
Seuraavana perinteisempi ohjelmarakenne. Siinä poistutaan
throw-komennolla ohjelamluupista, jos tulee ongelmia, esimerkiksi:
throw new
SQLiteException("Homma epäonnistui");
Tämä antaa catch:lle
ex.Message =
"unknown error\r\nHomma epäonnistui"
Näkyvää virheilmoitusta varten tätä voidaan paikata:
string zz =
ex.Message.Replace("unknown error\r\n", " ");
Ja sitten ohjelmakoodi kokonaisuudessaan:
int itu = 0;
int iar = 234; // esimerkissä käytetty arvo
int onok = 0; // tulos -1 (ongelma), 0 (ei muutoksia), 1
(kaikki ok)
SQLiteTransaction tr = null;
SQLiteCommand kome = null;
try
{
tr = conlite.BeginTransaction();
kome = new SQLiteCommand(conlite)
{
Transaction = tr,
CommandText = $"DELETE FROM Henkilot
WHERE idHlo = {iar}"
};
itu = kome.ExecuteNonQuery();
if (itu == 1)
{
kome.CommandText = $"DELETE FROM
HenkAlbt WHERE iHlo = {iar}";
itu = kome.ExecuteNonQuery();
if (itu >= 0)
{
tr.Commit(); //
nyt toteutetaan SQL-lauseet
onok = 1;
}
else
throw new
SQLiteException("HenkAlbt-siivous epäonnistui");
}
else
throw new
SQLiteException("Henkilot-taulusta ei saatu poistettua");
}
catch (SQLiteException ex)
{
MessageBox.Show($"Poistaminen
epäonnistui:\n{ex.Message}", "(E16-07) Ilmoitus");
onok = -1;
if (tr != null)
{
try
{
tr.Rollback();
MessageBox.Show("Poistokäskyt peruutettiin.", "(E16-08)
Ilmoitus");
}
catch (SQLiteException ex2)
{
MessageBox.Show($"Poiston peruutus epäonnistui:\n{ex2.Message}",
"(E16-09) Ilmoitus");
}
finally
{
tr.Dispose();
tr = null;
}
}
}
finally
{
if (kome != null)
kome.Dispose();
if (tr != null)
tr.Dispose();
}
// onok = -1 (ongelma), onok = 0 (ei muutoksia), onok = 1 (kaikki
ok)
--------------------------------
(sivua muokattu 11.2.2018)