Qué tipo de datos MySQL usar para almacenar valores booleanos

1208

Dado que MySQL no parece tener ningún tipo de datos 'booleanos', ¿qué tipo de datos 'abusa' para almacenar información verdadera / falsa en MySQL?

Especialmente en el contexto de escribir y leer desde / hacia un script PHP.

Con el tiempo, he usado y visto varios enfoques:

  • tinyint, campos varchar que contienen los valores 0/1,
  • campos varchar que contienen las cadenas '0' / '1' o 'verdadero' / 'falso'
  • y finalmente enum Campos que contienen las dos opciones 'verdadero' / 'falso'.

Nada de lo anterior parece óptimo. Tiendo a preferir la variante tinyint 0/1, ya que la conversión automática de tipos en PHP me da valores booleanos de manera bastante simple.

Entonces, ¿qué tipo de datos utilizas? ¿Hay un tipo diseñado para valores booleanos que he pasado por alto? ¿Ve alguna ventaja / desventaja al usar un tipo u otro?

Peter Mortensen
fuente
217
Cualquier persona que lea las respuestas anteriores a esta pregunta debe comprender que MySQL agregó un tipo de datos un poco en la versión 5. Use esa información como pueda. dev.mysql.com/doc/refman/5.0/en/bit-type.html
smp7d
3
¿Pregunta relacionada con la alternativa a muchos booleanos en MySQL?
tereško
77
para ver la versión actual del tipo booleano MYSQL, dev.mysql.com/doc/refman/5.5/en/numeric-type-overview.html verifique esto. de acuerdo con ese valor cero considerado falso
DevT
77
bit(1)es un poco ** para importar en Excel. Cambio a tinyint(1)obras.
Cees Timmerman
8
ahora tenemos booleanos después de 5 años
V-SHY

Respuestas:

1232

Para MySQL 5.0.3 y superior, puede usar BIT. El manual dice:

A partir de MySQL 5.0.3, el tipo de datos BIT se utiliza para almacenar valores de campo de bits. Un tipo de BIT (M) permite el almacenamiento de valores de M bits. M puede variar de 1 a 64.

De lo contrario, de acuerdo con el manual de MySQL, puede usar bool y boolean, que actualmente son alias de tinyint (1):

Bool, Boolean: estos tipos son sinónimos de TINYINT (1). Un valor de cero se considera falso. Los valores distintos de cero se consideran verdaderos.

MySQL también afirma que:

Tenemos la intención de implementar un manejo de tipo booleano completo, de acuerdo con SQL estándar, en una futura versión de MySQL.

Referencias: http://dev.mysql.com/doc/refman/5.5/en/numeric-type-overview.html

Markus
fuente
11
Sí, iría por esto o por un CHAR (1) y almacenaría 'Y' / 'N' o 'T' / 'F', etc., dependiendo del contexto. La ventaja de usar un tipo entero pequeño es que obtienes la máxima portabilidad entre los RDBMS-es
Roland Bouman
36
Optar por char, al menos en PHP, generará más código, ya !$booleanque nunca se evaluará correctamente sin un procesamiento adicional.
Mild Fuzz
10
@Pecerier Nada que no puedas buscar en Google, pero está bien, te voy a morder. En primer lugar, eche un vistazo a data0type.h. Tenga en cuenta que innodb no define de forma nativa un tipo BIT allí. Si tratara los campos BIT de la manera que usted describe, seguramente encontraríamos alguna pista de su existencia allí. En segundo lugar, lea mysqlperformanceblog.com/2008/04/23/… . Y no dude en aclararnos qué asombrosos clientes MySQL en el "marktetplace" juegan bien con los campos BIT. Serán útiles para cualquiera que haya perdido ese artículo, sin duda.
Roland Bouman
99
Cuando hago una selección de la línea de comando estándar mysql, los campos de bits del cliente aparecen completamente en blanco. Debido a esto, prefiero TINYINT (1).
Usuario
8
@MikePurcell Odio preguntar, pero ¿por qué querrías auto_incrementen una columna que representa un valor booleano?
Chris Hayes
248

BOOLy BOOLEANson sinónimos de TINYINT(1). Cero es false, cualquier otra cosa es true. Más información aquí .

Philip Morton
fuente
77
El (1)no hace más que determinar cómo se visualiza el valor, si usted es consciente de tamaño de almacenamiento a continuación, que desea utilizar BITen su lugar
JamesHalsall
35
@JamesHalsall: en realidad, BIT(1)y TINYINT(1)ambos usarán un byte de almacenamiento. Hasta MySQL 5.0.3, en BITrealidad era sinónimo de TINYINT. Las versiones posteriores de MySQL cambiaron la implementación de BIT. Pero incluso con el cambio de implementación, todavía no hay un beneficio de "tamaño de almacenamiento" para el BITtipo de datos (al menos con InnoDB y MyISAM; otros motores de almacenamiento, por ejemplo, NDB pueden tener alguna optimización de almacenamiento para múltiples declaraciones de columnas BIT). El problema más grande es que algún cliente Las bibliotecas no reconocen ni manejan adecuadamente las BITcolumnas de tipo de datos devueltas . A TINYINTfunciona mejor.
spencer7593
55
El manual de MySQL 5.0 dice claramente que un valor booleano es 1 o 0. La frase "cualquier otra cosa es true" no es cierta.
Walter
77
@Walter: En realidad es cierto, la explicación es algo deficiente. Brevemente, en un contexto booleano, una expresión puede evaluarse como NULL, FALSE o TRUE. En una declaración MySQL, una expresión evaluada en un contexto booleano se evalúa primero como un entero (los valores decimales y flotantes se redondean, las cadenas se convierten de la manera peculiar habitual en que MySQL convierte las cadenas en enteros). Un NULL es obviamente NULL (ni VERDADERO ni FALSO). Un valor entero de 0 se maneja como FALSO, y cualquier otro valor entero (1, 2, -7, etc.) se evalúa como VERDADERO. Por compatibilidad, imitamos esa lógica / manejo de booleano
TINYINT
44
@Walter: Esto es fácil de probar, por ejemplo SELECT 'foo' AS bar FROM dual WHERE -7. La expresión -7 se evalúa en un contexto booleano y la consulta devuelve una fila. Podemos probar con 0, o cualquier expresión que se evalúe como valor entero 0, y no se devuelve ninguna fila. Si la expresión en la cláusula WHERE se evalúa como cualquier valor entero no nulo que no sea cero, la expresión es TRUE. (Creo que los valores decimales y flotantes se "redondean" a entero, por ejemplo, se WHERE 1/3evalúa a WHERE 0. Obtenemos el mismo resultado con WHERE 'foo', porque la cadena 'foo'también se evalúa a valor entero 0.
spencer7593
71

Esta es una solución elegante que aprecio bastante porque utiliza cero bytes de datos:

some_flag CHAR(0) DEFAULT NULL

Para establecerlo en verdadero, establecer some_flag = ''y establecerlo en falso, establecer some_flag = NULL.

Luego, para comprobar si es verdadero, verifique si some_flag IS NOT NULL, y para verificar si es falso, verifique si some_flag IS NULL.

(Este método se describe en "MySQL de alto rendimiento: optimización, copias de seguridad, replicación y más" por Jon Warren Lentz, Baron Schwartz y Arjen Lentz).

RS
fuente
3
truco de lujo! Esto es útil si se trabaja con MySQL <5 y tal vez incluso una huella más ligera que BIT, sin embargo, en un esfuerzo por cumplir con la convención y un poco menos sobrecarga de cómputo (lógica frente a valor exacto), diría que BIT es la mejor manera de hacerlo.
zamnuts
59
Puede ser 'rápido', pero ofusca los datos de manera que cualquier desarrollador nuevo no tenga idea de lo que significa la columna.
Richthofen
55
Esto utiliza la misma cantidad de bytes que BIT (1)
SU Alaska
25
Buena suerte para que un ORM se asigne muy bien a esto.
Craig Labenz
44
Estoy de acuerdo con @Richthofen, y me resulta difícil imaginar una situación en la que alguna vez recomendaría usar esta solución. Sin embargo, si se va a utilizar, entonces, especificar como COMMENTen la definición de la columna que NULLindica falso e ''indica verdadero podría ser de alguna manera muy pequeña para ayudar a la comprensión futura.
eggyal
34

Si usa el tipo BOOLEAN, este tiene un alias para TINYINT (1). Esto es mejor si desea utilizar SQL estandarizado y no le importa que el campo pueda contener un valor fuera de rango (básicamente, todo lo que no sea 0 será 'verdadero').

ENUM ('False', 'True') le permitirá usar las cadenas en su SQL, y MySQL almacenará el campo internamente como un entero donde 'False' = 0 y 'True' = 1 en función del orden en que se especifica el Enum .

En MySQL 5+ puede usar un campo BIT (1) para indicar un tipo numérico de 1 bit. No creo que esto realmente use menos espacio en el almacenamiento, pero nuevamente le permite restringir los posibles valores a 1 o 0.

Todo lo anterior utilizará aproximadamente la misma cantidad de almacenamiento, por lo que es mejor elegir el que le resulte más fácil para trabajar.

Ciaran McNulty
fuente
8
Su comentario sobre ENUM no es cierto: intente CAST (yourenumcol AS UNSIGNED) y notará que False será 1 y True será 2. Otro problema con ENUM es que es demasiado fácil de insertar '' (cadena vacía ) No recomendaría usar esto.
Roland Bouman
44
En mi experiencia, usar un campo BIT (1) del código PHP fue un poco problemático. TINYINT (1) fue mucho más fácil y produjo un código más legible.
M-Peror
1
@ M-Peror - "usar un campo BIT (1) del código PHP fue un poco problemático" ... sin juego de palabras. :) Pero sí, estoy de acuerdo. Recuerdo que TINYINT (1) también es más fácil ... simplemente no puedo recordar por qué. ¿Alguien más tiene pensamientos sobre esto? BIT (1) parece más agradable en la superficie porque puede restringir a 0 o 1. Creo que BIT a veces se interpretó como datos binarios (dependiendo del lenguaje de programación y el controlador / biblioteca); mientras que TINYINT fue tratado más como un número.
BMiner
2
@BMiner - jaja, fue realmente involuntario, no lo noté :) Pero de hecho, si recuerdo correctamente, el campo de bits se interpretó como algo binario, mientras que el tinyint fue más fácil de tratar como un número y, por eso, más fácil utilizar en una expresión (booleana).
M-Peror
34

