Quale tipo di dati MySQL utilizzare per la memorizzazione di valori booleani


1208

Poiché MySQL non sembra avere alcun tipo di dati "booleano", quale tipo di dati "abusa" per la memorizzazione di informazioni vere / false in MySQL?

Soprattutto nel contesto della scrittura e della lettura da / verso una sceneggiatura PHP.

Nel tempo ho usato e visto diversi approcci:

  • tinyint, campi varchar contenenti i valori 0/1,
  • campi varchar contenenti le stringhe '0' / '1' o 'true' / 'false'
  • e infine enum Fields contenente le due opzioni 'true' / 'false'.

Nessuna delle precedenti sembra ottimale. Tendo a preferire la variante 0/1 tinyint, poiché la conversione automatica del tipo in PHP mi dà valori booleani piuttosto semplicemente.

Quindi quale tipo di dati usi? Esiste un tipo progettato per valori booleani che ho trascurato? Vedi qualche vantaggio / svantaggio usando un tipo o un altro?


217
Chiunque stia leggendo le vecchie risposte a questa domanda deve capire che MySQL ha aggiunto un po 'di tipo di dati nella versione 5. Usa queste informazioni come puoi. dev.mysql.com/doc/refman/5.0/en/bit-type.html
smp7d


7
per la versione corrente del tipo booleano MYSQL è disponibile- dev.mysql.com/doc/refman/5.5/en/numeric-type-overview.html controllare questo. secondo quel valore zero considerato falso
DevT

7
bit(1)è un po '** per importare in Excel. Passare alle tinyint(1)opere.
Cees Timmerman,

8
ora abbiamo booleano dopo 5 anni
V-SHY

Risposte:


1232

Per MySQL 5.0.3 e versioni successive, è possibile utilizzare BIT. Il manuale dice:

A partire da MySQL 5.0.3, il tipo di dati BIT viene utilizzato per memorizzare i valori del campo bit. Un tipo di BIT (M) consente la memorizzazione di valori M-bit. M può variare da 1 a 64.

Altrimenti, secondo il manuale di MySQL puoi usare boolean e boolean che al momento sono alias di tinyint (1):

Bool, Boolean: questi tipi sono sinonimi di TINYINT (1). Un valore pari a zero è considerato falso. I valori diversi da zero sono considerati veri.

MySQL afferma inoltre che:

Intendiamo implementare la gestione completa del tipo booleano, in conformità con lo standard SQL, in una futura versione di MySQL.

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


11
Sì, sceglierei questo o, per un CHAR (1) e memorizzerei "Y" / "N" o "T" / "F" ecc. A seconda del contesto. Il vantaggio di utilizzare un tipo intero piccolo è che si ottiene la massima portabilità su tutti gli RDBMS
Roland Bouman,

36
Andare per char, almeno in PHP, porterà a più codice in quanto !$booleannon verrà mai valutato correttamente senza ulteriore elaborazione.
Lieve Fuzz,

10
@Pecerier Niente che tu non possa cercare da solo, ma ok, ti ​​mordo. Prima di tutto, dai un'occhiata a data0type.h. Si noti che innodb non definisce in modo nativo un tipo BIT lì. Se trattasse i campi BIT nel modo in cui descrivi, sicuramente troveremmo qualche indizio della sua esistenza lì. In secondo luogo, leggi mysqlperformanceblog.com/2008/04/23/… . E non esitare a chiarirci quali incredibili client MySQL nel "marktetplace" giocano bene con i campi BIT. Saranno utili per chiunque abbia perso quell'articolo senza dubbio.
Roland Bouman,

9
Quando faccio una selezione dai campi bit client mysql della riga di comando standard appare completamente vuoto. Per questo motivo preferisco TINYINT (1).
Utente

8
@MikePurcell Odio chiedere, ma perché vorresti auto_incrementsu una colonna che rappresenta un valore booleano?
Chris Hayes,

248

BOOLe BOOLEANsono sinonimi di TINYINT(1). Lo zero è false, qualsiasi altra cosa è true. Maggiori informazioni qui .


7
Non (1)fa altro che determinare come viene visualizzato il valore, se sei consapevole delle dimensioni di archiviazione, allora vuoi usare BITinvece
JamesHalsall

35
@JamesHalsall: In realtà, BIT(1)ed TINYINT(1)entrambi utilizzeranno un byte di memoria. Fino a MySQL 5.0.3, BITera in realtà un sinonimo di TINYINT. Le versioni successive di MySQL cambiarono l'implementazione di BIT. Ma anche con la modifica dell'implementazione, il BITtipo di dati non presenta ancora alcun vantaggio in termini di "dimensioni di archiviazione" (almeno con InnoDB e MyISAM; altri motori di archiviazione, ad esempio NDB, possono avere una certa ottimizzazione di archiviazione per più dichiarazioni di colonne BIT). Il problema maggiore è che alcuni client le librerie non riconoscono o gestiscono in modo appropriato le BITcolonne del tipo di dati restituite . A TINYINTfunziona meglio.
spencer7593,

5
Il manuale di MySQL 5.0 dice chiaramente che un valore booleano è 1 o 0. La frase "qualcos'altro è true" non è vera.
Walter,

7
@Walter: In realtà è un po 'vero, la spiegazione è in qualche modo carente. In breve, in un contesto booleano, un'espressione può essere valutata come NULL, FALSE o TRUE. In un'istruzione MySQL, un'espressione valutata in un contesto booleano viene prima valutata come numero intero (i valori decimale e float vengono arrotondati, le stringhe vengono convertite nel solito modo stravagante che MySQL converte le stringhe in numero intero). Un NULL è ovviamente NULL (né VERO né FALSO). Un valore intero pari a 0 viene gestito come FALSO e qualsiasi altro valore intero (1, 2, -7, ecc.) Viene valutato come VERO. Per compatibilità, imitiamo quella logica / gestione di TINYINT booleano
spencer7593

4
@Walter: questo è facile da testare, ad es SELECT 'foo' AS bar FROM dual WHERE -7. L'espressione -7 viene valutata in un contesto booleano e la query restituisce una riga. Possiamo testare con 0 o qualsiasi espressione che restituisce il valore intero 0 e non viene restituita alcuna riga. Se l'espressione nella clausola WHERE restituisce un valore intero diverso da zero diverso da zero, l'espressione è TRUE. (Credo che i valori decimali e float vengano "arrotondati" a numeri interi, ad esempio WHERE 1/3valuta WHERE 0. Otteniamo lo stesso risultato con WHERE 'foo', perché la stringa 'foo'viene valutata anche come valore intero 0.
spencer7593

71

Questa è una soluzione elegante che apprezzo molto perché utilizza zero byte di dati:

some_flag CHAR(0) DEFAULT NULL

Per impostarlo su true, impostare some_flag = ''e per impostarlo su false, impostare some_flag = NULL.

Quindi per verificare la presenza di true, controlla se some_flag IS NOT NULLe per verificare la presenza di false, controlla se some_flag IS NULL.

(Questo metodo è descritto in "MySQL ad alte prestazioni: ottimizzazione, backup, replica e altro" di Jon Warren Lentz, Baron Schwartz e Arjen Lentz.)


3
trucco di fantasia! questo è utile se si lavora con MySQL <5 e forse anche con un footprint più leggero di BIT, tuttavia nel tentativo di rispettare la convenzione e un sovraccarico di calcolo leggermente inferiore (logica vs valore esatto) direi che BIT è il modo migliore di procedere.
Zamnuts,

59
Potrebbe essere "veloce", ma offusca i dati in modo tale che qualsiasi nuovo sviluppatore non abbia idea di ciò che rappresenta la colonna.
Richthofen,

5
Questo utilizza la stessa quantità di byte di BIT (1)
ITS Alaska

25
Buona fortuna ottenere un ORM per mappare bene a questo.
Craig Labenz,

4
Sono d'accordo con @Richthofen e trovo difficile immaginare una situazione in cui avrei mai sostenuto di utilizzare questa soluzione. Tuttavia, se deve essere utilizzato, specificare come a COMMENTnella definizione della colonna che NULLindica falso e ''indica vero potrebbe fare un piccolo passo in avanti per aiutare la comprensione futura.
Eggyal

34

Se si utilizza il tipo BOOLEAN, questo è alias su TINYINT (1). Questo è il migliore se vuoi usare un SQL standardizzato e non ti dispiace che il campo possa contenere un valore fuori range (praticamente tutto ciò che non è 0 sarà "vero").

ENUM ('False', 'True') ti consentirà di utilizzare le stringhe nel tuo SQL e MySQL memorizzerà il campo internamente come numero intero in cui 'False' = 0 e 'True' = 1 in base all'ordine in cui è specificato Enum .

In MySQL 5+ è possibile utilizzare un campo BIT (1) per indicare un tipo numerico a 1 bit. Non credo che questo in realtà utilizzi meno spazio nella memoria, ma consente nuovamente di limitare i possibili valori a 1 o 0.

Tutto quanto sopra utilizzerà approssimativamente la stessa quantità di spazio di archiviazione, quindi è meglio scegliere quello che trovi più facile da lavorare.


8
La tua osservazione sull'ENUM non è vera: prova CAST (yourenumcol AS UNSIGNED) e noterai che False sarà 1 e True sarà 2. Un altro problema con ENUM è che è troppo facile inserire '' (stringa vuota ). Consiglio di non utilizzare questo.
Roland Bouman,

4
Nella mia esperienza, l'uso di un campo BIT (1) dal codice PHP è stato un po 'problematico. TINYINT (1) era molto più semplice e produceva codice più leggibile.
M-Peror,

1
@ M-Peror - "usare un campo BIT (1) dal codice PHP è stato un po ' problematico" ... nessun gioco di parole previsto. :) Ma sì, sono d'accordo. Ricordo che anche TINYINT (1) era più facile ... non riesco proprio a ricordare il perché. Qualcun altro ha pensato su questo? BIT (1) sembra più carino in superficie perché è possibile limitare a 0 o 1. Penso che a volte BIT sia stato interpretato come dati binari (a seconda del linguaggio di programmazione e del driver / libreria); mentre TINYINT è stato trattato più come un numero.
BMiner,

2
@BMiner - ahah, è stato davvero non intenzionale, non me ne sono accorto :) Ma in effetti, se ricordo correttamente il campo bit è stato interpretato come qualcosa di binario, mentre il tinyint era più facile da trattare come un numero e per questo, più facile da usare in un'espressione (booleana).
M-Peror,

34

A questa domanda è stata data una risposta, ma ho pensato di aggiungere $ 0,02. Uso spesso un CHAR(0), dove '' == true and NULL == false.

Dai documenti mysql :

CHAR(0)è anche abbastanza utile quando è necessaria una colonna che può assumere solo due valori: Una colonna definita come CHAR(0) NULLoccupa solo un bit e può assumere solo i valori NULLe ''(la stringa vuota).


16
mm, sembra chiedere problemi se tu come me. Voglio dire, a seconda della lingua potrebbe essere troppo facile non individuare la differenza tra NULL e '' (ad esempio PHP).
Roland Bouman,

3
In termini di risparmio di spazio (il numero di byte utilizzati per rappresentare un valore booleano), questo approccio è chiaramente vincente. Questo salva un byte su TINYINT. Il rovescio della medaglia (come sottolineano alcuni commenti) è che alcuni client potrebbero avere difficoltà a distinguere tra una stringa NULL e una stringa vuota. Anche alcuni database relazionali (ad es. Oracle) non fanno distinzione tra una stringa di lunghezza zero e un NULL.
spencer7593,

