Introducción a las Bases de Datos Relacionales#

  • ¿Qué es una base de datos relacional?

  • SQLite, una base de datos local

  • Tablas

  • Consultas

  • Filtros

  • Agregación

Bases de Datos Relacionales#

Artículo en Wikipedia

  • “La base de datos relacional (BDR) es un tipo de base de datos (BD) que cumple con el modelo relacional (el modelo más utilizado actualmente para implementar las BD ya planificadas). Tras ser postuladas sus bases en 1970 por Edgar Frank Codd, de los laboratorios IBM en San José (California), no tardó en consolidarse como un nuevo paradigma en los modelos de base de datos.”

  • “Un sistema de software utilizado para mantener las bases de datos relacionales es un relational database management system (RDBMS) o sistema de gestión de bases de datos relacionales. Virtualmente, todos los sistemas de bases de datos relacionales utilizan SQL (Structured Query Language) para consultar y mantener la base de datos.”

Principales Características de una BBDD Relacional#

  • Una base de datos se compone de varias tablas, denominadas también relaciones.

  • No pueden existir dos tablas con el mismo nombre.

  • Cada tabla es a su vez un conjunto de campos (columnas) y registros (filas).

  • Se puede establecer una relación entre una tabla madre y una tabla hija. Ésta se lleva a cabo por medio de las llaves primarias y llaves foráneas.

  • Las llaves primarias son la clave principal de un registro dentro de una tabla y deben cumplir con la integridad de datos (se verá).

  • Las llaves foráneas se colocan en la tabla hija, contienen el mismo valor que la llave primaria del registro padre; por medio de éstas se hacen las formas relacionales.

SQLite, una BBDD Relacional Local#

SQLite home

“SQLite is a C-language library that implements a small, fast, self-contained, high-reliability, full-featured, SQL database engine. SQLite is the most used database engine in the world. SQLite is built into all mobile phones and most computers and comes bundled inside countless other applications that people use every day.”

La data de una base de datos SQLite se almacena como un archivo local en el sistema.

En el directorio ./data se encuentra el archivo northwind.db que contiene la BBDD Northwind originalmente proporcionada por Microsoft junto a Microsoft Access. Esta BBDD se utilizaba como introducción a las bases de datos y sigue siendo bastante utilizada en ese contexto.

Conexión a la BBDD#

import sqlite3

En este caso la extensión del archivo es db, pero puede ser cualquiera.

conn = sqlite3.connect('data/northwind.db')

Vemos el tipo del objeto obtenido.

conn
<sqlite3.Connection at 0x7f0304f63d30>

Enviar Comandos a la BBDD#

Para enviar comandos a la BD Se debe crear un objeto de tipo cursor. Los comandos son los que usualmente se conocen como operaciones CRUD:

  • CREATE: crear una tabla

  • READ: leer uno o varios registros de una o más tablas

  • UPDATE: actualizar los valores de un registro

  • DELETE: borrar tablas o registros de una tabla

cursor = conn.cursor()

Tablas de la BBDD#

qry = "SELECT * FROM sqlite_master" # instrucción en SQL

La variable anterior es un comando en SQL (structured query language).

SQL es un lenguaje declarativo, su sintaxis expresa lo que se quiere lograr y no cómo lograrlo. Python, en cambio, es un lenguaje imperativo su sintaxis expresa cómo realizar una tarea.

En la instrucción SQL anterior sqlite_master es una tabla y * indica que quiero obtener todos los campos. Sumando ambas partes, la instrucción equivale a decir: quiero todos los campos de todos los registros de la tabla sqlite_master.

Podemos ejecutar el comando con el objeto cursor.

result = cursor.execute(qry)
print(type(result))
<class 'sqlite3.Cursor'>

El objeto cursor almacena varias cosas después de haber ejecutado la query. Con la siguiente instrucción se obtienen los nombres de las columnas de la tabla sqlite_master.

nombres = [description[0] for description in result.description]
nombres
['type', 'name', 'tbl_name', 'rootpage', 'sql']

El resultado en sí de la query (los registros obtenidos) lo vemos con el siguiente loop:

for i, row in enumerate(result):
    print(row)
    print()
    if i > 10: break
('table', 'Categories', 'Categories', 2, 'CREATE TABLE [Categories]\n(      [CategoryID] INTEGER PRIMARY KEY AUTOINCREMENT,\n       [CategoryName] TEXT,\n       [Description] TEXT,\n       [Picture] BLOB\n)')

('table', 'sqlite_sequence', 'sqlite_sequence', 3, 'CREATE TABLE sqlite_sequence(name,seq)')

('table', 'CustomerCustomerDemo', 'CustomerCustomerDemo', 28, 'CREATE TABLE [CustomerCustomerDemo](\n   [CustomerID]TEXT NOT NULL,\n   [CustomerTypeID]TEXT NOT NULL,\n   PRIMARY KEY ("CustomerID","CustomerTypeID"),\n   FOREIGN KEY ([CustomerID]) REFERENCES [Customers] ([CustomerID]) \n\t\tON DELETE NO ACTION ON UPDATE NO ACTION,\n\tFOREIGN KEY ([CustomerTypeID]) REFERENCES [CustomerDemographics] ([CustomerTypeID]) \n\t\tON DELETE NO ACTION ON UPDATE NO ACTION\n)')

('index', 'sqlite_autoindex_CustomerCustomerDemo_1', 'CustomerCustomerDemo', 29, None)

('table', 'CustomerDemographics', 'CustomerDemographics', 30, 'CREATE TABLE [CustomerDemographics](\n   [CustomerTypeID]TEXT NOT NULL,\n   [CustomerDesc]TEXT,\n    PRIMARY KEY ("CustomerTypeID")\n)')

('index', 'sqlite_autoindex_CustomerDemographics_1', 'CustomerDemographics', 31, None)

('table', 'Customers', 'Customers', 32, 'CREATE TABLE [Customers]\n(      [CustomerID] TEXT,\n       [CompanyName] TEXT,\n       [ContactName] TEXT,\n       [ContactTitle] TEXT,\n       [Address] TEXT,\n       [City] TEXT,\n       [Region] TEXT,\n       [PostalCode] TEXT,\n       [Country] TEXT,\n       [Phone] TEXT,\n       [Fax] TEXT,\n       PRIMARY KEY (`CustomerID`)\n)')

('index', 'sqlite_autoindex_Customers_1', 'Customers', 33, None)

('table', 'Employees', 'Employees', 37, 'CREATE TABLE [Employees]\n(      [EmployeeID] INTEGER PRIMARY KEY AUTOINCREMENT,\n       [LastName] TEXT,\n       [FirstName] TEXT,\n       [Title] TEXT,\n       [TitleOfCourtesy] TEXT,\n       [BirthDate] DATE,\n       [HireDate] DATE,\n       [Address] TEXT,\n       [City] TEXT,\n       [Region] TEXT,\n       [PostalCode] TEXT,\n       [Country] TEXT,\n       [HomePhone] TEXT,\n       [Extension] TEXT,\n       [Photo] BLOB,\n       [Notes] TEXT,\n       [ReportsTo] INTEGER,\n       [PhotoPath] TEXT,\n\t   FOREIGN KEY ([EmployeeID]) REFERENCES [Employees] ([EmployeeID]) \n\t\tON DELETE NO ACTION ON UPDATE NO ACTION\n)')

('table', 'EmployeeTerritories', 'EmployeeTerritories', 67, 'CREATE TABLE [EmployeeTerritories](\n   [EmployeeID]INTEGER NOT NULL,\n   [TerritoryID]TEXT NOT NULL,\n    PRIMARY KEY ("EmployeeID","TerritoryID"),\n\tFOREIGN KEY ([EmployeeID]) REFERENCES [Employees] ([EmployeeID]) \n\t\tON DELETE NO ACTION ON UPDATE NO ACTION,\n\tFOREIGN KEY ([TerritoryID]) REFERENCES [Territories] ([TerritoryID]) \n\t\tON DELETE NO ACTION ON UPDATE NO ACTION\n)')

('index', 'sqlite_autoindex_EmployeeTerritories_1', 'EmployeeTerritories', 68, None)

('table', 'Order Details', 'Order Details', 69, 'CREATE TABLE [Order Details](\n   [OrderID]INTEGER NOT NULL,\n   [ProductID]INTEGER NOT NULL,\n   [UnitPrice]NUMERIC NOT NULL DEFAULT 0,\n   [Quantity]INTEGER NOT NULL DEFAULT 1,\n   [Discount]REAL NOT NULL DEFAULT 0,\n    PRIMARY KEY ("OrderID","ProductID"),\n    CHECK ([Discount]>=(0) AND [Discount]<=(1)),\n    CHECK ([Quantity]>(0)),\n    CHECK ([UnitPrice]>=(0)),\n\tFOREIGN KEY ([OrderID]) REFERENCES [Orders] ([OrderID]) \n\t\tON DELETE NO ACTION ON UPDATE NO ACTION,\n\tFOREIGN KEY ([ProductID]) REFERENCES [Products] ([ProductID]) \n\t\tON DELETE NO ACTION ON UPDATE NO ACTION\n)')

Finalmente, obtengamos los nombres de todas las tablas de la BBDD.

qry = "SELECT name FROM sqlite_master WHERE type = 'table'"

Esta instrucción significa: quiero el campo name de la tabla sqlite_master, pero sólo de los registros cuyo campo type sea igual a 'table'.

result = cursor.execute(qry)
Todos los Nombres#
for row in result:
    print(row)
('Categories',)
('sqlite_sequence',)
('CustomerCustomerDemo',)
('CustomerDemographics',)
('Customers',)
('Employees',)
('EmployeeTerritories',)
('Order Details',)
('Orders',)
('Products',)
('Regions',)
('Shippers',)
('Suppliers',)
('Territories',)

Contenido de una Tabla#

Ya que conocemos los nombres de las tablas, podemos investigarlas. Vamos a buscar los todos los campos de la tabla Categories, pero sólo un registro.

