MySQL - Guardar algunos días de la semana

MySQL – Guardar combinación de días de la semana

En este post te voy a enseñar a guardar solo algunos días de la semana elegidos por algún usuario usando MySQL usando un número entero, de modo que el usuario puede elegir todos los días de la semana, ninguno de ellos, solo sábado y domingo, solo lunes, solo el martes o cualquier combinación de días.

MySQL - Guardar algunos días de la semana
MySQL – Guardar algunos días de la semana

Vamos a usar un número entero y máscaras de bits para almacenar los días de la semana elegidos por el usuario, además de enseñarte a hacer consultas SQL para saber si cierto día de la semana se encuentra en los días que el usuario ha elegido.

Gracias a las máscaras de bits podemos almacenar una combinación de días de la semana con MySQL. Al final, MySQL solo guardará un entero cuyo valor máximo será 127.

Usar un byte para guardar días de la semana

Antes de revisar la conversión de los días a un entero, recordemos que un byte tiene 8 bits, y que una semana tiene 7 días. Así que un byte alcanza perfectamente para guardar los 7 días, guardando 1 día por bit. Con este método incluso sobra un bit.

Posicionalmente, de derecha a izquierda (suponiendo que el MSB está al inicio), los bits de un byte valen 128, 64, 32, 16, 8, 4, 2 y 1. Entonces podemos usar el bit que vale 1 (que a su vez es el LSB) para el domingo, 2 para lunes, 3 para martes y seguir así hasta llegar al sábado en el bit que vale 64.

En este caso el MSB que vale 128 no será usado.

Veamos algunos ejemplos:

  1. Si el usuario elige solo el domingo, el byte se verá así: 00000001, mismo que es 1 en base 10
  2. Para domingo y lunes: 00000011, mismo que es 3
  3. Sábado y martes: 01000100, mismo que es 68
  4. Domingo, miércoles, viernes: 00101001, que es 41 en decimal
  5. Todos los días: 01111111, es decir, 127
  6. Ningún día: 00000000, que equivale a 0 en decimal

Obviamente hay muchas combinaciones, solo te estoy mostrando cómo podemos guardar cuáles días han sido elegidos a partir de un simple byte, usando 7 bits, un bit por día, guardando todas las combinaciones posibles.

Convirtiendo días a máscara de bits

Para guardar la combinación de días de la semana primero necesitamos calcular una máscara de bits según los días elegidos por el usuario. Aquí interviene el lenguaje de programación, ya que MySQL solo guardará el entero, nosotros debemos convertir el arreglo de días a una máscara de bits.

Yo lo he hecho con Go y queda así:

func diasAMascaraDeBits(diasDeLaSemana []string) int {
	var mapaDeDiasConSuByteCorrespondiente = map[string]int{
		"domingo":   1 << 0, // 1
		"lunes":     1 << 1, // 2
		"martes":    1 << 2, // 4
		"miércoles": 1 << 3, // 8
		"jueves":    1 << 4, // 16
		"viernes":   1 << 5, // 32
		"sábado":    1 << 6, // 64
	}
	mascaraDeBits := 0
	for _, dia := range diasDeLaSemana {
		if byteCorrespondienteAlDiaDeLaSemana, byteExiste := mapaDeDiasConSuByteCorrespondiente[dia]; byteExiste {
			mascaraDeBits |= byteCorrespondienteAlDiaDeLaSemana
		}
	}
	return mascaraDeBits
}

El proceso ocurre en la línea que tiene mascaraDeBits |= byteCorrespondienteAlDiaDeLaSemana, ahí se está haciendo una operación de tipo OR que sería equivalente a: mascaraDeBits = mascaraDeBits | byteCorrespondienteAlDiaDeLaSemana

Recordemos que la operación OR va a devolver true si al menos uno de los dos elementos es true, pero en este caso solo se está encendiendo el bit correspondiente y que es el único bit encendido en el byte correspondiente al día de la semana.

¿Cómo sabemos cuál posición establecer? cada byte correspondiente al día de la semana en el mapa ya tiene un 1 establecido en la posición dependiendo del día.

Máscara de bits a días

Pasemos a convertir la máscara de bits, que será un día entero, a una lista de días. En este proceso también usamos un mapa, pero ahora comprobamos si cada día (representado por un bit) está presente en la máscara, y solo así agregamos el nombre del día a la lista.

Para este caso el mapa está inverso, pues la clave es el byte y el valor es el día.

func mascaraDeBitsAListaDeDias(enteroRepresentandoMascaraDeBits int) string {
	// Mapa inverso de valores en bits a días de la semana
	var mapaDeBytesConSuDiaCorrespondiente = map[int]string{
		1 << 0: "Domingo",   // 1
		1 << 1: "Lunes",     // 2
		1 << 2: "Martes",    // 4
		1 << 3: "Miércoles", // 8
		1 << 4: "Jueves",    // 16
		1 << 5: "Viernes",   // 32
		1 << 6: "Sábado",    // 64
	}
	var dias []string
	for byteDelDiaDeLaSemana, nombreDelDia := range mapaDeBytesConSuDiaCorrespondiente {
		if enteroRepresentandoMascaraDeBits&byteDelDiaDeLaSemana == byteDelDiaDeLaSemana {
			dias = append(dias, nombreDelDia)
		}
	}
	var diasComoCadena string
	for indiceDia, dia := range dias {
		diasComoCadena += dia
		if indiceDia+1 < len(dias) {
			diasComoCadena += ", "
		}
	}
	return diasComoCadena
}

De nuevo, el proceso ocurre en la siguiente línea:

if enteroRepresentandoMascaraDeBits&byteDelDiaDeLaSemana == byteDelDiaDeLaSemana

Para comprobar si un bit está encendido en cierta posición, podemos hacer una operación de tipo AND que va a devolver true solo si ambos bits en la misma posición están encendidos.

El resultado de la operación debe ser exactamente igual al byte del día de la semana que estamos comprobando, ya que el byte de entrada (byteDelDiaDeLaSemana) solo tiene un bit encendido, los demás están apagados.

Nota: es importante que comprendas la operación enteroRepresentandoMascaraDeBits&byteDelDiaDeLaSemana == byteDelDiaDeLaSemana ya que la usaremos de nuevo al consultar con MySQL.

Consultar si día de la semana está presente con MySQL

A partir del día de la semana (según los mapas definidos previamente) veamos cómo hacer una consulta SQL filtrando solo aquellos datos donde la máscara de bits contenga el día de la semana.

Vamos a suponer que queremos filtrar los datos donde los días elegidos contengan el día domingo, mismo que es representado como 1. La consulta queda así:

SELECT * FROM tabla WHERE ((tabla.mascaraDeBits & 1) = 1)

Suponiendo que necesitamos aquellas filas donde la combinación de los días de la semana contenga el día sábado, que corresponde al número 64, la consulta quedaría así:

SELECT * FROM tabla WHERE ((tabla.mascaraDeBits & 64) = 64)

Y podemos hacer esto con cualquier otro día de la semana.

Conclusión

Existen más maneras de guardar cualquier combinación de días de la semana en MySQL.

Yo quise hacerlo con máscaras de bits porque me parece interesante y optimizado, pero también podrías guardar la lista de días como una cadena separada con comas y luego usar INSTR o funciones similares para saber si, dado un nombre de día de la semana, el mismo está presente en la cadena de combinación de días.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *