SQL Os Tipos no SQLite

Na aula Tipos de Dados você viu que, no MySQL, no PostgreSQL e no SQL Server, o tipo da coluna é lei: coluna de número só aceita número. O SQLite pensa diferente. Nele, o tipo da coluna é uma sugestão, e quem tem tipo de verdade é cada valor. Isso se chama tipagem dinâmica. Nesta aula você vai ver o que isso quer dizer na prática (sim, dá para guardar a palavra "cinco" numa coluna INTEGER), como descobrir o tipo de cada valor com typeof() e como ligar o modo rígido do SQLite, as tabelas STRICT.

Você vai aprender
  • As cinco classes de armazenamento do SQLite: NULL, INTEGER, REAL, TEXT e BLOB.
  • A função typeof(), que diz o tipo de qualquer valor.
  • A afinidade: como o SQLite lê o tipo que você escreveu no CREATE TABLE e o que ele faz com cada valor que chega.
  • O que acontece quando um texto vai parar numa coluna INTEGER, e como isso estraga uma média sem dar erro.
  • As tabelas STRICT: os seis tipos que elas aceitam e as mensagens de erro reais delas.

A ideia: a etiqueta na gaveta

Pense no estoque da Loja Aurora. Cada gaveta tem uma etiqueta: "Lápis", "Canetas", "Borrachas". Se alguém põe uma borracha na gaveta dos lápis, a gaveta não reclama: a etiqueta só sugere o que deveria estar ali. Um funcionário caprichoso até tenta ajudar ("isto aqui é um lápis embrulhado, vou desembrulhar"), mas, se não der, a borracha fica lá dentro mesmo.

Uma coluna do SQLite funciona assim. O tipo que você escreve no CREATE TABLE é a etiqueta. Quando chega um valor, o SQLite tenta convertê-lo para o tipo da etiqueta; se a conversão não der certo sem perder nada, ele guarda o valor do jeito que veio.

MySQL, PostgreSQL, SQL ServerSQLite (normal)SQLite com STRICT
O tipo é…da colunade cada valorda coluna
A coluna INTEGER recebe 'cinco'ErroGuarda o texto 'cinco'Erro
ComparaçãoUma fechaduraUma etiquetaUma fechadura

1. As cinco classes de armazenamento

Todo valor guardado no SQLite pertence a uma de cinco classes de armazenamento (em inglês, storage classes). São só cinco, e é com elas que o SQLite trabalha por dentro:

ClasseO que éExemplo
NULLA ausência de valor (aula O Valor NULL)NULL
INTEGERNúmero inteiro, com sinal, até 8 bytes42
REALNúmero com casas decimais (ponto flutuante de 8 bytes)4.2
TEXTTexto'Ana'
BLOBBytes, guardados exatamente como chegaramx'CAFE'

Repare no que não está na lista: não existe classe para data nem para verdadeiro/falso. Datas são guardadas como TEXT ('2026-10-14') e o verdadeiro/falso como INTEGER (1 e 0), como você viu na aula anterior.

A função typeof()

Para saber a classe de um valor, use typeof() ("tipo de"). Ela devolve o nome da classe, em minúsculas:

SELECT typeof(42), typeof(4.2), typeof('Ana'), typeof(NULL), typeof(x'CAFE');
typeof(42)
integer: número sem ponto.
typeof(4.2)
real: número com ponto.
typeof('Ana')
text: o que está entre aspas simples é texto.
typeof(NULL)
null: o "vazio" também tem a sua classe.
typeof(x'CAFE')
blob: um x seguido de algarismos hexadecimais entre aspas escreve bytes. CAFE são dois bytes.

A função também serve nas colunas de uma tabela, linha por linha. Nos produtos da Loja Aurora, o preço é real e o estoque é integer:

SELECT nome, preco, typeof(preco), estoque, typeof(estoque)
FROM produtos
LIMIT 3;

E, junto com o GROUP BY (aula GROUP BY), ela faz um "raio X" de uma coluna inteira: quantos valores de cada classe existem lá. Na coluna email dos clientes:

SELECT typeof(email), count(*)
FROM clientes
GROUP BY typeof(email);

São 27 emails de texto e 3 NULL: os clientes 7, 15 e 23, que não informaram email.

Experimente: faça o mesmo "raio X" na coluna nascimento dos clientes. Quantos clientes estão sem data de nascimento?

SELECT typeof(email), count(*)
FROM clientes
GROUP BY typeof(email);
SELECT typeof(nascimento), count(*)
FROM clientes
GROUP BY typeof(nascimento);

2. A afinidade: o que o SQLite faz com o tipo que você escreveu

Você pode escrever praticamente qualquer nome de tipo no CREATE TABLE do SQLite: VARCHAR(100), DECIMAL(10,2), BOOLEAN, DATE… Ele aceita todos. Mas, por dentro, ele resume cada nome a uma de cinco afinidades (em inglês, type affinity): o tipo que aquela coluna "prefere".

A regra está na documentação do SQLite e é aplicada nesta ordem, olhando as letras do nome do tipo:

  1. Se o nome contém INT → afinidade INTEGER.
  2. Senão, se contém CHAR, CLOB ou TEXT → afinidade TEXT.
  3. Senão, se contém BLOB, ou se a coluna não tem tipo → afinidade BLOB (nenhuma conversão).
  4. Senão, se contém REAL, FLOA ou DOUB → afinidade REAL.
  5. Senão → afinidade NUMERIC.

Aplicando a regra aos nomes que você viu na aula anterior:

Você escreveRegraAfinidade
INTEGER, INT, SMALLINT, BIGINT1 (tem INT)INTEGER
TEXT, VARCHAR(100), CHAR(2)2 (tem CHAR ou TEXT)TEXT
BLOB, ou nenhum tipo3BLOB
REAL, FLOAT, DOUBLE PRECISION4REAL
NUMERIC, DECIMAL(10,2), BOOLEAN, DATE, DATETIME5 (nenhuma das anteriores)NUMERIC
FLOATING POINT (pegadinha!)1: "POINT" tem INT no meioINTEGER
STRING (pegadinha!)5: não tem nenhuma das palavrasNUMERIC

Duas consequências importantes. Primeira: os números entre parênteses são ignorados. VARCHAR(2) é só "texto", sem limite nenhum. Segunda: DATE e BOOLEAN não criam nada especial; eles caem na afinidade NUMERIC, a mesma do DECIMAL.

O que cada afinidade faz com um valor

A afinidade age no momento em que o valor entra (no INSERT ou no UPDATE):

AfinidadeQuando chega um texto que parece númeroQuando chega um número
TEXTFica textoVira texto
INTEGER e NUMERICVira número (inteiro, se der)Fica número; 3.0 vira o inteiro 3
REALVira número com decimaisVira número com decimais: 42 vira 42.0
BLOBFica como chegouFica como chegou

O exemplo clássico da documentação do SQLite: o mesmo texto '500.0' inserido em cinco colunas, uma de cada afinidade.

CREATE TABLE t1 (
    t  TEXT,     -- afinidade TEXT
    nu NUMERIC,  -- afinidade NUMERIC
    i  INTEGER,  -- afinidade INTEGER
    r  REAL,     -- afinidade REAL
    no BLOB      -- afinidade BLOB (nenhuma)
);

INSERT INTO t1 VALUES ('500.0', '500.0', '500.0', '500.0', '500.0');

SELECT t, typeof(t), nu, typeof(nu), i, typeof(i), r, typeof(r), no, typeof(no)
FROM t1;
t (TEXT)
Continua o texto 500.0.
nu (NUMERIC)
Virou o inteiro 500: o texto parecia um número, e um número sem parte decimal de verdade vira inteiro.
i (INTEGER)
A mesma coisa: inteiro 500.
r (REAL)
Virou o número com decimais 500.0 (classe real).
no (BLOB)
Nenhuma conversão: continua o texto 500.0.

3. Texto numa coluna INTEGER

Agora o caso que assusta quem vem de outros bancos. Uma coluna INTEGER recebe seis valores de tipos diferentes:

CREATE TABLE t (n INTEGER);

INSERT INTO t VALUES (10), ('20'), ('abc'), (3.0), (3.5), (NULL);

SELECT n, typeof(n) FROM t;
Valor inseridoGuardado comoPor quê
1010, integerJá era inteiro
'20'20, integerTexto que parece inteiro: a afinidade converteu
'abc'abc, textNão dá para converter sem perder nada: ficou texto mesmo, e sem erro
3.03, integerNúmero com decimal "de mentira": vira inteiro sem perder nada
3.53.5, realVirar inteiro perderia o .5, então ficou real
NULLNULL, nullNULL nunca é convertido

Numa coluna chamada "inteiro", três classes diferentes. Para o SQLite está tudo certo. Para quem consulta, nem tanto, como você vai ver agora.

O estrago: uma média que mente

A Ana montou uma tabela de notas dos produtos. Alguém digitou a nota por extenso:

CREATE TABLE notas (n INTEGER);

INSERT INTO notas VALUES (5), ('quatro'), (4);

SELECT avg(n) FROM notas;

A média deu 3.0. Mas as notas são 5 e 4, e a média delas é 4,5! O que aconteceu: o 'quatro' ficou guardado como texto, e na hora da conta o avg() tratou esse texto como 0. Resultado: (5 + 0 + 4) / 3 = 3. Nenhum erro, nenhum aviso. Só um relatório errado.

E na ordenação?

Quando uma coluna mistura classes, o ORDER BY segue uma ordem fixa: primeiro os NULL, depois os números (inteiros e reais juntos, pelo valor), depois os textos, depois os blobs.

CREATE TABLE t (n INTEGER);
INSERT INTO t VALUES (10), ('abc'), (9), (2.5), (NULL);

SELECT n, typeof(n) FROM t ORDER BY n;

Experimente: a afinidade também pode atrapalhar. Rode e veja: o CEP 04567000 perdeu o zero da frente, porque a coluna tem afinidade INTEGER e o texto foi convertido em número. Troque o tipo da coluna cep para que o CEP fique intacto.

CREATE TABLE enderecos (cliente_id INTEGER, cep INTEGER);
INSERT INTO enderecos VALUES (5, '04567000');

SELECT cep, typeof(cep) FROM enderecos;
CREATE TABLE enderecos (cliente_id INTEGER, cep TEXT);
INSERT INTO enderecos VALUES (5, '04567000');

SELECT cep, typeof(cep) FROM enderecos;

Cuidado com nomes de tipo inventados: STRING parece "texto", mas, pela regra 5, a afinidade dele é NUMERIC, e o zero da frente some do mesmo jeito:

CREATE TABLE t (codigo STRING);
INSERT INTO t VALUES ('0042');

SELECT codigo, typeof(codigo) FROM t;

4. Tabelas STRICT: o SQLite rígido

Desde a versão 3.37.0 (de 2021), o SQLite tem um modo rígido, por tabela: basta escrever STRICT ("estrito") depois do parêntese que fecha o CREATE TABLE. Numa tabela STRICT, o tipo da coluna vira fechadura: o valor que não pode ser convertido sem perda é recusado, com erro.

CREATE TABLE avaliacoes (
    id         INTEGER,
    produto_id INTEGER,
    nota       INTEGER,
    comentario TEXT
) STRICT;

INSERT INTO avaliacoes VALUES (1, 3, 'cinco', 'Chegou rápido');
) STRICT
A palavra vai fora dos parênteses, logo depois do ) que fecha a lista de colunas, e antes do ;.
cannot store TEXT value
"Não é possível guardar um valor TEXT…"
in INTEGER column
"…numa coluna INTEGER…"
avaliacoes.nota
"…avaliacoes.nota": a mensagem diz a tabela e a coluna, separadas por ponto. Ótimo para achar o problema num INSERT com muitas colunas.

No terminal, a mesma mensagem vem com o prefixo Runtime error: ("erro durante a execução") e, no fim, o número (19), o código que o SQLite usa para "uma restrição foi violada":

$ sqlite3 loja.db
SQLite version 3.50.4 2025-07-30 19:33:53
Enter ".help" for usage hints.
sqlite> CREATE TABLE avaliacoes (id INTEGER, produto_id INTEGER, nota INTEGER, comentario TEXT) STRICT;
sqlite> INSERT INTO avaliacoes VALUES (1, 3, 'cinco', 'Chegou rápido');
Runtime error: cannot store TEXT value in INTEGER column avaliacoes.nota (19)
sqlite> .quit

STRICT ainda converte quando não perde nada

O modo rígido não é implicante: se o valor pode virar o tipo da coluna sem perder nada, ele converte, como antes. O texto '5' vira o inteiro 5 sem problema:

CREATE TABLE avaliacoes (
    id         INTEGER,
    produto_id INTEGER,
    nota       INTEGER,
    comentario TEXT
) STRICT;

INSERT INTO avaliacoes VALUES (1, 3, '5', 'Chegou rápido');

SELECT nota, typeof(nota) FROM avaliacoes;

Já 3.5 numa coluna INTEGER perderia o .5, então é recusado:

CREATE TABLE notas (n INTEGER) STRICT;
INSERT INTO notas VALUES (3.5);

Os seis tipos de uma tabela STRICT

Numa tabela STRICT, toda coluna tem de ter tipo, e só valem estes seis nomes:

TipoAceita
INT ou INTEGERInteiros (e o que vira inteiro sem perda, como '5' ou 3.0)
REALNúmeros (um inteiro vira real: 42 vira 42.0)
TEXTTexto (um número vira texto)
BLOBBytes
ANYQualquer coisa, guardada exatamente como chegou, sem conversão nenhuma

Em todas, NULL é aceito, a menos que a coluna tenha a regra NOT NULL. Os nomes "dos outros bancos" não passam:

CREATE TABLE clientes_novos (
    nome  VARCHAR(100),
    email TEXT
) STRICT;

"Tipo de dado desconhecido para clientes_novos.nome: VARCHAR(100)." Em tabela STRICT, escreva TEXT. Datas também: DATE dá o mesmo erro, e a data vai numa coluna TEXT no formato 'AAAA-MM-DD'.

Como saber se uma tabela é STRICT

O PRAGMA table_list lista as tabelas, e a última coluna, strict, vale 1 nas tabelas rígidas. As da Loja Aurora são todas normais (0); a avaliacoes criada com STRICT aparece com 1:

CREATE TABLE avaliacoes (id INTEGER, nota INTEGER) STRICT;

SELECT name, strict
FROM pragma_table_list
WHERE schema = 'main' AND name NOT LIKE 'sqlite_%';

(pragma_table_list é o mesmo PRAGMA table_list, escrito como tabela, para poder usar SELECT e WHERE; o NOT LIKE 'sqlite_%' esconde a tabela interna sqlite_schema. Mais em PRAGMAs Úteis.)


Formas diferentes de proteger os tipos

FormaExemploQuando usar
Tabela STRICTCREATE TABLE avaliacoes (…) STRICT;Tabelas novas, quando o SQLite é 3.37.0 ou mais novo. A forma mais simples e completa
CHECK com typeof()nota INTEGER CHECK (typeof(nota) = 'integer')Para proteger uma coluna, ou quando a tabela precisa de um tipo como DATE no nome (aula CHECK)
Converter na consultaSELECT CAST(n AS INTEGER) FROM t;Para ler dados que já estão bagunçados. Não protege nada, só remenda
Confiar no programaO sistema da loja só manda númerosÉ o que muita gente faz, e funciona até o dia em que alguém digita "quatro"

O CHECK dá um erro diferente, que mostra a regra que falhou:

CREATE TABLE avaliacoes (
    id   INTEGER,
    nota INTEGER CHECK (typeof(nota) = 'integer')
);

INSERT INTO avaliacoes VALUES (1, 'cinco');

Quando dá errado

Texto numa coluna de número, sem erro nenhum
CREATE TABLE avaliacoes (id INTEGER, nota INTEGER);
INSERT INTO avaliacoes VALUES (1, 'cinco');

SELECT nota, typeof(nota) FROM avaliacoes;

O que acontece: o INSERT passa e o texto fica lá. O problema só aparece numa conta (a média que deu 3.0) ou numa ordenação. Como evitar: crie a tabela com STRICT, ou proteja a coluna com CHECK (typeof(nota) = 'integer'). Para achar o lixo numa tabela que já existe: SELECT * FROM avaliacoes WHERE typeof(nota) <> 'integer';.

STRICT com um tipo que ele não conhece
CREATE TABLE entregas (id INTEGER, saiu_em DATE) STRICT;

O que diz: unknown datatype for entregas.saiu_em: "DATE", "tipo de dado desconhecido". No terminal: Parse error: unknown datatype for entregas.saiu_em: "DATE". Como consertar: só INT, INTEGER, REAL, TEXT, BLOB ou ANY. A data vira TEXT.

STRICT com uma coluna sem tipo
CREATE TABLE avaliacoes (id INTEGER, comentario) STRICT;

O que diz: missing datatype for avaliacoes.comentario, "falta o tipo de dado". Na tabela normal, coluna sem tipo é permitida (afinidade BLOB); na STRICT, não. Como consertar: comentario TEXT.

Número com decimais numa coluna INTEGER de tabela STRICT

Você já viu: cannot store REAL value in INTEGER column notas.n. Como consertar: se a coluna precisa de decimais, ela é REAL. Se não precisa, arredonde antes de guardar: round(3.5) ou CAST(3.5 AS INTEGER) (que corta: dá 3).

O tipo que "parece texto" mas não é