Esta pregunta ha sido respondida pero pensé que tiraría mis $ 0.02. A menudo uso un CHAR(0), donde '' == true and NULL == false.

De documentos de mysql :

CHAR(0)También es bastante bueno cuando necesita una columna que puede tomar solo dos valores: una columna que se define como CHAR(0) NULLocupa solo un bit y puede tomar solo los valores NULLy ''(la cadena vacía).

Josh
fuente
16
mm, esto parece pedir problemas si tú como yo. Quiero decir, dependiendo del lenguaje, puede ser demasiado fácil no detectar la diferencia entre NULL y '' (por ejemplo, PHP).
Roland Bouman
3
En términos de ahorro de espacio (el número de bytes utilizados para representar un booleano), este enfoque es un claro ganador. Esto ahorra un byte sobre TINYINT. La desventaja (como señalan algunos comentarios) es que algunos clientes pueden tener dificultades para distinguir entre una cadena NULL y una cadena vacía. Incluso algunas bases de datos relacionales (por ejemplo, Oracle) no distinguen entre una cadena de longitud cero y una NULL.
spencer7593
3
¡Esto es muy inteligente! Solía ​​escribir código inteligente, ahora lo evito como la peste. Ahora quiero que mi código tenga una intención clara, no solo un comportamiento correcto. ¿Mi consejo? Solo haga esto si quiere confundir a alguien que tiene que soportar el código / base de datos. Por ejemplo, en PHP tanto ''y nullson valores Falsy.
CJ Dennis
1
@CJDennis Si ha abstraído la capa de su base de datos detrás del patrón del repositorio, no tiene que preocuparse por la oscuridad de esta solución.
prograhammer
17

