ReDim Preserve para cambiar más de una dimensión en .NET.

Recientemente en grupos de .NET salió el tema de cómo se pueden redimensionar las dos dimensiones de una matriz de dos dimensiones sin perder los valores que ya se tienen almacenados en dicha matriz.

Si fuera una matriz de una dimensión no habría ningún problema, ya que la opción Preserve nos permite hacer precisamente eso:

Dim myArray(3) As Int32
myArray(0) = 2
myArray(1) = 4
myArray(2) = 6
myArray(3) = 8
ReDim Preserve myArray(5)
myArray(4) = 10
myArray(5) = 12

Así, este código no da ningún problema. Y tampoco lo da este otro:

Dim myArray(3, 0) As Int32
myArray(0, 0) = 2
myArray(1, 0) = 4
myArray(2, 0) = 6
myArray(3, 0) = 8
ReDim Preserve myArray(3, 1)
myArray(0, 1) = 10
myArray(1, 1) = 12
myArray(2, 1) = 14
myArray(3, 1) = 1

Ya que aunque es una matriz de dos dimensiones, sólo estamos redimensionando la dimensión situada más a la derecha. En cambio si intentamos hacer esto que sigue:

Dim myArray(3, 0) As Int32
myArray(0, 0) = 2
ReDim Preserve myArray(4, 1)
myArray(4, 1) = 10

Nos dará una excepción de tipo ArrayTypeMismatchException y nos dirá que…

‘ReDim’ sólo puede cambiar la dimensión situada más a la derecha

…porque un ReDim Preserve en una matriz de dos dimensiones sólo puede actuar sobre la última dimensión.

Para solventar esto podemos utilizar la siguiente función:

'--------------------------------------------------------------------
' Author:      Albert Mata (www.albertmata.net)
' Date:        20081118
' Description: Simulates a ReDim Preserve action on 2-dimensions
'              arrays, allowing to change not only the last dimension
'              but both.
'--------------------------------------------------------------------
Public Function ReDimPreserve(ByVal M As Array, _
ByVal NewLimit0 As Integer, ByVal NewLimit1 As Integer) As Array
    If NewLimit0 >= M.GetUpperBound(0) _
    And NewLimit1 >= M.GetUpperBound(1) Then
        Dim NewArray(NewLimit0, NewLimit1) As [Int32]
        For i As Integer = 0 To M.GetUpperBound(0)
            For j As Integer = 0 To M.GetUpperBound(1)
                NewArray.SetValue(M.GetValue(i, j), i, j)
            Next
        Next
        Return NewArray
    Else
        Return M
    End If
End Function

De tal manera que ahora ante el siguiente código, donde creamos una matriz inicialmente de dimensiones (2,3) para luego redimensionarla a (4,5) y por tanto cambiando las dos dimensiones de la matriz, sin perder los valores que ya teníamos almacenados…

Debug.Print("Creating array with dimensions [2,3]")
Dim myArray(2, 3) As [Int32]
Debug.Print("Storing value 12 in position [0,2]")
myArray.SetValue(12, 0, 2)
Debug.Print("Storing value 15 in position [2,3]")
myArray.SetValue(15, 2, 3)
Debug.Print("Upper bound for first dimension = " _
            & (myArray.GetUpperBound(0)))
Debug.Print("Upper bound for second dimension = " _
            & (myArray.GetUpperBound(1)))
Debug.Print("Value in position [0,2] = " _
            & myArray.GetValue(0, 2).ToString)
Debug.Print("Value in position [2,3] = " _
            & myArray.GetValue(2, 3).ToString)
Try
    myArray.SetValue(24, 3, 4)
    Debug.Print("I can store values in position [3,4]")
Catch ex As Exception
    Debug.Print("I can't store values in position [3,4]")
End Try

Debug.Print("Changing array dimensions to [4,5]")
myArray = DirectCast(Me.ReDimPreserve(myArray, 4, 5), Integer(,))
Debug.Print("Upper bound for first dimension = " _
            & (myArray.GetUpperBound(0)))
Debug.Print("Upper bound for second dimension = " _
            & (myArray.GetUpperBound(1)))
Debug.Print("Value in position [0,2] = " _
            & myArray.GetValue(0, 2).ToString)
Debug.Print("Value in position [2,3] = " _
            & myArray.GetValue(2, 3).ToString)
Try
    myArray.SetValue(24, 3, 4)
    Debug.Print("I can store values in position [3,4]")