qry = "SELECT * FROM Categories LIMIT 1"
result = cursor.execute(qry)
print([d[0] for d in result.description])
['CategoryID', 'CategoryName', 'Description', 'Picture']
store = result.fetchall()
store
[(1,
  'Beverages',
  'Soft drinks, coffees, teas, beers, and ales',
  b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x02\x00\x00d\x00d\x00\x00\xff\xec\x00\x11Ducky\x00\x01\x00\x04\x00\x00\x00P\x00\x00\xff\xee\x00&Adobe\x00d\xc0\x00\x00\x00\x01\x03\x00\x15\x04\x03\x06\n\r\x00\x00\nc\x00\x00\x10\xe8\x00\x00\x19\xf4\x00\x00\'\xa4\xff\xdb\x00\x84\x00\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x03\x02\x02\x02\x03\x04\x03\x02\x02\x03\x04\x05\x04\x04\x04\x04\x04\x05\x06\x05\x05\x05\x05\x05\x05\x06\x06\x07\x07\x08\x07\x07\x06\t\t\n\n\t\t\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x01\x03\x03\x03\x05\x04\x05\t\x06\x06\t\r\x0b\t\x0b\r\x0f\x0e\x0e\x0e\x0e\x0f\x0f\x0c\x0c\x0c\x0c\x0c\x0f\x0f\x0c\x0c\x0c\x0c\x0c\x0c\x0f\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\xff\xc2\x00\x11\x08\x00n\x00\xaf\x03\x01\x11\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x00\xe8\x00\x00\x01\x04\x03\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x03\x04\x06\x07\x00\x02\x08\x01\t\x01\x00\x02\x03\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x03\x00\x01\x04\x05\x06\x07\x10\x00\x01\x04\x01\x03\x03\x04\x01\x03\x04\x03\x00\x00\x00\x00\x00\x01\x00\x02\x03\x04\x11\x10\x12\x05 !\x131"\x14\x0602#\x15A3%\x07$4\x16\x11\x00\x02\x00\x03\x05\x05\x04\x07\x04\x08\x07\x01\x00\x00\x00\x00\x01\x02\x00\x11\x03!1A\x12\x04Qq"2\x13\x10a\x81\x05\x91\xa1BRb#\x14 \xc1r30\xf0\xb1\xe1\x82\x92\xb24\xd1\xf1\xa2\xc2\xd2C\x93$\x12\x00\x00\x05\x03\x04\x03\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000\x01\x11! @1\x10P\x12"A\x022a\x13\x01\x00\x02\x02\x02\x01\x03\x03\x05\x01\x01\x00\x00\x00\x00\x00\x01\x00\x11!1AQa\x10q\x81\x91\xa1\xb1 \xf0\xc1\xd1\xe10\xf1\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00\x00\x01\xec>f\xdef\xce\r\xdbO\xd4v\xc6\x9e\xaf\x0b\xa3\xdcM\xaf/\xd2\x0e\xb7\xcc\xd6\x81\xe9H\xba\xdbS\x97J\xc4j!x\xf5S\xder\x80\xa5h2ov\xe1$IFa\xf4\xb4\x0e\xb6\xf5\x9c\x915h\\8u_\xab\xa9\xc8\x9c\xdfk&~\x0e\xd1\xdd\xe2\x1c\xda\xf2\xe4lJ8\xfd\x87.\xe1j\xd5By\xab\x85\xe4\x14\xca\xc7\xb2\xdc\t7]\xabT\xb5\x07\xd1\x7fe\xc6\xc92OneL\x93$\xc9<\x93\xdb\x95-6F\xdd>Jo\x19Hq\xf4U\xfc\xb3\xd2\xacUX\x04\x9b0\x8b\x10\xbe\x88\xfa;\xec9\x04n\xb2L\x93$\xc92L\x93$\x8e-\xf5\xbdl\x96\xe9X\xd1`\x80rcT\x8f\x1d\xb5\xce\x17\x88Q\xb7\x96\xa9\xd0\x81\xe6\x15\xc1\x9f\xe8o\xb4\x87Z\x87v\n\x90\xfb&I\xa0\xdbQdc6\xba\x8b\'P\x9e\xfc\x8c\x0c\xa5\xed\x13F\x84@\x86%\xb0\xcc\xcf\x102<\x12\xa5\xc7Q\x1c\x18-noW\xa4\xbd\xaf7P"\xd6$\xac\\Jl%\x1dS\xe1H\xd7\x07\x1d\x0b8&=\x0er\xf6\xe9\x1c>w\xf3\xde\xba\x01\x87\xa89o\xf4\xe1\xc3Y\xc6g\x84\xeb\xf1\xb3\x0eJ\x0cs\xd9\xd1\xfe\xff\x00\x93\x1a\x1b\x18-gD4L}\x10\xd16@\xc7q\x92\x08\x08\xedE\xa4\xea\xe6\x9f)\xee)i\xde\x1a\xf3\xb0\x8b\x14u:\x07\x11\xcaI[\x1f\x93\xb0\xf8\xdcBh\xbe\x90\xf7\x9cX\xc8\xd8Z\xb10\xc5Q\xb0\x84\xd1f\x92\x9cb\x1c\xa1\xd9\xe3zon/\xa2\xe6\xdeo\xb0\x01\xa9\xc9\x15\x10\xb5\xd9\xec\xc7Q\xabiX\xb6\x11\x13\\~\x1e\xc0\xe5\xbf\xaa\xfe\x85\xe5\xa3\t\xb0\xb0\xc4K\x15LB\x8d\xb8\x1e\x0b\r\x18\xdc]\x0eu`\xbd|\xef\xe5}\xfdu}\xe6-\xaf`\xebw\xbd\xafrY3J\xe6\xa8\x85e2\x90\xfa\x7f\xeb>b\t\x0cL$imB\x88"\x9a(Z\x9d\x11"\x1b\x17~\x11\xeb\xdbB\xe0\xf4\x14?\'\xd8\x8c\xbd\t3!)\x18\x0bZ\x1a0\x94\x84\x89\t\xbb\x06}Z\xf5\xdf\x1cn\xb8\x1e\x16\x95m\xa4N\xae\x03\x8fC\x9296\x85\x14`\xad*\x87\xe5z\xa1)\xd7T\xa3\xa8\xc06\xb2F\x96\xa7hA\x92\xb3?\x86\x94\x16\x13M<n\xdc\xf4^\x14\xc0.\x1cl6"\x85A\xf4\xcf`\xc6)\xe5j=\x91\x89Nj\xe5\xfaT9\xfd\xad\x96K\xb8\x9d\x18F\x85\xce\xae\x8f\x9eq\xcb`\xcc\xda\r\xe9\xe6\xf6O\xa4\xf9\xf0\xc8[\xdd(3z\xa6p\x81\x13\x16\xa8\x82\xda\xe2\xe8}\xdd5\x8b\xa9N\xe0\xef\xad\xcc\xed\xa3\x9d\xca\xb4]1^JHM\xb2\xd8\xe4\xd7-\xee\xf9\x8e\xd4\xeb\xf8\xb1\xd7\x16\x90Z\xd8\xb4\xac\xb8^\xc0\x199\tI\x81\xb2\xba\xe4~7\xa2\x8fg\xdc!}h\xa6\x1e\xe3\xb3\r.\xbc\xbaV\xebi&\xfb8\x92\xed\xdc?\xff\xda\x00\x08\x01\x01\x00\x01\x05\x02\xe4\xeb\xbaJ\xf3\xc4\xd14\xa3bo\x1bz\xd5_\xb3\xf3R}c\x80\xe4g}*\xdfX\xe7g\xfa\xdd\xe8\xe4l\xac\xd3\x98i<\x7f+\xf5\xbe3\x9f\x15\xdb8\xad\xcb\x1c\xc167\x17\'<\x80m\x80\x9b6\x14|\x86\x14\\\xc4\xdb\x9d\xcb\xe6L\x05-\n\x93\x91\xc5\xd0Mc\x18>\xcd\xf5\xd8>\xc9\xc7?\xfde\xf66I\xc7\xff\x00\xab\xb9\x0f\x99\x14M\x86=9\x89\x9b[\x89\xe3#|<o\xa2\xbb\x03\x9e,\xe44K\x95#\x8b\xda\xf3\x97y\xd4\x92vd\xcf\x0b\xcc\xd7\xb3\xf1s\xc7\xf9+\x01\xf9Y)\xf1\xef\\\xd5#\x04\xaf\xa1jG\x9e>\xe8O\xa3yJ\xdb\x10\x9f.\x13e\x05yN \xb5\r\x8f\xc3n\xd3\xc3`\x92\xbci\x85\xael\xf7c\x88\x99\xeeX\x06\x9c\xe5\xbc\xaf\x1b$29\x9bS\xda\x99f\xd5r/\x0b\n\xec\xd5#tUe\xf8\xfexd\x91\xb2\x95\xbd\xbd\x05\xc0\'N\xd0\xaer1B\xdb\xbc\xab\xac\x9al\xdd\x0f\xcf\x99\xd3\xc1F\x18\xdb\xb8\x04\\\xc58\xe3\xe5\x12\xd0\xfa\xd4\xa9\xfc\x07\x01*\x9f\xe9M\x94r\xfc\x1d\xde\n\x1e"\x99q\x11\x86\xb6w\xe2H\xef\xcb\x12o0\xd4\xdeV\xaa\xfeV\xb2\x7f/P)~\xc1]\x8a\xcf\xd8\xa7\x91IfIL]\xcd\xbeD\xf1\xbcO\x15K\xe1\xd7\xe4\xaf\xfc(/\xfd\xa2\xe6\xe9\xb9\xfb\xb2#\xc9[ro1;\x1b^\xf5\x8b\r\x8f\x92\xe4\xab\xaf\xb0\xf3797Uh\x89\xbb\x95\x91\xfb\x8f(\xbd:D\xe9J|\xa9\xd2-\xc9\x8a\x11\xdf\x90>NTZmj\xdc\xcf-5\x97K+\x9cUz\xf0\xcdD\xb1\xd23|\xb5\xdf\x071+W*\xe8\xa4\x96\x02\xd77\xb2\xb3\xfd\xc9\x13\xd3\xd3\x93\x93\x82\x01D\xd5]\xae\x0b\x9d\x7f\xc4\xe6~\xc1\xc85\x8d\xb59{\x89\xd2\xad\x97T\x99\xf4\xe1\xb1\xc7\xef\xcb\xfeG\xb8\xb7\xcc8\xbeL/;\n\xb2\xc7\x19\x1e\x14\x81=\xa5l(\xb1\x16 \xc5\x13{\xb6\x1a\xb1A\xf6\x8a\x8d\xb1[\x96\x9eWJ\xf3\x94P\xee=V\xe7\xb1E\x14\xb3(/\xcfVNG\x94\x17"\x0f!\xcd\xe4\xae\xb5\xa6+M_$\xb5\xd6\xa92X\x84 \x83]\x8aX\x8b\x1c\xe8\xd0bcUH$\x8e\x1eJ/-^O\x8em\x98\xec\xd5\x96\xbb\xd9\x81+\xe20\xd9\xb3T\xd4\xb7<\x12W|g\xf7&d\x91\xb9\xceikFP\x8c\x95\xe4>Ku\xc4\x91Sl\xacRVc\x8f\xc5\x18uxe2Dc|U7(\xa0\x89\x8b\xdf#\xdc\xd0\xe6\xd6\xb3\x1b\xe4\xb3]\xa5\\\xe12\xfb\xbc}\x83_\x95\xdd$6\xefAr\x1a\r\xac\xe2\xe8\xa3+\xe0\xbe\xdb\x7f\x84~\xe8\xb8\x98\x99\x0c\xac!\xecv\xf6\xc9\xbd\x931\x9b\xd9\xfaD \x99%\x91\x81?;\xa2o\xb4)$;\xbe\xc7Vz\x97`\xe4\x85\x88\xfd\xae\x92\x1eV\xe5\x19f\xe5\xabL\x1d\xff\x00\x9fr\xaf?\x05U\xe7\x9d\xae\xa6\xe7y\t\xc4N\xf76B\xe6\xd8o\xeeG\'\x8d\xd6\xa3\xde"\xf6\x8d\xbb\x8c\x8c\xde\xd7S{\\a\x1b#n\x0c\xfd\xa3\x97\rd\xdc\xdco\xb1~\xaf\xf1\xb7\x1b>\xe0l2G\x8d\xa5x\xeb\x95\xe2\xaa\x9c\xc8VCT$\xcb!\xd8\xa6\xfdn\xc2>\x87~[\xe7Cv\xfe\xdf\x1d\x9e\x9e\xc5&\xc50\xad\xe1\xe66\xfc\x99?\xea\xc5\xb9\x7f\xc8\xceyE\xfeQ\x7f\x93C\xf9DM\xfcp\xda\x7f\xff\xda\x00\x08\x01\x02\x00\x01\x05\x02{P\x1d\xc8^2A\xf65\x07\xed9\xd4\xa2\xdc\xe9?\xa3\xb5\xc2#@J\xdet\xc2\xc6\x8fn\xe4apB\x15\x8dN\xae\x19\x0f\x1a\xee\xd3\x1ac\xf3\xcc\xc4XW\x8c\xad\x85\x10B\xca\xca\xdc?\x11v\x13\x0e\x99\xd0\xb7*H\xf0\xb6\xe8\x1cB\x0f\xcap\x0b\x0b+=YN\x91g)\xbe\x99X\xd3(\xbd\xab1\xad\xb1\x94k\x82\x9f\tjr2d\x9d7\x95\xbdy\x17\x91\x19Qq+\x1a7\xd1\xa1H\xfd\xa1\xf6\x9cQ.+\x0bj\xdaP\xdc\x10y"g\x0e\xac,,j\x11\xf4.\xc0\x96B\xf2\xd1\xa6\xdd0\xb2\xbf\xa4\xc3\xba=GV\x0e\xf2+2\xa67\xa0\xb7\xb2\xc2\xf4S1a\x11\xf8\x00@a<\xa9XC\xc7H\x08\x14N\x85\x83W1aac\xa1\xa1H\xde\xcehpsKP\xeb=\x18XXDea\x06 :\t\xc2\xd8\n-:\x12\x82\xce\x9bP\xc7\xe4\x95\xb8.L=\xfc\xa4/ [\x9a\xb75yB3\x15\x94\x0eG\xe3t\x9d\xde0\x8bp\xb7,\xae\xcb\xb2%e4\x17\x1d\xbd\x87\xe1\xed\xa5\x8f\xd4=\n\xec\xbd\xab\xda\xbd\xab\xda\xbd\xaa\x1ci\xff\xda\x00\x08\x01\x03\x00\x01\x05\x02\x0e\xd7*\x9d\x7f\x91#\xc6\xc6\xcfM\x92\xb1\xc3\x07F\x14\xc9HG\xd5\x1e\xac,i\x95\x9d*Z09\xbc\xa4D?\x95n\x1c\xed\xc7A\xabz1\xf9\xda5\x0e\xc7F5(\xfe&\xb3(\xb7\x1a\x06em\x01d!\xa6\x16\xd0\xb6\xa9e\xc2\x0c+j-X\xe9\xda\x99\x12\x0c\xc2zkQ~\x98A\x8e^7\xac=y\x08N\x9b\xb4Q\xack\xb1x\xd7\x8dx\x8a\x11!\x18\x1a\x15\xea^\xa1\x8by\x8e\x9bSk4/\x10^ \x8ca\x18\x18U\xf8C\x0b\x07NVVVu*1\x92\x19\xb9\xd5\xe0\r@hOu\xea\x8b\x17!\x1fa\xf8\x06\xaf*\x12\xa9\xc4\x9a\xdd\\2\xb3\xddeJ\xc0\xf6\xec \xed\xeb\x1ae\x12Tc*\x9b\x83\x98\x8a\x1a\xb8\xa24{7\xaf\x16\x98@\xe9\x95\x9e\x82\xec\x98]\x87F\xe3\x1b\xa1\x99\xb2\x02\xb2\xb3\x90\n\'VwG\x03@Q\xd3+8Y[\x91:\x8fF\x8c\xa6Y-M\x91\xb9jkp\x9c\x16\x11p\x0b\xcf\x84\xed\xc5\xdd\'\xaa\xab\xfd\xadj\x93\xf4\x98\x1a\xe0+\x90\x83e\x08\xb2R\xbe3\x93j\xb0#\xd98c\xac\x15\x9e\x88\xe3\xf6\xc3&@\xf7\r\x8b\x05e\xcb.C+jy\xd8\x0b\xfb\xfe\x1e\xeb\xba\xa9\xfa\x7f\xab3\xd7k:\x7f\xff\xda\x00\x08\x01\x02\x02\x06?\x02\xbc\x9d\xe6DZE\x8cU!\x8d{f\xb6\x93`\x99\x13j\xc2G[\x8c\x0c\x0c\x0c\x1f\xc5Jk\x7f\xff\xda\x00\x08\x01\x03\x02\x06?\x02\xa2p\x81\xbdP~\x86\xa1\xbc\x1e\xfau\x0f\xbc\xb2\x071\xd6\xb9\xd1\xedQ\x8eb\x11Nr\x19Cln\x82-y t\x1d\xc6i\xc8\xea\x1drcQ\n\xa8>\x87\xd0\x9fe\xd6L\xe4\x99)\xed\xff\x00\xff\xda\x00\x08\x01\x01\x01\x06?\x02\xaf!?\x96\xd6\r\xd1\x91\x89\xa6r\xd91\x05zd\x1fiZ\xed\xe0\xc1\xa1\xa5\xa0r\xe7\x9a3Y\x9bt\xe2\x93\xe9\xd6Z\xddD\xa9i\xf3Z\x15\x8d\xacN\xe8Q\xa1L\xd5|\xcfM\xd4\xd6jZ\xd2\xe6\xa4\xf3H\\%\x1a\x02(}>\x93\xccUS]A\xad\xcc\x01\x92\xd5Y\xf1\x08J\x88f\x8e&\xa7\xb7T\xea&\xf4i\xb5D\xde\xa2qK\xcd\x94>\x9f\xcd:KSC\xadF\xe5ifI\xa3MH\xf0\x8a\x0b\xaau\xab\xaa\x14\xd4j*(\x90g\x97\x11\x03|\x0f\x81\xe0\x9e\xcb0\x85\xcfP\x02\xe6C~\xc8\x9eh\x900\xc0\xa4\x91}\xab-\xdd\x0b>Qt\xef\x9ffj\x94\x11\x98[9G\xf6\xa9e\xb2\x94IT(\x18\x08\xfa:\x95:\x15i\xb7SM^\xfc\xad\xde6@D\xd4\xe9\xaa \x9fI\xc3\xb5\x93\xee+dR\xad\xe6\xdeaM\xe8\xab\x03Qi\x16f2\xc2m(JIbS\x12Q\xdb\xe6Z\x87\xe5\xa3\xa5\xaa\xc7\xc1Lyu\x1a\x96=-5$q\xde\x10\x03\xd9U,\xc8G\xcb\x18\xce\x1eW\x88\xbe\xe8+2\xb3\xf6\x85\x86\x11\x8d\xb9-^\xe3\xb6-\x11\xc1|Nvl1\xd4\x9f\n\xda\x0e\x16~\x8e\x87\x93Sii\xa9\xbaj|\xe1\xfe\x159\xa9\xd1\xfe2&{\xb7\xf6\xec0\xc4\x0e\n\xdc\xbb\xf1\x8c\xf4\xb4\x95L\xc5\xad\x90\xca8\xa9\x05\xde\xe9\xff\x00(2\xd3\xb3K\xdd!\xbf\xa4\x98\xf9\x94]\x06\xd6R;-\x11\x97\xd8\xc4A\xe9\x93\x99y\xe9\xba\x94q\xbdZG\xf4,\x9afUooR\xdc\xa9\xbbi\xfd{\xa1\xa9\xd2\x07 l\xcdP\xda\xce\xcd\xcc\xecq&&\x86b\x19\x17\xe6\xd6Q6\xa6\xbe\xcf{\x9b\x94o\x8f\x92\xb9\xa7\x8a\x9e\x9d?\xfd\x08\xcc\x7f\x84G\x16\xa7\xa3P\xf35\x05\x91\xfef\xcc\xd0z\xae\xf5\x9b\xd9wbg\x12\xec&\x85z\x94\xbf\x03\x11\x04k4\xe9Y\xaf\x15\x93\xe5\xd4\xf1*$|D"\xe8\x9e\xa5Fnzu\x17\x90\xec\x98<^\xa8\xabQ\xe7\xd5*J\xa63\x94q\xf0UNG\xfd\xf1o\x17|_/\xb1i\x94Y\xc5\x07\xadP(\xf7\x05\xf1\x918h\x8b\x96*6!\x84T\xd0h\x9aY\x7f\xbc\xd5\x8f\xfa\xfe\x04\xf8\xcf\xaa\x14e\xb1NaL\x99\x8c\xde\xf1\x9f3w\x98\xb4\xc5\xae\x13|e\xd4UG\x11\x8e\xf5c\x1c\x1a\x8a\x94\xe7\xdf\x04\xe8|\xc1_\xe1x\xa9\xa9\xd5 \x08\xcd\x95[4\xc1l%\x1fR\xf6\xbb\xda\xb0F\xd1)\xc3E\x8d1\xb2>b\xc6 \xc7\xe7\x18\xb6\xa90r\xa1; \x8aG\xa6;\xa2n\xd3&\x04Tj_\xdej\x9f\xa3\xa4\x1f\x11\x1c\xde\x11J\x9f3\xcf=V\xc5\x9c\xdea\x98\x10\x1b\x02\xdb`\xca\xb1\x12\xd9dH\xea\x18\x0fD~{zbE\x98l\x9e0Z\x95PJ\xde\xa6\xc3\x13\\\xfd\xf9L\xe3\xcb\xfc\xbe\xadF\xe9\x06.\xc9w\x14\x000\x12\xeco\xb1\x7f\xd9\x11\xe5\xfav\xe5\xd3!\xad#\xf1\x7f\x94>\xa5\x85\xa0\x1c\x82\x0b\xd4{O*\xe0\x07k\xd6\xa6sU\x0bj,\xecn\xfbc\xa6+c\x9aL%\xeb\xb6/4\xdcl\x89VQPm\xb8\xc7\x96j\xd4\xd92\xbe\x9b\xa0K\xc7\xb1\xbfB\xcc\xab\x99\xc0\xf9k\xdf\x1aJ\xafu}+"\x9c3\xa5\xa2\x13N\x9c\xaa\x834\xa0\xf6\xadE\xb5n\xa9O\x06]\x91Z\xbe\x92\xa7U\x94\xf5ib{\xd0\xc6w\xe3\xb7\x8b\xbe2\xf4h\x89`\xcb\x15\x18\x0c\x8a\xa2v\\\xa6w\xc7N\xad\x8e\xbc.\xb1\x984\xc6\xd8n\x13\xe8\xed\xba.\xfb\n.\x04\xdaa\x16\x88\xe2\xb0\xb3\xfb\xde1\xa62\xe2\xa5W\x85\xbd\x7ftN\xa5\xe4\x0f\xb1(9X\xa8q\xc5#|7I\rB\x8b\x99\x80\xbe\rJ\x88\x95\xe7c\xada3g}\xf6E:Th}*L\xb5d[\x98\xe1\x80\x80\xf9Fq\xed\xdb\x05E[\r\xe2B*M\x81\xa8e(+^\x92\xd4\x95\xf9\x85\xb1\xf5:1\xf8\xe8\xff\x00\x84Z-\xc6.\x8c\xa7\xc3\xecg\xacr\xab~R\x1b\xf7\xc3\xfc\x048\xf0\x8b\x04\x9a\xf5=\xf1\x92\xaa\xcba\xc0\xee\x84\xcd\xcb\x98f\x9e\xc84\xaa\x83%k{\xc4\n.f\x84\x897\xc2\xd0i\xd4\xb0\x89\xc2\xda\xc01\x93e\xbeF\xf8\x08\xc9\x93%\xca@\x9c\x8d\xa37\xef\x85\x012\x95\xe6i\xce}\x862\xb5\xbb!\x8e_\x98\xa3\x84\xe3\x13\xb9[\x08,,\x07\x01\x07/\xa4\xc0\xa4\xe2dX\x1bd53z\x99Fg9F\xcc`I\x04\xf6\x9bbns\x08d70 \xf8\xc5o,\xd5\x9c\x95\xa9\xb1\x14+\x9b\xac\xdb\x0fGQH4\x8f\x120\x81\xf4f\xd72Z,q\xd8\x18\xfd\xf0\xaf_N\xf45ZA\x96\xb0q\xcc\xa3\x10q\x8f/\xd4\x13<\xf4r\xb6\xf1\t\x9fM-b\x80\xa6\xb8k\x0f\x84:\xd5\xd2\xd5\xd5\xb9\xe5\xa7L~\xa6\x0e\x97\xe9\xd6\x9d3b\x97\xcdQ\x86\xdc\xac\x16\x17KOGM\n\xcb>\xb2\x9a\x93\x99E\xd9q\x9e\xd8\xbb\xa4\x82\xc0\xa6\xd7\xf1\xc2*.,\x84X\x7fi\x86\x9c\x03\xe9\x87\x07\x03\xc3\xbb\x08\x1b\xa0\xcf\x08\xcd\xb2\xf8l\x85s8\x93\xd9?_`=\x92L/05\x81~MiJ\xa0\xf7\x85\xe0\xc0J\x9c\xe9q7\xca\x16F`0\xfd\xb1R\x8a\xd7~\x956*\x00\xd8\x0c\x7f\xf4yf\x97X\xcdc;\xd2Ui~$\x94qy\x02\xa9\xf8+\xd6\x1f\xee\x8e\xad\x0f%)T\\\xdfQW\xd6\'\x1c>U\xa7\xb3\x94\xba\x9a\x9f\xd6Le\x15\x05\x04\xf7i\x00\x9f\xb23Tba\x98(\x01o&\x1a$yLLs,,\xf0\x11(j#\xe5\x8cv\xc3d\xe2D\x94\xdf|K\x1d\xbd\x83\xde\xa8r/\xa2p\xce}\x818\x7f.\xf3:*\xda\x1a\xeb&\xaa\'5\x9e1\x96\x95Q^\x81\xe3\xd3j\x05\xa1\x94\xee\x89\xf7[\x05\xb3\t\xb4_\x16\xd7\xff\x00I\x8f\xcfo\xe5\x8e\x06f\xf0\x94l\x85\xa6\x96\x93\xea\xef\x85\xa6,\xa6,\x9e;\xe1\xa3\x8a^=\x96]\x1c?t\x1c\xdc\xd8\xc3\xfe.=\xd0\xde\xb8\xb2\x137\xbd\xc1\xbeF*\xf5\\\x84\xcasn\xf4B\xdb>\x0f\xbc\xc0\x15,Y\xfc\x86\xef\xc4A\x96\xcbc\x84\x13\xbe,S\xea\x8b\x9e=\xb8\xb9\xe3\xe7/\x061^_\x99+\x7f\x0fg\xff\xda\x00\x08\x01\x01\x03\x01?!qKQ\xfb=Lg2l\xbe\xf1\xb84RAY\x03\xa0\xab)\xbd\xd2\xb3V\xb3^w\x0e>\x10\x85\xa8\xb4\xedF\xaf\xc44\x80y\x86\xfdy\xe8\xb9UU\xbd\x92\xf9\x8b\xbas\xe2\xaa\xa9}\xc1\xf5\x1eC7\xb4\x7f\x12\xd9\xd3\xab@\xc3\x11\xab\xfb\xa5\x13\xc4v\x8e\x80a\n\xf8>ny\x92\x1fi\x96\xbb\x97\x1aic\xd4I\x03\x8d\xac\xa1i\xf4\x81l\xaemO\xf2`\x99M\x0e5\x04\xa8c\x8a\xb3\xd0\xce+\xa9d\x01\x95\xf64E\x8aK:\x87\x16\x14|\xaa\xb3\xdc\x1dg\x15\x15\x8b\xef0F\x98\x05F\x0e\xa4f\x19\\\x16\x8c$\xa9\x05\xa9#h\x15\x1b\xcd\x8f+\x155X\xbf\x88_\xd0\xf5\x83G\xab\xca\x177\xc2\xf9\x86\x07\x85\xaf\xbe\x04j\x08\xc3\xbaW\x8b%\xfc\xcaZ\xabi|G\xca\xcb\xb5\xc7gp\xd1\xcc\xc8\x84\xb3\xe8t<\xe6\x16\r\xdc\xd4l\x96=\xca\x0bly?0\xdf\xd7\xe6\x9eO\xf9\xb9\xe8Q\x9c\x7fs@\xe0\xf8D\xb2s,\xe6vh\xe6U=,\xd5_A4\xca\x82?%*|"W\xb83D\xd37\xec"t\x0f\xfeD\x90\x06\xa9\xf0\x12\xf4\xc1\xe7\x99]\x01a\xc9\xfcT$\x8a\xbc\xa4\xe9\x00\x0f\rg\xfe&\x11\x18\xb6\\\xd3\xed\xe8\xe7\xb6~s%\xabK(\xcc=w\xd3\x03\xa4\xa5\x00)bk\xc8^\xc3\x04\x9d\xa3)u\x91\xf1\xbe\xf2\x87\xddz\xbe/O\xac\xbc\x19mr\xf0\xae&wIM\xcc\t\xde\xc7\xdc\x19X\xa3\x04]\xe0\xbe]\xf3\x0c\xb3\xe3\x07\x05a\xad\xed\xec\x97\x03\xa9p\xc0\xe6U\xd0\xd0\xb0\x96f\xbf"V5u\xdf\xf5\x07\xe3\xe0\xe3\xf4h\xdfs92\xf1\xaf\xac\xec\xb5>^\x18\xbb4\xc1\xcfW9\xcc\x07\xda\xbf\xd9\x83\xea1V\xfa\xb3\x0f\xd0\xdd\xccO\xda\xf2.mrO\'\x8a\x8d<\x17\xa2)J\xad\xde\xa0\x90<\x86\xa9\xf0\xcd\x95\x1dE[X\xc1W\xf3(\x08\xf0\x87\xdc\xa85dwD\xd0|q6\xf6Y\xc09W\xcc(9e\xee\x8b\xf0?\x13\x0f\xe4\x12\xb0e\xe5")_\xa2\xa9O\x0f\xcb>\x8d[\x9e\xf6\x13<SH\xcc@\x96Z\xb3\x01\xdb2\x19\xe1\xbc\xe5\xe0\xe7\xde\xa1f\xb3\x9eU\xb4{b\x92\xfcfe\xa09b\x88\xd7F\'\xeb\xb6X$\xb6\x03\xfc\xa5\xa5\xa1\xce\xe6K\xe2\x0b6p|\xa2\xf1\nr\x1ecwm\xda|DFw\xf2b\xd3\xc1p\x8d\xa2\x94\xbc\xfb_\xe0\x95\\s\xd1\xed%\xb2\xe9u\x96_HA\x94\x83E\xac?d:v\x08\xc5Phb@\xdd\\V\x01-\x15\xe3\xab-|\xde\xbcM\x00\x95\x14\xddU\xd2\x04CcVs\x9e%OA\xff\x00\xf1\x97\xbfJ\xee\x88\xfd\x08\xcb\xd9t\x11\xd2?\xb3\xf8\'(70\xb8\x96\x07\xd0[\x8a\xd4\xe7N\r/\x05\xf8\xcc\xb4\xf5\xed\xc8\xd7\xe6\xab\xe6SN\xda\xca\xa5\xb2\xca\xf9\x97M\xc3\x8b\x99lmA\x0e\x9d\x18D\xdd\xce8"\xb7&\xc1k\xd9\x88!*\'\x040^\xd0s\xc4\n$\x11v\x80\x06{\x87\xbd\x03A+\x90\x94\xb0\\\xa3A\x16\x07\xd7.\x89\xbc\xe6)\x1c4\x9fh\xaf?\xa7\xa8\xe7\xd45\xadA\xb6\x8e\xe7*i\xe5\x878\xd7vh\x04\xd4\x0c\xe7\x84\xaa\xb3\xe9,+\xccPtY\xc4\x05\x00\xb5\xc0\x10\x18\xa7\xc1\x80t\xd6\xe3\xa5D\xc8\x00\xe46\xe7\xa9y\x07Eg\x1d\xb1\xfd\xaa\x19\x12h-\xd5\xb1\xa2\x00{6O\xbejS0\xe8L\xaf`\x07\xe6\x03n\x16W\xc7\xb4\xb4?\x00 \xf1u\x01b\xd6<\xa7p#\x81a\xc7\xbco_#\xfd\xc5]"\xbb\x1d2\xce!\xdc\te$\xbe3\xf0 \x99\xbdC\xb7\xfdK\xe6\xce\x9eNO\x91\xe4\x999\xb9,\x87jc\xf1\xeeFY\x95$\xb2\x9b\xb7g\xb9.\x14\x9e\xca\xb3\xefL \xed\x87\xab\x1e=\xcac\x01B_\xa7\x80\x1e\xe4\xba\x97X\x94\xd02\xfe\xd8\x95\x0f\xcermq\xa3\xe3\xd2\x08\x81h\xb6/i*\t\xe2\xe6M\xad\x1fL\xd7\x98#zy\xbe\\c\x11H[r\xc0,\xbeX\xe8F\x8d\xcf,7\xc4\xcek\xdf\x9a\xe7\xe6-P\xba\xe4\xfe\xa5\xf9\x85\xbd\x8b=\xe55\xd4\xe1^|\xc1\x17\xe9\xd0\xa8N]}d\xc0\xfc\xf7\x16\xc1\x15\xc6N\x7f\xd8\x17\xea\xc1\x81x\xb4\xe7\xff\x00R\xce\x08\x81Z\xcdOp\xcc\x91\x88/=\xbf#\xf6\x96\xbd\x8e26\xb5\xdb3\x0e+\xec\x1d\xa5\x8f\x89h\xe6\x98\xff\x00\x02)8\x11\xe7Q\xd3T\xb9\x18\x14\xd1\xcb\xf39\xee\x92\x17\x07/E\xeb\xed+\xbe\x96\x86\xcd\xd1o\xd2\xbcL\\\xe3\xf0N\xe5\xd7\xba]6\xaf\xf7\xb3\x16\x0f\x05}\xe5X\xf7\x08\xa1h*\xf7\x95\xacSQ\'\xc1\xa6QSNIg!\x8e\x9a\x97\x98\x0f\x90\xc7\xe0\xde\xd0\n\xba\x0bW\x00k\n\xd2x\xbc\x9e8\xe2-h\xcd\xf2\x02\xfe\xb2\xe9\x83\xed(\xaap\x91\xf5q{_#\x16u\x9d\x0f\xd1r\x84\xe2\x99\x8a\xe4\xd9%2\xab\xb0#\xde\x1f\xdaZ\xb1\xc5R\xbe$\xda\xc8]\xb6\xab\x0e\x8167\x8d\x1ec4\x9d~\t\x90;\xfcy\x94e\x7fp\xeab3\x00k\xc1\x10C\x97>\xd2\x98\xd87\xe5\xe6\xc80\x12$\xc1|&\x19\x91\xf7\xc6\xf1s\x01U\xd3|\xd9?A\x98!K\xd3Gp\'H\x81D\xa5\xbc\x0f&M\xcbj\xe6\xa9\xd9\x9c,\xd3\x05\x0e\x0bW\x93\x0ce[Z\x0f?1nU\xd9\x9f\xeas\x13\xc1/\xdb\xff\x00\xb8\x03+\xc5\x81\xf9\x9c\xca\xf2b4\'-\xe9\xca\xf6\x9b\x93\xef\x90\xedy\x9cm\xe3\xf0M5\xf0\xc2S|37.\x952\xbfT~`N\x05}\xdc9\x9d\xae\xa96\xd3\xe2r\xd5\xfb\x07\xc5\xc5\xd9K%\xe3\x96\x94\x02\x8d\x0c\xf8\xc0\xcc\xf1\x12y\x9e\xa7\xef\x89\xecV\xf8E>\xd4#\xc4\xfc\x7f\xa9\x7f\xf9\xbf\xb9\xed\xfbb\xe0\xbfH\xcc\xa3\x91\xaf\x8ef\xbdU9\xef\xf7\xdc\xcd\xcf\xff\xda\x00\x08\x01\x02\x03\x01?!L\xca=\x16\x00\x8e\x8a\x98X\xde\xe4\xa4\xbf^\xd70\x82L!P\xbf\xa7]B\x1b\x99w\x13\xd1IPMD12\xc1\x07\xea\xc6_A\x825\x19IL\xed\tR\xbfUzs\xfa+b\xf8\xf4X\xf0\x10\x82\x10\xc7\xfc\x82,\xb6\x11\xac\xb5\x89\xca44\x88\xceJ\x06\x89\xb1\xa4n\xde\x8f\xcc\xf3\x85\xa5\xfe\x8b#$nZLX\xc1\nz$\x8d\xdb\x15\xf4\x9b$\x131\x9c\xe8\x8b\xe0\xf4ZJ\'\x9c\xacL^=8\x92*\x8eH7Dj6l\xbfpp\xf4\x8a\xd7\x84%IQ\xf4\xa9^\xbb\xeb\xc9\x98@\xfah\t\xe9Z\x85\x1e\x81\x12\xed\x13o\x88\'\xfc\x07\xd6\t\xc5\xf4\x0fB\x03oK\xcb\x84\xa9b/\xea}@\x18\x80U\xea8\xfa \xa8\x11\xf4\x1fA\x08\x0c\xc5T[*_r\xcc\xfa\xb6\x9e\x81\xe8\xf5\x99dT\xbe\x11\x1a\xf4*Vj%C\xd7\tr\xa2\\)))\x06\x17r\xdd\xc1%\xfav\xa6\xa6D\x7f\x88\x842zY\x17\xa3z&\xd9\x86\x98\xf4\xe7\xd3\x98\xfa\xb0e\xfa%\xbcF\x9e\xd0\xe0\xc0\x96l3\xf6\xd8\x1f\xfa\x9d\x027\xc4\xca\\V\xa1\xe8F\\#\x12Uz\x12\x97\xa2\n\xae%\xdb!x\x19\xee\x97\xda\x04f\xb1\x02~\x88\xfa\x13\x9f\xd0\xc4\xa4vO)^\x95EMz\x1d\x1e\x9f\xff\xda\x00\x08\x01\x03\x03\x01?!\xc5\xe9p\x80Oy\xe6_\xf8@q1#\x13\x0cV[=hfe\x9e\x92\x9aj\x070~\x83\xd1\x1e\x8b\xf5.P\xe5\x8e\xe5\xb1\xb3\xe2\x05Q\xbf1\x91m\xf5\xdb\xd0\x8b\x1e\x84\xa8EJ\x95\x1f\xf9\xd9\xfa \xdc\xa9^\x8a\x95\xe8A\x13\xfe)\x14\xeaTl\xb8\x86\xd7\xfb\x1e)H@1\xf4\x9eA\x15kq\xeb\xe8[\xf4T\x1b\x11\x84f\xd0S\x823*\x0ep\x08t\xcavN\x02\x10\xebr\xd6\xdb\x95\xa8D\x19^%\xa5\xe1\x0e\xdfN\xc7\xa10\xc5\xc7\xa3\xd6\xd4\xe1\xa0|E\x98-\x93rC.R\x92W\xa8\xc2\x0f@\x81\xfd\x11J\x90\x1a%\x1e\x8ag9\x98\x81u\x1b\xc1}5\x0fB_\xeac\x95\xb2\xdc\xe5\x07\xad\x08T\x8c\xd4\xa7qW4\x12[!\x17\xe8z\x0c=\x17\x17\x10\xc6-l\xcc<\x9co\xde\x11\xd1\x16=\x1fD\x99I6\x9d\xc0T\xb2xJ\xf0\xcb\xf4\x17\x97.\\\xd4zG\xd7\xd9\xdc\xb4}\x1c\x9e\x92\xd2\xf6\x12\xe3\xd0\xde\xa3p\xc6-\xf3\x12Q\x15\xcb\xf4[\xd5Y\x00\tu\x99z|\xd3a\xa6`\xce;\x8f\xc0A\xb2*\xf1\x1f\xdav\x99\x161P\xb6"%\xf0\xe8\x83\x18MM\xcd zT\xa9P^\xd2\x0b\x92^\xdd\xc0Jf~S&\x9b\xed&\r\xfbe\xd0d\\\xfb\xca\x8a%\x83\x93\xc4#\x06>\x97\x03\xd4z;~\x12\xe7\x0ce\x0c\xc4\x94J8\x9e)n\xe1\x05dW\xf4x\x98\x8c7\xe9\x98C\x16\x93\xacP-E\xf1/\xc4\xbf\x12\xfcKb\xc6\xf5\x1b\x9f\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00\x00\x10\t\xcc\xe8\xe3\xf3\x07\xd1\xe0-@>\x0e\xc8_\xfd8\x9b I(\x11\xfe\xa7G\xb6\x00\x00\x00\n\x04\xad\xfaE\xa7@\x12\xba\x85\x7f\rh<\xb8\xbah\xef\xcc]\x15$\x12\xfc\x91\xd5>z\x0e\xcc\xdf\x9cfa\x0b)\xb3\xf9.o\x08\x1c^\x9d/\xc0D\xb5\xed$Q\x1e{\x87,\x98<\xa1\xbb\xf3\xa2X\xd7uP@x\x80\xd4\x97\xc6^i\xc31Hl\x1fS\xbb\xee\xc7\xa9w\xddAuo\xff\xda\x00\x08\x01\x01\x03\x01?\x10\xaf;\xac+\xcc@\xbc\xb8L\xbc\xe7\xd4p\xb2Z\xd8\x01\xaa#\\\xc8\xc6\xb5\xaaeZ\x8dIf\xdc\xd0\x10$3\xd1\x0f32\xe78\x15*B\xd4\xbb\xb0\xa8\xcb<P\xcd*\xe2\xed>#hfu\xc4\x06\xcb\xa1\xd6\x95\x01f\x07\xe8v?I\x7fOK\xc3\x17\xd4\x9c5\xbbL\x13/*\x9bAf\x94\xa9qOU\xef\x8a\xdd\xaaP\xe2;\x88\x05J\xe0\x19\x82\xe0\x0b\xf1\xfe\x96f\xacF\xc7VC6\x04+OL\xa8\xdf&M\x02\xb2\xd3\xc1\xd4j\xc5\xc8L\x0bu@(0c\xde*\xd2V\xc8\xc0\xab3\xde\xee6\xb8\xa4\xc0TB\xaa,F\xca\xbc\xc7\x1b\xa2\xa8tX\x008\r\xca\xd1B\x94X\x9dT\xa6\xc9\xdbt\xc7M+\x87\x189\x08\xbf\x9a\xa0\xb6,\x9cpk:\x97\xcc\xe0\xb2]]\x01\xcd\x104\xca\x87Wr\xd4\xd8\xd6JA\x86\xb7\xc5\xe8:\xdc\xedZw\xa5c\x87\xa9\xcf\xf4\xbfa!V\xb9B"B\x94\x0e\x80\xe0\x0c\x10\x9c\xff\x001\xf4\xa0\xa0\x1b`\xbeU\x07\x96!\x16\xa6\xec8\xdd8H\x12\xee_bGQ\x99\x17\x84&\xe3\x11\x8c\x1a\xcb}_5\x11o \xb6\xd3\x93\xb8\xcaj\xd6\x10L\x13WT\xf8\x81KrS\x89\x1b*\x818\xbb\x990R\xec6\xf6k\xef\x1ePR\xa8\xbd%\xd0"\xb5u\x930\xcb\x009(\xefz{FH\xdf\n\x14\xb4\xddY\x8d\xea\x1a%J\xfd\x15\xe9]\xfa,\t\x00\x0b\x981^\xa6\xf4\xd2V\xa9v\xb7\xccY\x07Dx\x18X\x9dU\x91{\xa6\xc1A\x10\x1c.OyC\x10\\q\x9c.\x95\x95\x83\xa2v\x85@r\x0e\x91\'c\x1b<\xa9\x15\xed7@\xe1[\xe90f\x0c\xa4\x00\x04\xa08\xd90\xb9\xad1\xa3\xc2@\x8c\xa1X^\x00.).\xdb>x\xc1/L\x9aN\x9c\xa58?\xf1L\x00\xe4,X\x82\xacZ\xec\xb6\x88\xe5i\xe9e\xad\x01\xac\xbe\xc5\x14C\x9dx\x1e\xce\x11\xa4}\xe2|0\x91\x90\xd63AM\x8a&8\xa2\x87\xf2\xde\x9d9;\xd6S\x89\x1a\xee\xd1[\xf6\x9b\xe8\x8f\x14\xc8\x83\xab\x9a\xe1\x93\xff\x00f\xa5\x94\x8d\xf8\xc7\x13(\x19\x8a\xc2/O,\xa8\x10\x8e0\xc6B\x8d\xa3Km\x17>\xc4\x89C\x95\xd6\xc0VB\xd8\xc70\x14\xe0\xc5Y\x16Ze\xac\x13\x83.\xac:\x87\x90J\xb3\x0f"B\x0eXUB;B\x95\xf8\xa8H\xbd\x85\x11\xf5\x96U\xde=j\xb3Z\x00\xba\xea\xe0HGeac\xb5\xfc\\\xa6\x1db\x15\xb8(o\x9c\xe8\x94\xfb\x9b9&\x13?\x13\x0e\xaa+\xc1\x7f\x1e\xe2\x00z;D+-s\xb4C\x03B(\xd76\x03_\x14mupDXuB\xab\xe03+\xe3/(sH[\x15\xbf\xe4\xdb\xddo\x8f\x98\xa5\x9a\xe0\x1e\xab\xd9Nz\x82\xd3\xe8\\\x94\xc6u\xfaD\xd8\xe4,\xae\x97\x03\x93\x88\xbd\xf9\xd3.\x97\x16%\x0f\x97\xb8P.\x92\x8a\x07@\x9d\xbf\x06b\x0e^>\x14\xe3\xaf\xdd\xcc\x9bI\xfcp\xd7\x99\x95\xd0\x85`\xf6\x8dJ\x9fs\xf4\xba\xcf\x88\x15f\xd8\xa9;\xc9P\xda\x07M\x9f\x98U\x07\xe0\x8f\xcc5\xb8\x05\x8a\x1cg]u\x03\xa1\x1de\x12\x9bZe\x8fp\n\xdb\xbc\xb9\x8dq\xc1\x0b\xee\xcd\x00\x98\xe8?`\x03\x98uB\x10E\x1d[e\x10G\xa0\x02\x1d\x0f\xee<\x06b\x1e\x10\\&\xec\xddY\xc0\xd10\xce\xbe\x07\x9a\xbc\x9a\xf6\x85\x84\xc1\xd1\x00\x1fc.\x91\x17[\xd9B\xa1\x87\x1b\xe4\x95b\xe3\x8b\x8aRS\xc3r\xb6B0\x85\\\tU1\xd4 \x89:\x14\n*\xd1\x07V\xca\xc5\xe0oUm\xfcKsX%TL\xcf,\x11\xa7:\x89M\xb0\x97\xa3\xabj(\xab\xba\xe28s.)\x95K.\xa3||g\xf8\x88\x80\xe1x1|\xf0\x10\xb6\x14\xa2\x97\x01\xba\xe6\xbf\x11\xc1\x9ag\xd3\xc8\x1a\x1a\xad\x16\xcc\x00R\xd0\xb7\xce\xdd\xb0V\xca\xc1|Z1{\x0b[01\xb4\xcc\x01\x93\x8c\xcb(\xa3\x87\x12\xa6\x7f\x80\x00\x05+H\x8c|\x84\x06\x0fyL\xbe\x9f2\xbbXZ\xdb<\xd5\x80\xe7l!\xb8\xe1\x9c.\xf3\xefr\xc7\x0c\xe2%N\xf9\xc8\x8d\xa5P\x90\xae0\xc48\xccJ\xe60\xa73H\xc4\xa2m\xb0\xd7\x15\xceAjU\x06J\x06Vi\xc9\x03\xc2 8{\x00\n\xef6\xe7\xe2"\xa1\xb7T\xcbY\xd5{DJ\xc5\x8c\x95/\x00\x84p\xd9\xab0\xe6#\x06\x1ak\x94\xa1\x93\x8106\x033\xc0\xa6T\x0emD(\xach\xd4\'\x8bmAA\x90\x83f\xd6\xae\'\x14\xf0\xd0\r\xb0\\)]\xcb@\x15<\x85Y\x19=\xa6\x12\xec\xa5\x0c\xaa\xf5PU\x8e\xe2\x1f\x11\x1a\xd0\xa0\xd2<2\xc1Ls\x13\x80\xf0X\xf2\xb4\xf7h\x99\xb31\x9b*\x10<\xa1\xdbsD2\n\r\xd1\x98\xd9h\x82\x06\x8d\xb4R\x9b\xb2\x8a\x87\xf5b\xa5\xadB\xa77\xc0\xcc\xa6\x80\xeb\x9a\x7f\x01c\xf1\xdcu\x19J\xfb\xb1\xf3R\xe1\x1c\x8b\xb6}\xea)\'\xd1Z\xae\x00#\xfa\x06`1\x14\x03:f\x1a\x12\x94!\x9a\x11\x80\xac\xe0\xea\xec\xb3\x82\xc1\x82\xda>\x11\xf2\x15y\x05s0n\xcc\xe3\xa2#\xe6a\x93]\xe1\x9b\xe4\x82m\xf6m\x08\x89\xc3z\t\x0b-A\xc0\x15Ki\x9cW\xde\n\xa9W\xab\xa1\xd3\xdcc\xc9\xd8%\x03H\xe5\xc3\xb3\xe4\xea.\xbf{\x04,:q\xef.\xc5\'M\xb3\xee>\xd0\xa6\xcd\x1c\xa0\xd6\x17\xd9\xfd@JF\xd0\xd42Q\x02\x12#Z\xd9wMz9\xdf\xbdX\x96\xe2\xf9\x1f\x16c\xea\x98J\xb5H\x1f\x08\xcd\xe4\xce#\xc7\x1f\xe3\x98\xe1;\xe4\xd3Ll\x95\x002As\xd9`\x00\x15\xefEm/)-\xc3SV\x15j\xb0\x1f\x02_Q5\x8e\xce\x89{\x14\x0fLQ\xd3\x02d&\xa1h\x94\xe3\xb9I\x10-\x9a\n\xca\x8f\x95Q\xc0\x85\xef\x00\x12d\xaa\x01\x1a\x079\xba\xb9I\x12\x011\xea.\x83k\\KZ\x0c-D\xef-\xb5\xcc\xcd?\x9b\x97\x8b\x90\x97\x8e\xe3\xcbdY\xc8S\x14\xdf9\x98"\xca\x00^Z\xbb\xe69\xef]\xe6\x14\xde("k\x0b\x14\xd2\x8c\x84KB%\xf1\x06\xeb(\t\x92\x809\xad\x88^P\x0c\xa0\xcem\xc7\xd0\xdc\x00q\x12\xe4\x04\xca\xbe&\xe1b\xc9q0s\x83\\T>\xc8\x8fh_ff\x8e\x1e\x15/\xb0\x04\xae\x17\x9cY.\x03#\xa8U\x83M\xd6\x82=2\xf8\x8c\xe8P\x00\x058\x11\xefq\x14\x05K\xd8<\xb8i\x106\xe7P\x95\x9e\x90\x91\x11\x97g7J\xa3U99;\x03*+./x\x08rx\x18\x1cJa\x8ad\xf5x\xc2\x92\x84\x15\x8a\xe1\x86\xb4-\xd3\x94\x15\x7fD\x8c.\xe2\x00\xde\x11\xb6\xd5(\xd5\xe2\xf0G\x16\xb3E\xadi\x19\x95\xcfr\xb0d\x9c\xf8$\x15.5\x7f\x8a\x1ah\xb3\r`\xe1\xf6\xee\x01\xa1.\xbc\x96\x0f\xb7\xdeqe\xf4\x94^\xa5@8/&=\xe0\x92*s\x05\x13\x0fo\xf7\x0b\xa8 8\xaeE\xe4)1\x13\x93\x01O\xd3?$Er-\x9e\xcaE$Q\x1e\x95\xdaB\xde@\xb3\x89P\xc5\x8a\x01L\xd0\xa3\xb3Zj\xbc\x14\xfa\x93T4\x1d\x8b\xb7\x0cJ\x88\x01,\x14PA\xa0s\tp\x08\xa1\x086\x81\xa6[8\xe9l\xd2\xab\xf5\x8e;\xc0\xd6/\x80\xcf\xa4\x1b\x9a\x06\xb7\x02\x1b\xa5\x82\xda\xb2B\xe0^Y\xc4609\x98\xd9U\xec\x82\xdc\xb0-\xa2\xc2\xd6\xf7\x9c\xc40\xc58\x84\xb8\xcbD\xbc\x97\xa8\\\x9dT\xe0=\xb8\xa7\xe6\xbf\x88w\xd0\xb16\xbb\xf89%\tQ\x1eM\x1bx\xb9\xbc\xcb\x19\xc7&\xeaUhMh\xe1\\\x88\xc2\xd5R\xd4\x94n\xdb\xfb\xea\xee\x0b\x05\xbf\xedCZ\x99\xa1\x9e\x9f\x88\xa8BRv#x\xa7DP\x17*au+Q\xee\x7f\xe6@F\xc3\xbc\tB\xeaU\x93\x80\x11\x81V\xbd2\xb3\x9a\x04\x95Uom\xad\x1cq\xd6!&[@+\xdc\xaa\xbecx\x9d\x13\xed_\xca\x00\xbf2\x0f\xd6\xe5|\x8f\x85_x|\x05\xab8\xef6~\xd1NA\xd9_\x99\x8b\xd3\xa0\x83\x9a\x9c\x0bX\x1d\x90\xace\x10\xbcen\xaa\x8dK\xf34\xc9_3\x9f\xa4\xba\xb9\x7f\xc2\x1b\xb9\x89\xdf-\xed\xce\xb3\x1e\x1a\xf5\xf7\xebsCr\xf3q\xf1]Ty\x9bL+F\xbbn\xaa\x02\xd0g(0^\x11y\xbe\xa0mW\xdb\xfcB\x8bKa}\xed}\xef\xcc6"\x8bd\x11\x160g\x04\x12\xb4\x16)c\x11\xc5\x91\x07M\x9f\t\xd4\xb6\x93kG.#%7\xb5\xd7}\xcba\x17\x91\x8b\xe3i\xf7\x96\x18\x9c-\x15\xb6\x1e\xcc\xae\xdc2\x1c\xb1\xd2\xff\x00bS]%\x9b\xfd\t\xdc\xdf\xba\xaa\xb4\xb9\x87\x8c0\xf1x\x9d\x97w\x8fy\xff\xda\x00\x08\x01\x02\x03\x01?\x10D\x1d\t\xb25Ub`Z\x0e\xf9\x8d\xce?n\xe1F\xf3e\xab\xb6\x10tl\x7f0L\xc3\xe8\xf4\xc2\x98x\x14\xec@\xd1{\x97\x8f\x12\x95\x830\x0c\xb1.\x93&%\x8d\xa5\xf3\xe8\xea8\x1aCs\x00\xec%\x9c\x10\x01\x89m\xe2\x00\x15\x9d\xc7\xa2\xac%\x04\xc2\x0bu\x03P\xd4q\x1d\xc9VQ*g\xcf\xa4\x90\x99\x8a7*1\x1be2?AR\xa2\\\n\x9e^\x854\x87\xaa\xa2i\x94T\xe0|}\xc9\xd7\xfcM\x92>"u,\xc4\xcd\x1c\xe2\x8fx\x7f\xc4\x8b\xe2[H\x84\xc4,6\xc6\x013Oo\xf6=\x95~X\xc3l&\x98&\x14C\x1b\xe4)\x8f,p\xca\xe3\xf7\xef)\x9e\x1e\x7f\x873\x16\xa2\xa5\x99%\x7fB[`\x98 \xb4\xb3\xd1\x17\xe7]J\x94\xb9\xcc\xfe!\x02\\\xdd\xb3\x00\x0c\xdcT\xe3\xbf\x7fFn\x88\xaf\x11\xc7\x0f\xbc\x9f\xc1\x0f)\x82c\xe6\x0ba\x940\xde\xccN\xc4\xf0\xcax34\x04\xf0$\xb9\xb60e\xdb\xd4G>c\xa5\xe9\x03,\xb3\xb1A\x94%{\xc5\xe9\xccZ\x97\xe26\x8b\xc0\xf2\xc1h\xba\x89\x0b\x99\xbcB0\x94\xccLL& \xb5(\'\x02*\x8a\xe6\\\x1dJ\xdd\xe2\t\x00\x82\xb7hD\x95S,\xcf\'*7\x87\xe8\xcbn\xda\x00@\xc4\xdaT\xa8\x91+\xd3\x02\x0c\xd9\x11\x98\xcab-_\xb4\xa1#An\xe0\x08\x15\x1d7\x15\r\x901D\xab\x98$Q}\xfby\x94\xd5\xad\x8c\xe7\x19b"S\xe9M\\s\x89^\x9a\x80\xd5\xca\xc6\x91\x1eCL\xeffWg\xac\x151\xc7\xa0\x8e\xa2\x99.P\x81Sc\xc8\xf1\x025\xa8\xd0\xc4M\xd0\xbfBt\x8f\x84ELK\x82\xee\x1e \xec\xe8\x8b\xedf\njyu(\x07\xcf\x0cq\xa4\tt\xb9PT\xd6\xe6#\x81\xad\x10\xa4!U\x1aTaf\xa6\x01 \x11\x13(\xd2\x91N\xe3\x9c0K\xd9\x1c0\xd5^\x07\xf8\x9b\x17\xcb\xfb\x99(V%\xe4W5\xf8\x805\x98<\x8b\x02\xe1+\xdeRk\xe5"\xdb::\xe6\t\x83\x14\xdb\xcf\xa5T%\xca\x08\x1fOC<C\x89a\x08;\x9d\x82\x86S\x9f\xc2Q\xf5\x19K\x81\x8f\xe3\x05E\x7f\xd27g\xde\x81\xff\x00\xaa`J\x1e\x08\xa5k\x1b\x84\r\xbf\x11\xd9\x12\xe5\xb9\x84\xf9\x96\xaa\x871#D\xc7\x06\xa3L\xcbp\x8c\x977\x1cmL1\xa0"\xcc\xbb\xcc!O(\x07\r\xfa$\x86\xa2c\xaa\xaf\xac\xd3\xd6\xd7\x9fKq3[\xeb\xd1\xaeeZx\xc1Gu\x16&\x9c?\xbc\xc0T\x16\xe5\x95\xdb\xf3\x0eG\xf3<\x9f\x98v\xfebW\t\xf7\x87._\xc7\xa7\xff\xda\x00\x08\x01\x03\x03\x01?\x10,#1j\x11\xf0\xc2E\x8a\xe9\xe5A\xe0yeM\x08\xd0U;\xfag\xaf\x12\x8bJ(\xc2Q\xa7\xb3\xc3\xab\xc6r\x8e4\x94\xfcz\xe0\x1d0\x1a\x0edq\xcd<x\x89\xb4\xd5\xb1\xe0\x97\x19A\xe6\x12\xaeRK@\x18\xa9R\x08\x82\x8a\x8ac:\n\x87e\xde<\x91`7H\xbf\x8c\xb7\x05^\x8a(\x07\xd9}\xe6\xd2K}q\x10\xd4\x17\x01\xa9\xbebC\n\x95\xe8\x0f\xd0\xc6\xc7\xfc\xef[\xa8\xfa"\xccsa<\xd2S\xb8yB0\x83\x11@^b\x1f\xf1\x7f$\xa8\x11C\x024\\\xbf\xc1\xcc\xe9\x7f\x7f\xa3Dg*\xf3\xfeF\xf2\x04\xa2M\xf7\xa0\xb0a\xbfx.\xab\x93P\xd0\xb3\xdf\x89QY\x10\rb%\xc7\xe8\x13\xa2o\xb1\x1d\x10\xf9\x7f\x89~\xdb\xdcw_\x13\x86-\x1d\xc4\x9a\xfd\xf8:=\xa5\x97\x1b\x8b\xc0,n\xc8ywX#)\x1a \xdb\x15\x00\xa7\xa3m\x16\xca\x8b\xe9\xb9\xe3\'\x12\xa7re\x1c\xb0\x01\x89\xacs\x811\xd1*\n\xb2\xf3\x04?(.\r\xfbM0\x96J#\xf5\ri\xf4\x85h\x1bz%\xae\x10\x87^\x8b\xc2\xb3\xbe$\x1b\x8a\x89p\xf8`\x8egp(|\xf7\x0cK\xa8\x00\xd2]Y"\x07\x89\x90\x8cE\xc0\xfc\xea\x1cK\xb3OE\n0l\x81Q\\\x19u\x1c\xa8\xe6\x00\xdf"L\xa0\xca\xe3\xda\x16(\xc73\x19\x1c\x85^\x18\xa1\xf1\x1f\xfd\x11\x1f\xc0\xc7\xbf\x0c4\xd1)\xfe\xfebg\x10\x10\x98A\xb6qA6\xf4\x90\xb8\xc9a\x9f\x18|M\xc8`9 V%\xa2Kr\xdc\xbb\x84sR\x90!\xbb\xae\xa2{\xb5\xf7\xea\x16\x05\x0eb\x95\xef\xb9s\x99G*\xa5\xaf\xbb6\x83!\x0b!\x87\xa1!\x984\x1b\xad\xc1/8\xfa\xcd\xa9>\x83\xfb\x96\xbb<\xad\x9eIz\xb0\x8a\xbc\xc0\x8e\xca\x81w\x1f\xbb\x96D]\xa8\xab*\xb6K\x81U\x80\xf2\xad\x07\xcc\xe6%\x88\x10=\\\xb7p\x15\x9a\x85\x8d\xcb\xc7\x19eX3\x04\xa9\xb3?H\x16TB\x9d\xf9\x97\xbc%1\xf5\x9by\xc0\xff\x00$\xca\xe0\xf9\xcf\xb45\xab\x98\x82\x9c\xb8\x860\x83\xb6a\x83u\xd6&\xbf\x1d<DeG.\xa5!\xc4G%\xe3\xef, \xa6\x0cG)\xb4\xccK\x92\xf4za\x1e\x90\x80v\xfe\x88\xca\xb4\xf5\xc3\x01*\xa1I\x03.\x03z\xfcO\xb0\xa2O\xa30\x8a\x9eR\x1b\xa1\xed\x8e\xca\xf9\xaf\xc4^\x8by/\xe6\x1a\x0c\xe5D+\xe5\x85\x88\x80s*\xc33\x83S\x02\xe7"Z\xf3\xa8\xd2F\xaf\x88\xb5\x07P\x0e\xbd\xb8\x86\x02\x8d\x93\x9d\x10U\x8e",Tp*\xfc\xcf\x07\xeb\x07\x00\x8e\xe5b\xd7\t\xad\x88\xfd\x1b\x98\x80z\x18\xe7q;\xca\xd1\xd4\xa6\xb1\x87X\xb2\xa8\xa9\x98\xec\xea\xe6\x17\xcd\xe4\x98\xb4bqq\xff\x00\xf5/\xc0\xfb\x7fq}>\xa4_O\xa9\x0e\x02\'\x05|"\xf9\x7f\xb3\xff\xd9\x00')]