El bit solo es ventajoso sobre las diversas opciones de bytes (tinyint, enum, char (1)) si tiene muchos campos booleanos. Un campo de bits aún ocupa un byte completo. Dos campos de bits encajan en ese mismo byte. Tres, cuatro, cinco, seis, siete, ocho. Después de lo cual comienzan a llenar el siguiente byte. En última instancia, los ahorros son tan pequeños que hay miles de otras optimizaciones en las que debe centrarse. A menos que esté tratando con una enorme cantidad de datos, esos pocos bytes no sumarán mucho. Si está utilizando bit con PHP, debe convertir los valores que entran y salen.

Thor
fuente
1
+1 para el comentario de conversión de texto. Para agregar a esto cuando trabaje con lenguajes de programación, evite usar técnicas de programación perezosa a favor de la coherencia. Utilice operadores idénticos en lugar de solo iguales. En el caso de PHP if ($ var == "") será verdadero para 0, falso, nulo, indefinido y "". para probar todos los valores, a menudo es mejor usar if (true === empty ($ var)) ya que también evitará los errores indefinidos. También debe validar el tipo de datos con el que está trabajando if (is_int ($ var) && $ var === 0) o escribirlo para forzarlo a ser un tipo de datos específico (int) $ var para la tarea.
fyrye
@Thor ¿es esto cierto para MySQL en la misma medida en que es cierto para MSSQL? Estoy migrando una nueva aplicación que aún no ha entrado en producción de MSSQL a MySQL. No estoy usando PHP, sino que estoy convirtiendo C # a Java 8. Dado que Java es un lenguaje fuertemente tipado, no estoy preocupado por el manejo de tipos ... solo todos los indicadores de bits que se moverían de un byte por hasta 8 indicadores a 1 byte por cada indicador dado TINYINT (1). ¿Conoces alguna documentación sobre este tema para MySQL?
Zack Jannsen
1
@Thor Al hacer una investigación más profunda, está claro cuál debería ser la respuesta. El cambio ocurre y hemos visto mejoras en este manejo. Conozca su idioma que estará en la Capa de aplicación / Capa de acceso a datos y conozca el soporte de sus bibliotecas. Actualmente estoy usando Java y BIT (1) es la opción recomendada en este momento para bibliotecas como Hybernate y el uso de JDBC. Aquí está la URL [Ver tabla 5.2]: dev.mysql.com/doc/connector-j/en/…
Zack Jannsen
12

Hasta que MySQL implemente un tipo de datos de bit, si su procesamiento es realmente presionado por espacio y / o tiempo, como con transacciones de alto volumen, cree un campo TINYINT llamado bit_flagspara todas sus variables booleanas, y enmascare y cambie el bit booleano que desea en su SQL consulta.

Por ejemplo, si su bit más a la izquierda representa su campo bool, y los 7 bits más a la derecha no representan nada, entonces su bit_flagscampo será igual a 128 (10000000 binario). Enmascara (oculta) los siete bits más a la derecha (usando el operador bit a bit &) y desplaza el octavo bit siete espacios a la derecha, terminando con 00000001. Ahora el número entero (que, en este caso, es 1) es tu valor.

SELECT (t.bit_flags & 128) >> 7 AS myBool FROM myTable t;

if bit_flags = 128 ==> 1 (true)
if bit_flags = 0 ==> 0 (false)

Puede ejecutar declaraciones como estas mientras prueba

SELECT (128 & 128) >> 7;

SELECT (0 & 128) >> 7;

etc.

Como tiene 8 bits, tiene potencialmente 8 variables booleanas de un byte. Algún programador futuro usará invariablemente los próximos siete bits, por lo que debe enmascarar. No solo cambies, o crearás un infierno para ti y para los demás en el futuro. Asegúrese de que MySQL haga su enmascaramiento y cambio; esto será significativamente más rápido que tener el lenguaje de secuencias de comandos web (PHP, ASP, etc.). Además, asegúrese de colocar un comentario en el campo de comentarios MySQL para subit_flags campo.