Catch ex As Exception
    Debug.Print("I can't store values in position [3,4]")
End Try

…obtenemos esta salida en la Ventana Inmediato:

Creating array with dimensions [2,3]
Storing value 12 in position [0,2]
Storing value 15 in position [2,3]
Upper bound for first dimension = 2
Upper bound for second dimension = 3
Value in position [0,2] = 12
Value in position [2,3] = 15
*** 'System.IndexOutOfRangeException' EXCEPTION ***
I can't store values in position [3,4]
Changing array dimensions to [4,5]
Upper bound for first dimension = 4
Upper bound for second dimension = 5
Value in position [0,2] = 12
Value in position [2,3] = 15
I can store values in position [3,4]

La función propuesta no es ni mucho menos perfecta. Tendría que hacerse más genérica, permitir redimensionar no dos sino N dimensiones, controlar mejor posibles errores y demás. Pero puede ser una primera aproximación para resolver situaciones de este tipo…

PD. También es posible utilizar el método Array.Copy en lugar de iterar por las dos dimensiones de la matriz, aunque en este caso lo he hecho así para que resulte más evidente el proceso.

Estadística de visitas (200810).

Venga, una vez más, publico las estadísticas de visitas a este blog del mes recién cerrado. Como siempre, si hay alguien interesado en realizar el seguimiento, puede hacerlo a través del tema habitual visitas, que aglutina todos estos posts. Este mes los resultados han sido especialmente buenos (no me preguntéis por qué) y me da que marcarán un listón difícil de superar por meses venideros.

Octubre 2008.

Número de visitas totales:       3339     (+ 88,75%)
Páginas individuales vistas:     4215     (+ 83,82%)
Promedio de tiempo en el sitio:  00:01:06 (-  8,33%)
Porcentaje de rebote:            85,12%   (+  1,40%)
Porcentaje de visitas nuevas:    82,42%   (+  0,96%)

El día con más visitas del mes ha sido el 28 de octubre con un total de 173.

Y los tres posts que durante el mes han sido más visitados son los siguientes:

1. Informe en .NET con Crystal Reports y base de datos MySQL. (9,21%)
2. Pasando parámetros al informe en .NET con Crystal Reports. (6,81%)
3. Crear librería .dll en .NET… ¡y utilizarla! (6,50%)

Lo cual me gusta porque los posts más visitados venían siendo siempre los mismos y andaba yo ya con la sensación de que no lograba aportar nada que tuviera el mismo interés de nuevo, pero el que en octubre ha sido el segundo más visitado es un post del, precisamente, 1 de octubre.

Y por último… este mes recupero la sección Premio Google del Mes. No es que haya habido una búsqueda especialmente absurda que ha venido a parar a este blog, pero sí ha habido una que me ha resultado curiosa, así que proclamo ganador de esta nueva edición y por tanto merecedor del…

Premio Google del Mes de Octubre del 2008

…a quien hizo la búsqueda en Google:

5 horas para ejecutar un archivo bat

Sorprendente. No quiero ni pensar qué hará dicho archivo (ni cómo)… :-)

Correspondencia entre tipos de datos en MySQL, en VisualBasic.NET y en .NET Framework.

Estos últimos días he estado liado programando una clase que tira intensamente de reflexión (me refiero a System.Reflection, no a que haya estado reflexionando intensamente, que también) para relacionar un objeto de una clase determinada (la que sea) con una tabla en la base de datos. Así, utilizando esa clase auxiliar podemos decirle que cargue un objeto a partir de un registro de una tabla o que lo guarde en ella. Y sirve para cualquier clase que tenga una tabla relacionada en la base de datos. Quizá otro día cuelgo esa clase… Pero el caso es que mientras lo desarrollaba he tenido algunos problemillas por no encajarme exactamente los tipos de datos que me devolvía MySQL y los que esperaba .NET, así que tras haber estado buscando cuáles son las equivalencias exactas, paso a exponer la tabla de correspondencias entre tipos de datos tanto en MySQL como en VisualBasic.NET como en el .NET Framework.