El campo Picture es un blob, o sea un objeto en formato binario, que en este caso es una imagen. En la siguiente celda se muestra el código para desplegar la imagen.

Usando ChatGPT …

Este código se obtuvo utilizando el siguiente prompt en ChatGPT:

“I have an image saved as a blob field in a SQLite table. I would like to display the image in a Jupyter notebook by reading the blob. Do you know how I can accomplish that using Python?”

from IPython.display import display
from PIL import Image
import io

image_data = store[0][3]
image = Image.open(io.BytesIO(image_data))
display(image)
_images/7b509f366a2a1c603768795acc77034b40caaf4da307eaa99f7f7c2dd927a252.png

Busquemos sólo los campos que no son blob.

qry = "SELECT CategoryID, CategoryName, Description FROM Categories"
rows = cursor.execute(qry).fetchall()
for row in rows:
    print(row)
(1, 'Beverages', 'Soft drinks, coffees, teas, beers, and ales')
(2, 'Condiments', 'Sweet and savory sauces, relishes, spreads, and seasonings')
(3, 'Confections', 'Desserts, candies, and sweet breads')
(4, 'Dairy Products', 'Cheeses')
(5, 'Grains/Cereals', 'Breads, crackers, pasta, and cereal')
(6, 'Meat/Poultry', 'Prepared meats')
(7, 'Produce', 'Dried fruit and bean curd')
(8, 'Seafood', 'Seaweed and fish')

Llave Única#

Fijémonos específicamente en el campo CategoryID. Para conocer la estructura de la tabla Categories volvemos a consultar la tabla sqlite_master y vemos el contenido del campo sql del registro correspondiente.

qry = "SELECT sql FROM sqlite_master WHERE name = 'Categories'"
rows = cursor.execute(qry).fetchall()
print(rows[0][0]) # rows es [(un_elemento,)]
CREATE TABLE [Categories]
(      [CategoryID] INTEGER PRIMARY KEY AUTOINCREMENT,
       [CategoryName] TEXT,
       [Description] TEXT,
       [Picture] BLOB
)

El comando que creó la tabla Categories estableció que el campo CategoryID es una llave única o primaria, que corresponde a un campo único que permite identificar de forma unívoca un registro. Es como una huella digital del registro, dos o más registros NO pueden compartir una llave primaria.

En el caso de la tabla Categories la llave primaria es un campo entero que se asigna automáticamente cada vez que se inserta un nuevo registro (AUTOINCREMENT), sin embargo, cualquier campo que sea único, puede ser una llave primaria.

Llaves Foráneas#

Hagamos el mismo ejercicio con la tabla Products.

qry = "SELECT * FROM Products LIMIT 1"
result = cursor.execute(qry)
print([d[0] for d in result.description])
['ProductID', 'ProductName', 'SupplierID', 'CategoryID', 'QuantityPerUnit', 'UnitPrice', 'UnitsInStock', 'UnitsOnOrder', 'ReorderLevel', 'Discontinued']
for row in result:
    print(row)
