Para evitar confusiones, a lo largo de esta sección intentaré usar “base de datos” para referirme a RDBMS, “tabla” para hablar de cada uno de los componentes de una base de datos y “matriz” cuando hable del tipo de datos que hasta ahora hemos almacenado en un data.frame. Empecemos, pues por leer dos matrices de datos que utilizaremos a lo largo del ejercicio.

La primera es una versión limpia de una encuesta de opinión política en España. La encuesta está centrada en comportamiento electoral y pregunta a los entrevistados por información sociodemográfica habitual, por su intención de voto, por su recuerdo de voto y por dos variables que son claves en las encuestas electorales: por qué partido siente más simpatía y su autoubicación en una escala ideológica (en donde 1 representa la extrema izquierda y 10, la extrema derecha).

cis <- readRDS('./assets/clean-data.RDS')
dim(cis)

Veamos ahora unos descriptivos de variables que nos pueden interesar en el análisis:

summary(cis[, c("age", "gender", "education", "ideology", "pid", "pastvote")])

Todos los análisis a continuación los podríamos ejecutar directamente en R, pero vamos a ver cómo los haríamos en SQL.

En primer lugar, tenemos que escoger la base de datos que usaremos. Por lo general, esto nos viene dado. En este caso podemos escoger entre alguno de los muchos productos en el mercado (MySQL, PostgreSQL, SQL Server). En este caso, usaremos una base de datos más sencilla y que vive en memoria llamada SQLite.

El proceso para trabajar con SQLite es similar al de otras bases de datos, con la diferencia de que en este caso no necesitamos conectarnos a otra máquina/servidor para acceder a los datos, con lo que evitamos el problema de autenticarnos.

Nos conectaremos a SQLite usando el paquete DBI que nos ofrece formas de conectarnos a bases relacionales y el paquete RSQLite que ofrece la conexión de DBI con SQLite.

library(DBI)
install.packages("RSQLite")
con <- dbConnect(RSQLite::SQLite(), ":memory:")

La parte fundamental de lo que acabamos de hacer es la conexión a la base de datos usando dbConnect. En este caso, hemos utilizado el driver de SQLite y trabajaremos en memoria. Para trabajar con otras bases de datos remotas pasaríamos aquí el driver correspondiente a la base de datos en cuestión así como los parámetros de autenticación necesarios (servidor, base de datos, usuario, clave secreta y puerto de conexión).

Empezaremos por obtener un listado de las tablas disponibles en la base de datos que acabamos de crear:

dbListTables(con)

Por ahora, la base está vacía. Copiemos la matriz de datos cis a la base de datos. Para ello le daremos un nombre a la tabla:

dbWriteTable(con, "cis", cis)
dbListTables(con)

Ahora que los datos están en la base de datos, podemos empezar a interactuar con ella. Para ello usaremos SQL.

Operaciones básicas

SELECT

La estructura de las búsquedas con SQL es siempre la misma. Usaremos la cláusula SELECT y la tabla a la que nos referimos usando FROM. Para enviar nuestras búsquedas usaremos la conexión que hemos creado. En este caso, con y pasaremos la búsqueda a través de la función dbSendQuery. Algo importante a recordar es que una cosa es enviar la búsqueda y otra distinta recuperar los datos que la búsqueda produce.

res <- dbSendQuery(con, "SELECT gender FROM cis")
dbFetch(res)

Podemos seleccionar más de una columna haciendo un listado

res <- dbSendQuery(con, "SELECT gender, age FROM cis")
dbFetch(res)

Y podemos seleccionar también todas las columnas utilizando el carácter especial * (la razón de usar asterisco quedará más clara cuando veamos expresiones regulares).

dbFetch(dbSendQuery(con, "SELECT * FROM cis"))

Podemos también obtener solo un grupo de filas utilizando LIMIT

dbFetch(dbSendQuery(con, "SELECT * FROM cis LIMIT 5"))

COUNT, SUM, AVG, MIN, MAX

Podemos empezar a hacer cosas más interesantes utilizando funciones de agregación. Por ejemplo, podemos contar el número de filas en la tabla:

dbFetch(dbSendQuery(con, "SELECT COUNT(*) FROM cis"))

O podemos contar el número de filas distintas en la tabla. Por ejemplo, ¿cuántos diferentes valores de edad tenemos en la tabla?

dbFetch(dbSendQuery(con, "SELECT COUNT(DISTINCT age) FROM cis"))

Podemos obtener también descriptivos de las variables