+--------------------+----------+----------------+
|       MYSQL        |  VB.NET  | .NET Framework |
+--------------------+----------+----------------+
| TINYINT            | SByte    | SByte          |
| TINYINT UNSIGNED   | Byte     | Byte           |
| SMALLINT           | Short    | Int16          |
| SMALLINT UNSIGNED  | UShort   | UInt16         |
| MEDIUMINT          | Integer  | Int32          |
| MEDIUMINT UNSIGNED | UInteger | UInt32         |
| INT                | Integer  | Int32          |
| INT UNSIGNED       | UInteger | UInt32         |
| BIGINT             | Long     | Int64          |
| BIGINT UNSIGNED    | ULong    | UInt64         |
+--------------------+----------+----------------+

Teniendo en cuenta estas relaciones, todo funciona a las mil maravillas… :-)

Integer en VisualBasic.NET no es una clase.

Miniapunte que puede parecer absurdo pero que me ha hecho perder un ratito. El caso es que estaba intentando conseguir que un atributo de tipo Integer sin inicializar tuviera un valor nulo, para así poder insertarlo en un registro de una tabla de base de datos como NULL, ya que dada la naturaleza del atributo, un valor 0 (que es como un atributo de tipo Integer queda automáticamente inicializado) y un valor NULL significan cosas muy distintas. Pero resulta que no se puede hacer, ya que Integer no es una clase en VisualBasic.NET sino sólo un tipo de datos.

En el caso de un atributo de tipo String no hay problema, porque String sí puede ser una clase y por tanto sí acepta valores nulos, pero no así en el caso de Integer y Double, que son sólo tipos de datos y automáticamente quedan inicializados a 0. La solución -no óptima- puede pasar por utilizar la clase Object en su lugar o bien -algo más óptima en tanto que menos genérica- utilizar la clase Nullable y crear el atributo de tipo Nullable (Of Integer) o Nullable (Of Double).

Y la confusión me ha venido motivada porque en Java Integer sí es una clase…

En los grupos de discusión de Microsoft, Enrique Martínez ha dado una excelente explicación que copio literalmente:

«Tipos de datos» son todos: el tipo de dato Integer, el tipo de datos String, el tipo de datos Object, etc. Lo que ocurre es que mientras que los tipos de datos Object y String están definidos como «Class» (un tipo de datos por referencia), los tipos de datos como Short, Integer, Long, Single, Double, etc., se encuentran definidos como «Structure» (un tipo de datos por valor). Por este motivo, los valores de los tipos de datos «por referencia» (las clases), se inicializan a un valor «Nothing» en Visual Basic, mientras que los valores de los tipos de datos «por valor» (las estructuras), en principio se inicializan a 0.

Resumiendo, ante el código siguiente:

Dim n As Integer
Debug.Print(”El valor de n es: ” & n.ToString)
Debug.Print(”¿Es n nulo? ” & IsNothing(n))

Se obtiene la salida:

El valor de n es: 0
¿Es n nulo? False

Mientras que ante este otro código:

Dim n As Nullable(Of Integer)
Debug.Print(”El valor de n es: ” & n.ToString)
Debug.Print(”¿Es n nulo? ” & IsNothing(n))

Obtenemos esta otra salida:

El valor de n es:
¿Es n nulo? True

Ídem con Double, Long… ;-)

Pepecar o una mala opción para alquilar un coche.

pepecarComo cualquier autónomo, mis necesidades de desplazamiento son constantes y bastante ineludibles, así que cuando hace cosa de un par de meses mi coche dijo de golpe y sopetón hasta aquí hemos llegado, se me planteó un problema urgente que resolver. Me decidí a contratar un vehículo de renting a través de CaixaRenting (concretamente un Mazda2 que de momento va muy bien), pero como quiera que el plazo de entrega era de entre tres y cuatro semanas, opté también por alquilar un vehículo durante ese período para poder seguir acudiendo a las instalaciones del cliente principal para el cual trabajo.

Así que estuve sopesando varias alternativas para alquilar un coche para salir del apuro durante esas tres semanas. Y de entre todas las opciones y como soy gran defensor de las compañías de bajo coste en todos los sectores, me decidí por alquilar en Pepecar. Craso error. No le recomendaría a nadie hacerlo. Y paso a explicar mis motivos.

Alquilé un Smart Fortwo (el modelo más baratito que tenían disponible) para 22 días. En su página web mostraban un precio de lo más competitivo, algo inferior a las compañías de alquiler de vehículos tradicionales (Europcar, Hertz, Avis, etc). Como muestra, un simulacro que acabo de hacer ahora mismo para un alquiler de 22 días del mismo vehículo y en la misma sucursal:

O sea, unos 300 euros. Un buen precio, sí señor, unos 13 euros diarios, está bien. No obstante, a la que pasamos a la siguiente pantalla de contratación nos encontramos lo siguiente:

pepecar

No necesito silla de niño, no haré más de 500 kilómetros diarios y estoy seguro que no cancelaré. Puedo ahorrarme esos suplementos. La asistencia en carretera tiene un precio coherente y aunque es una práctica bastante triste que tengas que contratarla aparte, la marqué para no tener que andar empujando el Smart si se me quedaba tirado…

No obstante la cobertura extra de daños es exagerada. Prácticamente te dobla el precio del alquiler. O sea, que como reclamo muestran un precio muy interesante y después te dicen… paga el doble de lo que creías que ibas a pagar o hazte cargo de cualquier ralladita o golpecito que le des o te den al coche. No sé, esta clase de prácticas siempre me han parecido muy mediocres, la verdad. Pero el caso es que asumí el riego de tener que abonar los daños que pudiera causarle al coche y no marqué ese importe que casi me doblaba el importe reclamo con el que Pepecar me había llevado hasta ese punto.

Pues bien, durante esas tres semanas hubo un día que fui a recoger el coche donde lo había estacionado y me lo encontré con una rueda menos de las esperadas:

Vamos a ahorrarnos los calificativos que me merece quien se dedica a ir robando ruedecillas ajenas. El caso es que llamé a los Mossos d’Esquadra para presentar la correspondiente denuncia y me dijeron que enseguida venían para levantar acta de daños. Mientras tanto llamé a Pepecar. Aquí viene otra práctica a mi entender patética: su único teléfono de atención telefónica es un 807. O sea, que Pepephone vela por nuestras facturas de móvil pero después Pepecar nos penga un sablazo de padre y muy señor mío como les tengamos que preguntar cualquier cosita. Está bien, sí, sí, muy coherente.

Llegaron los Mossos y para mi sorpresa inicial me comentaron que no iban a levantar acta de daños alguna porque en realidad el coche no presentaba el más mínimo daño (lo cual de hecho era cierto) sino que simplemente había sufrido un robo. Pero que un robo y un daño son cosas distintas y que lo que sí tenía que hacer era ir a la comisaría más cercana a presentar denuncia por robo, pero que de acta de daños nanai.

A partir de aquí me salto las penurias de ese día (caminar arriba y abajo cargando los bártulos, acudir a la comisaría, acudir al taller, utilizar el transporte público -de un polígono industrial, o sea andar mucho-, no poder trabajar en toda la jornada, etc). Hasta que por la tarde me llaman desde Pepecar y me dicen que por la rueda me van a cargar 300 euros en mi tarjeta de crédito porque no contraté la cobertura extra de daños. Les explico que según mi entender (y el de los Mossos) lo que le ha pasado al coche no es un daño sino un robo, y que por tanto no aplica la cobertura de daños y que no soy yo sino la compañía con la que tengan contratado el seguro del coche la que debe hacer frente a la incidencia…

Como no nos ponemos de acuerdo contacto con la Agència Catalana de Consum en el 012 para consultarles el caso. Me hacen saber que Pepecar debe facilitarme una copia de la póliza de seguro que tiene contratada el Smart en cuestión porque así podremos ver si dicha póliza incluye el robo o no (prácticamente cualquier póliza a terceros suele incluir dicha contingencia), ya que si lo incluye es efectivamente la aseguradora y no yo quien debe hacerse cargo del importe de la rueda.

Informo de todo ello a Pepecar, pero me comentan que esa póliza está en la central de Madrid (cómo no) y que en la sucursal no disponen de copias (para qué). Quedo con ellos que para el día que tengo que devolver el coche me lo tengan por favor preparado… y hasta hoy.

Devolví el coche, no tenían la copia de la póliza, les di más días de margen para que por favor me la remitieran via correo electrónico, pero han pasado ya dos semanas desde entonces y no he recibido noticia alguna de Pepecar. Así que les acabo de enviar una hoja de reclamaciones (y archivos adjuntos varios) como primer paso para intentar recuperar esos 300 euros que sigo pensando no deberían haberme reclamado a mí sino a su compañía de seguros.