Encontrarás estos sitios útiles cuando implementes este método:

Jonathan
fuente
77
Esto parece una forma terrible de ofuscar la intención de los futuros programadores. Seguro parece un montón de problemas para guardar 7 bytes (¡suponiendo que esté utilizando los 8 bools en esa única tabla!)
Sí, el
@yep no hay ofuscación en absoluto! ¡Escriba documentación y comentarios de MySQL explicando cada campo en la tabla (como se menciona en la respuesta)! La estrategia de desenmascaramiento de MySQL sugerida parece sólida y es mejor almacenar hasta 16 campos booleanos diferentes con solo un par de columnas que tener 16 de ellos. Si es demasiado confuso usar la manipulación de bits y prefiere usar su lenguaje de secuencias de comandos web para obtener cada booleano, simplemente guárdelo como VARCHARy realice el procedimiento de desenmascaramiento en el código (tampoco es necesario que lo limite a 8 campos) ...
CPHPython
El BITtipo existe. Ver dev.mysql.com/doc/refman/8.0/en/bit-type.html
dolmen el
10

Me cansé de tratar de obtener ceros, NULLS y '' redondear con precisión un bucle de valores PHP, MySql y POST, así que solo uso 'Sí' y 'No'.

Esto funciona perfectamente y no necesita un tratamiento especial que no sea obvio y fácil de hacer.

Geoff Kendall
fuente
17
Si realmente quisiera desperdiciar tanto espacio y comprometer el rendimiento, al menos podría haber hecho CHAR (1) con las opciones Y y N.
ILikeTacos
3
En la mayoría de las situaciones del mundo real, existe una diferencia real entre un "no" y una mera ausencia de información. Por ejemplo, es posible que desee marcar una casilla de verificación de forma predeterminada si un usuario aún no dijo "no". Exactamente cuánto espacio crees que estás ahorrando y cuánto procesamiento haces cada vez que necesitas distinguir entre un falso y un NULL, si de hecho incluso PUEDES distinguir? En un mundo de imágenes almacenadas y video digital, el bit o dos de ahorro de espacio es completamente irrelevante, pero la claridad y el procesamiento reducido son reales.
Geoff Kendall
8
Esta respuesta no es incorrecta porque funcionará y no es tan mala como la gente le da crédito. Para la mayoría de los proyectos (es decir, tamaños de tabla <1mil filas) Las diferencias de rendimiento entre las soluciones proporcionadas serán despreciables. No me quejaré si mis consultas regresan en 7 vs 5 milisegundos ... Sin embargo, para ser justos, si sus tablas están creciendo en las filas de 10 mil o más, esta probablemente no sea la solución preferida.
Brad
1
+1 de mi parte por usar el tipo de datos ENUM. Personalmente prefiero esta notación: ENUM ('y', 'n'). Es compacto (solo un byte de largo), intuitivo y atractivo como convención de nivel de aplicación para todos los indicadores booleanos. Puede usarlo directamente con campos de formulario HTML. por ejemplo con PHP: <select name = "production"> <option value = "y" <? = $ production === 'y'? 'selected = "selected"': ''? >> Sí </option> <option value = "n" <? = $ production === 'n'? 'selected = "selected"': ''? >> No </option> </select>
Vlado
2
Lol, esto me llamó la atención, pero tengo que decir que @GeoffKendall tiene razón. En una tonelada de casos no hay necesidad de un rendimiento óptimo y cualquier método que haga el trabajo por usted es el método correcto.
Madmenyo
6

Refiriéndose a este enlace tipo de datos booleanos en Mysql , de acuerdo con el uso de la aplicación, si uno solo quiere que se almacene 0 o 1, el bit (1) es la mejor opción.