(1, 'Chai', 1, 1, '10 boxes x 20 bags', 18, 39, 0, 10, '0')

Poner atención en los campos SupplierIDy CategoryID que establecen una relación entre esta tabla y las tablas Suppliers y Categories. Vamos a buscar la estructura de la tabla.

qry = "SELECT sql FROM sqlite_master WHERE name = 'Products'"
rows = cursor.execute(qry).fetchall()
print(rows[0][0])
CREATE TABLE [Products](
   [ProductID]INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
   [ProductName]TEXT NOT NULL,
   [SupplierID]INTEGER,
   [CategoryID]INTEGER,
   [QuantityPerUnit]TEXT,
   [UnitPrice]NUMERIC DEFAULT 0,
   [UnitsInStock]INTEGER DEFAULT 0,
   [UnitsOnOrder]INTEGER DEFAULT 0,
   [ReorderLevel]INTEGER DEFAULT 0,
   [Discontinued]TEXT NOT NULL DEFAULT '0',
    CHECK ([UnitPrice]>=(0)),
    CHECK ([ReorderLevel]>=(0)),
    CHECK ([UnitsInStock]>=(0)),
    CHECK ([UnitsOnOrder]>=(0)),
	FOREIGN KEY ([CategoryID]) REFERENCES [Categories] ([CategoryID]) 
		ON DELETE NO ACTION ON UPDATE NO ACTION,
	FOREIGN KEY ([SupplierID]) REFERENCES [Suppliers] ([SupplierID]) 
		ON DELETE NO ACTION ON UPDATE NO ACTION
)