Pero en cualquier caso, mi recomendación personal para cualquiera que quiera alquilar un coche durante unos días es que no lo haga con Pepecar. Si contratas el seguro opcional te estarás moviendo en los mismos o mayores precios que otras compañías. Si no lo contratas te arriesgas a que cualquier mínimo incidente que tengas (ni hablemos de que tengas la mala fortuna de que te roben el coche) convierta el alquiler en una pequeña ruina. Entiendo que hay mejores alternativas, dadas las circunstancias. Y con suerte no tendrán un número 807 como único número de teléfono para atención telefónica.

PD1. Seguiré informando de cómo evoluciona este tema.

PD2. Todo el personal con el que traté de Pepecar, pese a nuestra manera dispar de entender lo que había pasado, educadísimo y muy amable en el trato, que una cosa no quita la otra.

UPDATE my_data SET my_age = 30;

Pues nada, ya de lleno en los treinta y con un montón de proyectos en mente, a ver qué sale de todo ello. De buenas a primeras con este blog llevo ya, sin darme cuenta, más de cinco meses compartiendo miniapuntes de programación (y de lo que va saliendo) y la experiencia está siendo de lo más positiva y enriquecedora. Pero andan también por ahí otros proyectos a los que habrá que ir dando forma cuando las horas no escaseen como estas últimas semanas… Lo que faltan no son ideas, es tiempo.

Primera vez con Amazon.

Minipost para comentar que ayer recibí mi primer pedido en Amazon (US). Todo estupendo. Llegó en 15 días clavados desde que hice el pedido (obvio seleccioné el transporte estándar más económico), así que es un período de lo más razonable. Los libros han llegado en perfectísimo estado, ni un rasguño, ni un toque, nada. Y el precio, como es bien sabido en Amazon, la mar de aceptable. Lástima del incremento que suponen los gastos de envío, a ver si pronto tenemos Amazon propio.

Los libros. El primero es el Getting Things Done, libro de productividad del que tengo referencias excelentes desde hace tiempo, la última de David Santos Orcero, y que quería agenciarme sí o sí. Ya está en mis manos, a ver cuando voy sacando ratos para comenzar a aplicar sus técnicas.

El segundo es el manual de certificación de MySQL. Para la próxima primavera tengo intención de sacarme dicha certificación, así que me ha parecido adecuado ir agenciándome ya el libro para comenzar a mirármelo con buenos ojos. Sobre este tema habla por ejemplo Pedro Cambra en su blog.

Software malintencionado de Windows.

Vaya por delante que en general me produce más simpatía como empresa Google que Microsoft. También es cierto que creo que en algunos puntos no andan tan lejos la una de la otra, y que si Microsoft hubiera hecho algunas de las cosas que Google ha llevado a cabo nos habríamos lanzado a su yugular, mientras que con Google somos mucho más permisivos y tolerantes. Pero bueno, se habrán ganado ese margen de crédito, no me parece mal.

El caso es que estos últimos días toquiteando cositas en mi PC me he encontrado con dos mensajes, uno de cada compañía, que me han resultado graciosos, con la salvedad que uno pretendía serlo y el otro no.

Voy primero con el de Google. En su día instalé el navegador Chrome, pero en tanto que nunca lo estaba utilizando y que esperaré a que esté en versiones más avanzadas y estables, decidí desinstalarlo. Al hacerlo me salió este mensaje:

Me hizo gracia ese ¿Es que hemos hecho algo mal? en tono victimista pero cómico. No sé, me pareció un guiño divertido con los usuarios.

Ahora el de Microsoft. Como sabréis, Windows Vista incorpora un gestor de actualizaciones bastante decente. Al menos si lo tienes bien configurado para que no tome el poder de tu máquina sin consultarte nada. Pues bien, en la última propuesta de actualizaciones me ha mostrado una bien curiosa:

¿Cómo que Herramienta de eliminacion de software malintencionado de Windows? ¿Me está diciendo que tengo en mi ordenador software malintencionado de Windows? ;-) No, no vale decir que eso es una redundancia… Pero cielos, a qué nivel de autocrítica han llegado los chicos de Microsoft. Admiten ya que existe tal cosa como software malintencionado de Windows. ¿Qué hará esa herramienta de eliminación de dicho software? ¿Un format C:?

PD. Lo sé, el ‘de Windows’ no se refiere realmente a ’software malintencionado’ sino a ‘Herramienta de eliminación’, pero igual les habría quedado mejor (¡aunque menos cómica!) una redacción como ‘Herramienta de Windows de eliminación de software malintencionado’…

Pinceladas de álgebra relacional.