Vidz
fuente
66
Es cierto que BIT(1)solo permitirá que se almacene un valor b'0'o b'1'. El mayor problema con el BITtipo de datos es que varias bibliotecas de clientes tienen una variedad de manejo inestable del tipo de datos. Verifique el comportamiento en varias herramientas SQL (SQLyog, TOAD para MySQL, SQL Developer), herramientas que "hacen ingeniería inversa" de modelos de bases de datos, y varios clientes, como JDBC, PHP, Perl DBI, y para una buena medida, pruebe algunos marcos ORM ( Hibernate, Mybatis, JPA). En términos de facilidad de uso, la compatibilidad herramienta / marco / soporte nativo TINYINT(1)es el claro ganador.
spencer7593
Si. Se completa depende del marco que se considere para la aplicación. Por ejemplo, el marco Phalcon de PHP no maneja el tipo de datos Bit
Vidz
Para el registro, MyBatis admite ambos BITy TINYINT. Consulte la clase JdbcType de MyBatis, mybatis.org/mybatis-3/apidocs/reference/org/apache/ibatis/type/…
Lucky
1
@Vidz Le doy más uno por la mención de BIT (1) pero también les diría a los desarrolladores que leen esto: conozca su idioma que estará en la capa de aplicación / capa de acceso a datos y conozca el soporte de sus bibliotecas. Actualmente estoy usando Java y BIT (1) es la opción recomendada en este momento para bibliotecas como Hybernate y el uso de JDBC. Aquí está la URL [Ver tabla 5.2]: dev.mysql.com/doc/connector-j/en/…
Zack Jannsen
6

Como MySQL (8.0.16) y MariaDB (10.2.1) implementaron la restricción CHECK, ahora usaría

bool_val TINYINT CHECK(bool_val IN(0,1))

Usted sólo será capaz de almacenar 0, 1o NULL, así como los valores que se pueden convertir a 0o 1sin errores, como '1', 0x00, b'1'o TRUE/FALSE .

Si no desea permitir NULL, agregue la NOT NULLopción

bool_val TINYINT NOT NULL CHECK(bool_val IN(0,1))

Tenga en cuenta que prácticamente no hay diferencia si usa TINYINT, TINYINT(1)oTINYINT(123) .

Si desea que su esquema sea compatible hacia arriba, también puede usar BOOLoBOOLEAN

bool_val BOOL CHECK(bool_val IN(TRUE,FALSE))

demostración de violín db <>

Paul Spiegel
fuente
¿qué pasa con la enumeración (0, 1)
santiago arizti
3
@santiagoarizti ENUM(debe ser enum('0', '1')- nota: esas son cadenas) no es una buena idea. Hay demasiados problemas debido a cómo se almacena internamente y a cómo se tratan los valores que no son de cadena. P.ej. 0y FALSE no se puede almacenar. 1y TRUEconvertirse '0'. Y se 2convierte '1'.
Paul Spiegel
La mejor respuesta ... para aquellos que usan MySQL 8+
dolmen
2

Después de leer las respuestas aquí, decidí usar bit(1)y sí, de alguna manera es mejor en espacio / tiempo, PERO después de un tiempo cambié de opinión y nunca lo volveré a usar. Complicó mucho mi desarrollo al usar declaraciones preparadas, bibliotecas, etc. (php).

Desde entonces, siempre uso tinyint(1), parece lo suficientemente bueno.

Lémures
fuente
3
¿Te importaría explicar de qué manera ha complicado tu desarrollo?
Chazy Chaz
@ChazyChaz espera verdadero / falso en lugar de 1/0, a diferencia de otros dbs como SQL Server. Esto a veces puede conducir a situaciones extrañas en las que crees que lo estás configurando como verdadero, pero en realidad no sucede.
maembe
0

Puede usar el tipo de datos BOOL, BOOLEAN para almacenar valores booleanos.

Estos tipos son sinónimos de TINYINT (1)

Sin embargo, el tipo de datos BIT (1) tiene más sentido para almacenar un valor booleano (ya sea verdadero [1] o falso [0]), pero es más fácil trabajar con TINYINT (1) cuando está generando datos, consultas, etc. y para lograr la interoperabilidad entre MySQL y otras bases de datos. También puede consultar esta respuesta o hilo .

MySQL también convierte los tipos de datos BOOL, BOOLEAN a TINYINT (1).

Además, lea la documentación

Premkumar chalmeti
fuente