STRING, CARACTERES, NOME… qualquer nome inventado sem CHAR, CLOB ou TEXT cai na afinidade NUMERIC, e um código como '0042' vira o número 42. Como evitar: use os nomes de sempre (TEXT, VARCHAR(n)) e confira com typeof().

Nos outros bancos

No MySQL (no modo estrito, que é o padrão do MySQL 8.4), no PostgreSQL e no SQL Server, o tipo da coluna é sempre lei: um texto que não é número, numa coluna de número, dá erro, como numa tabela STRICT do SQLite. Neles, todos os valores de uma coluna têm o tipo da coluna. Se um dia a Loja Aurora mudar para um desses bancos, os dados "bagunçados" de uma tabela SQLite normal são os que vão dar trabalho na mudança: mais um motivo para usar STRICT desde o começo.


Referência rápida

EscritaO que faz
typeof(x)A classe do valor: null, integer, real, text ou blob
CAST(x AS INTEGER)Converte para inteiro ('12abc' dá 12; 'abc' dá 0; 3.99 dá 3)
CREATE TABLE t (…) STRICT;Tabela rígida: recusa o valor que não cabe no tipo
INT, INTEGER, REAL, TEXT, BLOB, ANYOs únicos tipos de uma tabela STRICT
SELECT name, strict FROM pragma_table_list;Mostra quais tabelas são STRICT
CHECK (typeof(col) = 'integer')Protege uma coluna só, numa tabela normal
Mensagem do SQLiteQuer dizer
cannot store TEXT value in INTEGER column t.cTabela STRICT: um texto que não é número numa coluna inteira
cannot store REAL value in INTEGER column t.cTabela STRICT: um número com decimais numa coluna inteira
unknown datatype for t.c: "DATE"Tabela STRICT com um tipo fora dos seis
missing datatype for t.cTabela STRICT com uma coluna sem tipo
CHECK constraint failed: typeof(nota) = 'integer'A regra CHECK recusou o valor

Exercícios

1. Qual é a classe?

Complete com o que o SQLite responde para SELECT typeof(1.15);:

2. A tabela rígida

Complete para que a tabela recuse texto na coluna nota:

CREATE TABLE notas (n INTEGER) ;

3. Numa tabela normal (sem STRICT), a coluna n INTEGER recebe 'abc'. O que acontece?

4. Qual é a afinidade de uma coluna declarada como VARCHAR(2)?

5. Qual destes CREATE TABLE dá erro?

6. Numa tabela STRICT, a coluna n INTEGER recebe o texto '7'. O que acontece?

Missão final: tranque a tabela

Missão: rode como está: o INSERT com a nota 'cinco' passou, e o texto foi parar na coluna INTEGER. Mude a tabela para que o SQLite recuse essa avaliação com o erro cannot store TEXT value in INTEGER column avaliacoes.nota. (Uma palavra resolve.)

CREATE TABLE avaliacoes (
    id         INTEGER,
    produto_id INTEGER,
    nota       INTEGER,
    comentario TEXT
);

INSERT INTO avaliacoes VALUES (1, 3, 'cinco', 'Chegou rápido');

SELECT nota, typeof(nota) FROM avaliacoes;
CREATE TABLE avaliacoes (
    id         INTEGER,
    produto_id INTEGER,
    nota       INTEGER,
    comentario TEXT
) STRICT;

INSERT INTO avaliacoes VALUES (1, 3, 'cinco', 'Chegou rápido');

SELECT nota, typeof(nota) FROM avaliacoes;

Aqui o erro é o resultado que você quer: ele prova que a tabela agora protege os dados. Troque 'cinco' por 5 e rode de novo para ver a avaliação entrar.


Resumo

  • No SQLite, o tipo é de cada valor, não da coluna (tipagem dinâmica). Os valores são de cinco classes: NULL, INTEGER, REAL, TEXT, BLOB.
  • typeof(x) diz a classe de um valor; com GROUP BY typeof(col), você faz um raio X da coluna.
  • O tipo escrito no CREATE TABLE vira uma afinidade (INTEGER, TEXT, BLOB, REAL ou NUMERIC), pelas letras do nome. Os números entre parênteses são ignorados.
  • A afinidade converte quando pode sem perda ('20' vira 20, 3.0 vira 3); quando não pode, guarda como veio. Texto numa coluna INTEGER passa sem erro e estraga médias e ordenações.
  • Tabelas STRICT (desde o SQLite 3.37.0) recusam o valor errado: cannot store TEXT value in INTEGER column avaliacoes.nota. Elas só aceitam INT, INTEGER, REAL, TEXT, BLOB e ANY.