Hoy algo de teoría. Como sabemos, el lenguaje estándar para realizar consultas en bases de datos es el SQL (Structured Query Language). Este lenguaje permite definir y manipular bases de datos relacionales, que son las bases de datos con las que solemos trabajar (como MySQL, Oracle, SQL Server y demás). Hay otro tipo de bases de datos que no son relacionales, pero su uso a día de hoy es tremendamente minoritario.

En una base de datos relacional la información está estructurada en forma de relaciones (ojo, no confundirse, en este ámbito ‘relación’ equivale a lo que habitualmente llamamos ‘tabla’) dando lugar a lo que se conoce como modelo relacional. En este modelo relacional las consultas se pueden llevar a cabo con lenguajes relacionales de dos tipos:

1) Lenguajes basados en álgebra relacional, que se inspira en la teoría de conjuntos para ir construyendo paso a paso el procedimiento a seguir para obtener los datos deseados. Estos lenguajes son lenguajes, pues, procedimentales.

2) Lenguajes basados en el cálculo relacional, que se basa en el cálculo de predicados de la lógica matemática. Estos lenguajes no describen un procedimiento y por tanto se les llama lenguajes declarativos (no procedimentales).

SQL es un lenguaje mixto, ya que incluye temas de álgebra relacional y otros de cálculo relacional, aunque predominan estos últimos, por lo que se considera un lenguaje declarativo. No obstante, hoy vamos a ver algunas pinceladas básicas de álgebra relacional.

Para especificar una consulta en álgebra relacional hay que ir siguiendo una serie de pasos que sirven para ir construyendo nuevas relaciones (recordemos: análogas a las tablas) a partir de las relaciones existentes. En estos pasos se utilizan las operaciones del álgebra relacional, que resumidas de un modo muy escueto son las siguientes.

0. Renombramiento.

Se trata de una operación que nos facilita trabajar con relaciones cambiándoles el nombre a ellas o a sus atributos. Así…

R (h) := RelacionConNombreOriginal (AtributoConNombreOriginal)

…renombra la relación a ‘R’ y uno de sus atributos a ‘h’ (del mismo modo que relaciones vienen a ser lo que habitualmente llamamos tablas, atributos vienen a ser lo que llamamos campos o columnas en esas tablas).

1. Unión.

Se trata de generar una nueva relación juntando todas las tuplas (tuplas = registros) de las dos relaciones que intervienen en la unión. Está claro que las dos relaciones deberán tener estructuras semejantes. Además, como en una relación (como en una tabla) no puede haber tuplas (registros) duplicadas, si al unirlas se da alguna duplicidad, se elimina directamente. Se denota con…

R := T U S

…donde ‘U’ representa el símbolo de unión y ‘T’ y ‘S’ son las relaciones de partida.

2. Intersección.

Se trata de generar una nueva relación con las tuplas que aparecen a la vez en las dos relaciones que intervienen en la intersección. También aquí las dos relaciones deberán tener estructuras semejantes. Se denota con…

R := T (U) S

…donde ‘(U)’ pretende ser el símbolo de intersección (’U’ invertida) que no sé cómo representar aquí en el blog y ‘T’ y ‘S’ son las relaciones de partida.

3. Diferencia.

Se trata de generar una nueva relación con las tuplas que aparecen en una primera relación pero no en una segunda. Nuevamente ambas relaciones deben tener estructuras semejantes. Se denota con…

R := T - S

…donde ‘T’ y ‘S’ son las relaciones de partida.

4. Producto cartesiano.

Se trata de generar una nueva relación combinando cada una de las tuplas de una primera relación con cada una de las tuplas de una segunda relación. Así pues la relación resultante tendrá tantos atributos como la suma de los atributos de cada una de las relaciones de partida. Y tendrá tantas tuplas como el producto de tuplas de ambas relaciones. Se denota con…

R := T x S

…donde ‘T’ y ‘S’ son las relaciones de partida.

5. Selección.

Se trata de generar una nueva relación con un subconjunto de las tuplas de la relación de partida. Para ello es preciso expresar alguna condición que las tuplas deberán cumplir para formar parte de la nueva relación. Se denota con…

R := T(C)

…donde ‘T’ es la relación de partida y ‘C’ la condición (puede ser compuesta) que deben cumplir las tuplas de ‘T’ para formar parte de ‘R’.

6. Proyección.

