Mostrando entradas con la etiqueta SQLite. Mostrar todas las entradas
Mostrando entradas con la etiqueta SQLite. Mostrar todas las entradas

martes, 19 de octubre de 2010

Conceptos básicos de Python y SQLite

En el presente post vamos a aprender los conceptos básicos para desarrollar código en Python utilizando la base de datos SQLite. Se presupone que el lector conoce los conceptos básicos de Python y de SQL, y que ya tiene instalado en su máquina tanto el intérprete de Python como SQLite.

Librería pysqlite

Lo primero que debemos hacer es descargar e instalar la librería pysqlite desde su página web: http://code.google.com/p/pysqlite

En el caso de tener Windows, descargamos un archivo ejecutable (.exe) y lo ejecutamos, instalándolo como cualquier aplicación Windows.

En el caso de tener Linux, descargamos el archivo comprimido (.tar.gz), y lo descomprimimos. Una vez descomprimido, instalamos la librería mediante el siguiente comando:

$ python setup.py build
$ python setup.py install


Conexión a la base de datos

Para realizar una conexión a una base de datos SQLite, ésta debe estar arrancada:

$ sqlite3 mibasedatos.bbdd

Para realizar una conexión a una base de datos, importar la librería:

from pysqlite2 import dbapi2 as sqlite

A continuación se establece la conexión indicando el archivo de base de datos:

con = sqlite.connect('mibasedatos.bbdd')


Consultas de datos

Para todas las operaciones con la base de datos, hay que crear un cursor a partir de la conexión:

cur = con.cursor()

A partir del cursor, se pueden ejecutar las operaciones que se requieran, como la consulta de datos:

cur.execute('select campo from tabla')

Al ejecutar la consulta, el cursor apunta antes del primer registro o fila. Los siguientes comandos avanzan hasta el siguiente registro:

cur.next() # Avance
cur.fetchone() # Avance y recupera registro

Para obtener de una vez todos los registros del cursor:

cur = con.cursor()
cur.execute('select * from tabla')
result = cur.fetchall()

En la variable result se almacenará una lista o secuencia de tuplas, cada una de las cuales corresponde a un registro, y el valor de cada uno de los campos se encuentra separado por comas:

print result
[(0, u'valor1', 87), (1, u'valor2', 32), (2, u'valor3', 38)]

En este ejemplo, el resultado da 3 registros con tres columnas o campos, de los cuales, el primero y el último son numéricos, y el segundo es alfanumérico.

print result[1] # registro 2
(1, u'valor2', 32)
print result[2][1] # campo 2 del registro 3
valor3

Recorrer el resultado resulta muy sencillo, utilizando un iterador sobre la secuencia:

cursor = con.cursor()
cursor.execute('SELECT id, nombre, nick, password, rol FROM usuarios')
for fila in cursor:
   print 'id:', fila[0]
   print 'Nombre:', fila[1]
   print 'Nick:', fila[2]
   print 'Password:', fila[3]
   print 'Rol:', fila[4]
   print '*'*30


Actualizaciones

Por actualización se entiende toda aquella operación que realiza algún cambio, lo que implica algún tipo de escritura. Existen varios tipos de escritura o modificaciones en la base de datos. El primero afectaría únicamente a los datos de las tablas, lo que se consigue mediante las sentencias UPDATE, INSERT y DELETE de SQL. Otro afectaría a la estructura de la base de datos, pudiendo crear, modificar o eliminar tablas o índices.


Actualización de datos en tablas

Para todas ellas, se utilizará un cursor y la función execute(), tal y como vimos anteriormente:

from pysqlite2 import dbapi2 as sqlite
con = sqlite.connect("ticube.bbdd")
cur = con.cursor()
cur.execute("insert into tbconfig (parametro, valor, descripcion) values ('PARAM1', '10', 'Parametro 1')")
cur.lastrowid

En este ejemplo se inserta un registro en una tabla. La última línea retorna el número de la última fila afectada en el cursor. Si sólo se realizó esta operación de escritura, pero no afecta a ningún registro, no retornará nada. Si todo fue bien, retornará (en este caso) 1.

El cursor acumulará operaciones de escritura, a medida que se vayan ejecutando. Pero estas operaciones no se hacen efectivas físicamente en la base de datos. Ello se debe a que se encuentran en una transacción.