3
Questo è molto intelligente! Scrivevo un codice intelligente, ora lo evito come la peste. Ora voglio che il mio codice abbia un intento cristallino, non solo un comportamento corretto. Il mio consiglio? Fallo solo se vuoi confondere chiunque debba supportare il codice / database. Ad esempio, in PHP entrambi ''e nullsono valori falsi.
CJ Dennis,

1
@CJDennis Se hai estratto il livello del database dietro il modello di repository, non devi preoccuparti dell'oscurità di questa soluzione.
prograhammer


17

Bit è vantaggioso solo rispetto alle varie opzioni di byte (tinyint, enum, char (1)) se hai molti campi booleani. Un campo bit occupa ancora un byte intero. I campi a due bit si adattano allo stesso byte. Tre, quattro, cinque, sei, sette, otto. Dopo di che iniziano a riempire il byte successivo. In definitiva i risparmi sono così piccoli che ci sono migliaia di altre ottimizzazioni su cui dovresti concentrarti. A meno che tu non abbia a che fare con un'enorme quantità di dati, quei pochi byte non sommeranno molto. Se stai usando bit con PHP, devi digitare i valori in entrata e in uscita.


1
+1 per il commento di typecasting. Per approfondire questo aspetto quando si lavora con linguaggi di programmazione, evitare l'uso di tecniche di programmazione pigre a favore della coerenza. Usa operatori identici anziché semplicemente uguali. Nel caso di PHP if ($ var == "") sarà true per 0, false, null, undefined e "". per testare tutti i valori è spesso meglio usare if (true === vuoto ($ var)) poiché eviterà anche gli errori indefiniti. Dovresti anche convalidare il tipo di dati con cui stai lavorando if (is_int ($ var) && $ var === 0) o digitarlo per forzarlo a diventare un tipo di dati specifico (int) $ var per l'attività.
Fyrye,

@Thor è vero per MySQL nella stessa misura in cui è vero per MSSQL? Sto migrando una nuova applicazione che non è ancora entrata in produzione da MSSQL a MySQL. Non sto usando PHP ma sto convertendo C # in Java 8. Dato che Java è un linguaggio fortemente tipizzato, non sono preoccupato per la gestione dei tipi ... solo tutti i bit flag che si sposterebbero da un byte per un massimo di 8 flag in 1 byte per ogni flag dato TINYINT (1). Conosci qualche documentazione su questo argomento per MySQL?
Zack Jannsen,

1
@Thor Facendo qualche ricerca più approfondita è chiaro quale dovrebbe essere la risposta. Il cambiamento accade e abbiamo visto miglioramenti in questa gestione. Conosci la tua lingua che sarà nel Livello applicazione / Livello accesso dati e conosci il supporto delle tue librerie. Attualmente sto usando Java e BIT (1) è la scelta consigliata in questo momento per librerie come Hybernate e l'uso di JDBC. Ecco l'URL [Vedi tabella 5.2]: dev.mysql.com/doc/connector-j/en/…
Zack Jannsen,

12

Fino a quando MySQL non implementa un tipo di dati bit, se l'elaborazione viene realmente pressata per spazio e / o tempo, come ad esempio con transazioni ad alto volume, crea un campo TINYINT chiamato bit_flagsper tutte le variabili booleane e maschera e sposta il bit booleano desiderato nel tuo SQL query.