Se trata de generar una nueva relación con sólo algunos de los atributos de los que consta la relación de partida. Se denota con…

R := T [A1, A2, A3...]

…donde ‘T’ es la relación de partida y las ‘A’ representan los atributos que se quieren trasladar a la relación resultante ‘R’.

7. Combinación.

Se trata de generar una nueva relación a partir de un producto cartesiano entre dos relaciones de partida, con la salvedad que sólo formarán parte de la nueva relación las tuplas resultantes del producto que cumplan unas determinadas condiciones de igualdad entre atributos. Dios, suena fatal… pero es simplemente un JOIN. Se denota con…

R := T [B] S

…donde ‘T’ y ‘S’ son las relaciones de partida y ‘B’ es el conjunto de condiciones.

8. Combinación natural.

Igual que la combinación, sólo que no es necesario especificar las condiciones ‘B’ puesto que se asume que los atributos que se quieren igualar son los que se llaman igual en las dos relaciones de partida. Se denota con…

R := T * S

…donde ‘T’ y ‘S’ son las relaciones de partida.

Expresado así todo ello suena bastante complejo, la verdad. Pero si conocemos algo de SQL y hemos ido identificando las instrucciones veremos que es mucho más sencillo de lo que parece. Los renombramientos vienen a ser alias, las uniones vienen a ser UNIONs, las selecciones son SELECT * con
cláusulas WHERE, las proyecciones son SELECT con una selección de campos, etc.

Vamos a ver ahora un ejemplo interesante de cómo se construye una consulta con álgebra relacional. Imaginemos que tenemos una relación con las siguientes tuplas (voy a representarlo como tabla con registros para que resulte más familiar):

+-----------------+
|   biblioteca    |
+-------+---------+
| libro | paginas |
+-------+---------+
|   AAA |     125 |
|   BBB |     250 |
|   CCC |     300 |
|   DDD |     105 |
+-------+---------+

Si en SQL quisiéramos saber cuál es el menor número de páginas nos bastaría con hacer…

SELECT MIN(paginas) 
FROM biblioteca;

Y si quisiéramos saber cuál es el libro con ese menor número de páginas tendríamos que hacer…

SELECT libro 
FROM biblioteca 
WHERE paginas = (SELECT MIN(paginas) FROM biblioteca);

¿Pero cómo conseguimos encontrar esto con álgebra relacional donde NO tenemos una maravillosa función MIN()? Tenemos que hacerlo por pasos, más o menos así:

Paso 1.

Obtener una relación sólo con los números de páginas.

R1 := biblioteca [paginas]

+---------+
| paginas |
+---------+
|     125 |
|     250 |
|     300 |
|     105 |
+---------+
Paso 2.

Renombrar esta relación para poder llevar a cabo el siguiente paso. Podríamos haberlo hecho directamente en el paso 1, pero así queda más claro.

R2 (pags) := R1 (paginas)

+------+
| pags |
+------+
|  125 |
|  250 |
|  300 |
|  105 |
+------+
Paso 3.

Llevar a cabo una combinación entre ‘biblioteca’ y la recién obtenida ‘R2′ estableciendo como condición que ‘paginas’ sea mayor que ‘pags’.

R3 := biblioteca [paginas > pags] R2

+-------+---------+------+
| libro | paginas | pags |
+-------+---------+------+
|   AAA |     125 |  105 |
|   BBB |     250 |  125 |
|   BBB |     250 |  105 |
|   CCC |     300 |  125 |
|   CCC |     300 |  250 |
|   CCC |     300 |  105 |
+-------+---------+------+

Como vemos, hemos obtenido una relación donde aparecen todas las combinaciones en que ‘paginas’ tiene un valor superior a algún valor de ‘pags’. En esa relación tenemos pues todos los valores de ‘paginas’ que son mayores que algún otro valor de ‘pags’, y por tanto sólo faltan los valores de ‘paginas’ que no son mayores a ningún otro valor de ‘pags’ (o sea, los valores mínimos).

Paso 4.

Hacer una proyección para quedarnos sólo con los valores de ‘paginas’.

R4 := R3 [paginas]

+---------+
| paginas |
+---------+
|     125 |
|     250 |
|     300 |
+---------+
Paso 5.

Efectuar una diferencia entre los números de páginas de la relación original (lo tenemos en ‘R1′) y los que acabamos de obtener.

R5 := R4 - R1