Una transacción resulta muy útil cuando hay un encadenamiento de operaciones de escritura que tienen dependencias unas de otras. Cuando se completan todas las operaciones de escritura, si no se ha producido ningún tipo de error, se puede procesar toda la transacción indicando a la conexión de la base de datos que perpetre definitivamente la transacción con todas las operaciones realizadas:

con.commit()

Si se produjera algún error durante la transacción, podrían abortarse todas las operaciones de escritura indicadas en el cursor para evitar inconsistencias de datos. Para ello, una vez se detecte algún error o inconsistencia, se indicaría a la conexión de la base de datos que dé marcha atrás, cancelando todas las operaciones realizadas:

con.rollback()



Actualización de la estructura de la BBDD

También es posible acceder y modificar la estructura de la base de datos, pudiendo crear nuevas tablas, añadir campos a las tablas existentes, crear o borrar índices, etc.

El proceder es idéntico a como hemos visto antes, a través del cursor. El siguiente ejemplo crea una sencilla tabla en nuestra base de datos:

cursor.execute("CREATE TABLE configuracion (parametro VARCHAR(30) NOT NULL, valor VARCHAR(30))")

A diferencia de la actualización de datos en las tablas, este tipo de actualizaciones se realiza directamente, y no requiere de transacciones.


Consultas parametrizadas

Imaginemos que se pretende hacer una carga masiva de datos de una tabla. En lugar de tantas sentencias INSERT como conjuntos de valores (cosa inviable si los datos son dinámicos), se pueden recoger éstos de otra consulta o de un fichero, y asignar dichos valores a unas variables y utilizar el valor de éstas en una única sentencia INSERT:

from pysqlite2 import dbapi2 as sqlite
con = sqlite.connect("ticube.bbdd")
cursor = con.cursor()
parametros=['UNO','DOS',’TRES’]
valores=['1','2','3']
max=len(parámetros)
for i in range(0,max):
   cursor.execute("INSERT INTO configuracion (parametro, valor) VALUES (?, ?)", (parametros[i], valores[i]))
con.commit()

En este ejemplo se insertarán 3 registros en la tabla configuracion, obtenidos de dos secuencias (parametros y valores). El símbolo ? (interrogación), reserva el valor que va a tomar de la lista de parámetros que se especifican a continuación, en el mismo orden en que se enumeran.

Esta capacidad se puede aplicar también a otras consultas de actualización (como UPDATE o DELETE) o a consultas de datos (SELECT).



Cerrar siempre

SQLite es una base de datos muy versátil, ligera y práctica. Ello se consigue sacrificando una parte esencial en entornos distribuídos: la concurrencia. Así es: SQLite no es concurrente. Por ello, para evitar problemas e inconsistencias por la concurrencia, deberemos siempre cerrar todos los cursores y conexiones una vez han sido utilizados y no los utilicemos en cada módulo. Ello libera la conexión para nuevas operaciones.

El método close() se encarga de esta operación:

cursor.close()
con.close()

El orden de apertura y cierre es importante:

1) Abrir conexión
2) Abrir cursor
3) Operaciones
4) …
5) Cerrar cursor
6) Cerrar conexión

Safe Creative #1010197608377

sábado, 19 de septiembre de 2009

Base de datos bloqueada en SQLite y .NET

En post anteriores se ha hablado sobre las bondades de SQLite trabajando en conjunción con .NET, un gran equipo que es eficiente y rápido (amén de optimizado por lo recursos que consume), pero puede dar alguno que otro quebradero de cabeza, especialmente con el bloqueo de base de datos (database locked).

La principal razón de que esto ocurra es que SQLite trabaja en una única sesión, por lo que la concurrencia queda descartada para grandes pretensiones. Incluso con esto en mente, trabajando en una aplicación de escritorio (no en servidor), esto puede suponer también un problema cuando queremos trabajar con varias consultas anidadas y actualizaciones.

En este post voy a contar mi experiencia y cómo he capeado este problema.

Ante todo, hay que reutilizar el código, por lo que el acceso a base de datos la realizo desde una clase con métodos públicos. En esta clase defino los objetos clave para el uso de la base de datos:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SQLite; // No olvidar esta referencia

// Clase específica para acceso y carga de datos desde la base de datos
namespace TI_Facturas
{
  class DatosManager
  {
    private SQLiteConnection con = null;
    private SQLiteCommand cmd = null;
    private SQLiteDataReader dtr = null;
 }
}