Ad esempio, se il bit più a sinistra rappresenta il campo bool e i 7 bit più a destra non rappresentano nulla, il bit_flagscampo sarà uguale a 128 (10000000 binario). Maschera (nascondi) i sette bit più a destra (usando l'operatore bit per bit &) e sposta l'ottavo bit di sette spazi a destra, finendo con 00000001. Ora l'intero numero (che, in questo caso, è 1) è il tuo valore.

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

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

È possibile eseguire istruzioni come queste durante il test

SELECT (128 & 128) >> 7;

SELECT (0 & 128) >> 7;

eccetera.

Dato che hai 8 bit, hai potenzialmente 8 variabili booleane da un byte. Qualche futuro programmatore utilizzerà invariabilmente i successivi sette bit, quindi è necessario mascherare. Non limitarti a cambiare, o in futuro creerai l'inferno per te e gli altri. Assicurati che MySQL esegua il mascheramento e lo spostamento: ciò sarà significativamente più veloce rispetto al linguaggio di scripting Web (PHP, ASP, ecc.). Inoltre, assicurati di inserire un commento nel campo dei commenti di MySQL per il tuo bit_flagscampo.

Troverai utili questi siti durante l'implementazione di questo metodo:


7
Questo sembra proprio un modo terribile di offuscare l'intenzione per i futuri programmatori. Sicuramente sembra un sacco di problemi per salvare 7 byte (supponendo che tu stia usando tutti e 8 i bool in quel singolo tavolo!)
sì,

@yep non c'è affatto offuscamento! Scrivi documentazione e commenti MySQL che spiegano ogni campo della tabella (come indicato nella risposta)! La strategia di smascheramento di MySQL suggerita sembra solida e archiviare fino a 16 diversi campi booleani con solo un paio di colonne è meglio che averne 16 invece. Se è troppo confuso usando la manipolazione dei bit e preferisci usare il tuo linguaggio di scripting web per ottenere ogni booleano, semplicemente memorizzalo come un VARCHARe fai la procedura di smascheramento nel codice (non devi limitarlo a 8 campi) ...
CPHPython


10

Mi sono stufato di provare a ottenere zero, NULL e '' accuratamente intorno a un ciclo di valori PHP, MySql e POST, quindi uso semplicemente 'Sì' e 'No'.

Funziona perfettamente e non necessita di trattamenti speciali non ovvi e facili da eseguire.


17
Se davvero volessi sprecare così tanto spazio e compromettere le prestazioni, avresti almeno potuto fare CHAR (1) con le opzioni Y e N.
ILikeTacos,

3
Nella maggior parte delle situazioni del mondo reale esiste una reale differenza tra un "no" e una semplice assenza di informazioni. Ad esempio, potresti voler selezionare una casella di spunta per impostazione predefinita se un utente non ha ancora detto "no". Esattamente quanto spazio pensi di risparmiare e quanta elaborazione fai ogni volta che devi distinguere tra un falso e un NULL - se davvero PUOI persino distinguere? In un mondo di immagini archiviate e video digitali, il bit o due del risparmio di spazio è assolutamente irrilevante, ma la chiarezza e l'elaborazione ridotta sono reali.
Geoff Kendall,

8
Questa risposta non è sbagliata perché funzionerà e non è così male come le persone gli danno credito. Per la maggior parte dei progetti (es. Dimensioni della tabella <1mil righe) Le differenze di prestazioni tra le soluzioni fornite saranno trascurabili. Non mi lamenterò se le mie domande torneranno in 7 vs 5 millisecondi ... Ad essere onesti, tuttavia, se i tuoi tavoli crescono nelle righe da 10mil o più, probabilmente questa non è la soluzione preferita.
Brad

1
+1 da parte mia per l'utilizzo del tipo di dati ENUM. Personalmente preferisco questa notazione: ENUM ('y', 'n'). È compatto (solo un byte), intuitivo e di bell'aspetto come convenzione a livello di applicazione per tutti i flag booleani. Puoi usarlo direttamente con i campi modulo HTML. ad esempio 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 questo ha attirato la mia attenzione ma devo dire che @GeoffKendall ha ragione. In molti casi non è necessario per prestazioni ottimali e qualunque metodo faccia il lavoro per te è il metodo giusto.
Madmenyo,

6

Facendo riferimento a questo collegamento Tipo di dati booleano in Mysql , in base all'utilizzo dell'applicazione, se si desidera memorizzare solo 0 o 1, il bit (1) è la scelta migliore.


6
È vero che BIT(1)consentirà solo di memorizzare un valore b'0'o b'1'. Il problema più grande con il BITtipo di dati è che varie librerie client hanno una varietà di gestione traballante del tipo di dati. Verifica il comportamento in vari strumenti SQL (SQLyog, TOAD for MySQL, SQL Developer), strumenti che "decodificano" i modelli di database e vari client, come JDBC, PHP, Perl DBI e, per buona misura, testano alcuni framework ORM ( Hibernate, Mybatis, JPA). In termini di facilità d'uso, compatibilità strumento / framework / supporto nativo, TINYINT(1)è il chiaro vincitore.
spencer7593,

Sì. Il completamento dipende dal framework preso in considerazione per l'app. Ad esempio, il framework Phalcon di PHP non gestisce il tipo di dati Bit
Vidz,

Per la cronaca, MyBatis supporta sia BITe TINYINT. Consultare la classe JdbcType di MyBatis, mybatis.org/mybatis-3/apidocs/reference/org/apache/ibatis/type/…
Lucky

1
@Vidz Ne do uno in più per la menzione di BIT (1) ma vorrei anche far notare agli sviluppatori che leggono questo: conoscere la lingua che si troverà nel livello di applicazione / livello di accesso ai dati e conoscere il supporto delle librerie. Attualmente sto usando Java e BIT (1) è la scelta consigliata in questo momento per librerie come Hybernate e l'uso di JDBC. Ecco l'URL [Vedi tabella 5.2]: dev.mysql.com/doc/connector-j/en/…
Zack Jannsen,

6

Poiché MySQL (8.0.16) e MariaDB (10.2.1) hanno entrambi implementato il vincolo CHECK, ora vorrei usare

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

Lei sarà solo in grado di memorizzare 0, 1o NULL, così come i valori che possono essere convertiti in 0o 1senza errori come '1', 0x00, b'1'o TRUE/ FALSE.

Se non si desidera consentire NULL, aggiungere l' NOT NULLopzione

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

Nota che non c'è praticamente alcuna differenza se usi TINYINT, TINYINT(1)o TINYINT(123).

Se si desidera che il proprio schema sia compatibile verso l'alto, è anche possibile utilizzare BOOLoBOOLEAN

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

db <> demo violino


che dire di enum (0, 1)
santiago arizti

3
@santiagoarizti ENUM(deve essere enum('0', '1')- nota: quelli sono stringhe) non è una buona idea. Ci sono troppi problemi dovuti al modo in cui viene archiviato internamente e al modo in cui vengono trattati i valori senza stringa. Per esempio. 0e FALSE non può essere memorizzato. 1e TRUEdiventare '0'. E 2diventa '1'.
Paul Spiegel,

La migliore risposta ... per coloro che usano MySQL 8+
dolmen

2

Dopo aver letto le risposte qui ho deciso di usare bit(1)e sì, è in qualche modo meglio nello spazio / tempo, MA dopo un po 'ho cambiato idea e non lo userò mai più. Ha complicato molto il mio sviluppo, quando ho usato istruzioni preparate, librerie ecc. (Php).

Da allora, uso sempre tinyint(1), sembra abbastanza buono.


3
Vuoi spiegare in che modo ha complicato il tuo sviluppo?
Chazy Chaz,

@ChazyChaz si aspetta vero / falso invece di 1/0, a differenza di altri dbs come SQL Server. Questo a volte può portare a strane situazioni in cui pensi di impostarlo su vero, ma in realtà non accade.
maembe

0

È possibile utilizzare il tipo di dati BOOL, BOOLEAN per la memorizzazione di valori booleani.

Questi tipi sono sinonimi di TINYINT (1)

Tuttavia, il tipo di dati BIT (1) ha più senso archiviare un valore booleano (true [1] o false [0]) ma TINYINT (1) è più semplice da utilizzare quando si esegue l'output dei dati, l'esecuzione di query e così via e per ottenere l'interoperabilità tra MySQL e altri database. Puoi anche controllare questa risposta o discussione .

MySQL converte anche i tipi di dati BOOL, BOOLEAN in TINYINT (1).

Inoltre, leggi la documentazione

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.