+---------+
| paginas |
+---------+
|     105 |
+---------+

¡Bingo! Ya tenemos el valor mínimo de páginas… vamos ahora a terminar encontrando el título del libro con el menor número de páginas…

Paso 6.

Realizar una combinación natural entre la relación original y esta última.

R6 := biblioteca * R5

+-------+---------+
| libro | paginas |
+-------+---------+
|   DDD |     105 |
+-------+---------+
Paso 7.

Y para obtener el título del libro… una última proyección.

R7 := R6 [libro]

+-------+
| libro |
+-------+
|   DDD |
+-------+

Así pues, vemos que para llevar a cabo consultas con álgebra relacional debemos seguir una técnica procedimental en que vamos marcando los pasos que hay que ir siguiendo (haz esto y hazlo así). En cambio con SQL no necesitamos hacerlo de ese modo, ya que como hemos comentado al principio del post SQL es un lenguaje declarativo (haz esto, no me importa cómo lo hagas).

Nunca está de más tener algunas nociones de teoría de bases de datos, ¿no? ;-)

Valoración personal sobre el Barcelona PHP Conference.

Comentaba unos posts atrás mi intención de acudir al Barcelona PHP Conference que organizaban la gente del PHP Barcelona User Group. Pues bien, fui. Y disfruté. La verdad es que fue un evento excepcional, tal como ellos mismos comentan en sus conclusiones en su blog. Así que voy a limitarme a comentar algunos puntos que en mi opinión merecen mención especial:

1. Las instalaciones.

El Citilab de Cornellà es un centro absolutamente ideal para este tipo de actos. Mi más sincera enhorabuena para los responsables de haber llevado a cabo un proyecto como el Citilab. Tiene varias salas de conferencias absolutamente preparadas y habilitadas para llevar a cabo eventos de este tipo con toda comodidad. Dispone de facilidades tecnológicas varias y de un completo equipamiento que lo convierten en un marco ideal para actividades como estas ponencias. Genial, en serio.

2. La organización.

Estupenda. Desde el principio hasta el fin. La página web con información, la inscripción a la llegada, el respeto por los horarios, el asegurarse que nada falle, la contratación de un cátering exquisito… ni un pero se les puede poner. Incluso se molestaron en pasar a los asistentes una encuesta de satisfacción y mejora para próximas ediciones. Muy, muy bien.

3. El contenido de las conferencias.

Pude asistir a las de Marcus Bointon hablando de ‘Email in PHP’ (OK, understood, we won’t use mail() function!), Arno Schneider hablando de ‘Rasmus think again! Agile Framework == happy PHP Developer’ (YES! I’ll use a framework, I promise!), Pau Garcia-Milà hablando de ‘eyeOS: Open Source Web Desktop System in PHP’ (enlazo la web del proyecto eyeOS y no la suya personal porque creo que no la actualiza demasiado ;-) ) y finalmente Scott MacVicar hablando de ‘SQLite3′. Todas ellas estuvieron muy bien. Si tuviera que quedarme con alguna escogería las de Arno Schneider y Pau Garcia-Milà. La de Arno porque me pareció un brillantísimo ponente que domina contenidos y continentes, formas y fondos y que convierte una hora de ponencia en un rato fantástico. La de Pau porque siendo los dos del mismo pueblo todavía no había tenido oportunidad de escucharle en directo hablando de su proyecto, y la verdad es que me he marcado como asignatura pendiente el echarle un buen vistazo e intentar participar desarrollando algo en cuanto tenga algo más de tiempo.

Por desgracia algunas ponencias se solapaban y no pude asistir a otras que seguro también habrían resultado interesantes. No obstante, bajo mi criterio es un punto favorable lo de programar dos ponencias en paralelo para ofrecer a la gente varias alternativas en lugar de un único recorrido obligatorio.

4. Los patrocinadores (enlazados todos ellos aquí y aquí).

Siempre son necesarios en eventos así. Además en este caso tuvieron una presencia adecuada (no intrusiva), obsequiaron a los asistentes con algún detallito promocional y ayudaron a crear ambiente, así que genial.

Y nada más. Simplemente esperar la próxima edición confiando que no bajen el listón ni un centímetro y felicitar nuevamente a toda la organización por lo estupendo del resultado final.




Creative Commons License
El blog de Albert Mata by Albert Mata is licensed under a Creative Commons Reconocimiento-Compartir bajo la misma licencia 2.5 España License.