Los objetos deben tener un uso exclusivo cada vez, por lo que se definen dos métodos: uno para iniciarlos y otro para despacharlos.


  // Inicializa los objetos de base de datos
  private void initSQL()
  {
    con = null;
    cmd = null;
    dtr = null;

    con = new SQLiteConnection("Data Source=ti_facturas.db;Version=3;New=False;Compress=True;");
    con.Open();
  }

  // Prepara la clase para ser despachada
  public void dispose()
  {
    if (cmd != null)
      cmd.Dispose();
    if (dtr != null)
    {
      dtr.Close();
      dtr.Dispose();
    }
    if (con != null)
    {
      con.Close();
      con.Dispose();
    }

    con = null;
    cmd = null;
    dtr = null;

    GC.Collect();
  }


El método initSQL() ha de invocarse antes de empezar cualquier operación con la base de datos. A continuación se realizan las operaciones con la base de datos (accesos y actualizaciones). Y por último se invoca al método dispose() para finiquitar los objetos.

La sentencia GC.Collect() permite vaciar el Garbage Collector, un repositorio que .NET utiliza para depositar los objetos que dejan de ser utilizados. Es posible que en algunas ocasiones, aunque cerremos convenientemente los objetos para despacharlos al Garbage Collector, éste aún pueda tener la referencia a SQLite y no haya procesado su limpieza automática, por lo que nos dará, inevitablemente, el conocido error de "database locked", a pesar de ser meticulosos y despachar a medida que dejamos de utilizar.

Recomiendo utilizar en esta clase los métodos de acceso a la base de datos, e ir invocándolo desde nuestras propias clase. Por ejemplo, ahí van las más utilizadas de forma genérica:


// Ejecuta una sentencia de actualizacion (NO SELECT)
public String executeNonQuery(string sql)
{
  String result = "" ;

  try
  {
    initSQL();

    cmd = new SQLiteCommand(sql, con);
    int rows = cmd.ExecuteNonQuery();

    if (rows == 0)
      result = "No se ha realizado ningun cambio [" + sql + "]";
  }
  catch (Exception argEx)
  {
    result = "Error al ejecutar SQL ["+sql+"]: " + argEx.Message;
  }

  dispose();

  return result;
}

// Obtiene el valor de un unico campo para una consulta de un solo registro
public String getUniqueValue(string sql)
{
  String result = "";

  try
  {
    initSQL();
    cmd = new SQLiteCommand(sql, con);
    dtr = cmd.ExecuteReader();
    dtr.Read();
    result = (String)dtr[0].ToString();
  }
  catch (Exception argEx)
  {
    result = "";
  }

  dispose();

  return result;
}

// Obtiene el conjunto de registros de una consulta SQL Select
public SQLiteDataReader executeReader(string sql)
{
  try
  {
    initSQL();
    cmd = new SQLiteCommand(sql, con);
  dtr = cmd.ExecuteReader();
  }
  catch (Exception argEx)
  {
  dtr = null;
  }

  return dtr;
}


EJEMPLOS DE USO:

Ejemplo 1: Actualización


string sql="insert into paises (id_pais, pais) values (1, 'Spain')";
DatosManager dm = new DatosManager();
dm.executeNonQuery(sql);
dm.dispose();


Ejemplo 2: Consulta campo especifico en registro unico


string sql="select nombre from clientes where id_cliente=32";
DatosManager dm = new DatosManager();
string nombre=dm.getUniqueValue(sql);
dm.dispose();


Ejemplo 3: Consulta con conjunto de varios registros


string sql="select id_provincia, provincia from provincias order by provincia";
DatosManager dm = new DatosManager();
SQLiteDataReader dr=dm.executeReader(sql);
while (dr.Read())
{
  // Tratamiento de cada registro
}
dr.Close(); // Despachar primero el DataReader
dr.Dispose();
dm.dispose();


Safe Creative #1001195348532

jueves, 17 de septiembre de 2009

Consultas SQLite desde .NET

SQLite se integra muy bien con .NET, siendo muy fácil desarrollar un proyecto con esta base de datos, con unos costes muy reducidos al no tener que pagar licencias propietarias, como en el caso de Oracle o de SQL Server.