dbFetch(dbSendQuery(con, "SELECT MIN(ideology) FROM cis"))
dbFetch(dbSendQuery(con, "SELECT AVG(ideology) FROM cis"))

Algo que será muy útil será poder dar nombres a las columnas a través de la creación de “alias” con la cláusula AS.

dbFetch(dbSendQuery(con, "SELECT AVG(ideology) as mean_ideology FROM cis"))

Podemos establecer un alias para referirnos a operaciones más complejas:

dbFetch(dbSendQuery(con, "SELECT MAX(ideology) - MIN(ideology) AS diff FROM cis;"))

Antes de continuar, vamos a cerrar la conexión a la base de datos

dbDisconnect(con)
LS0tIApUaXRsZTogIkludHJvZHVjY2nDs24gYSBTUUwiCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCICVkLCAlWScpYCIKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRSwgY2FjaGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChldmFsID0gRkFMU0UpCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcucGF0aCA9ICcuL2Fzc2V0cy8nKQpgYGAKClBhcmEgZXZpdGFyIGNvbmZ1c2lvbmVzLCBhIGxvIGxhcmdvIGRlIGVzdGEgc2VjY2nDs24gaW50ZW50YXLDqSB1c2FyCiJiYXNlIGRlIGRhdG9zIiBwYXJhIHJlZmVyaXJtZSBhIFJEQk1TLCAidGFibGEiIHBhcmEgaGFibGFyIGRlIGNhZGEKdW5vIGRlIGxvcyBjb21wb25lbnRlcyBkZSB1bmEgYmFzZSBkZSBkYXRvcyB5ICJtYXRyaXoiIGN1YW5kbyBoYWJsZQpkZWwgdGlwbyBkZSBkYXRvcyBxdWUgaGFzdGEgYWhvcmEgaGVtb3MgYWxtYWNlbmFkbyBlbiB1biBgZGF0YS5mcmFtZWAuCkVtcGVjZW1vcywgcHVlcyBwb3IgbGVlciBkb3MgbWF0cmljZXMgZGUgZGF0b3MgcXVlIHV0aWxpemFyZW1vcyBhIGxvCmxhcmdvIGRlbCBlamVyY2ljaW8uIAoKTGEgcHJpbWVyYSBlcyB1bmEgdmVyc2nDs24gbGltcGlhIGRlIHVuYSBlbmN1ZXN0YSBkZSBvcGluacOzbiBwb2zDrXRpY2EKZW4gRXNwYcOxYS4gTGEgZW5jdWVzdGEgZXN0w6EgY2VudHJhZGEgZW4gY29tcG9ydGFtaWVudG8gZWxlY3RvcmFsIHkKcHJlZ3VudGEgYSBsb3MgZW50cmV2aXN0YWRvcyBwb3IgaW5mb3JtYWNpw7NuIHNvY2lvZGVtb2dyw6FmaWNhCmhhYml0dWFsLCBwb3Igc3UgaW50ZW5jacOzbiBkZSB2b3RvLCBwb3Igc3UgcmVjdWVyZG8gZGUgdm90byB5IHBvciBkb3MKdmFyaWFibGVzIHF1ZSBzb24gY2xhdmVzIGVuIGxhcyBlbmN1ZXN0YXMgZWxlY3RvcmFsZXM6IHBvciBxdcOpIHBhcnRpZG8Kc2llbnRlIG3DoXMgc2ltcGF0w61hIHkgc3UgYXV0b3ViaWNhY2nDs24gZW4gdW5hIGVzY2FsYSBpZGVvbMOzZ2ljYSAoZW4KZG9uZGUgMSByZXByZXNlbnRhIGxhIGV4dHJlbWEgaXpxdWllcmRhIHkgMTAsIGxhIGV4dHJlbWEgZGVyZWNoYSkuCgpgYGB7cn0KY2lzIDwtIHJlYWRSRFMoJy4vYXNzZXRzL2NsZWFuLWRhdGEuUkRTJykKZGltKGNpcykKYGBgCgpWZWFtb3MgYWhvcmEgdW5vcyBkZXNjcmlwdGl2b3MgZGUgdmFyaWFibGVzIHF1ZSBub3MgcHVlZGVuIGludGVyZXNhcgplbiBlbCBhbsOhbGlzaXM6CgpgYGB7cn0Kc3VtbWFyeShjaXNbLCBjKCJhZ2UiLCAiZ2VuZGVyIiwgImVkdWNhdGlvbiIsICJpZGVvbG9neSIsICJwaWQiLCAicGFzdHZvdGUiKV0pCmBgYAoKVG9kb3MgbG9zIGFuw6FsaXNpcyBhIGNvbnRpbnVhY2nDs24gbG9zIHBvZHLDrWFtb3MgZWplY3V0YXIgZGlyZWN0YW1lbnRlCmVuIFIsIHBlcm8gdmFtb3MgYSB2ZXIgY8OzbW8gbG9zIGhhcsOtYW1vcyBlbiBTUUwuIAoKRW4gcHJpbWVyIGx1Z2FyLCB0ZW5lbW9zIHF1ZSBlc2NvZ2VyIGxhIGJhc2UgZGUgZGF0b3MgcXVlIHVzYXJlbW9zLgpQb3IgbG8gZ2VuZXJhbCwgZXN0byBub3MgdmllbmUgZGFkby4gRW4gZXN0ZSBjYXNvIHBvZGVtb3MgZXNjb2dlcgplbnRyZSBhbGd1bm8gZGUgbG9zIG11Y2hvcyBwcm9kdWN0b3MgZW4gZWwgbWVyY2FkbyAoW015U1FMXShodHRwczovL3d3dy5teXNxbC5jb20vKSwKW1Bvc3RncmVTUUxdKGh0dHBzOi8vd3d3LnBvc3RncmVzcWwub3JnLyksIFtTUUwgU2VydmVyXShodHRwczovL3d3dy5taWNyb3NvZnQuY29tL2VuLXVzL3NxbC1zZXJ2ZXIvZGVmYXVsdC5hc3B4KSkuIEVuIGVzdGUgY2FzbywgdXNhcmVtb3MgdW5hIGJhc2UgZGUKZGF0b3MgbcOhcyBzZW5jaWxsYSB5IHF1ZSB2aXZlIGVuIG1lbW9yaWEgbGxhbWFkYSBbU1FMaXRlXShodHRwczovL3d3dy5zcWxpdGUub3JnL2luZGV4Lmh0bWwpLgoKRWwgcHJvY2VzbyBwYXJhIHRyYWJhamFyIGNvbiBTUUxpdGUgZXMgc2ltaWxhciBhbCBkZSBvdHJhcyBiYXNlcyBkZQpkYXRvcywgY29uIGxhIGRpZmVyZW5jaWEgZGUgcXVlIGVuIGVzdGUgY2FzbyBubyBuZWNlc2l0YW1vcwpjb25lY3Rhcm5vcyBhIG90cmEgbcOhcXVpbmEvc2Vydmlkb3IgcGFyYSBhY2NlZGVyIGEgbG9zIGRhdG9zLCBjb24gbG8KcXVlIGV2aXRhbW9zIGVsIHByb2JsZW1hIGRlIGF1dGVudGljYXJub3MuIAoKTm9zIGNvbmVjdGFyZW1vcyBhIFNRTGl0ZSB1c2FuZG8gZWwgcGFxdWV0ZSBgREJJYCBxdWUgbm9zIG9mcmVjZQpmb3JtYXMgZGUgY29uZWN0YXJub3MgYSBiYXNlcyByZWxhY2lvbmFsZXMgeSBlbCBwYXF1ZXRlIGBSU1FMaXRlYCBxdWUKb2ZyZWNlIGxhIGNvbmV4acOzbiBkZSBgREJJYCBjb24gU1FMaXRlLiAKCgpgYGB7cn0KbGlicmFyeShEQkkpCmluc3RhbGwucGFja2FnZXMoIlJTUUxpdGUiKQpjb24gPC0gZGJDb25uZWN0KFJTUUxpdGU6OlNRTGl0ZSgpLCAiOm1lbW9yeToiKQpgYGAKCkxhIHBhcnRlIGZ1bmRhbWVudGFsIGRlIGxvIHF1ZSBhY2FiYW1vcyBkZSBoYWNlciBlcyBsYSBjb25leGnDs24gYSBsYQpiYXNlIGRlIGRhdG9zIHVzYW5kbyBgZGJDb25uZWN0YC4gRW4gZXN0ZSBjYXNvLCBoZW1vcyB1dGlsaXphZG8gZWwKZHJpdmVyIGRlIFNRTGl0ZSB5IHRyYWJhamFyZW1vcyBlbiBtZW1vcmlhLiBQYXJhIHRyYWJhamFyIGNvbiBvdHJhcwpiYXNlcyBkZSBkYXRvcyByZW1vdGFzIHBhc2Fyw61hbW9zIGFxdcOtIGVsIGRyaXZlciBjb3JyZXNwb25kaWVudGUgYSBsYQpiYXNlIGRlIGRhdG9zIGVuIGN1ZXN0acOzbiBhc8OtIGNvbW8gbG9zIHBhcsOhbWV0cm9zIGRlIGF1dGVudGljYWNpw7NuCm5lY2VzYXJpb3MgKHNlcnZpZG9yLCBiYXNlIGRlIGRhdG9zLCB1c3VhcmlvLCBjbGF2ZSBzZWNyZXRhIHkgcHVlcnRvCmRlIGNvbmV4acOzbikuIAoKRW1wZXphcmVtb3MgcG9yIG9idGVuZXIgdW4gbGlzdGFkbyBkZSBsYXMgdGFibGFzIGRpc3BvbmlibGVzIGVuIGxhCmJhc2UgZGUgZGF0b3MgcXVlIGFjYWJhbW9zIGRlIGNyZWFyOgpgYGB7cn0KZGJMaXN0VGFibGVzKGNvbikKYGBgCgpQb3IgYWhvcmEsIGxhIGJhc2UgZXN0w6EgdmFjw61hLiBDb3BpZW1vcyBsYSBtYXRyaXogZGUgZGF0b3MgYGNpc2AgYSBsYQpiYXNlIGRlIGRhdG9zLiBQYXJhIGVsbG8gbGUgZGFyZW1vcyB1biBub21icmUgYSBsYSB0YWJsYToKCmBgYHtyfQpkYldyaXRlVGFibGUoY29uLCAiY2lzIiwgY2lzKQpkYkxpc3RUYWJsZXMoY29uKQpgYGAKCkFob3JhIHF1ZSBsb3MgZGF0b3MgZXN0w6FuIGVuIGxhIGJhc2UgZGUgZGF0b3MsIHBvZGVtb3MgZW1wZXphciBhCmludGVyYWN0dWFyIGNvbiBlbGxhLiBQYXJhIGVsbG8gdXNhcmVtb3MgU1FMLiAKCiMgT3BlcmFjaW9uZXMgYsOhc2ljYXMKCiMjIFNFTEVDVAoKTGEgZXN0cnVjdHVyYSBkZSBsYXMgYsO6c3F1ZWRhcyBjb24gU1FMIGVzIHNpZW1wcmUgbGEgbWlzbWEuIFVzYXJlbW9zCmxhIGNsw6F1c3VsYSBgU0VMRUNUYCB5IGxhIHRhYmxhIGEgbGEgcXVlIG5vcyByZWZlcmltb3MgdXNhbmRvIGBGUk9NYC4KUGFyYSBlbnZpYXIgbnVlc3RyYXMgYsO6c3F1ZWRhcyB1c2FyZW1vcyBsYSBjb25leGnDs24gcXVlIGhlbW9zIGNyZWFkby4KRW4gZXN0ZSBjYXNvLCBgY29uYCB5IHBhc2FyZW1vcyBsYSBiw7pzcXVlZGEgYSB0cmF2w6lzIGRlIGxhIGZ1bmNpw7NuCmBkYlNlbmRRdWVyeWAuIEFsZ28gaW1wb3J0YW50ZSBhIHJlY29yZGFyIGVzIHF1ZSB1bmEgY29zYSBlcyBfZW52aWFyXwpsYSBiw7pzcXVlZGEgeSBvdHJhIGRpc3RpbnRhIF9yZWN1cGVyYXJfIGxvcyBkYXRvcyBxdWUgbGEgYsO6c3F1ZWRhCnByb2R1Y2UuIAoKYGBge3J9CnJlcyA8LSBkYlNlbmRRdWVyeShjb24sICJTRUxFQ1QgZ2VuZGVyIEZST00gY2lzIikKZGJGZXRjaChyZXMpCmBgYAoKUG9kZW1vcyBzZWxlY2Npb25hciBtw6FzIGRlIHVuYSBjb2x1bW5hIGhhY2llbmRvIHVuIGxpc3RhZG8KCmBgYHtyfQpyZXMgPC0gZGJTZW5kUXVlcnkoY29uLCAiU0VMRUNUIGdlbmRlciwgYWdlIEZST00gY2lzIikKZGJGZXRjaChyZXMpCmBgYAoKWSBwb2RlbW9zIHNlbGVjY2lvbmFyIHRhbWJpw6luIHRvZGFzIGxhcyBjb2x1bW5hcyB1dGlsaXphbmRvIGVsCmNhcsOhY3RlciBlc3BlY2lhbCBgKmAgKGxhIHJhesOzbiBkZSB1c2FyIGFzdGVyaXNjbyBxdWVkYXLDoSBtw6FzIGNsYXJhCmN1YW5kbyB2ZWFtb3MgZXhwcmVzaW9uZXMgcmVndWxhcmVzKS4KCmBgYHtyfQpkYkZldGNoKGRiU2VuZFF1ZXJ5KGNvbiwgIlNFTEVDVCAqIEZST00gY2lzIikpCmBgYAoKUG9kZW1vcyB0YW1iacOpbiBvYnRlbmVyIHNvbG8gdW4gZ3J1cG8gZGUgZmlsYXMgdXRpbGl6YW5kbyBgTElNSVRgCgpgYGB7cn0KZGJGZXRjaChkYlNlbmRRdWVyeShjb24sICJTRUxFQ1QgKiBGUk9NIGNpcyBMSU1JVCA1IikpCmBgYAoKIyMgQ09VTlQsIFNVTSwgQVZHLCBNSU4sIE1BWAoKUG9kZW1vcyBlbXBlemFyIGEgaGFjZXIgY29zYXMgbcOhcyBpbnRlcmVzYW50ZXMgdXRpbGl6YW5kbyBmdW5jaW9uZXMgZGUKYWdyZWdhY2nDs24uIFBvciBlamVtcGxvLCBwb2RlbW9zIGNvbnRhciBlbCBuw7ptZXJvIGRlIGZpbGFzIGVuIGxhIHRhYmxhOgoKYGBge3J9CmRiRmV0Y2goZGJTZW5kUXVlcnkoY29uLCAiU0VMRUNUIENPVU5UKCopIEZST00gY2lzIikpCmBgYAoKTyBwb2RlbW9zIGNvbnRhciBlbCBuw7ptZXJvIGRlIGZpbGFzIF9kaXN0aW50YXNfIGVuIGxhIHRhYmxhLiBQb3IKZWplbXBsbywgwr9jdcOhbnRvcyBkaWZlcmVudGVzIHZhbG9yZXMgZGUgZWRhZCB0ZW5lbW9zIGVuIGxhIHRhYmxhPwoKYGBge3J9CmRiRmV0Y2goZGJTZW5kUXVlcnkoY29uLCAiU0VMRUNUIENPVU5UKERJU1RJTkNUIGFnZSkgRlJPTSBjaXMiKSkKYGBgCgpQb2RlbW9zIG9idGVuZXIgdGFtYmnDqW4gZGVzY3JpcHRpdm9zIGRlIGxhcyB2YXJpYWJsZXMKCmBgYHtyfQpkYkZldGNoKGRiU2VuZFF1ZXJ5KGNvbiwgIlNFTEVDVCBNSU4oaWRlb2xvZ3kpIEZST00gY2lzIikpCmRiRmV0Y2goZGJTZW5kUXVlcnkoY29uLCAiU0VMRUNUIEFWRyhpZGVvbG9neSkgRlJPTSBjaXMiKSkKYGBgCgpBbGdvIHF1ZSBzZXLDoSBtdXkgw7p0aWwgc2Vyw6EgcG9kZXIgZGFyIG5vbWJyZXMgYSBsYXMgY29sdW1uYXMgYSB0cmF2w6lzCmRlIGxhIGNyZWFjacOzbiBkZSAiYWxpYXMiIGNvbiBsYSBjbMOhdXN1bGEgYEFTYC4KCmBgYHtyfQpkYkZldGNoKGRiU2VuZFF1ZXJ5KGNvbiwgIlNFTEVDVCBBVkcoaWRlb2xvZ3kpIGFzIG1lYW5faWRlb2xvZ3kgRlJPTSBjaXMiKSkKYGBgCgpQb2RlbW9zIGVzdGFibGVjZXIgdW4gYWxpYXMgcGFyYSByZWZlcmlybm9zIGEgb3BlcmFjaW9uZXMgbcOhcwpjb21wbGVqYXM6CgpgYGB7cn0KZGJGZXRjaChkYlNlbmRRdWVyeShjb24sICJTRUxFQ1QgTUFYKGlkZW9sb2d5KSAtIE1JTihpZGVvbG9neSkgQVMgZGlmZiBGUk9NIGNpczsiKSkKYGBgCgpBbnRlcyBkZSBjb250aW51YXIsIHZhbW9zIGEgY2VycmFyIGxhIGNvbmV4acOzbiBhIGxhIGJhc2UgZGUgZGF0b3MKCgpgYGB7cn0KZGJEaXNjb25uZWN0KGNvbikKYGBgCg==