El comando que creó la tabla Products estableció que los campos CategoryID y SupplierID fueran llaves foráneas (FOREIGN KEY), es decir, que deben corresponder a registros existentes de las tablas Categories y Suppliers respectivamente. Además se indica qué campo de esas tablas es el que está relacionado con el campo de la tabla Products. Adicionalmente, las instrucciones ON DELETE NO ACTION y ON UPDATE NO ACTION indican que la BBDD no permitirá que al borrar o actualizar un registro se viole la integridad referencial de la BBDD.

De Wikipedia: “La integridad referencial es una propiedad de una base de datos relacional que significa que una llave foránea de una tabla de referencia siempre debe aludir a un registro válido de la tabla a la que se haga referencia. La integridad referencial garantiza que la relación entre dos tablas permanezca sincronizada durante las operaciones de actualización y eliminación.”

Tipos de Datos#

En el ejemplo anterior vemos también que los distintos campos tienen definido un tipo de dato específico. Por ejemplo, los campos SupplierID y CategoryID están definidos como INTEGER, el campo UnitPrice aparece como NUMERIC que, en esta versión de SQLite, significa que puede tener decimales.

Algunos de los campos especifican además un valor DEFAULT. En el caso de la tabla Products esto significa que, si al ingresar un nuevo registro, no especificamos, por ejemplo, el campo UnitPrice, SQLite le asignará 0 como valor por default