El primer paso a dar es instalarse el conector System.Data.SQLite.dll (se puede descargar de http://sqlite.phxsoftware.com). Una vez instalado, hay que hacer referencia a dicho conector mediante Project > Add Reference... (Proyecto > Añadir referencia...), buscar el conector y OK (Aceptar).

El siguiente paso es añadir al inicio del código el uso de las clases de esta librería:

using System.Data.SQLite; 

Los ejemplos que aquí se muestran están escritos en C#, pero la adaptación a VB.NET no debería suponer gran transcendencia.

En primer lugar hay que encerrar todo el proceso de acceso a la base de datos en un bloque try...catch. Dentro de este bloque comenzaremos abriendo la conexión, mediante:

SQLiteConnection sql_con = new SQLiteConnection ("DataSource=ARCHIVOBBDD;

   Version=3;New=False;Compress=True;");
sql_con.Open();

donde, ARCHIVOBBDD es el nombre del archivo de la base de datos. Si este archivo se encuentra en una ruta fija, hay que indicar toda la ruta. Si se escribe únicamente el nombre del archivo, éste debería estar ubicado en el directorio debug y release del proyecto.

A continuación se crea el comando de la sentencia a ejecutar (en este caso una consulta o SELECT):

string CommandText = "select id_empresa, nombre from EMPRESAS order by nombre";
SQLiteCommand sql_cmd = new SQLiteCommand(CommandText, sql_con);

El siguiente paso será ejecutar el comando para obtener el resultado de la consulta, el cual estará en un DataReader:

SQLiteDataReader sql_dtr = sql_cmd.ExecuteReader();

En este momento, el puntero del cursor se encuentra justo antes del primer registro. Con el método Read(), se obtenerá el siguiente registro o fila, retornando true si se leyó correctamente, o false si no se leyó, en cuyo caso se habrá alcanzado el final del resultado. La lectura de todos los registros se puede realizar dentro de un bucle como éste:

while (sql_dtr.Read())

{

   ... // Bloque para tratar los campos de la fila obtenida

}

El acceso a cada campo se realiza a modo de array con el DataReader, comenzando desde 0, que sería el primer campo, hasta n-1. También se podría acceder indicando dentro del elemento del array el nombre del campo entre comillas, en lugar del índice. Las dos siguientes líneas realizan lo mismo:

string nombreEmpresa = (string)sql_dtr[1];

string nombreEmpresa = (string)sql_dtr["nombre"];

En todos los casos hay que hacer un casting del tipo de dato (anteponiendo y encerrando entre paréntesis el tipo de dato), por lo que hay que tener especial cuidado con los datos que son numéricos o de fecha. Las siguientes líneas son una muestra de ello, aunque no tengan que ver con el ejemplo que estamos planteando:

long lData = (Int64)sql_dtr[0];  // Tipo entero largo

Single sData = (Single)sql_dtr[1];  // Tipo decimal

Double dData = (Double)sql_dtr[0];  // Tipo decimal de doble precisión

Para hacer una captura correcta de datos, lo mejor es comprobar si el dato no es nulo y un parseo en consecuencia, como en el siguiente ejemplo:

Single sCant=0;
string sData = dtr[0].ToString();
if (sData != null && sData != "")
     sCant = Single.Parse(sData);  

   

Si el dato no admite nulos, se puede realizar el parseo directamente:

long plazo = (long)long.Parse(sql_dtr[6].ToString()) ; 

Double dData = Double.Parse(sql_dtr[0].ToString());

DateTime dtFecha = DateTime.Parse(sql_dtr["fecha"].ToString());

Una vez se han leído los registros y no se vaya a utilizar más el DataReader, hay que cerrarlo para optimizar recursos:

sql_dtr.Close();

Si la conexión ya no es necesaria por el momento, cerrarla también:

sql_con.Close();

El código completo del ejemplo, es el siguiente, y su objetivo es cargar una lista de empresas en una combo (lista desplegable):


// Carga la combo de empresas
private void loadEmpresas()
{
  try
  {
    SQLiteConnection sql_con = new SQLiteConnection

      ("Data Source=ti_facturas.db;Version=3;

      New=False;Compress=True;");
    sql_con.Open();

    string CommandText =

      "select id_empresa, nombre from EMPRESAS order by nombre";
    SQLiteCommand sql_cmd = new SQLiteCommand(CommandText, sql_con);
    SQLiteDataReader sql_dtr = sql_cmd.ExecuteReader();

    DataTable dt;
    dt = new DataTable("Datos");

    dt.Columns.Add("id_empresa");
    dt.Columns.Add("empresa");

    DataRow dr;

    while (sql_dtr.Read())
    {
      dr = dt.NewRow();
      dr["id_empresa"] = ((Int64)sql_dtr[0]);
      dr["empresa"] = (string)sql_dtr[1];
      dt.Rows.Add(dr);

    }

    cboEmpresa.DataSource = dt;
    cboEmpresa.ValueMember = "id_empresa";
    cboEmpresa.DisplayMember = "empresa";

    sql_dtr.Close();
    sql_con.Close();
  }
  catch (Exception argEx)
  {
    Console.WriteLine("Exception message: " + argEx.Message);
  }
}




Safe Creative #1001195348549

lunes, 14 de septiembre de 2009

Primeros pasos en SQLite

INTRODUCCION

SQLite es una base de datos que sorprende por lo increíblemente ligera que es (apenas ocupa 250KB), pero no por ello deja de ser potente, rápida y eficiente, basándose en SQL estándar para su gestión.




Las características más importantes de SQLite son las siguientes:
- Transacciones atómicas, aisladas y consistentes.
- No requiere instalación ni administración.
- Implementa la mayor parte del estándar SQL92
- Una base de datos completa se almacena en un único archivo en el disco duro.
- Soporta bases de datos que ocupan terabytes, y cadenas y BLOBs que ocupan gigabytes.
- Ejecución muy rápida y consume muy pocos recursos de la máquina.
- No requiere de dependencias de terceros ni externas.
- Es de dominio público.
- Disponible para Linux, Windows y MacOS X.
- Incluye una consola para la gestión y administración de bases de datos.
- Se puede utilizar en proyectos escritos en:
   - Basic y dialectos (Visual Basic, PowerBasic, PureBasic...)
   - C y C++
   - C# y VB .NET
   - Clipper
   - Lisp
   - Curl
   - D
   - Delphi
   - Macromedia Director
   - Flash Actionscript 2
   - Fortran
   - Java
   - Javascript
   - Matlab
   - .NET
   - Perl
   - PHP
   - PL/SQL
   - Python
   - Ruby
   - Smalltalk
   - Tcl/tk
   - Y otros...

INSTALACION

Para instalar SQLite hemos de acceder a su página principal

http://sqlite.org

Y descargar (Download) la última versión ejecutable (Binaries) de la plataforma (Windows, Linux, MacOS X) en la que vayamos a utilizarla. También aquí se puede descargar la documentación de la misma.

El archivo está comprimido, y simplemente hay que descomprimirlo en la carpeta de nuestro disco de nuestra preferencia.


CONSOLA SQLITE

Una vez descomprimido hemos de abrir una consola de comandos (en caso de Windows, también se llama consola DOS, y se accede mediante "tecla Windows" + "R", y ejecutar "cmd") para utilizar SQLite. Una vez tengamos abierta la consola de comandos, hay que acceder al directorio donde se descomprimió SQLite, y ejecutar el comando:

sqlite3

donde es un path a un archivo donde contendrá una base de datos. Si no existe, creará la base de datos. Si existe, utilizará ésta. Como recomendación, poner una extensión fácil de reconocer, como ".db" o ".sqlite".

La ejecución de este comando nos introducirá en la consola de SQLite, donde podremos ejecutar cualquier comando SQL para gestionar la base de datos. Asimismo, también hay otros comandos específicos de esta consola:

.help ó .h > Muestra una ayuda de los comandos disponibles
.quit ó .q > Termina la sesión de consola SQLite, retornando a la de comandos
.exit ó .e > Termina la sesión de consola SQLite, retornando a la de comandos
.databases > Lista las bases de datos en uso
.tables > Lista las tablas de la base de datos
.tables 'P%' > Lista las tablas que empiecen por 'P' (patrón LIKE)
.backup ARCHIVO > Realiza una copia de seguridad de la base de datos principal a ARCHIVO
.backup 'BBDD' ARCHIVO > Realiza una copia de seguridad de la base de datos 'BBDD' a ARCHIVO
.schema TABLA > Muestra la estructura de la tabla especificada (en formato SQL), incluyendo los índices
.indices TABLA > muestra los índices de la tabla especificada
.output stdout > Establece la salida a pantalla (por defecto)
.output ARCHIVO > Establece la salida al archivo indicado en archivo
.mode MODO > Establece el modo de salida, donde MODO puede ser uno de estos valores:
csv > valores separados por comas
column > columnas alineadas a la izquierda (ver .width)
html > código TABLE en HTML
insert > código INSERT en SQL
line > un valor por línea
list > valores delimitados por .separator (por defecto)
tabs > valores separados por tabuladores
tcl > lista de elementos tcl
.width num > Establece el ancho para las columnas (salida)
.separator string > Establece string como cadena delimitadora (salida)
.dump > Vuelca en la salida la estructura y datos de la base de datos, es decir, muestra las sentencias SQL (creación de tablas, índices, inserción de datos actuales, etc.), de todas las tablas de la base de datos.
.dump TABLA > Vuelca en la salida la estructura y datos de la tabla especificada, es decir, que muestra las sentencias SQL para la TABLA especificada
.restore ARCHIVO > Restaura la copia de seguridad realizada con .backup almacenada en ARCHIVO, en la base de datos en curso
.restore 'BBDD' ARCHIVO > Restaura la copia de seguridad almacenada en ARCHIVO, en la base de datos especificada con 'BBDD'
.read ARCHIVO > Ejecuta las sentencias SQL que están almacenadas en ARCHIVO

EJEMPLOS

.mode csv
select * from paises;

Muestra los datos de la tabla paises separados por comas.

.mode html
select * from paises;

Muestra los datos de la tabla paises en una tabla HTML

.mode insert
select * from paises;

Muestra las sentencias INSERT para cada uno de los registros de la tabla paises.

.output paises.sql
.mode insert
select * from paises;

Vuelca en el archivo paises.sql las sentencias INSERT para los datos de la tabla paises.

.output paises.sql
.dump paises

Vuelca en el archivo paises.sql las sentencias de creación de tabla e índice, así como la inserción de datos, para la tabla paises.

.output stdout
.dump paises

Restaura la salida a la pantalla, y vuelca la tabla paises en dicha salida.


IDE's PARA SQLITE

Afortunadamente, existen IDE o interfaces de usuario, que permitirán gestionar nuestras bases de datos SQLite, sin necesidad de la consola. Algunas de las más populares son:
- SQLite Administrator > http://sqliteadmin.orbmu2k.de/
- SQLite Database Browser > http://sqlitebrowser.sourceforge.net/



Safe Creative #1001195348556

Uso de fechas en SQLite

Este es mi primer post sobre SQLite, una base de datos muy ligera, pero potente y rápida, y que no necesita apenas administración. Es software libre, por lo que no necesitarás pagar licencias por su uso, y tendrás un importante ahorro de costes. Por otro lado, hay versiones para varias plataformas, incluyendo a Microsoft Windows, Linux, BSD, etc.

En un reciente proyecto personal en el que estoy embarcado, utilizo C#, en donde sólo es necesario una referencia a la librería System.Data.SQLite.dll (se puede descargar de http://sqlite.phxsoftware.com/). En el ordenador final, a la hora de instalarse, no es necesario tener el motor de la base de datos, pues la propia librería actúa como tal, y de una manera muy rápida y eficiente.

Pero SQLite puede utilizarse de muchas maneras, como embebido en C, o desde Java, o desde PHP (sólo por citar las más populares). Además, existen multitud de IDE's para diseñar y gestionar la base de datos, y no sólo una consola de comandos, por lo que no hay excusa para no probar esta base de datos.

Querría haber empezado una serie de posts sobre SQLite desde el principio (cosa que haré más adelante), pero por tiempo empezaré directamente con algo que puede despistarnos con respecto a otras bases de datos, y son el uso de fechas.

Un campo fecha se define como:

CREATE TABLE (nombre_campo DATE ...);

Aunque permite insertar o modificar los datos en el formato 'dd/mm/yyyy' es recomendable almacenar las fechas en el formato ordenable, del estilo '2004-06-26T20:11:04' (en .NET se realiza mediante String.Format("{0:s}", valor_datetime). Aunque este formato es válido, a la hora de comparar fechas tendrá en cuenta también la hora, por lo que si la precisión que se desea es sobre el día, y no sobre día y hora, el formato recomendado sería 'yyyy-mm-dd', que en .NET se obtendría mediante String.Format("{0:yyyy'-'MM'-'dd}", valor_datetime).

La razón de de este formato es que SQLite, internamente, la fecha se almacena como una cadena de texto.

Pero a la hora de hacer una búsqueda por fechas, del estilo:

SELECT ...
FROM ...
WHERE fecha1>fecha2

nos llevaremos una sorpresa al comprobar que no nos hace mucho caso.

A la hora de comparar fechas, hay que utilizar el formato juliano, y ello se consigue mediante una función especial de SQLite.

SELECT ...
FROM ...
WHERE julianday(fecha1)>julianday(fecha2)

De esta manera la comparación de fechas funcionará sin problemas.

Safe Creative #1001195348570