Tiempo de lectura: 3 minutos

Bits, Bytes, Nibles, Sets, Enums, Flags en PHP y MySQL en pro de eficiencia

Por Nestor Mata Cuthbert

Es posible sacar más provecho al espacio y los datos en la base de datos y el codigo, y es de hecho muy importante, para algunos enfoques se puede utilizar datos a nivel de bits de manera eficiente, tanto en el codigo PHP, MySQL y en Drupal.

Antes de mejorar algo o sacarle más provecho es importante entenderlo, para esto vamos a hacer un repaso de algebra booleana.
Un Byte son 8 bits, 1 byte (tiny int en mysql) nos permite guardar 256 valores diferentes, 2 Bytes (smallint en mysql) nos dan 65.536 valores diferentes y 4 Bytes (int en mysql) nos dan 4.294.967.296 valores diferentes y el BigInt usando 8 Bytes.
También algunos engines soportan el uso de Bit como tipo de dato, soportando hasta 64 bits por tabla.
Además, esta los tipos Enum y Set.

ENUM

El ENUM puede contener hasta 65.535 valores diferentes (y el vacio o cero) y esta catalogado dentro de los tipos de datos de string por la manera en la que se utiliza, ya que cada valor esta asociado a un string y se utiliza como si se entuviera ingresando o sacando strings que dentro del engine son representados por un valor numerico.
Al final de cuentas, cualquier tipo numerico, inclyendo el Bit puede ser utilizado de la misma manera que un Enum, con la unica diferencia que el valor que representa debera ser interpretado en la aplicación y no es tan legible en la base de datos.
La manera propuesta en ese caso, es usar un numero para representar un estado, teniendo solamente 1 valor al mismo tiempo salvado por cada dato, lo más usual es utilizar tiny o small ints ya que no se suelen manejar grandes cantidades de flags o estados.

Un ejemplo de esto es utilizar un campo en la base de datos para indicar el estado actual de una cuenta bancaria, donde digamos que tenemos los siguientes estados:

EstadoValor numerico
En revision0
Denegada1
Activa2
Suspendida3
Inactiva4

Una cuenta puede contener solo uno de estos valores al mismo tiempo y por eficiencia utilizamos ya sea un Enum que se representa como un valor numerico en la base de datos o un tiny int y en nuestra aplicacion interpretamos cada valor numerico como un estado.

CREATE TABLE Cuenta (
    Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    -- Mas campos
    Estado ENUM('En revision', 'Denegada', 'Activa', 'Suspendida', 'Inactiva') NOT NULL
)

Y se puede utilizar algo asi:

<?php
// Actualice el estado de la cuenta
$estado = 'Activa';
$cuenta = 123;
$connection->query("UPDATE Cuenta SET Estado = '$estado' WHERE Id = $cuenta");
?>

Que a nivel de espacio y de datos es equivalente a lo siguiente:

CREATE TABLE Cuenta (
    Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    -- Mas campos
    Estado TINYINT NOT NULL
)

Y se puede utilizar algo asi:

<?php
define('ESTADO_EN_REVISION', 0);
define('ESTADO_DENEGADA', 1);
define('ESTADO_ACTIVA', 2);
define('ESTADO_SUSPENDIDA', 3);
define('ESTADO_INACTIVA', 4);
$estado = ESTADO_ACTIVA;
$cuenta = 123;
$connection->query("UPDATE Cuenta SET Estado = $estado WHERE Id = $cuenta");
?>

SET

Ahora, esta el otro tipo de uso, que consta de utilizar varios valores simultaneos enmascarados en un mismo dato.

Probablemente el ejemplo de uso mas claro es el tipo Set que puede contener 64 valores simultaneos, al utilizar un tipo SET de MySql, se define los diferentes valores que puede contener, se define en strings que son representados por valores numericos de exponente 2, esto permite que cada valor pueda coexistir con los demas ya que cada valor ocupa un bit binario en una posicion diferente.

Tomemos como ejemplo caracteristicas de un producto el cual puede tener los siguientes estados simultaneamente:

EstadoValor BinarioValor Numerico
Activo000000011
Excento000000102
Perecedero000001004
En Descuento000010008

Nota: Los valores decimales aumentan en exponente 2, o sea 2 elevado a la posicion del digito.

Como se puede notar de sus valores binarios estos valores puede coexistir al mismo tiempo, por ejemplo, si aplicamos el operador binario “O” (OR) a un par de valores, digamos, que el producto esta activo y en descuento:

00000001 | 00001000 = 00001001

o que es lo mismo 1+8=9

Y a este podemos agregarlo un estado mas en cualquier momento:

00001001 | 00000010 = 00001011

Además con la operacion “O Exclusivo” (XOR) podemos remover cualquier estado, por ejemplo, quitemos el estado de activo:

00001011 ^ 00000001 = 00001010

Esto se puede implementar de la siguiente manera:

CREATE TABLE Producto (
    Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    -- Mas campos
    Estado SET('Activo', 'Excento', 'Perecedero', 'En Descuento') NOT NULL
)

Y utilizarse algo asi:

<?php
// Busque producto que tenga X estado usando FIND_IN_SET
$estado = 'Activo';
$connection->query("SELECT * FROM Producto WHERE FIND_IN_SET('$estado', Estado)");
// Busque producto con X estado usand Y BINARIO (AND)
$estado = 1;
$connection->query("SELECT * FROM Producto WHERE Estado & $estado");
// En el select, lo que devuelve la base de datos es un string separado por comas para los diferentes valores.
// Agregue un estado a un producto
$producto = 123;
$connection->query("UPDATE Producto SET Estado = Estado | 1 WHERE Id = $producto");
$connection->query("UPDATE Producto SET Estado = 'Activo,Excento' WHERE Id = $producto");
?>

Que es equivalente a lo siguiente:

CREATE TABLE Producto (
    Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    -- Mas campos
    Estado TINYINT NOT NULL
)

Esta es una manera de reducir el tamaño de los datos en la base de datos de manera eficiente.

Suscribase

* campo requerido

 RSS Feed

Renovado!

Completamente renovado, más rápido y mejorado.
Este blog fue hecho de nuevo desde cero cambiando las tecnologías.
Ahora usa Jekyll, SASS, Foundation, Node.js, AJAX y ESI.
Suscribase para saber cuando explico como fue creado.