Finalmente, vemos también que algunos campos tienen definidos CHECK, esto significa que, antes de ingresar un nuevo producto, SQLite verificará que, por ejemplo, el campo UnitsInStock sea mayor o igual a 0.

Consultas (queries)#

qry = "SELECT * FROM Customers LIMIT 10"
result = cursor.execute(qry)
print([d[0] for d in result.description])
['CustomerID', 'CompanyName', 'ContactName', 'ContactTitle', 'Address', 'City', 'Region', 'PostalCode', 'Country', 'Phone', 'Fax']
for row in result:
    print(row)
('ALFKI', 'Alfreds Futterkiste', 'Maria Anders', 'Sales Representative', 'Obere Str. 57', 'Berlin', 'Western Europe', '12209', 'Germany', '030-0074321', '030-0076545')
('ANATR', 'Ana Trujillo Emparedados y helados', 'Ana Trujillo', 'Owner', 'Avda. de la Constituciun 2222', 'Mexico D.F.', 'Central America', '05021', 'Mexico', '(5) 555-4729', '(5) 555-3745')
('ANTON', 'Antonio Moreno Taqueria', 'Antonio Moreno', 'Owner', 'Mataderos  2312', 'Mexico D.F.', 'Central America', '05023', 'Mexico', '(5) 555-3932', None)
('AROUT', 'Around the Horn', 'Thomas Hardy', 'Sales Representative', '120 Hanover Sq.', 'London', 'British Isles', 'WA1 1DP', 'UK', '(171) 555-7788', '(171) 555-6750')
('BERGS', 'Berglunds snabbkˆp', 'Christina Berglund', 'Order Administrator', 'Berguvsvgen  8', 'Lulea', 'Northern Europe', 'S-958 22', 'Sweden', '0921-12 34 65', '0921-12 34 67')
('BLAUS', 'Blauer See Delikatessen', 'Hanna Moos', 'Sales Representative', 'Forsterstr. 57', 'Mannheim', 'Western Europe', '68306', 'Germany', '0621-08460', '0621-08924')
('BLONP', 'Blondesddsl pere et fils', 'Frederique Citeaux', 'Marketing Manager', '24, place Kleber', 'Strasbourg', 'Western Europe', '67000', 'France', '88.60.15.31', '88.60.15.32')
('BOLID', 'Bulido Comidas preparadas', 'MartIn Sommer', 'Owner', 'C/ Araquil, 67', 'Madrid', 'Southern Europe', '28023', 'Spain', '(91) 555 22 82', '(91) 555 91 99')
('BONAP', "Bon app'", 'Laurence Lebihan', 'Owner', '12, rue des Bouchers', 'Marseille', 'Western Europe', '13008', 'France', '91.24.45.40', '91.24.45.41')
('BOTTM', 'Bottom-Dollar Markets', 'Elizabeth Lincoln', 'Accounting Manager', '23 Tsawassen Blvd.', 'Tsawassen', 'North America', 'T2F 8M4', 'Canada', '(604) 555-4729', '(604) 555-3745')

Vemos que si bien obtenemos el resultado deseado, el formato de los registros es un poco incómodo. Vamos a ver como estructurarlo un poco mejor.

Resultado de una Consulta a un DataFrame#

Importamos la librería pandas.

import pandas as pd

Utilizamos el método read_sql para almacenar el resultado de la query en un DataFrame.

df_customers = pd.read_sql(qry, conn)

Podemos así producir un output mucho más legible.

df_customers
CustomerID CompanyName ContactName ContactTitle Address City Region PostalCode Country Phone Fax
0 ALFKI Alfreds Futterkiste Maria Anders Sales Representative Obere Str. 57 Berlin Western Europe 12209 Germany 030-0074321 030-0076545
1 ANATR Ana Trujillo Emparedados y helados Ana Trujillo Owner Avda. de la Constituciun 2222 Mexico D.F. Central America 05021 Mexico (5) 555-4729 (5) 555-3745
2 ANTON Antonio Moreno Taqueria Antonio Moreno Owner Mataderos 2312 Mexico D.F. Central America 05023 Mexico (5) 555-3932 None
3 AROUT Around the Horn Thomas Hardy Sales Representative 120 Hanover Sq. London British Isles WA1 1DP UK (171) 555-7788 (171) 555-6750
4 BERGS Berglunds snabbkˆp Christina Berglund Order Administrator Berguvsvgen 8 Lulea Northern Europe S-958 22 Sweden 0921-12 34 65 0921-12 34 67
5 BLAUS Blauer See Delikatessen Hanna Moos Sales Representative Forsterstr. 57 Mannheim Western Europe 68306 Germany 0621-08460 0621-08924
6 BLONP Blondesddsl pere et fils Frederique Citeaux Marketing Manager 24, place Kleber Strasbourg Western Europe 67000 France 88.60.15.31 88.60.15.32
7 BOLID Bulido Comidas preparadas MartIn Sommer Owner C/ Araquil, 67 Madrid Southern Europe 28023 Spain (91) 555 22 82 (91) 555 91 99
8 BONAP Bon app' Laurence Lebihan Owner 12, rue des Bouchers Marseille Western Europe 13008 France 91.24.45.40 91.24.45.41
9 BOTTM Bottom-Dollar Markets Elizabeth Lincoln Accounting Manager 23 Tsawassen Blvd. Tsawassen North America T2F 8M4 Canada (604) 555-4729 (604) 555-3745

Agregaciones#

Vamos a calcular cuantos productos distintos hay.

qry = "SELECT COUNT(ProductName) AS CuantosProductos FROM Products"
print(qry)
SELECT COUNT(ProductName) AS CuantosProductos FROM Products
df_temp = pd.read_sql(qry, conn)
df_temp
CuantosProductos
0 77

Agrupaciones#

Esta vez vamos a calcular cuántos productos distintos hay, pero agrupando por categoría de producto y proveedor. Adicionalmente, para facilitar la lectura, vamos a ordenar el resultado por la categoría y luego por el proveedor.

qry = ("SELECT SupplierID, CategoryID, COUNT(ProductName) AS CuantosProductos FROM Products "
       "GROUP BY CategoryID, SupplierID "
       "ORDER BY SupplierID ASC, CategoryID ASC")
print(qry)
SELECT SupplierID, CategoryID, COUNT(ProductName) AS CuantosProductos FROM Products GROUP BY CategoryID, SupplierID ORDER BY SupplierID ASC, CategoryID ASC
df_temp = pd.read_sql(qry, conn)
df_temp.head(10)
SupplierID CategoryID CuantosProductos
0 1 1 2
1 1 2 1
2 2 2 4
3 3 2 2
4 3 7 1
5 4 6 1
6 4 7 1
7 4 8 1
8 5 4 2
9 6 2 1

Podemos verificar que el número de productos cuadra con el cálculo anterior.

num_prod = df_temp['CuantosProductos'].sum()
print(f'Número total de productos: {num_prod}')
Número total de productos: 77

JOINS#

El output que obtenemos utilizando sólo los campos SupplierID y CategoryID no es muy agradable y no sirve para presentar un dashboard o un informe. Esto lo podemos solucionar con una query que haga JOIN con las tablas Supplier y Category para obtener los nombres de proveedores y categorías. Como se aprecia en la imagen siguiente, existen muchos tipos de JOIN, pero comenzaremos con el más usual, el LEFT JOIN.

sql-joins

Vamos entonces a escribir la query anterior, pero esta vez obteniendo los nombres que nos interesan. Notar el uso de los alias t1, t2 y t3 para los nombres de las tablas. Esto hace un poco más legible la query.

qry = ("SELECT t2.CompanyName, t3.CategoryName, COUNT(t1.ProductName) AS CuantosProductos FROM Products t1 "
       "LEFT JOIN Suppliers t2 ON t1.SupplierID = t2.SupplierID "
       "LEFT JOIN Categories t3 ON t1.CategoryID = t3.CategoryID "
       "GROUP BY t2.CompanyName, t3.CategoryName "
       "ORDER BY t2.CompanyName ASC, t3.CategoryName ASC")
print(qry)
SELECT t2.CompanyName, t3.CategoryName, COUNT(t1.ProductName) AS CuantosProductos FROM Products t1 LEFT JOIN Suppliers t2 ON t1.SupplierID = t2.SupplierID LEFT JOIN Categories t3 ON t1.CategoryID = t3.CategoryID GROUP BY t2.CompanyName, t3.CategoryName ORDER BY t2.CompanyName ASC, t3.CategoryName ASC
df_temp = pd.read_sql(qry, conn)
df_temp.head(10)
CompanyName CategoryName CuantosProductos
0 Aux joyeux ecclesiastiques Beverages 2
1 Bigfoot Breweries Beverages 3
2 Cooperativa de Quesos 'Las Cabras' Dairy Products 2
3 Escargots Nouveaux Seafood 1
4 Exotic Liquids Beverages 2
5 Exotic Liquids Condiments 1
6 ForIts d'erables Condiments 1
7 ForIts d'erables Confections 1
8 Formaggi Fortini s.r.l. Dairy Products 3
9 G'day, Mate Grains/Cereals 1

Más Filtros#

Seleccionemos los productos de los proveedores 1 y 2.

qry = "SELECT * FROM Products WHERE SupplierID = 1 OR SupplierID = 2"
df_temp = pd.read_sql(qry, conn)
df_temp
ProductID ProductName SupplierID CategoryID QuantityPerUnit UnitPrice UnitsInStock UnitsOnOrder ReorderLevel Discontinued
0 1 Chai 1 1 10 boxes x 20 bags 18.00 39 0 10 0
1 2 Chang 1 1 24 - 12 oz bottles 19.00 17 40 25 0
2 3 Aniseed Syrup 1 2 12 - 550 ml bottles 10.00 13 70 25 0
3 4 Chef Anton's Cajun Seasoning 2 2 48 - 6 oz jars 22.00 53 0 0 0
4 5 Chef Anton's Gumbo Mix 2 2 36 boxes 21.35 0 0 0 1
5 65 Louisiana Fiery Hot Pepper Sauce 2 2 32 - 8 oz bottles 21.05 76 0 0 0
6 66 Louisiana Hot Spiced Okra 2 2 24 - 8 oz jars 17.00 4 100 20 0

Los productos del proveedor 1 en la categoría 1.

qry = "SELECT * FROM Products WHERE SupplierID = 1 AND CategoryID = 1"
df_temp = pd.read_sql(qry, conn)
df_temp
ProductID ProductName SupplierID CategoryID QuantityPerUnit UnitPrice UnitsInStock UnitsOnOrder ReorderLevel Discontinued
0 1 Chai 1 1 10 boxes x 20 bags 18 39 0 10 0
1 2 Chang 1 1 24 - 12 oz bottles 19 17 40 25 0

Herramientas GUI#

Si bien es totalmente posible administrar una BD con una interfaz basada en comandos (como esta), a veces es muy cómodo disponer una aplicación que utilice una GUI. Las dos siguientes son buenas alternativas para trabajar con SQLite.

sqlitebrowser

sqlitestudio