SQLAlchemy: UPDATE e DELETE

UPDATE e DELETE

Vimos nas sessões anteriores como aplicar os comandos do SQL, INSERT e SELECT, para a inserção de dados em um banco de dados e a recuperação das informações desejadas, em termos do SQLAlchemy. Claro que precisamos também de UPDATE E DELETE para atualizar as informações e linhas existentes. Essa seção cobre essas funções no CORE SQLAlchemy. As operações UPDATE e DELETE com ORM são normalmente chamadas internamente no objeto Session.

As construções com UPDATE e DELETE podem ser usadas diretamente com o ORM, usando o padrão conhecido denominado “atualização e exclusão habilitadas para ORM”. Por isso é necessário compreender essas construções feitas no CORE, antes de usá-las com o ORM.

UPDATE

No SQLAlchemy usamos a função update() para gerar uma instância do objeto Update que representa uma instrução UPDATE em SQL, responsável pela atualização de dados em uma tabela.

Assim como ocorre com insert(), existe uma forma padrão de emitir um update() que executa um UPDATE em uma única tabela por vez, sem retornar nenhuma linha. Alguns back-ends, por outro lado, dão suporte a UPDATEs que modificam várias tabelas de uma vez, além de suporte a RETURNING, de permite o retorno das colunas modificadas, como veremos.

from sqlalchemy import update
query = (
    update(aluno)
    .where(aluno.c.nome == "Marcos")
    .values(sobrenome="Abudab")
)
print(query)
↳ UPDATE aluno SET sobrenome=:sobrenome WHERE aluno.nome = :nome_1
# que, após a substituição dos parâmetros se torna
↳ UPDATE aluno SET sobrenome='Abudab' WHERE aluno.nome = 'Marcos'


O método Update.values() define qual será o conteúdo dos elementos SET em uma instrução UPDATE. Os parâmetros podem ser passados como pares chave = valor usando os nomes das colunas como chaves. Por ex., para atualizar o sobrenome de todos os alunos acrescentando “da Silva” fazemos:

query = update(aluno).values(sobrenome = aluno.c.sobrenome + " da Silva")
print(query)
↳ UPDATE aluno SET sobrenome=(aluno.sobrenome || :name_1) (' da Silva',)

Para fazer várias atualizações (no contexto “executemany”) usamos a construção bindparam() pode ser usada para configurar parâmetros vinculados; estes substituem os lugares onde os valores literais normalmente iriam:

from sqlalchemy import bindparam
query = (
    update(aluno)
    .where(aluno.c.nome == bindparam("nomeantigo"))
    .values(nome = bindparam("nomenovo"))
)
with engine.begin() as conn:
    conn.execute( query, 
        [
            {"nomeantigo": "Pedro", "nomenovo": "George"},
            {"nomeantigo": "Anita", "nomenovo": "Anitta"},
            {"nomeantigo": "Aluisio", "nomenovo": "Alonso"},
        ],
    )
[SQL]
UPDATE aluno SET nome=? WHERE aluno.name = ? 
[('Pedro', 'George'), ('Anita', 'Anitta'), ('Aluisio','Alonso')]

Observe que em .where() temos um operador de comparação == enquanto em values() temos uma atribuição, =. Fornecemos uma lista de dicionários com as chaves “nomeantigo” e “nomenovo” para serem substituídos como parâmetros nas três consultas a serem realizadas.

Outras possibilidades de uso de UPDATE

Updates Correlacionados: Uma instrução UPDATE pode usar linhas de outras tabelas obtidas em uma subconsulta. Subconsultas podem ser usada no lugar de qualquer expressão de coluna:

scalar_subq = (
    select(endereco.c.email)
    .where(endereco.c.aluno_id == aluno.c.id)
    .order_by(endereco.c.id)
    .limit(1)
    .scalar_subquery()
)
query = update(aluno).values(sobrenome=scalar_subq)
print(query)

↳ UPDATE aluno SET sobrenome=(SELECT endereco.email FROM endereco
     WHERE endereco.aluno_id = aluno.id ORDER BY endereco.id LIMIT 1)

LIMIT é uma cláusula SQL que especifica o número de linhas que devem ser retornadas no resultado de uma consulta. Nem todos os SGBDS dão suporte a esse recurso.

UPDATE FROM: Alguns bancos de dados, como PostgreSQL e MySQL, aceitam a sintaxe “UPDATE FROM” onde tabelas adicionais podem ser declaradas diretamente em uma cláusula FROM especial. No SQLAlchemy esse tipo de consulta é gerada quando existirem tabelas adicionais na cláusula WHERE da instrução:

query = (
    update(aluno)
    .where(aluno.c.id == endereco.c.aluno_id)
    .where(endereco.c.email == "asilva@gmail.com")
    .values(sobrenome="Silva")
)
print(query)

↳ UPDATE aluno SET sobrenome=:sobrenome FROM endereco
  WHERE aluno.id = endereco.aluno_id AND endereco.email = :email_1

Não se esqueça de que os parâmetros são substituídos na execução da query: :sobrenome -> 'Silva':email_1 -> 'asilva@gmail.com'.

UPDATE múltiplas tabelas: existe no MySQL uma forma específica para atualizar múltiplas tabelas simultaneamente. No SQLAlchemy nos referimos aos objetos Table adicionais dentro das cláusulas values.

query = (
    update(aluno)
    .where(aluno.c.id == endereco.c.aluno_id)
    .where(endereco.c.email == "patrick@aol.com")
    .values(
        {
            aluno.c.sobrenome: "Jones",
            endereco.c.email: "jones@aol.com",
        }
    )
)
from sqlalchemy.dialects import mysql
print(query.compile(dialect=mysql.dialect()))

↳ UPDATE aluno, endereco
  SET endereco.email=%s, aluno.sobrenome=%s
  WHERE aluno.id = endereco.aluno_id AND endereco.email = %s

Updates com parâmetros ordenados: Outra característica que existe apenas no MySQL é que a ordem dos parâmetros fornecidos na cláusula SET de um UPDATE modifica a ordem de execução de cada expressão. Para conseguir isso usamos o método Update.ordered_values() que aceita uma sequência de tuplas que são executadas na ordem em que aparecem na expressão.

query = update(tabela).ordered_values((tabela.c.y, 20), (tabela.c.x, tabela.c.y + 10))
print(query)
↳ UPDATE tabela SET y=:y, x=(tabela.y + :y_1)

O comando faz tabela.y=20 e depois tabela.x=tabela.y + 10 (ou seja, y=20, x =30).

Função delete()

A função delete() retorna uma instância do objeto Delete que contém as instruções de apagamento, traduzidas como um SQL DELETE, que apaga linhas de uma tabela, modificado pela clásula WHERE. Em geral a instrução não retorna linhas, embora seja possível o retorno de dados específicos em algumas variantes de SGBD.

from sqlalchemy import delete
query = delete(aluno).where(aluno.c.nome == "Mauro")
print(query)
↳ DELETE FROM aluno WHERE aluno.nome = 'Mauro'

DELETE em múltiplas tabelas: Assim como ocorre com UPDATE, é possível realizar apagamentos de linhas em várias tabelas, dependendo do dialeto usado. Por exemplo, no MySQL podemos usar:

query = (
    delete(aluno)
    .where(aluno.id == endereco.c.aluno_id)
    .where(endereco.c.email == "mauro@igmail.com")
)
from sqlalchemy.dialects import mysql
print(query.compile(dialect=mysql.dialect()))
↳ DELETE FROM aluno USING aluno, endereco
    WHERE aluno.id = endereco.aluno_id AND endereco.email = %s

Obtendo o número de linhas afetadas por UPDATE e DELETE: Tanto Update quanto Delete permitem o retorno de número de linhas afetadas pelo procedimento. Esse valor é extraído do atributo CursorResult.rowcount quando usamos Core Connection, acessado por meio de Connection.execute().

with engine.begin() as conn:
    result = conn.execute(
        update(aluno)
        .values(sobrenome="Aquino")
        .where(aluno.c.nome == "José")
    )
    print(result.rowcount)

[SQL]
UPDATE aluno SET sobrenome=? WHERE nome = ? ('Aquino', 'José')
# é retornado
↳ 1

CursorResult é uma subclasse de Result que contém outros atributos. Uma instância dessa subclasse é retornada sempre que uma instrução é passada para o método Connection.execute(). Quando se usa ORM o método Session.execute() sempre retorna um objeto CursorResult quando se executa INSERT, UPDATE, ou DELETE.

Sobre o atributo CursorResult.rowcount:

  • seu valor é o número de linhas que satisfazem a cláusula WHERE da instrução, independente do número de linhas efetivamente modificada.
  • esse valor pode não estar disponível para instruções UPDATE ou DELETE que usam RETURNING, ou para casos de execução executemany. Isso depende do módulo DBAPI em uso e das opções configuradas.
  • Existe o atributo CursorResult.supports_sane_multi_rowcount que indica se esse valor estará disponível para o backend em uso. Alguns drivers, especialmente dialetos de terceiros para bancos de dados não relacionais, podem não oferecer suporte a CursorResult.rowcount. A propriedade CursorResult.supports_sane_rowcount indicará isso.
  • rowcount é usado pelo processador do ORM para validar se uma instrução UPDATE ou DELETE correspondeu ao número esperado de linhas afetadas.

Usando RETURNING com UPDATE e DELETE: Assim como Insert, Update e Delete também dão suporte à cláusula RETURNING. Ele é inserido com os métodos Update.returning() e Delete.returning(). Quando o backend do BD aceita RETURNING as colunas selecionadas de todas as linhas que satisfazem o critério WHERE são retornadas no objeto Result como um objeto iterável. Isso significa que as linhas que podem ser percorridas por iteração.

query = (
    update(aluno)
    .where(aluno.c.nome == "Marcos")
    .values(sobrenome="Olímpio")
    .returning(aluno.c.id, aluno.c.nome)
)
print(query)
↳ UPDATE aluno SET sobrenome=:sobrenome
  WHERE aluno.nome = :nome_1
  RETURNING aluno.id, aluno.nome

query = (
    delete(table)
    .where(aluno.c.nome == "Jones")
    .returning(aluno.c.id, aluno.c.nome)
)
print(query)
↳ DELETE FROM aluno WHERE aluno.nome = :nome_1
  RETURNING aluno.id, aluno.nome

Bibliografia

Esse texto é baseado primariamente na documentação do SQLAlchemy, disponível em SQLAlchemy 2, Documentation. Outras referências no artigo Python e SQL: SQLAlchemy.

SQLAlchemy: INSERT e SELECT

Inserindo e selecionando dados

Definimos uma tabela na seção sobre Metadados. Uma vez definidas as tabelas com seus tipos de dados, vínculos e relacionamentos, o próximo passo consiste em realizar operações de inserção, extração, modificação e apagamento de dados.

INSERT

No SQL dados são inseridos nas tabelas com a instrução INSERT. Tanto ao usar CORE ou ORM a instrução INSERT é gerada com função insert(). No CORE usamos insert(tabela).values(valores). O objeto query obtido tem a representação de string mostrada abaixo. Ele possui o método compile() com têm parâmetros como params que armazena os campos e valores associados na consulta.

from sqlalchemy import insert
query = insert(aluno).values(matricula="12345-67890", nome="Marcos", sobrenome="Sobral")
print(query)
↳ INSERT INTO aluno (matricula, nome, sobrenome) VALUES (:matricula, :nome, :sobrenome)

# a query compilada possui propriedades
query_compilada = query.compile()
print(query_compilada.params)
{matricula:"12345-67890", nome:"Marcos", sobrenome:"Sobral"}

# para efetivar a consulta
with engine.connect() as conn:
    result = conn.execute(query)
    conn.commit()

# que gera a consulta
[SQL]
INSERT INTO aluno (matricula, nome, sobrenome) VALUES (?, ?)
    ("12345-67890", "Marcos", "Sobral")

# a chave primária da linha inserida pode ser recuperada
result.inserted_primary_key
↳ (1,)

A chave primária pode ser obtida quando a consulta insere apenas 1 linha. O método inserted_primary_key retorna uma tupla contendo todas as colunas que são chaves primárias (pois podem existir várias pks). Isso significa que uma cláusula RETURNING é inserida automaticamente sempre que o banco de dados subjacente der suporte à essa característica. No entanto é possível retornar outros valores além da chave primária. Isso é feito com o método Insert.returning(). Nesse caso o objeto Result retornado contém linhas que podem ser percorridas e lidas.

insert_query = insert(aluno).returning(aluno.c.matricula, aluno.c.nome, aluno.c.sobrenome)
print(insert_query)

[SQL]
INSERT INTO aluno (matricula, nome, sobrenome)
    VALUES (:matricula, :nome, :sobrenome)
    RETURNING aluno.matricula, aluno.nome, aluno.sobrenome

Para instruções INSERT o recurso RETURNING pode ser usado para instruções de uma única linha ou para múltiplas linhas (desde que tenham suporte no dialeto usado). RETURNING pode também ser usado com as instruções UPDATE e DELETE.

INSERT inclue a cláusula VALUES automaticamente se insert().values() não for usado. Se a consulta for executada com uma lista de valores, uma consulta é feita para cada elemento da lista. Por exemplo:

# se nenhum valor for fornecido
print(insert(aluno))
[SQL]
INSERT INTO aluno (id, matricula, nome, sobrenome, enderecos)
       VALUES (:id, :matricula, :nome, :sobrenome, :enderecos)

# fornecendo os valores
valores = [{matricula:"12345-67890", nome:"Marcos", sobrenome:"Sobral"},{matricula:"54321-12345", nome:"Joana", sobrenome:"Rosa"}]
with engine.connect() as conn:
    result = conn.execute(insert(alunos), valores,)
    conn.commit()

[SQL]
INSERT INTO aluno (id, matricula, nome, sobrenome, enderecos) VALUES (?, ?, ?)
       [("12345-67890", "Marcos", "Sobral"),("54321-12345", "Joana", "Rosa")]

No código acima valores é uma lista de dicionários, cada dicionário com os pares campo: valor. A consulta gerada é exibida, sempre respeitando o dialeto usado para o banco de dados usado. Uma consulta “vazia”, que insere apenas os valores default, pode ser realizada, mostrada abaixo.

# para inserir todos os valores default
insert(alunos).values().compile(engine)
[SQL]
INSERT INTO aluno DEFAULT VALUES

SELECT


A função select() é usada tanto no Core (passado com Connection.execute()) quanto ORM (passado com Session.execute()), resultando no objeto Result que contém as linhas retornadas pela consulta. Para a abordagem com ORM existem muitas outras formas de aplicar SELECT.

Podemos passar argumentos posicionais para a função select() para representar qualquer quantidade de objetos Table ou Column (ou outros objetos compatíveis). A cláusula FROM é inferida a partir desses argumentos.

from sqlalchemy import select

print(select(aluno))
↳ SELECT id, matricula, nome, sobrenome, enderecos FROM aluno

# alternativamente, podemos escolher as colunas a serem retornadas
print(select(aluno.c["nome", "sobrenome"]))
↳ SELECT aluno.nome, aluno.sobrenome FROM aluno

O modificador WHERE é um método do objeto retornado por select().

from sqlalchemy import select
query = select(aluno).where(aluno.c.nome == "Marcos")
print(query)
↳  SELECT id, matricula, nome, sobrenome, enderecos FROM aluno
   WHERE aluno.nome = :nome_1

# a consulta pode ser efetivada com connection.execute(query)
# e o resultado percorrido em um loop
with engine.connect() as conn:
    for linha in conn.execute(query):
        print(linha)

[SQL]
SELECT id, matricula, nome, sobrenome, enderecos FROM aluno
       WHERE user_account.name = ? ('Marcos',)

# uma única linha é retornada
↳ (1, '12345-67890', 'Marco', 'Sobral', '')
O SQLAlchemy adota uma forma de métodos encadeados (method chaining), que é chamada de generativa (generative) na documentação. Essa é uma técnica de orientação a objetos onde um objeto pode ter suas propriedades configuradas através de chamadas sucessivas aos seus métodos. Para isso cada método retorna o objeto modificado ou um novo objeto construído com as novas propriedades. Os métodos podem ser novamente chamados nesse novo objeto.Esse é o caso de objetos Select e Query. Um objeto Select, por exemplo, pode receber chamadas sucessivas aos métodos where() e order_by(). Assumindo tabela possui os campos id, campo1 e campo2:

query = (
        select(tabela.c.campo1)
        .where(tabela.c.id > 5)
        .where(tabela.c.campo2.like("e%"))
        .order_by(tabela.c.campo2)
    )

O método order_by() fornece a campo para ordenamento do resultado da consulta.
ORDER BY: Na linguagem de consulta SQL podemos ordenar as linhas retornadas por meio da cláusula ORDER BY. No SQLAlchemy ORDER BY é inserido com o método Select.order_by() que aceita parâmetros posicionais. Ordenamento crescente ou decrescente é obtido com os modificadores ColumnElement.asc() e ColumnElement.desc(). Por exemplo, consultas básicas no CORE e ORM podem ser obtidas:

print(select(aluno).order_by(aluno.c.name))
↳ SELECT aluno.id, aluno.nome, aluno.sobrenome FROM aluno ORDER BY aluno.nome
  
# usando classes do ORM
print(select(Aluno).order_by(Aluno.sobrenome.desc()))
↳ SELECT aluno.id, aluno.name, aluno.sobrenome FROM aluno ORDER BY aluno.sobrenome DESC

SELECT com ORM

Na abordagem ORM usamos Session.execute() para efetivar a consulta. Agora o resultado é formado não apenas por linhas de tuplas mas pelas próprias instâncias da classe Aluno.

query = select(Aluno).where(Aluno.nome == "Marcos")
with Session(engine) as session:
    for linha in session.execute(query):
        print(linha)
        
[SQL]
SELECT id, matricula, nome, sobrenome, enderecos FROM aluno
       FROM aluno WHERE aluno.nome = ? ('Marcos',)

# um único objeto é retornado
↳ (Aluno(id=1, matricula='12345-67890', nome='Marcos', sobrenome='Sobral', enderecos=''),)

A forma como esse objeto é exibido (com print()) é definida no método __repr__ (ou __str__).

Objetos gerados pelo ORM, sejam as classes que criamos como Aluno ou as colunas Aluno.nome são inseridos nas consultas SELECT da mesma forma que as próprias tabelas no CORE, gerando consultas idênticas.

# Lembrando que Aluno é a classe associada à tabela aluno
print(select(Aluno))
↳ SELECT id, matricula, nome, sobrenome, enderecos FROM aluno

Os comandos são executados com Session.execute(). Diferente das consultas com CORE, agora cada linha do resultado é um objeto Row, que são instâncias do objeto Aluno.

row = session.execute(select(Aluno)).first()
[SQL]
SELECT aluno.id, aluno.nome, aluno.sobrenome, aluno.enderecos FROM aluno

print(row)
(Aluno(id=1, matricula="12345-67890", nome='Marcos', sobrenome='Sobral'),)

# a primeira linha pode ser obtida
row = session.execute(select(Aluno)).first()

# outro método fornecido por conveniência é Session.scalars(), com o mesmo resultado
aluno = session.scalars(select(Aluno)).first()

Na consulta acima row (instância de Row) tem apenas um objeto (que é row[0]).

Para selecionar colunas específicas os atributos coluna da classe table são passados como argumento em select().

print(select(Aluno.matricula, Aluno.nome))
↳ SELECT aluno.matricula, aluno.nome FROM aluno

row = session.execute(select(Aluno.matricula, Aluno.nome)).first()
print(row)
↳ ('12345-67890', 'Marcos')

Essa abordagem pode ser mixta, como mostrado abaixo. Fazemos uma consulta no campo Aluno.nome e na tabela inteira Endereco. No objeto resultado usamos o método where() que restringe quais os endereços serão selecionados por linha. O método all() retorna todos os resultados obtidos na consulta.

session.execute(select(Aluno.nome, Endereco)
       .where(Aluno.id == Endereco.aluno_id)
       .order_by(Aluno.nome)).all()
[SQL]
SELECT aluno.nome, endereco.id, endereco.email, endereco.aluno_id
       FROM aluno, endereco WHERE aluno.id = endereco.aluno_id ORDER BY aluno.nome	

Aliases são úteis em consultas SQL, principalmente quando se deseja citar várias tabelas em uma única consulta ou para simpĺificar a exibição de colunas com nomes longos ou aquelas construídas programaticamente. No SQLAlchemy elas são denominadas labels, inseridas nas consultas com o método ColumnElement.label().

query = select(("Nome do aluno: " + aluno.c.nome).label("Nome"),).order_by(aluno.c.nome)
[SQL]
SELECT "Nome do aluno: " || aluno.nome AS Nome FROM aluno ORDER BY aluno.nome

# o resultado da consulta é
↳ Nome do aluno: Joana
  Nome do aluno: Marcos

Vale lembrar que || é o operador de concatenação no SQLite e AS é opcional no SQLite (e em vários outros BDs), tanto para tabelas quanto para nome das colunas. É comum se usar AS em nomes de campos e ignorá-lo para nomear tabelas.

# são equivalentes:
SELECT aluno.nome, endereco.email FROM aluno JOIN endereco ON aluno.id = endereco.aluno_id
SELECT a.nome, e.email FROM aluno a JOIN endereco e ON a.id = e.aluno_id

# alias em nomes de campos são úteis em resultados compostos
SELECT a.nome || " " || a.sobrenome AS "Nome do Aluno"  FROM aluno a

O exemplo abaixo ilustra o uso de DESC para a ordenação em ordem descendente e o uso do alias na ordenação.

from sqlalchemy import desc
query = select(Aluno.matricula, Aluno.nome + " " + Aluno.sobrenome.label("nomealuno"))
        .order_by("nomealuno", desc("matricula"))
print(query)
SELECT aluno.matricula, aluno.nome || " " || aluno.sobrenome AS nomealuno
FROM aluno ORDER BY matricula, nomealuno DESC

Cláusula WHERE

Os operadores padrões de comparação do Python são usados para gerar objetos de consulta, e não apenas retornarem valores booleanos. Esses objetos são passados para o método Select.where()

print(aluno.c.nome == "Roberto")
↳ aluno.nome = :nome_1

print(endereco.c.aluno_id > 10)
↳ endereco.c.aluno_id > :aluno_id_1

print(select(aluno).where(aluno.c.nome == "Roberto"))
↳ SELECT aluno.id, aluno.matricula, aluno.nome, aluno.sobrenome, aluno.enderecos
  FROM aluno WHERE aluno.nome = :nome_1

Condições encadeadas com AND são produzidas pelo uso múltiplo de Select.where() ou pelo uso de múltiplas expressões como argumento de where().


# múltiplos where()
print(
     select(endereco.c.email)
     .where(user_table.c.name == "Joana")
     .where(endereco.c.aluno_id == aluno.c.id)
)

# ou, múltiplos argumentos (o que é equivalente)
print(select(endereco.c.email).where(user_table.c.name == "Joana",
      endereco.c.aluno_id == aluno.c.id))

# em ambos os casos o resultado é
[SQL]
↳ SELECT endereco.email FROM endereco, aluno
   WHERE aluno.nome = :nome_1 AND enderco.aluno_id = aluno.id

As junções lógicas AND e OR são obtidas com o uso das funções and_() e or_().

from sqlalchemy import and_, or_
print(select(Endereco.email).where(
    and_(
        or_(Aluno.nome == "Marcos", Aluno.nome == "Joana"),
        Endereco.aluno_id == Aluno.id,)
    )
)
↳ SELECT endereco.email FROM endereco, aluno
  WHERE (aluno.name = :name_1 OR aluno.name = :name_2)
  AND endereco.aluno_id = aluno.id

Observe que, se A, B e C são testes booleanos então and_(or_(A, B),C) é o mesmo que (A OR B) AND C).

Para outras comparações podemos usar filtros Select.filter_by() que recebe argumentos nomeados para testar em valores nas colunas ou nomes de atributos no ORM. O filtro age sobre a última cláusula FROM ou última tabela em Join.

print(select(Aluno).filter_by(nome="Marcos", matricula="12345-67890"))
↳ SELECT aluno.id, aluno.matricula, aluno.nome,  aluno.sobrenome, aluno.enderecos
  FROM aluno WHERE aluno.nome = :nome_1 AND aluno.matricula = :matricula_1

Vimos que a tabela default de onde os campos serão pesquisados com SELECT é inferida pelo código da pesquisa. Para usar mais de uma tabela temos que listar cada uma como argumentos, separados por vírgula.

# consulta em uma única tabela	
print(select(aluno.c.name))
↳ SELECT aluno.nome FROM aluno

# consulta em mais de uma tabela	
print(select(aluno.c.nome, endereco.c.email))
↳ SELECT aluno.nome, endereco.email FROM aluno, endereco

Para juntar (com JOIN) as tabelas usamos um dos dois métodos: Select.join_from(), que permite indicar o lado esquerdo e direito da junção explicitamente, e Select.join() que apenas define o lado direito (sendo o esquerdo inferido). Com Select.select_from() podemos explictar a tabela que queremos na cláusula FROM.

# usando .join_from()
print(select(aluno.c.nome, endereco.c.email).join_from(aluno, endereco))
↳ SELECT aluno.nome, endereco.email FROM aluno JOIN endereco ON aluno.id = endereco.aluno_id

# usando .join()
print(select(aluno.c.nome, endereco.c.email).join(endereco))
↳ SELECT aluno.nome, enderco.email FROM aluno JOIN endereco ON aluno.id = endereco.aluno_id

# usando .select_from()
print(select(endereco.c.email).select_from(aluno).join(endereco))
↳ SELECT endereco.email FROM aluno JOIN endereco ON aluno.id = endereco.aluno_id

Gerando cláusula ON: Nos exemplos anteriores vimosque a clásula ON foi inserida automaticamente. Isso ocorreu porque existe um relacionamento entre as tabelas aluno e endereco por meio de uma foreignkey. Se não exitir um vínculo desse tipo, ou se existirem vínculos entre várias tabelas a cláusula ON pode ser especificada explicitamente em ambas as funções Select.join() e Select.join_from().

print(
     select(endereco.c.email)
     .select_from(aluno)
     .join(endereco, aluno.c.id == endereco.c.aluno_id)
)
↳ SELECT endereco.email FROM aluno JOIN endereco ON aluno.id = endereco.aluno_id

LEFT OUTER JOIN, FULL OUTER JOIN: os dois métodos também admitem a especificação que leva à construção de LEFT OUTER JOIN e FULL OUTER JOIN. Isso é feito com Select.join.isouter e Select.join.full.

print(select(aluno).join(endereco, isouter=True))
↳ SELECT aluno.id, aluno.nome, aluno.sobrenome 
  FROM aluno LEFT OUTER JOIN endereco ON aluno.id = endereco.aluno_id

print(select(user_table).join(address_table, full=True))
↳ SELECT aluno.id, aluno.nome, aluno.sobrenome 
  FROM aluno FULL OUTER JOIN endereco ON aluno.id = endereco.aluno_id

Obs.: Existe o método Select.outerjoin() equivalente ao uso de .join(..., isouter=True).
SQLAlchemy não dá suporte à RIGHT OUTER JOIN. Para conseguir o mesmo efeito inverta a ordem das tabelas e use LEFT OUTER JOIN.

Bibliografia

Esse texto é baseado primariamente na documentação do SQLAlchemy, disponível em SQLAlchemy 2, Documentation. Outras referências no artigo Python e SQL: SQLAlchemy.

SQLAlchemy: Agrupamentos e Subqueries


Agrupamentos e ordenações

GROUP BY e funções agregadas: A cláusula GROUP BY permite o agrupamento de linhas de forma a aplicar funções agregadas sobre os grupos gerados. Para estabelecer condições e filtros sobre linhas agrupadas não usamos WHERE e sim a cláusula HAVING. Funções de agregação são uma forma de inserir cálculos, tais como somas, contagem, médias ou localização de máximos e mínimos, sobre todos os elementos de um grupo.

Resultado de GROUP BY para uma tabela hipotética

No SQLAlchemy as funções de agregação estão em um namespace chamado de func, que é o construtor de instâncias da classe Function. Por exemplo, para contar quantas linhas tem a tabela aluno escolhemos uma coluna com valores únicos (como id) e contamos quantas linhas existem.

from sqlalchemy import func
print(func.count(aluno.c.id))
↳ count(user_account.id)

Nesse último caso, como nenhum agrupamento foi feito com GROUP BY, o grupo considerado consiste na tabela inteira e a contagem se refere a todas as linhas. Caso a tabela tenha sido particionada com GROUP BY as funções de agregação serão aplicadas a cada grupo individualmente. Para selecionar ou filtrar grupos usamos a cláusula HAVING sobre valores agregados. SQLAlchemy fornece os métodos Select.group_by() e Select.having().

with engine.connect() as conn:
    result = conn.execute(
        select(Aluno.nome, func.count(Endereco.id).label("count"))
        .join(Endereco)
        .group_by(Aluno.nome)
        .having(func.count(Endereco.id) > 1)
    )
    print(result.all())

[SQL]
SELECT aluno.nome, count(endereco.id) AS count
FROM aluno JOIN endereco ON aluno.id = endereco.aluno_id
GROUP BY aluno.nome HAVING count(address.id) > 1

A consulta acima faz um JOIN das tabelas aluno e endereco, o que pode retornar várias linhas para cada aluno se ele tiver mais de um endereço registrado. Em seguida ela faz um agrupamento pelo nome e conta quantos endereços existem, retornando apenas aqueles que tem mais de um endereço.

O uso de aliases ou labels podem tornar uma instrução SQL mais legível por evitar repetições de nomes. Aliases também podem ser usados para ordenação e agrupamento.

from sqlalchemy import func, desc
query = (
    select(Endereco.aluno_id, func.count(Endereco.id).label("n_enderecos"))
    .group_by("aluno_id")
    .order_by("aluno_id", desc("n_enderecos"))
)
print(query)
↳ SELECT endereco.aluno_id, count(endereco.id) AS n_enderecos
  FROM endereco GROUP BY endereco.aluno_id ORDER BY endereco.aluno_id, n_enderecos DESC

Essa consulta faz um agrupamento da tabela endereco no campo endereco.aluno_id (ou seja, agrupa todos os endereços de cada aluno), conta endereços armazenando a contagem como n_enderecos e exibe o resultado por ordem crescente de endereco.aluno_id, decrescente em n_enderecos. O nome provisório n_enderecos (um label ou alias) é retornado e serve como ordenador das linhas no resultado.

Aliases, no CORE: Também acontece de precisarmos usar o nome de várias tabelas quando fazemos consultas com JOIN e os nomes precisam ser usados várias vezes na mesma consulta. Na SQLAlchemy Expression Language esses aliases são construídos como o objeto Alias retornados pelo método FromClause.alias(). Um objeto construído como um Alias tem a mesma estrutura de uma Table, possuindo objetos Column em sua coleção Alias.c.

alias_1 = aluno.alias()
alias_2 = aluno.alias()
print(
    select(alias_1.c.name, alias_2.c.name)
    .join_from(alias_1, alias_2, alias_1.c.id > alias_2.c.id)
)
↳ SELECT alias_1.nome AS nome_1, alias_2.nome AS nome_2
  FROM aluno AS alias_1 JOIN aluno AS alias_2 ON alias_1.id > alias_2.id

Duas “tabelas” temporárias são construídas a partir da tabela aluno, com os nomes alias_1 e alias_2, que podem ser consultadas independentemente. Essa consulta retorna todos os pares de nomes da tabela aluno, sem repetições.

Subqueries e CTEs

Subqueries (subconsultas) são instruções SELECT realizadas entre parênteses, dentro de uma consulta que a envolve (geralmente outro SELECT). O resultado da subconsulta é usado para a consulta que a envolve. O SQLAlchemy usa o objeto Subquery (em Select.subquery()) para representar uma subconsulta. Qualquer objeto pode ser usado como um elemento FROM dentro de uma construção select() maior.

Como exemplo vamos construir uma subconsulta que selecionará uma contagem agregada de linhas da tabela de endereços (funções agregadas e GROUP BY foram introduzidas anteriormente em Funções Agregadas com GROUP BY / HAVING):

sub_query = (
    select(func.count(endereco.c.id).label("contagem"), endereco.c.aluno_id)
    .group_by(endereco.c.aluno_id).subquery()
)

# sub_query é uma consulta SELECT (sem os parênteses)
print(sub_query)
↳ SELECT count(endereco.id) AS countagem, endereco.aluno_id
  FROM endereco GROUP BY endereco.aluno_id

O objeto Subquery se comporta como qualquer outro objeto FROM (como uma Table), incluindo uma Subquery.c que contém as colunas selecionadas, de onde podemos fazer referência às colunas aluno_id e aquela rotulada “contagem” que contém o resultado da função agregada. Essa subconsulta pode ser usada como parte de FROM em uma nova consulta.

print(select(sub_query.c.user_id, sub_query.c.contagem))
↳ SELECT alias_1.aluno_id, alias_1.contagem FROM
  (SELECT count(endereco.id) AS contagem, endereco.aluno_id
  FROM endereco GROUP BY endereco.aluno_id) AS alias_1

A subconsulta retorna uma tabela com linhas agrupadas por aluno (aluno.id) e o número de endereços registrados para cada um deles no campo “contagem”. A consulta externa simplesmente lista esses linhas. Consultas mais gerais podem ser realizadas, usando a subquery como uma tabela qualquer.

query = select(aluno.c.nome, aluno.c.sobrenome, sub_query.c.contagem).join_from(aluno, sub_query)
print(query)
↳ SELECT aluno.nome, aluno.sobrenome, alias_1.contagem FROM aluno JOIN
     (SELECT count(endereco.id) AS contagem, endereco.aluno_id AS aluno_id FROM endereco
     GROUP BY endereco.aluno_id) AS alias_1 ON aluno.id = alias_1.aluno_id

A subconsulta gera a tabela com os campos alias_1.contagem e alias_1.aluno_id. A consulta externa faz um JOIN dessa tabela com aluno, retornando nome e sobrenome de cada aluno, junto com o número de endereços cada um tem registrado. A cláusula ON, nesse caso, foi inferida com base nos vínculo e na chave extrangeira (aluno.id = alias_1.aluno_id).

CTEs ou Common Table Expression (Expressões Comuns de Tabelas) são usadas de modo similar às subconsultas mas possuindo recursos adicionais. Um objeto CTE é construído com método Select.cte() e, da mesma forma que as subqueries, pode ser usado como tabela dentro da cláusula FROM. O uso é muito similar ao das subqueries mas o comando SQL gerado é bastante diferente.

ctable = (
    select(func.count(endereco.c.id).label("contagem"), endereco.c.aluno_id)
    .group_by(endereco.c.aluno_id)
    .cte()
)
query = select(aluno.c.nome, aluno.c.sobrenome, ctable.c.contagem).join_from(aluno, ctable)

print(query)
↳ WITH anon_1 AS
  (SELECT count(endereco.id) AS contagem, endereco.aluno_id AS aluno_id
    FROM endereco GROUP BY endereco.aluno_id)
  SELECT aluno.nome, aluno.sobrenome, anon_1.contagem
  FROM aluno JOIN anon_1 ON aluno.id = anon_1.aluno_id

A cláusula WITH gera uma tabela temporária (criada pela consulta entre parânteses) a atribui a ela o aliás anon_1. Essa tabela pode ser usada na consulta que se segue.

Subqueries e CTEs com ORM

Aliases, no ORM: O equivalente ORM para gerar aliases é a função aliased(). Quando aplicada a um objeto de classes mapeadas em tabelas, como Aluno e Endereco, essa função retorna um objeto que representa o objeto Table original, mantendo toda a sua funcionalidade ORM original.

from sqlalchemy.orm import aliased
Alias_1 = aliased(Address)
Alias_2 = aliased(Address)
print(
    select(Aluno)
    .join_from(Aluno, Alias_1)
    .where(Alias_1.email == "marcos@aol.com")
    .join_from(Aluno, Alias_2)
    .where(Alias_2.email == "soares@gmail.com")
)
↳ SELECT aluno.id, aluno.nome, aluno.sobrenome
  FROM aluno
  JOIN endereco AS alias_1 ON aluno.id = alias_1.aluno_id
  JOIN endereco AS alias_2 ON aluno.id = alias_2.aluno_id
  WHERE alias_1.email = :email_1 AND alias_2.email = :email_2

O SELECT acima seleciona id, nome e sobrenome da tabela aluno que têm dois endereços de e-mail especificados (no caso “marcos@aol.com” e “soares@gmail.com”).

Subqueries e CTEs: No caso das classes do ORM o método subquery() gera objeto semelhante à Table, com todas as suas propriedades. O mesmo ocorre com aliased() que pode armazenar uma Tabela inteira ou o resultado de uma consulta.

sub_query = select(Endereco).where(~Endereco.email.like("%@gmail.com")).subquery()
endereco_query = aliased(Endereco, sub_query)
query = (
    select(Aluno, endereco_query)
    .join_from(Aluno, endereco_query)
    .order_by(Aluno.id, endereco_query.id)
)
with Session(engine) as session:
    for aluno, endereco in session.execute(query):
        print(f"{aluno} {endereco}")

# a seguinte consulta é executada
[SQL]
SELECT aluno.id, aluno.nome, aluno.sobrenome,
  anon_1.id AS id_1, anon_1.email, anon_1.aluno_id
FROM aluno JOIN (
  SELECT endereco.id AS id, endereco.email AS email, endereco.aluno_id AS aluno_id
     FROM endereco WHERE endereco.email NOT LIKE ?
  ) AS anon_1 ON aluno.id = anon_1.user_id
  ORDER BY aluno.id, anon_1.id ('%@gmail.com',)

Nessa consulta anon_1 é a representação SQL de endereco_query que, por sua vez, é um aliás para a consulta em sub_query sobre a tabela endereco. A consulta retorna id, nome e sobrenome de alunos, id e email de endereços para aqueles que não possuem email do gmail. Uma consulta análoga com cte pode ser realizada substituindo subquery() por cte() na consulta acima. A consulta gerada usa WITH.

O operador de negação ~ foi usado acima. Por exemplo: ~Aluno.enderecos.any() seleciona as linhas
de Aluno que não possuem endereços cadastrados.

query = select(Aluno.nome).where(~Aluno.enderecos.any())
session.execute(query).all()
# a seguinte consulta é executada
↳ SELECT aluno.nome FROM aluno
  WHERE NOT (EXISTS (SELECT 1 FROM endereco WHERE aluno.id = endereco.aluno_id)) 

A consulta interna retorna 1 se a linha de aluno possui algum endereço associado.

Subqueries escalares e correlacionadas

Uma subquery escalar (scalar subquery) é uma subquery que retorna apenas 1, ou nenhuma, linha em uma única coluna. Diferente de uma consulta com mais de uma linha e uma coluna, ela pode ser usada em cláusulas WHERE de um SELECT subjacente (que envolve a subquery).
Esse tipo de objeto é obtido com o método .scalar_subquery() e é frequentemente usado com funções de agregamento.

sub_query = (
    select(func.count(endereco.c.id))
    .where(aluno.c.id == endereco.c.aluno_id)
    .scalar_subquery()
)
print(sub_query)
↳ (SELECT count(endero.id) AS count_1 FROM endereco, aluno
  WHERE aluno.id = endereco.aluno_id)

O comando SQL gerado é uma query usual e o resultado da consulta é uma única linha com a coluna count_1. A subquery pode ser usada como qualquer outra expressão de coluna.

print(sub_query == 5)
↳ (SELECT count(endereco.id) AS count_1 FROM endereco, aluno
  WHERE aluno.id = endereco.aluno_id) = :param_1 {5, }

No exemplo abaixo a subquery é usada em uma consulta ****

Uniões entre tabelas: UNION

Com consultas SQL usuais podemos unir o resultado de duas consultas obtidas com SELECT usando os operadores UNION e UNION ALL. Eles produzem o conjunto de todas as linhas resultantes em cada uma das consultas envolvidas. Também estão disponíveis consultas com INTERSECT [ALL] e EXCEPT [ALL]. No SQLAlchemy essas operações são obtidas com as funções union(), intersect() e except_(), além de union_all(), intersect_all() e except_all() para incluir o modificador ALL. Todas essas funções aceitam um número arbitrário de conjuntos selecionáveis.

O resultado dessas funções é um CompoundSelect que é usado da mesma forma que um objeto resultante de Select, embora tenha menos métodos. Por exemplo:

from sqlalchemy import union_all

query1 = select(aluno).where(aluno.c.nome == "Marcos")
query2 = select(aluno).where(aluno.c.nome == "Joana")
u = union_all(query1, query2)
with engine.connect() as conn:
    result = conn.execute(u)
    print(result.all())
[SQL]
SELECT aluno.id, aluno.nome, aluno.sobrenome, aluno.enderecos
FROM aluno WHERE aluno.nome = ?
UNION ALL
SELECT aluno.id, aluno.nome, aluno.sobrenome, aluno.enderecos
FROM aluno WHERE aluno.nome = ?

↳ (Marcos', 'Joana')

O resultado são os dados dos alunos “Marcos” e “Joana”.

Ainda usando a união construída acima podemos ilustrar como usar o resultado da união (que é um objeto CompoundSelect) como uma subquery. O próprio objeto possui um método Subquery.

u_subq = u.subquery()
query = (
    select(u_subq.c.name, aluno.c.email)
    .join_from(endereco, u_subq)
    .order_by(u_subq.c.name, endereco.c.email)
)
with engine.connect() as conn:
    result = conn.execute(query)
    print(result.all())

Uniões com ORM

Os exemplos acima mostram a união construída à partir de objetos Table retornando linhas do BD. Usando os objetos do ORM (as classes representando tabelas) construímos um objeto CompoundSelect que representa a união das entidades ORM e, portanto, das tabelas que elas mapeiam. O método Select.from_statement() pode ser usado para converter o objeto obtido de union_all() em um selecionável (que é uma coleção). Nesse caso UNION representa a consulta inteira, não necessitando de critérios adicionais. O exemplo tem o mesmo efeito que a consulta anterior feita com CORE.

query1 = select(Aluno).where(Aluno.nome == "Marcos")
query2 = select(Aluno).where(Aluno.nome == "Joana")
u = union_all(query1, query2)

orm_query = select(Aluno).from_statement(u)
with Session(engine) as session:
    for obj in session.execute(orm_query).scalars():
        print(obj)

Alternativamente, uma UNION pode ser usada como subquery e composta com outro objeto ORM por meio da função aliased(). No exemplo abaixo podemos adicionar critérios adicionais como ORDER BY fora do próprio UNION, inserindo filtros ou ordenamentos nas colunas geradas em uma subconsulta.

aluno_alias = aliased(Aluno, u.subquery())
orm_query = select(aluno_alias).order_by(aluno_alias.id)
with Session(engine) as session:
    for obj in session.execute(orm_query).scalars():
        print(obj)

↳ Aluno(id=1, matricula="12345-67890", nome='Marcos', sobrenome='Sobral', enderecos="")
  Aluno(id=2, matricula="54321-12345", nome='Joana', sobrenome='Rosa', enderecos="")

[SQL]
SELECT anon_1.id, anon_1.matricula, anon_1.nome, anon_1.sobrenome, anon_1.enderecos
FROM (SELECT aluno.id AS id, aluno.matricula AS matricula,
      aluno.nome AS nome, aluno.sobrenome AS sobrenome, aluno.enderecos AS enderecos
      FROM user_account WHERE user_account.name = ?
   UNION ALL
      SELECT aluno.id AS id, aluno.nome AS nome, aluno.sobrenome AS sobrenome,
      aluno.enderecos AS enderecos
FROM aluno WHERE aluno.nome = ?) AS anon_1 ORDER BY anon_1.id
('Marcos', 'Joana')

O resultado, visualizado com print(obj) são objetos ORM (como Aluno).

A subquery EXISTS


No SQL podemos usar o operador EXISTS junto com subconsultas escalares (aqueles que retornam apenas uma linha, ou nenhuma) para retornar um booleano informando se a instrução SELECT retorna uma linha (TRUE) ou nenhuma (FALSE). Esse funcionalidade é conseguida no SQLAlchemy com o uso de uma variante do objeto ScalarSelect chamado Exists.

subq = (
    select(func.count(endereco.c.id))
    .where(aluno.c.id == endereco.c.aluno_id)
    .group_by(endereco.c.aluno_id)
    .having(func.count(endereco.c.id) > 1)
).exists()
with engine.connect() as conn:
    result = conn.execute(select(aluno.c.nome).where(subq))
    print(result.all())
    
[SQL]
SELECT aluno.nome FROM aluno WHERE EXISTS
  (SELECT count(endereco.id) AS count_1 FROM endereco
   WHERE aluno.id = endereco.aluno_id GROUP BY endereco.aluno_id
   HAVING count(endereco.id) > ?) (1,)

A consulta SELECT em subq é uma consulta que faz uma JOIN de aluno com endereco, agrupa e conta quantos endereços existem para cada aluno, retornando apenas aqueles que possuem mais de um endereço. A função exists() retorna TRUE se existir alguma linha nessa consulta, FALSE se nenhuma linha existe. Esse conjunto de boolenos é usado na consulta seguinte para retornar os nomes dos alunos que satisfazem à condição descrita.

EXISTS é mais usada como uma negação, como em NOT EXISTS. Ela fornece uma forma de localizar linhas de uma tabela associada à outra tabela que não possui linhas no relacionamento. Por exemplo, para encontrar nomes de alunos que não possuem endereços de e-mail podemos fazer:

subq = (select(endereco.c.id).where(aluno.c.id == endereco.c.aluno_id)).exists()
with engine.connect() as conn:
    result = conn.execute(select(aluno.c.nome).where(~subq))
    print(result.all())

[SQL]
SELECT aluno.nome FROM aluno WHERE NOT (EXISTS 
   (SELECT endereco.id FROM endereco WHERE aluno.id = endereco.aluno_id)
)

Observe o uso de ~ para negar o resultado de subq dentro da segunda cláusula WHERE.

Funções SQL

Funções do SQL são utilizadas em conjunto com agrupagamentos e filtros (GROUP BY, HAVING), ou sobre linhas ou campos individuais. Elas foram introduzidas na seção sobre agrupamentos. No SQLAlchemy o objeto func funciona como uma fábrica de funções (objetos da classe Function) que podem ser usados em uma construção tipo select() para representar uma função SQL. Elas consistem em um nome, parênteses (na maioria das vezes) e possíveis argumentos. Seguem alguns exemplos de funções SQL.

# count(): função sobre linhas agregadas, conta quantas linhas foram retornadas
print(select(func.count()).select_from(aluno))
↳ SELECT count(*) AS count_1 FROM aluno

# lower(): converte um string em minúsculas:
print(select(func.lower("A String With Much UPPERCASE")))
↳ SELECT lower(:lower_2) AS lower_1
(a string with much uppercase)

# now(): fornece data e hora atual. In sqlite:
query = select(func.now())
with engine.connect() as conn:
    result = conn.execute(query)
    print(result.all())

SELECT CURRENT_TIMESTAMP AS now_1

O resultado, a consulta SQL, depende do dialeto e BD usado. Como os diversos dialetos incluem muitas funções (que podem variar entre eles) o método func aceita parâmetros de forma liberal, tentando contruir com eles uma função válida. Por outro lado existe um conjunto pequeno de funções comuns a diversas versões do SQL, como count, now, max, concat, que possuem versões pre-definidas.

# uso de nome genérico
print(select(func.uma_funcao_qualquer(tabela.c.campo, 17)))
↳ SELECT uma_funcao_qualquer(tabela, :uma_funcao_qualquer_2) AS uma_funcao_qualquer_1 FROM tabela

# uso de função comum no postgresql e no oracle
from sqlalchemy.dialects import postgresql
print(select(func.now()).compile(dialect=postgresql.dialect()))
↳ SELECT now() AS now_1

from sqlalchemy.dialects import oracle
print(select(func.now()).compile(dialect=oracle.dialect()))
↳ SELECT CURRENT_TIMESTAMP AS now_1 FROM DUAL

O segundo exemplo acima compara a geração da função SQL no PostgreSQL e no Oracle para a função now().

Tipos definidos de retorno: Algumas funções (mas não todas) retornam objetos com tipo de dados SQL definido. Eles serão chamados aqui de “tipos de retorno SQL” para diferenciá-los do “tipo de retorno” de uma função do Python. O tipo de retorno SQL de qualquer função SQL pode ser verificado com a leitura do atributo Function.type. Por exemplo func.now().type retorna DateTime(). Essa verificação é útil principalmente para debugging.

Considerar o tipo de retorno SQL pode ser importante dentro de uma declaração longa. Operadores matemáticos têm melhor desempenho quando atuam sobre expressões que retornam Integer ou Numeric, por exemplo. Existem operadores que esperam receber parâmetros JSON e funções que retornam colunas ao invés de linhas; as chamadas funções com valor de tabela. Portanto pode ser importante detectar o tipo de objeto que está em uso em algum ponto do código.

O tipo de retorno SQL da função também pode ser significativo quando o SQLAlchemy deve processar o resultado sobre um conjunto de resultados. Um bom exemplo são as funções de datas no SQLite, onde o SQLAlchemy deve converter strings em objetos datetime() do Python. Para garantir que um objeto de tipo específico seja aplicado a uma função passamos o parâmetro Function.type_ especificando esse tipo. No exemplo abaixo passamos a classe JSON para gerar a função PostgreSQL json_object(), lembrando que o tipo de retorno do SQL será do tipo JSON:

# passando a classe JSON para gerar um json_object() do PostgreSQL
from sqlalchemy import JSON
function_expr = func.json_object('{a, 1, b, "def", c, 3.5}', type_=JSON)	
query = select(function_expr["def"])
print(query)
↳ SELECT json_object(:json_object_1)[:json_object_2] AS anon_1

O retorno dessa consulta será do tipo JSON.

As funções built-in, como count, max, min, algumas funções de data como now e funções de string como concat, têm tipos de retorno SQL pré-estabelecidos. Em alguns casos esse tipo depende dos argumentos fornecidos.

m1 = func.max(Column("Coluna_de_inteiros", Integer))
m1.type
↳ Integer()

m2 = func.max(Column("Coluna_de_strings", String))
m2.type
↳ String()

Funções de data e hora do SQLAlchemy correspondem às expressões SQL DateTime, Date ou Time. Uma função de string, como concat() retorna strings, como esperado.

func.now().type
↳ DateTime()

func.current_date().type
↳ Date()

func.concat("x", "y").type
↳ String()

No entanto o SQLAlchemy não tem tipo definido de retorno para a maioria das funções SQL. Isso significa que ele não tem essas funções pre-definidas mas apenas converte as consultas em SQL válido. Por exemplo, func.lower() e func.upper() para converter strings em minúsculas e maiúsculas, têm tipo de retorno SQL “nulo”. Isso não significa nenhum problema para funções simples (como lower() e upper()) pois strings podem ser recebidas do banco de dados sem tratamento de tipo no lado do SQLAlchemy e as regras internas de coerção de tipo do SQLAlchemy podem interpretar corretamente a intenção: por ex., o operador Python + do python é interpretado como operador de concatenação de strings ou soma, dependendo dos argumentos usados.

# upper() e json_object() não tem tipo pre-definido	
func.upper("lowercase").type
↳ ()

func.json_object('{"a", "b"}').type
↳ NullType()


# inferência automática de tipo
print(select(func.upper("tudo minúscula") + " sufixo"))
↳ SELECT upper(:upper_1) || :upper_2 AS anon_1

Funções de janela SQL

Técnicas Avançadas de Função SQL: alguns gerenciadores de BD, como o PostgreSQL, dão suporte a um conjunto mais avançados de técnicas no uso de funções, em particular aquelas que retornam colunas ou tabelas, muito usadas quando se trabalha com dados em formato JSON.

Vimos que as funções SQL de agregação reúnem linhas sob a cláusula GROUP BY e realizam o cálculo sobre os grupos resultantes. A informação individual de cada linha fica perdida. As funções da janela SQL são diferentes: eles calculam seu resultado com base em um conjunto de linhas, retendo as informações individuais das linhas. Com elas podemos gerar um conjunto de resultados que incluem atributos de uma linha individual.

No SQLAlchemy, todas as funções SQL geradas pelo namespace func incluem um método FunctionElement.over() que insere o modificador OVER na consulta SQL para a determinação de janelas.

Uma função comum usada com funções de janela é a função row_number() que conta as linhas. Podemos particionar essa contagem de linhas em relação ao nome de usuário para numerar os endereços de e-mail de usuários individuais:

query = (
    select(
        func.row_number().over(partition_by=aluno.c.nome),
        aluno.c.nome,
        endereco.c.email,
    )
    .select_from(aluno)
    .join(endereco)
)
with engine.connect() as conn:  
    result = conn.execute(query)
    print(result.all())
[SQL]
SELECT row_number() OVER (PARTITION BY aluno.nome) AS anon_1,
aluno.nome, endereco.email
FROM aluno JOIN endereco ON aluno.id = endereco.aluno_id

A consulta acima faz a junção entre aluno e endereco (pelo id do aluno) e retorna quantas linhas existem para cada aluno, com seu respectivo email.

Coerção de tipos de dados

No SQL podemos obrigar um valor resultado de uma consulta a ter um tipo específico, como string ou inteiro. Isso é feito com o operador CAST. No SQLAlchemy temos a função cast() que recebe uma expressão de coluna e um tipo de dado como argumento.

from sqlalchemy import cast
query = select(cast(user_table.c.id, String))
with engine.connect() as conn:
    result = conn.execute(query)
    result.all()
[SQL]
SELECT CAST(aluno.id AS VARCHAR) AS id FROM aluno
# resultando em strings
[('1',), ('2',), ('3',)]

Ocorre às vezes a necessidade de informar ao SQLAlchemy o tipo de dado de uma expressão para uso no código python mas sem renderizar a expressão CAST do lado SQL. Para conseguir isso podemos usar a função type_coerce(). Ela é particularmente importante quando usamos o tipo de dados JSON que pode em si mesmo conter informação de tipos de dados. No ex. usamos type_coerce() para entregar uma estrutura Python como uma string JSON em uma das funções JSON do MySQL:

import json
from sqlalchemy import JSON, type_coerce
from sqlalchemy.dialects import mysql

s = select(type_coerce({"nome_campo": {"foo": "bar"}}, JSON)["nome_campo"])
print(s.compile(dialect=mysql.dialect()))
↳ SELECT JSON_EXTRACT(%s, %s) AS anon_1

Bibliografia

Esse texto é baseado primariamente na documentação do SQLAlchemy, disponível em SQLAlchemy 2, Documentation. Outras referências no artigo Python e SQL: SQLAlchemy.

SQLAlchemy – ORM (Exemplo de Uso)


SQLAlchemy ORM

O SQLAlchemy Object Relational Mapper fornece métodos de associação de classes Python definidas pelo usuário com tabelas de banco de dados e instâncias dessas classes (objetos) com linhas em suas tabelas correspondentes, tipos de dados, vínculos e relacionamentos. Ele sincroniza de forma transparente todas as mudanças de estado entre objetos e suas linhas relacionadas, e inclui uma forma de expressação de consultas ao banco de dados como manipulações das classes do Python.

O ORM está construído sobre a SQLAlchemy Expression Language (CORE) mas enfatizando muito mais o modelo definido pelo usuário, garantindo a sincronia entre as duas camadas. Um aplicativo pode ser construído com o uso exclusivo do ORM, embora existam situções em que a Expression Language pode ser usada para fazer interações específicas com o banco de dados.

CREATE TABLE: Para ilustrar a criação de tabelas, inserção de dados, alteração e apagamento de valores listamos aqui o código do python. Outputs de código são precedidos pelo sinal e os comandos SQL emitidos internamente em quadros iniciados por [SQL].

A classe DeclarativeBase é a base de todas as classes que geram as classes do Python mapeadas em tabelas do banco de dados. Os tipos de cada coluna são informados com anotações com tipos tratados por Mapped[type], onde type é
int (INTEGER), str (VARCHAR), etc. Campos que podem ser nulos são declarados com o modificador Optional[type] (caso contrário o campo é NOT NULL).

A função mapped_column informa tipos e todos os demais atributos da coluna, como a informação de que ela é uma chave estrangeira. A função relationship() estabelece relacionamentos entre classes (portanto entre campos das tabelas). O método de classe __repr__() não é obrigatório mas pode ser útil para debugging. O parâmetro echo=True faz com que o comando SQL subjacente seja exibido no console.

from typing import Optional
from sqlalchemy import create_engine, ForeignKey, String
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship

engine = create_engine('sqlite:///contatos.db' , echo=True)

class Base(DeclarativeBase):
    pass

class Pessoa(Base):
    __tablename__ = "pessoas"

    id: Mapped[int] = mapped_column(primary_key=True)
    nome: Mapped[str] = mapped_column(String(30))
    sobrenome: Mapped[Optional[str]]
  
    enderecos: Mapped[list["Endereco"]] = relationship(back_populates="pessoa",
               cascade="all, delete-orphan")
    def __repr__(self) -> str:
        return f"Pessoa (id = {self.id!r}, nome={self.nome!r}, sobrenome={self.sobrenome!r})"

class Endereco(Base):
    __tablename__ = "enderecos"

    id: Mapped[int] = mapped_column(primary_key=True)
    pessoa_id: Mapped[int] = mapped_column(ForeignKey("pessoas.id"))

    email: Mapped[Optional[str]]
    endereco: Mapped[Optional[str]]
    pessoa: Mapped["Pessoa"] = relationship(back_populates="enderecos")

    def __repr__(self) -> str:
        return f"Endereco: (id = {self.id!r}, email = {self.email!r})"

Base.metadata.create_all(engine)

O comando Base.metadata.create_all(engine) cria um banco de dados e tabelas, se elas não existirem previamente. Os seguintes comandos são gerados.

[SQL]
CREATE TABLE pessoas (
	id INTEGER NOT NULL, 
	nome VARCHAR(30) NOT NULL, 
	sobrenome VARCHAR, 
	PRIMARY KEY (id)
)
CREATE TABLE enderecos (
	id INTEGER NOT NULL, 
	pessoa_id INTEGER NOT NULL, 
	email VARCHAR, 
	endereco VARCHAR, 
	PRIMARY KEY (id), 
	FOREIGN KEY(pessoa_id) REFERENCES pessoas (id)
)

Essa estrutura é denominada Mapeamento Declarativo (Declarative Mapping), responsável pela definição das classes Python e das tabelas, campos e relacionamentos que ficam armazenados em um objeto MetaData (embora esse não seja mencionado explicitamente no código). Temos, como resultado, a criação das tabelas e campos:

pessoas
id
nome
sobrenome
enderecos
id
pessoa_id
email
endereco

INSERT: Para inserirmos valores nas tabelas instanciamos objetos das classes Pessoa e Endereco (que são atribuidos ao campo Pessoa.enderecos). Criamos um objeto session = Session(engine) (dentro de um gerenciador de contexto width) e depois acrescentamos os objetos à sessão com session.add_all([lista_de_objetos]). Nenhuma alteração é gravada no banco de dados até a emissão de session.commit().

galileu = Pessoa(nome="Galileu", sobrenome="Galilei")
paulo = Pessoa(
    nome="Paul",
    sobrenome="Adrian Dirac",
    enderecos=[Endereco(email="pamdirac@hotmail.com")],
)
alberto = Pessoa(
    nome="Albert",
    sobrenome="Einstein",
    enderecos=[Endereco(email="albert@tre.org")],
)
ricardo = Pessoa(
    nome="Richerd",
    sobrenome="Feynman",
    enderecos=[
        Endereco(email="feynman@caltech.edu", endereco="R. Bahia, 2311"),
        Endereco(email="richar@google.com"),
    ],
)

width Session(engine) as session:
    session.add_all([paulo, alberto, ricardo, galileu])
    session.commit()

O nome Richerd foi digitado com erro propositalmente. As consultas são emitidas:

[SQL]
INSERT INTO pessoas (nome, sobrenome) VALUES (?, ?), (?, ?), (?, ?), (?, ?)
    ('Paul', 'Adrian Dirac', 'Albert', 'Einstein', 'Richerd', 'Feynman', 'Galileu', 'Galilei')

INSERT INTO enderecos (pessoa_id, email, endereco) VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?), (?, ?, ?)
(5, 'pamdirac@hotmail.com', None, 6, 'albert@tre.org', None, 7, 'feynman@caltech.edu', 'R. Bahia, 2311', 7, 'richar@google.com', None)

Como resultado temos as tabelas com os seguintes valores:

id nome sobrenome
1 Paul Adrian Dirac
2 Albert Einstein
3 Richerd Feynman
4 Galileu Galilei
id pessoa_id email endereco
1 1 pamdirac@hotmail.com NULL
2 2 albert@tre.org NULL
3 3 feynman@caltech.edu R. Bahia, 2311
4 3 richar@google.com NULL

SELECT: Consultas podem ser feitas com a classe select. Uma query tem a sintaxe básica query = select(Classe_tabela).where(condicao_na_classe). o resultado é um iterável:

from sqlalchemy import select

session = Session(engine)
query = select(Pessoa).where(Pessoa.nome.in_(["Galileu", "Paul"]))

for p in session.scalars(query):
    print(p)
↳   Pessoa (id = 1, nome='Paul', sobrenome='Adrian Dirac')
    Pessoa (id = 4, nome='Galileu', sobrenome='Galilei')    

A consulta equivalente é:

[SQL]
SELECT pessoas.id, pessoas.nome, pessoas.sobrenome FROM pessoas
    WHERE pessoas.nome IN (?, ?) ('Galileu', 'Paul')

Uma consulta SELECT * pode ser feita diretamente por id:

print(session.get(Pessoa, 4))
↳ Pessoa (id = 4, nome='Galileu', sobrenome='Galilei')
print(session.get(Pessoa, 1).sobrenome)
↳ Adrian Dirac

JOIN: Para realizar consulta com relacionamentos usamos join.

query = (select(Endereco)
    .join(Endereco.pessoa)
    .where(Pessoa.nome == "Richard")
    .where(Endereco.email == "richar@google.com")
)
result = session.scalars(query).one()

print(result)
↳ Endereco: (id = 4, email = 'richar@google.com')
[SQL]
SELECT enderecos.id, enderecos.pessoa_id, enderecos.email, enderecos.endereco 
    FROM enderecos JOIN pessoas ON pessoas.id = enderecos.pessoa_id 
    WHERE pessoas.nome = ? AND enderecos.email = ? ('Richard', 'richar@google.com')

O resultado de print acima decorre da forma como definimos o método __repr__. Qualquer propriedade do objeto pode ser obtida, por exemplo com print(result.id). Em particular result.pessoa é o objeto pessoa associado a esse endereço e print(result.pessoa.nome) imprime o nome “Richard”.

UPDATE: Para alterar um campo de um registro recuperamos o objeto correpondente ao registro e alteramos a propriedade desejada. A alteração só é gravada no BD com session.commit(), quando é emitido e executado o UPDATE.

rick = session.execute(select(Pessoa).filter_by(nome="Richerd")).scalar_one()
print(rick)
↳ Pessoa (id = 3, nome='Richerd', sobrenome='Feynman')

rick.nome = "Richard"
print(rick in session.dirty)
↳ True

# para verificar a alteração (na classe)
rick_nome = session.execute(select(Pessoa.nome).where(Pessoa.id == 3)).scalar_one()
print(rick_nome)
↳ Richard

print(rick in session.dirty)
↳ False

session.commit()

O modificador scalar_one() só pode ser usado quando a consulta retorna apenas uma linha (um objeto). Caso contrário uma exceção é lançada. Após a alteração o objeto fica na coleção Session.dirty até que um commit seja emitido. No caso acima o commit foi implícito, ocorrido quando a query SELECT foi executada.
A consulta resulta em:

[SQL]
SELECT pessoas.id, pessoas.nome, pessoas.sobrenome FROM pessoas 
    WHERE pessoas.nome = ? ('Richerd',)
# depois
UPDATE pessoas SET nome=? WHERE pessoas.id = ? ('Richard', 3)

Uma alteração em um campo exige a recuperação desse objeto seguida da alteração propriamente dita depois a gravação no BD.

query = select(Pessoa).where(Pessoa.id == 3)
p = session.scalars(query).one()
p.sobrenome = "Dawkings"
print(p)
↳ Pessoa (id = 3, nome='Richard', sobrenome='Dawkings')

# para gravar no BD
session.commit()

As consultas são emitidas:

[SQL]
SELECT pessoas.id, pessoas.nome, pessoas.sobrenome  FROM pessoas 
    WHERE pessoas.id = ? (3,)

UPDATE pessoas SET sobrenome=? WHERE pessoas.id = ? ('Dawkings', 3)

DELETE: para uma operação de apagamento de uma linha de tabela recuperamos essa linha (em um objeto) e a apagamos com session.delete(objeto).

p = session.get(Pessoa, 1)
print(p)
↳ Pessoa (id = 1, nome='Paul', sobrenome='Adrian Dirac')

session.delete(p)
session.commit()

Os seguintes comandos SQL são gerados:

[SQL]
SELECT pessoas.id AS pessoas_id, pessoas.nome AS pessoas_nome, pessoas.sobrenome AS pessoas_sobrenome 
   FROM pessoas WHERE pessoas.id = ? (1,)

DELETE FROM enderecos WHERE enderecos.id = ? (1,)
DELETE FROM pessoas WHERE pessoas.id = ? (1,)

Devido aos vínculos estabelecidos na definição da tabela (e, portanto, também da classe) enderecos, relationship(back_populates="pessoa", cascade="all, delete-orphan") ao ser apagada a linha da pessoa de id = 1 as linhas vinculadas da tabela enderecos também são apagadas.

Python e SQL: SQLAlchemy

SQL e SQLAlchemy

Nesse site: Linguagem de Consulta SQL,
Um projeto Python: SQLite.Essas notas e o código listado estão baseados na versão 2.0 do SQLAlchemy que é a versão lançada em 26 de janeiro de 2023. Um documento de migração, para quem está habituado com versões anteriores, está disponível em SQLAlchemy 2.0 – Major Migration Guide.

SQL é uma linguagem de consulta a bancos de dados relacionais universalmente usada para aplicativos em desktop ou na web. Existem muitas bibliotecas de integração desses bancos com o Python, inclusive o sqlite3 que vem instalado na biblioteca padrão, já descrito aqui em linhas básicas. Uma biblioteca Python poderosa e flexível muito usada é a SQLAlchemy, criada por Mike Bayer em 2005, de código aberto e disponibilizado sob licença MIT. Com ela se pode fazer consultas tradicionais, usando as queries padrões do SQL, mas também utilizar ferramentas que abstraem essas consultas associando as tabelas de banco de dados com classes. Ela pode ser usada para fazer a conexão com os bancos de dados mais comuns, como o Postgres, MySQL, SQLite, Oracle, entre outros.

Com o SQLAlchemy podemos abstrair do código específico do banco de dados subjacente. Com instruções comuns para todos os bancos ele facilita a migração de um banco para outro, sem maiores dificuldades. Além disso ele cuida de problemas de segurança comuns, tais como ataques de injeção de SQL. O SQLAlchemy é bastante flexível e permite duas formas principais de uso: o SQL Expression Language (referido como Core) e Object Relational Mapping (ORM), que podem ser usados separadamente ou juntos, dependendo das necessidades do aplicativo.

SQLAlchemy Core e ORM

SQL Expression Language (CORE): é uma forma de representar instruções e expressões SQL comuns de modo pitônico, uma abstração das consultas SQL sem se afastar muito delas. Ela é uma interface bem próxima das bancos de dados mas padronizado para ser consistente com muitos desses bancos. Além disso ela fundamenta o SQLAlchemy ORM.

SQLAlchemy ORM: é um mapeador relacional de objeto (ORM, Object Relational Mapper) que fornece uma abstração de alto nível sobre a SQL Expression Language. Ele utiliza um sistema declarativo semelhante aos utilizados em outros ORMs como, por exemplo, o do Ruby on Rails.

Diferente da maioria das outras ferramentas SQL/ORM, o SQLAlchemy não tenta ocultar os detalhes do mecanismo de SQL, deixando expostos e sob controle do programador todos os processos envolvidos. Ele estabelece uma associação entre o banco de dados e classes, geralmente atribuindo uma classe a cada tabela e cada instância dessa classe com linhas da tabela.

Instalando o SQLAlchemy

Um ambiente virtual é recomendado (embora não obrigatório).

# criamos um ambiente virtual com o comando
$ python3 -m venv ~/Projetos/.venv
# para ativar o ambiente virtual
$ cd ~/Projetos/.venv
$ source bin/activate

# instalamos o sqlalchemy (última versão publicada)
$ pip install sqlalchemy

# para a distribuição Anaconda do Python
$ conda install -c anaconda sqlalchemy

# criamos uma pasta para o sqlalchemy
$ mkdir ~/Projetos/.venv/sqlalchemy
$ cd ~/Projetos/.venv/sqlalchemy

# para verificar a versão instalada iniciamos o python e carregamos o sqlalchemy
$ python
>>> import sqlalchemy
>>> sqlalchemy.__version__
'2.0.0rc3'

O sqlalchemy consegue se conectar com banco de dados sqlite sem a necessidade de nenhum drive adicional. Para o PostgreSQL podemos usar o psycopg2, instalado com pip install psycopg2. Para o MySQl uma boa opção é o PyMySQL (pip install pymysql). Para nosso processo de aprendizado usaremos o SQLite.

A engine do SQLAlchemy

Para estabelecer contato com o banco de dados criamos uma instância do objeto da classe engine com create_engine que usa uma string de conexão (connection string), uma string com formato próprio para fornecer o tipo do banco, detalhes de autenticação (usuário e senha), localização do banco (servidor ou arquivo), e a DBAPI usada.

A DBAPI (Python Database API Specification) do Python é um driver usado pelo SQLAlchemy para interagir com o banco de dados escolhido. Por exemplo, nos nossos exemplos estamos usando sqlite3, da biblioteca padrão.

A DBAPI é uma API de baixo nível usado pelo Python para conectar ao banco de dados. O sistema de dialetos do SQLAlchemy é construído pela DBAPI que fornece classes específicas para lidar com o mecanismo de BD usado, como POSTGRES, MYSQL, SQLite, etc.

Por exemplo, para uma conexão com um arquivo meu_banco.db do SQLite usamos:

from sqlalchemy import create_engine

# abaixo alguns exemplos de strings de conexão
engine1 = create_engine("sqlite:///meu_banco.db")
engine2 = create_engine("sqlite:////home/projeto/db/meu_banco.db")
engine3 = create_engine("sqlite:///:memory:")
# no windows
engine4 = create_engine("sqlite:///c:\\Users\\projeto\\db\\meu_banco.db")

# para efetivar a conexão
connection = engine1.connect()

# para ativar um serviço de log usamos echo=True
engine1 = create_engine("sqlite:///meu_banco.db", echo=True)

No caso 1 o arquivo está na pasta default, no 2 o caminho completo é informado. A conexão em engine3 cria um banco na memória (sem ser gravado em disco), o que é útil para aprendizado e experimentação. Em 4 se mostra a sintaxe de pastas para o Windows. A função create_engine retorna uma instância da engine mas não estabelece a conexão, o que é chamado de lazy connection. Essa conexão só é efetivada quando, pela primeira vez, alguma ação é executada no banco. Se o arquivo meu_banco.db não existe ele é criado com esse processo.

O ajuste do parâmetro opcional echo = True faz com que todas as operações feitas no banco sejam também exibidas no console com a sintaxe do SQL. Nessas notas exibiremos os comandos mostrados nesse log com a marcação [SQL].

Conexão

O SQLAlchemy Core usa uma linguagem de expressão (SQLAlchemy Expression Language) como forma de interagir com o código Python. Uma forma de enviar comandos SQL literais consiste no uso da função text(), útil no aprendizado e experimentação mas não muito usado na prática em projetos. Para efetivar a conexão usamos o método engine.connect(). No código abaixo o banco meu_banco.db será criado na pasta do projeto, se já não existir. O objeto engine é o elemento básico no relacionamento com o BD, basicamento feito através de sua função connect():

from sqlalchemy import create_engine, text
engine = create_engine("sqlite:///meu_banco.db")

with engine.connect() as conn:
    conn.execute(text("CREATE TABLE IF NOT EXISTS coordenadas (x int, y int)"))
    conn.execute(
        text("INSERT INTO coordenadas (x, y) VALUES (:x, :y)"),
        [{"x": 1, "y": 1}, {"x": 2, "y": 4}, {"x": 3, "y": 9}],
    )
    result = conn.execute(text("SELECT * FROM coordenadas"))
    print(result.all())

    # nenhuma alteração foi feito no banco de dados. Alterações são feitas com
    conn.commit()
    # agora o INSERT foi efetivado no BD

# o print acima exibe    
↳ [(1, 1), (2, 4), (3, 9)]

O objeto result é um iterador que fica esgotado após a operação print(result.all()). Se quisermos utilizar esse resultado posteriormente temos que refazer a consulta ou armazenar os valores. O gerenciador de contexto with garante que a conexão (atribuída à variável conn) é criada e fechada após a operação, o que garante que os recursos usados são liberados. Podemos percorrer result em um loop:

with engine.connect() as conn:
    result = conn.execute(text("SELECT x, y FROM coordenadas"))
    for linha in result:
        print(f"x = {linha.x}  y = {linha.y}")
        # ou  print(f"x = {linha[0]}  y = {linha[1]}")
        
# output
↳ x = 1  y = 1
  x = 2  y = 4
  x = 3  y = 9

Result possui vários métodos de busca e transformações de linhas. Um deles é result.all() visto acima, que retorna uma lista de todos os objetos Row. Ele age como um iterador do Python. Cada linha é um objeto row representado por uma tupla (e agindo como tuplas nomeadas). Para recuperar esses valores podemos usar fazer uma atribuição de tuplas, usar índices ou usar os nomes das tuplas nomeadas.

# feita a consulta
result = conn.execute(text("SELECT x, y FROM coordenadas"))

# qualquer um dos métodos pode ser usado:
# atribuição de tuplas
for x, y in result:
    print(x, y)

# uso de índices
for row in result:
    print(row[0], row[1])
    
# tuplas nomeadas
for row in result:
    print(row.x, row.y)

Também podemos usar as linhas recebidas mapeando o resultado em dicionários com o modificador Result.mappings():

result = conn.execute(text("SELECT x, y FROM coordenadas"))
for dict_row in result.mappings():
    x = dict_row["x"]
    y = dict_row["y"]
    ...

Passando parâmetros

O método Connection.execute() aceita parâmetros que modificam a consulta feita. Por exemplo, para fazer uma consulta SELECT, atendendo a alguns critérios, inserimos o modificador WHERE à instrução.

with engine.connect() as conn:
    query = text("SELECT x, y FROM coordenadas WHERE y > :y")
    result = conn.execute(query, {"y": 2})
    for row in result:
        print(f"x = {row.x}  y = {row.y}")
[SQL]
SELECT x, y FROM coordenadas WHERE y > 2        
# resulta em
↳ x = 2  y = 4
  x = 3  y = 9

O valor do parâmetro em :y é lido no dicionário, resultando em WHERE y > 2. Essa técnica é chamada de “estilo de parâmetro qmark” e deve sempre ser usada para evitar ataques de injeção SQL no aplicativo.

Múltiplos parâmetros podem ser passados. Podemos enviar vários parâmetros para o método Connection.execute() por meio de uma lista de dicionários (no estilo conhecido como executemany). Isso já foi feito na nossa primeira operação de inserção.

# vamos apagar todas as linhas da tabela
with engine.connect() as conn:
    result = conn.execute("DELETE FROM coordenadas")
    conn.commit()

# agora vamos inserir várias linhas de uma vez
with engine.connect() as conn:
    query = text("INSERT INTO coordenadas (x, y) VALUES (:x, :y)")
    values = [{"x": 11, "y": 12}, {"x": 13, "y": 14}, {"x": 15, "y": 16}]
    conn.execute(query, values,)
    conn.commit()
# o BD agora contém a tabela mostrada na figura.


No código acima, values é uma lista de dicionários e a operação de INSERT é feita uma vez para cada item da lista.

Metadata, Table e Column

Nos bancos de dados relacionais os objetos mais básicos são as tabelas que são, por sua vez, constituídas por colunas e linhas, cada uma delas com seu correspondente objeto do Python via SQLAchemy.

Classe MetaData: O SQLAlchemy mantém um objeto chamado MetaData que armazena toda a informação sobre as tabelas usadas, as colunas, vínculos e relacionamentos. A sintaxe de criação de um objeto MetaData é a seguinte:

from sqlalchemy import MetaData
metadata_objeto = MetaData()

É comum que um único objeto MetaData sirva para armazenar todas as tabelas de um aplicativo, geralmente como uma variável de nível de módulo. Pode ocorrer, embora seja menos comum, que existam vários objetos MetaData. Mesmo assim as tabelas continuam podendo se relacionar entre elas.

Table e Column: Objetos Table são inicializados em um objeto MetaData através do construtor de tabelas onde o nome é fornecido. Argumentos adicionais são considerados objetos de coluna. Objetos Column representam cada campo na tabela. A sintaxe de definição de uma tabela é variavel = Table("nome_tabela", metadata, Columns ...).

from sqlalchemy import Table, Column, Integer, Numeric, String

# tabela alunos
alunos = Table("alunos", metadata,
    Column("id", Integer(), primary_key=True), 
    Column("matricula", String(50), nullable=False, unique=True),
    Column("nome", String(50), index=True, nullable=False),
    Column("sobrenome", String(50)),
    Column("idade", Integer()),
    Column("curso", String(50)),
    Column("nota_final", Numeric(2, 2)),
    Column("nascimento", DateTime()), 
    Column("atualizado", DateTime(), default=datetime.now, onupdate=datetime.now)
)

# tabela notas
notas = Table("notas", metadata,
    Column("id", Integer(), primary_key=True), 
    Column("id_aluno", ForeignKey("aluno.id"), nullable=False),
    Column("nota", Numeric(2, 2)),
    Column("data_prova", DateTime())
)

# as chaves primárias podem ser visualizadas
print(alunos.primary_key)
# resulta em:
PrimaryKeyConstraint(Column('id', Integer(), table=, primary_key=True, nullable=False))

# as tabelas são criadas no BD com
engine = create_engine('sqlite:///meu_banco.db')
metadata.create_all(engine)

O campo id é uma chave primária, nome é um índice, usado para agilizar consultas. O construtor de table usa vários construtores de colunas, cada um com seu nome e definição. O campo matricula não pode ser nulo nem repetido (nullable=False, unique=True). O campo atualizado é um campo de datas com default (now), e é atualizado automaticamente toda vez que o registro é alterado. Os parênteses no import servem para quebrar a linha sem a necessidade de uso da barra invertida, \.

Quando uma coluna é definida como ForeignKey dentro da definição da tabela, como foi feito acima, o tipo de dado pode ser omitido pois é automaticamente ajustado de acordo com a coluna a que se refere. No caso acima id_aluno tem o mesmo tipo que aluno.id, que é um inteiro.

Chaves e vínculos: (Keys, Constraints) são formas de forçar algum critério sobre os dados e seus relacionamentos. Chaves primárias (primary keys ou “PK”) são identificadores únicos e nunca nulos usados em relacionamentos. Vimos que escolhemos um campo como chave primária usando primary_key=True. Vários campos podem ser usados em chaves compostas. Nesse caso a chave será usada como uma tupla contendo os vários campos. O vínculo UniqueConstraint (informado com unique=True) é a exigência de que um valor não pode ser duplicado no campo. Além desses temos o CheckConstraint que estabelece que os dados satisfaçam regras definidas pelo programador. Todos esses campos podem ser definidos em linhas próprias, depois das definições das colunas, como mostrado abaixo:

from sqlalchemy import PrimaryKeyConstraint, UniqueConstraint, CheckConstraint

PrimaryKeyConstraint("id", name="aluno_pk")
UniqueConstraint("matricula", name="aluno_matricula")
CheckConstraint("nota_final >= 0.00", name="aluno_nota")

Índices: são usados para agilizar buscas de valores em um campo e devem ser aplicados a campos que servem para buscas em uma tabela. Além da criação com index=True usado na tabela alunos podemos criar o índice explicitamente com

from sqlalchemy import Index
Index("ix_alunos_nome", "alunos_nome")

Mais de uma coluna podem ser usadas como índice.

Relacionamentos, chaves estrangeiras: O próximo passo é o estabelecimento de relacionamentos. Por ex., a tabela notas tem cada registro (linhas da tabela) vinculado à um aluno. Essa associação permite uma relação um-para-muitos, no nosso caso com a possibilidade de registrar várias notas para cada aluno. Isso é feito com a seguinte alteração na tabela notas para incluir uma chave estrangeira (forein key):

from sqlalchemy import ForeignKey
notas = Table("notas", metadata,
    Column("id", Integer(), primary_key=True),
    Column("id_aluno",  ForeignKey("alunos.id")),
    ...
)
# as outras colunas ficam inalteradas
# alternativamente podemos definir a chave em uma linha posterior à definição

from sqlalchemy import ForeignKeyConstraint
ForeignKeyConstraint(["id_aluno"], ["alunos.id"])

Claro que as tabelas podem ter várias chaves estrangeiras. Após todas as definições as alterações podem ser executadas e tornadas permanentes com create_all.

from sqlalchemy import MetaData
metadata_objeto = MetaData()

# ... definições de tabelas

metadata_objeto.create_all(engine)

Por default create_all() não recria tabelas que já existem. Podemos, portanto, executar o comando várias vezes.

Resumindo: O objeto MetaData armazena uma coleção de objetos Table que, por sua vez, armazena objetos Column e Constraint. Essa estrutura de objetos é a base da maioria das operações do SQLAlchemy, tanto Core quanto ORM.

Executando o código: Juntando as partes, colocamos todos os comando em um arquivo sqlal.py e o executamos com python sqlal.py.

from datetime import datetime
from sqlalchemy import (MetaData, Table, Column, Integer, Numeric, String,
                        DateTime, ForeignKey, create_engine)
metadata = MetaData()

# tabela alunos
alunos = Table("alunos", metadata,
    Column("id", Integer(), primary_key=True), 
    Column("matricula", String(50), nullable=False, unique=True),
    Column("nome", String(50), index=True, nullable=False),
    Column("sobrenome", String(50)),
    Column("idade", Integer()),
    Column("curso", String(50)),
    Column("nota_final", Numeric(2, 2)),
    Column("nascimento", DateTime()), 
    Column("atualizado", DateTime(), default=datetime.now, onupdate=datetime.now)
)

# tabela notas
notas = Table("notas", metadata,
    Column("id", Integer(), primary_key=True),
    Column("id_aluno",  ForeignKey("alunos.id")),
    Column("nota", Numeric(2, 2)),
    Column("data_prova", DateTime())
)

engine = create_engine("sqlite:///meu_banco.db", echo=True)
metadata.create_all(engine)

Podemos notar que a construção de um objeto Table tem semelhança com o processo de declarar um comando SQL CREATE TABLE. Foram usados os objetos: Table que representa uma tabela no banco de dados e fica armazenado em uma coleção MetaData; Column que representa uma coluna de uma tabela. A declaração de colunas incluem seu nome, e o tipo de objeto. A coleção de objetos coluna pode ser acessada por meio de um array associativo em Table.c.

alunos.c.nome
↳ Column('nome', String(length=50), table=)

alunos.c.keys()
↳ ['id', 'id_aluno', 'nome', 'data_prova']

Após a execução desse código temos as tabelas ilustradas na figura abaixo, inclusive o relacionamento de notas.id_alunos como foreign key ligado ao campo alunos.id.

Inserção de dados

Após a definição das tabelas, colunas e relacionamentos podemos inserir dados.

# inserção de dados
query = alunos.insert().values(
    matricula = "943.232-90",
    nome = "Arduino",
    sobrenome = "Bolivar",
    idade = "17",
    curso = "Eletrônica",
    nota_final = 17.20,
    nascimento = ""
)

print(str(query))

# o seguinte output é obtido:

↳ INSERT INTO alunos
      (matricula, nome, sobrenome, idade, curso, nota_final, nascimento, atualizado)
  VALUES
      (:matricula, :nome, :sobrenome, :idade, :curso, :nota_final, :nascimento, :atualizado)

print(query.compile().params)    
# o seguinte output é obtido:
↳     {"matricula": "943.232-90",
       "nome": "Arduino",
       "sobrenome": "Bolivar",
       "idade": "17",
       "curso": "Eletrônica",
       "nota_final": 17.2,
       "nascimento": "21/01/2023",
       "atualizado": None}    

Note que :nome_campo é a forma usado pelo SQLAlchemy para a representação de string dos valores dos campos em str(query). Internamente os dados são tratados por questões de segurança, como um ataque de injeção SQL. Os valores a serem inseridos podem ser visualizados com query.compile().params. Note que, nas consultas de inserção, não fornecemos valores para os campos de inserção automática, id e atualizado.

De modo similar podemos usar os demais métodos como update(), delete() e select() para gerar consultas UPDATE, DELETE e SELECT respectivamente. Finalmente podemos garantir a persistência dos dados gravando no BD esses valores.

resultado = connection.execute(query)
print(resultado.inserted_primary_key)
↳ (1,0)

O útimo comando imprime o id da linha gravada.

Reflexão de tabelas


Além das consultas de criação de tabelas precisamos usar bancos de dados com tabelas já criadas, com suas colunas e relacionamentos estabelecidos. O SQLAlchemy consegue isso com as chamadas reflexões de tabelas (table reflections), o processo de gerar objetos Table (o seus componentes) lendo o estado de um banco de dados já construído.

Veremos uma breve apresentação dessa operação, para ser mais explorada em outra seção. Como exemplo desse processo vamos usar a tabela alunos definida anteriormente. A forma mais básica de se fazer isso é construindo um objeto Table fornecendo o nome da tabela e o objeto Metadata que a contém.

from sqlalchemy import (MetaData, Table, Column, Integer, Numeric, String,
                        DateTime, ForeignKey, create_engine)
metadata = MetaData()
engine = create_engine('sqlite:///meu_banco.db')

tbl_alunos = Table("alunos", metadata, autoload_with=engine)

print(tbl_alunos.c.keys())
↳ ['id', 'matricula', 'nome', 'sobrenome', 'idade', 'curso', 'nota_final', 'nascimento', 'atualizado']

Também podemos importar para as nossas classes mais de uma tabela de cada vez.

engine = create_engine('sqlite:///meu_banco.db')
metadata = MetaData()
metadata.reflect(bind=engine)
tbl_alunos = metadata.tables["alunos"]
tbl_notas = metadata.tables["notas"]

print("Alunos:", tbl_alunos.c.keys())
print("Notas:", tbl_notas.c.keys())

↳ Alunos: ['id', 'matricula', 'nome', 'sobrenome', 'idade', 'curso', 'nota_final', 'nascimento', 'atualizado']
↳ Notas: ['id', 'id_aluno', 'nota', 'data_prova']

Uma vez importada a tabela podemos extrair dela todas os dados, bem como realizar as alterações usuais de inserção e apagamento.

Tabelas e tipos de dados

O SQLAlchemy define diversos tipos de dados destinados a abstrair os tipos usados nos bancos SQL. Um exemplo disso é tipo genérico booleano que geralmente usa o tipo SQL BOOLEANO (True ou False no Python). No entanto ele possui também o SMALLINT para BDs que não suportam BOOLEANOs. Essa adaptação é automática e o desenvolvedor só tem que lidar com os campos bolleanos do Python: (True / False). A tabela mostra tipos genéricos e suas associações.

SQLAlchemy Python SQL
BigInteger int BIGINT
Boolean bool BOOLEAN ou SMALLINT
Date datetime.date DATE (SQLite: STRING)
DateTime datetime.datetime DATETIME (SQLite: STRING)
Enum str ENUM ou VARCHAR
Float float ou Decimal FLOAT ou REAL
Integer int INTEGER
Interval datetime.timedelta INTERVAL ou DATE from epoch
LargeBinary byte BLOB ou BYTEA
Numeric decimal.Decimal NUMERIC ou DECIMAL
Unicode unicode UNICODE ou VARCHAR
Text str CLOB ou TEXT
Time datetime.time DATETIME

Bibiografia

Michael Bayer: SQLAlchemy. In Amy Brown and Greg Wilson, editors, The Architecture of Open Source Applications Volume II: Structure, Scale, and a Few More Fearless Hacks 2012 aosabook.org

Um projeto SQLite


SQLite

SQLite é um mecanismo de banco de dados relacional de código aberto, escrito em C por D. Richard Hipp, em 2000. Ele não necessita da instalação de um servidor para o seu funcionamento, como ocorre com a maioria dos demais bancos de dados, acessando diretamente um arquivo em disco. O estado completo de uma base de dados fica armazenado nesse arquivo, geralmente com extensão arquivo.db, .sqlite ou .db3. que pode ser utilizado diretamente em diversas plataformas. Ele não é um aplicativo independente e sim uma biblioteca que é incorporada junto com os aplicativos, sendo o mecanismo de banco de dados mais usado, servindo para armazenamento e manipulação de dados em navegadores da Web, sistemas operacionais, telefones celulares e outros sistemas integrados.

O SQLite usa uma sintaxe de SQL parecida com a do PostgreSQL, mas não impõe a verificação de tipo, tornando possível, por exemplo, se inserir uma string em uma coluna definida como inteiro. Como em outras versões de bancos de dados relacionais, o SQLite armazena dados em tabelas que podem conter campos de vários tipos de dados, como texto ou números inteiros. Essas tabelas podem ser acessadas (construídas, modificadas e consultadas) por meio de consultas SQL que realizam operações “CRUD” (criar, ler, atualizar, excluir). Um banco de dados SQLite pode ser criptografado usando o SQLite Encryption Extension (SEE) ou outra tecnologia, de forma a proteger dados de extrações não autorizadas.

Diversos Sistemas Gerenciadores de Bancos de Dados, SGBD, (em inglês RDBMS, Relational Database Management System) estão disponíveis para o SQLite, entre eles o SQLiteStudio, DB Browser, DBeaver. Também existem diversos plugins para navegadores e IDEs (como o VSCode).

SQLite no VSCode

O VSCode possui um plugin muito interessante para auxiliar no desenvolvimento com SQLite. Para usá-lo instale o plugin (extension) vscode-sqlite. Os seguintes recursos ficam disponíveis:

  • consulta a bancos de dados SQLite, visualização de tabelas, exportações de resultados para json, csv e html.
  • uso da barra lateral (explorer) para listar bancos de dados, tabelas, colunas e views.
  • preenchimento automático de palavras-chave, nomes de tabelas e colunas. Para isso deve-se vincular um banco de dados a um documento SQL, usando o comando: USE DATABASE.
  • emissão de mensagens de erros amigáveis apontando o local do erro.

Existem comandos para criar novos arquivos sqlite, novas consultas, editar e executar scripts de consulta, visualização rápida de BD, fechar e removar um BD, atualizar, exibir resultados de consultas.

Para iniciar o uso crie um documento, por exemplo meuBD.db. Clique nele (com botão direito) e gere um nova consulta. O arquivo é transformado automaticamente em um banco do SQLite. Também clicando com botão direito selecione “Open Database” para visualizar as tabelas e ter acesso a vários outros comandos. Consultas digitadas no editor podem ser executadas com as opções “Run Query” ou “Run Selected Query”.

Comandos SQLite

Existem diversas maneiras de inserir comandos SQL em um banco SQLite. Em um gerenciador de bancos de dados podemos abrir uma área de inserção de consultas e digitar, interativamente esses comandos. Alternativamente, se temos uma sequência de consultas válidas gravadas no arquivo consultas.sqljunto podemos executar no prompt de comando:

cat consultas.sql | sqlite3 test1.db
# ou
sqlite3 test2.db ".read consultas.sql"
# ou
sqlite3 test3.db < definicoes.sql

# no windows
sqlite3.exe test4.db ".read definicoes.sql"

Esses comandos produzem respectivamente os arquivos test_n.db após as execuções das consultas em definicoes.sql.

Projeto: vendas

Como uma ilustração de uso do SQLite vamos construir uma banco de dados para controle de uma loja, com tabelas para seus clientes, funcionários, fornecedores e itens vendidos. Todas as consultas SQL podem estar em um único arquivo .sql, mas nós as listaremos por partes para comentar os seus efeitos.

Consultas de definição das tabelas

O comando CREATE TABLE serve para inserir uma nova tabela no banco de dados. IF NOT EXISTS instrui a consulta a ser executada somente se a tabela ainda não foi criada. As definições de campo seguem dentro dos parênteses. O campo id INTEGER PRIMARY KEY AUTOINCREMENT é um índice inteiro, usado como chave primária e de autoincremento. Campos definidos como NOT NULL não podem ser deixados nulos.

Primeiro criamos uma tabela no prompt de comando:

>> sqlite3 vendas.db

Em seguida criamos as tabelas. Para usar esse arquivo podemos usar os comandos listados acima ou abrir um gerenciador de bancos de dados e inserir as consultas nele. O uso do VSCode ou diretamente no código com o sqlite3 do python são exemplos desse uso.

-- criando a tabela clientes
CREATE TABLE IF NOT EXISTS clientes (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   nome VARCHAR(100) NOT NULL,
   sobrenome TEXT NOT NULL,
   email VARCHAR(100) NOT NULL,
   cidade TEXT NOT NULL,
   estado VARCHAR(2) NOT NULL,
   cep INTEGER NOT NULL
);

-- criando a tabela de funcionarios
CREATE TABLE IF NOT EXISTS funcionarios (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   nome VARCHAR(100) NOT NULL,
   sobrenome TEXT NOT NULL,
   email TEXT NOT NULL,
   senha VARCHAR(20) NOT NULL,
   cargo VARCHAR(133),
   endereco TEXT NOT NULL,
   cidade VARCHAR(150) NOT NULL,
   estado VARCHAR(2) NOT NULL,
   cep INTEGER NOT NULL
);

-- Criando a tabela fornecedores
CREATE TABLE IF NOT EXISTS fornecedores (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   nome VARCHAR(100) NOT NULL,
   cnpj VARCHAR(100) NOT NULL,
   email VARCHAR(100) NOT NULL,
   endereco TEXT NOT NULL,
   cidade VARCHAR(150) NOT NULL,
   estado VARCHAR(2) NOT NULL,
   cep INTEGER NOT NULL
);

Após a execução desses consultas temos as tabelas clientes, funcionarios e fornecedores. Digamos que queremos alterar uma tabela para acrescentar, renomear ou excluir algum campo. ALTER TABLE é a consulta usada.

-- para alterar uma tabela (alterando clientes)
ALTER TABLE clientes ADD data_aniversario DATE;
ALTER TABLE clientes ADD endereco TEXT NOT NULL;

-- para renomear um campo
ALTER TABLE clientes RENAME data_aniversario TO aniversario;
-- para excluir um campo
ALTER TABLE clientes DROP COLUMN aniversario;

Primeiro inserimos data_aniversario e endereco, depois renomeamos data_aniversario para aniversario, depois excluímos o campo aniversario. Da mesma forma tabelas inteiras podem ser excluídas com DROP TABLE.

-- criar uma tabela para ser apagada depois
CREATE TABLE IF NOT EXISTS usuarios (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   nome VARCHAR(100) NOT NULL,
   codigo INTEGER NOT NULL
);
-- excluir a tabela recém criada
DROP TABLE usuarios;

Operações de apagamento devem ser executadas com cuidado pois os dados não podem ser recuperados!

Observe que no modelo adotado acima os cargos dos funcionários são inseridos como textos que devem ser repetidos para diversos funcionários com o mesmo cargo. Outra possibilidade consiste em ter uma tabela com os cargos em separado, referenciados na tabela funcionarios por meio de um id. Vamos fazer essas alterações.

-- criar a tabela cargos   
CREATE TABLE IF NOT EXISTS cargos (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   descricao VARCHAR(200) NOT NULL
);   
-- exclui a coluna cargo de funcionarios   
ALTER TABLE funcionarios DROP COLUMN cargo;
-- insere cargo_id 
ALTER TABLE funcionarios ADD COLUMN cargo_id INTEGER REFERENCES cargos(id);

O último comando falaria se não existisse a tabela referenciada cargos. O campo cargo_id é uma chave estrangeira (foreign key), ligado à tabela cargos, pelo seu campo id. Vamos aplicar o mesmo conceito na criação de chaves estrangeiras para produtos vendidos e vendas.

-- tabela produtos
CREATE TABLE IF NOT EXISTS produtos (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   fornecedor_id INTEGER,
   descricao VARCHAR(100),
   preco DECIMAL(10,2),

   FOREIGN KEY (fornecedor_id) REFERENCES fornecedores (id)
);

-- tabela vendas
CREATE TABLE IF NOT EXISTS vendas (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   cliente_id INTEGER NOT NULL,
   funcionario_id INTEGER NOT NULL,
   data_venda DATETIME NOT NULL,
   total DECIMAL (10,2) NOT NULL,
   descricao TEXT,

   FOREIGN KEY (cliente_id) REFERENCES clientes (id),
   FOREIGN KEY (funcionario_id) REFERENCES funcionarios (id)
);

-- tabela itens_vendas
CREATE TABLE IF NOT EXISTS itens_vendas (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   venda_id INTEGER NOT NULL,
   produto_id INTEGER NOT NULL,
   quantidade INTEGER NOT NULL,
   subtotal DECIMAL (10,2) NOT NULL,

   FOREIGN KEY (venda_id) REFERENCES vendas (id),
   FOREIGN KEY (produto_id) REFERENCES produto (id)
);

Os relacionamentos entre as tabelas expressam o fato de que um funcionário pode fazer várias vendas, cada venda pode conter vários ítens de produtos, um único fornecedor pode ser responsável por vários produtos e mais de um funcionário podem ter o mesmo cargo.

Vemos que as, após todos esses passos, as tabelas possuem os seguintes relacionamentos:

Consultas de inserção e leitura de dados

Uma vez definidas as tabelas passamos a inserir dados. Faremos também consultas para verificar os dados inseridos. Dados são inseridos com INSERT INTO. O nome da tabela e os campos são fornecidos e os valores a inserir. SELECT * significa “selecione todos os campos”.

INSERT INTO clientes (nome, sobrenome, email, cidade, estado, cep, endereco)
VALUES
('Caio', 'Zuretta', 'cz@hotmail.com', 'Seattle', 'WA', 123456789,'23rd Street AWE');
SELECT * FROM clientes;

id  nome  sobrenome   email            cidade   estado cep         endereco
1   Caio  Zuretta     cz@hotmail.com   Seattle  WA     123456789   23rd Street AWE

-- inserindo outro cliente
INSERT INTO clientes (nome, sobrenome, email, endereco, cidade, estado, cep )
    VALUES ('Polka', 'Brita', 'pbrita@gmail.com', 'Av. Contorno 432', 'Belo Horizonte', 'MG', 30876786);

SELECT id, nome, sobrenome FROM clientes;
id  nome   sobrenome
1   Caio   Zuretta
2   Polka  Brita

-- várias linhas podem ser inseridas na mesma consulta
INSERT INTO clientes (nome, sobrenome, email, endereco, cidade, estado, cep)
    VALUES
 ('Antonio', 'Tony', 'tmatador@gigamail.com.br', 'R. Pedro II, 34', 'Rio de Janeiro', 'RJ', 21654897),
 ('Martha', 'Maertis', 'marthis@onlymail.com', 'R. Joinha, 654', 'Goiania', 'GO', 41546546),
 ('Orlando', 'Orlandis', 'orlas@gmail.com', 'Av, Só que Não, 34', 'Itabira', 'MG', 35654654),
 ('Mirtes', 'Mello', 'mellom@gmail.com', 'SQL 123', 'Brasília', 'DF', 145428214);

-- uma operação sobre todos as linhas de um campo UPDATE clientes
SET email = UPPER(email);
SELECT email FROM clientes WHERE nome="Mirtes";
email
MELLOM@GMAIL.COM

-- vamos retornar as minúsculas no email
UPDATE clientes SET email = LOWER(email);
SELECT email FROM clientes WHERE nome="Mirtes";
email
mellom@gmail.com

-- inserindo dados na tabela funcionarios
INSERT INTO funcionarios (nome, sobrenome, email, senha, cargo_id, endereco, cidade, estado, cep)
VALUES
('Pedro', 'Altusser', 'pedroalt@email.com', '123456', 1, 'R. João Paulo, 534', 'Rio de Janeiro', 'RJ', 21654897),
('Levindo', 'Lopes', 'lopest@gmail.com', '234456', 1, 'R. Paulo II, 534', 'Ardósias', 'RJ', 21115114),
('Silvana', 'Gomes', 'silvana@gmail.com', '344456', 2, 'R. Paulo I, 4', 'Ardósias', 'RJ', 21651145),
('Lucas', 'Sêtte', 'lucas@gmail.com', '3er456', 3, 'R. Bahia, 1355', 'Belo Horizonte', 'MG', 31454232);

SELECT nome, email, cidade, estado, cep, cargo_id FROM funcionarios WHERE estado = "MG";
nome    email            cidade            estado   cep          cargo_id
Lucas   lucas@gmail.com  Belo Horizonte    MG       31454232     3

-- inserindo cargos
INSERT INTO cargos (descricao) VALUES ('Gerente'), ('Vendedor'), ('Desenvolvedor');

SELECT * FROM cargos;
id   descricao
1    Gerente
2    Vendedor
3    Desenvolvedor

-- alterar o valor de um campo já inserido
UPDATE funcionarios SET cargo_id=1 WHERE id=1;

-- agora a tabela funcionarios está no estado
SELECT * FROM funcionarios;
id nome    sobrenome  email               senha   endereco            cidade         estado cep       cargo_id
1  Pedro   Altusser   pedroalt@email.com  123456  R. João Paulo, 534  Rio de Janeiro RJ     21654897  3
2  Levindo Lopes      lopest@gmail.com    234456  R. Paulo II, 534    Ardósias       RJ     21115114  1
3  Silvana Gomes      silvana@gmail.com   344456  R. Paulo I, 4       Ardósias       RJ     21651145  2
4  Lucas   Sêtte      lucas@gmail.com     3er456  R. Bahia, 1355      Belo Horizonte MG     31454232  3


Uma vez preenchidas as tabelas podemos fazer consultas de todos os tipos. Para ler dados da tabela funcionarios com a descrição dos cargos em cargos usamos INNER JOIN. As duas consultas abaixo são equivalentes:

-- INNER JOIN
SELECT f.nome, c.descricao FROM funcionarios f
  INNER JOIN cargos c WHERE f.cargo_id = c.id;

SELECT f.nome, c.descricao FROM funcionarios f
  INNER JOIN cargos c ON (f.cargo_id = c.id);

nome    descricao
Pedro    Desenvolvedor
Levindo    Gerente
Silvana    Vendedor
Lucas    Desenvolvedor

-- aliases podem ser dados para qualquer campo. Strings são concatenados com ||
SELECT nome || " " || sobrenome  as 'Funcionário' FROM funcionarios;
Funcionário
Pedro Altusser
Levindo Lopes
Silvana Gomes
Lucas Sêtte

Aliases foram usados acima para atribuir nomes às tabelas (como em FROM funcionarios f INNER JOIN cargos c) ou a compos resultados da consultas (como em nome || " " || sobrenome as 'Funcionário').

Consultas podem ser modificadas pelas condições em WHERE, e partes dos campos podem ser encontrados com LIKE. % representa qualquer grupo de caracteres, _ (underline) significa um caracter.

-- Uma consulta simples com dupla condição
SELECT id, nome || " " || sobrenome  as 'Funcionário', estado
    FROM clientes  WHERE estado = "MG" and id=2;
id   Funcionário   estado
2   Polka Brita   MG

-- para apagar um ou mais registros (o registro listado acima)
DELETE FROM clientes  WHERE estado = "MG" and id=2;

-- nome iniciado com "Ma" e a letra "a" no sobrenome
SELECT id, nome, sobrenome FROM clientes WHERE nome LIKE 'Ma%' AND sobrenome LIKE '%a%';
id   nome   sobrenome
4   Martha   Maertis

SELECT id, nome, sobrenome FROM clientes WHERE nome LIKE '_a%';
id   nome    sobrenome
1   Caio    Zuretta
4   Martha    Maertis

SELECT id, nome, sobrenome FROM clientes WHERE nome LIKE '_a___a';
id   nome    sobrenome
4   Martha    Maertis

Para fazer outras consultas cruzadas, em mais de uma tabela, vamos entrar dados nas tabelas fornecedores e produtos.

-- inserindo fornecedores e produtos
INSERT INTO fornecedores (nome, cnpj, email, endereco, cidade, estado, cep)
    VALUES
    ('Microsoft', '234.456-098', 'ms@ms.com', 'R. Pedro Alves, 34', 'Sorocaba', 'SP', 1234567),
    ('Apple', '212.1226-128', 'apps@apps.com', 'R. Gerino Silva, 456', 'Brasília', 'DF', 61256767), 
    ('Lenovo', '2456.1567-676', 'lenovo@lenovo.com', 'R. Power Guido, 786', 'Manaus', 'AM', 23452345),
    ('Dell', '222.453-444', 'del@del.com', 'R. Vaga Errante, 13', 'Sorocaba', 'SP', 1234567),
    ('Logitec', '666.7777-888', 'logi@log.com', 'R. Ulva Gods Silva, 90', 'Brasília', 'DF', 61256767),
    ('Multilaser', '1111.9999-888', 'miltila@multi.com', 'R. Volvo Zona, 76', 'Itabira', 'MG', 3114045);
    
INSERT INTO produtos (fornecedor_id, descricao, preco)
    VALUES
    (2, 'iPAD', 12345.80),
    (1, 'Windows 11', 67.90),
    (5, 'Teclado sem fio', 99.00),
    (3, 'Notebook Intel', 1560.00),
    (15, 'Mouse Chines', 13.33),
    (16, 'Chingling Roteador', 59.89);

SELECT * FROM fornecedores;
SELECT * FROM produtos;
-- as tabelas resultado estão nas imagens abaixo


Com essas definições de valores vamos fazer algumas consultas para exibir a sintaxe de consultas JOIN. JOIN e INNER JOIN são idênticos.

SELECT p.id, f.nome, p.descricao, p.preco FROM produtos p
    JOIN fornecedores f ON  (p.fornecedor_id = f.id);
id  nome         descricao         preco
1   Apple        iPAD              12345.8
2   Microsoft    Windows 11        67.9
3   Logitec      Teclado sem fio   99
4   Lenovo       Notebook Intel    1560


SELECT p.id 'COD.', f.nome 'Vendedor', p.descricao 'Produto' , p.preco 'Preço'
    FROM produtos p RIGHT JOIN fornecedores f ON  (p.fornecedor_id = f.id);
COD.    Vendedor      Produto           Preço
1       Apple         iPAD              12345.8
2       Microsoft     Windows 11        67.9
3       Logitec       Teclado sem fio   99
4       Lenovo        Notebook Intel    1560
NULL    Dell          NULL              NULL
NULL    Multilaser    NULL              NULL

Suponha que desejamos fazer um relatório com todas as vendas para o cliente de sobrenome “Arbinger”. Podemos primeiro encontrar o id do cliente e, daí, todas as vendas para ele. Fazemos um JOIN para encontrar o nome dos funcionários que fizeram as vendas.

-- o id do cliente é
SELECT id FROM clientes WHERE  sobrenome="Arbinger";
3

-- as vendas desse cliente
SELECT * FROM vendas
    WHERE  cliente_id = (SELECT id FROM clientes WHERE  sobrenome="Arbinger");
    
--  as vendas associadas ao funcionário vendedor
SELECT v.data_venda, v.total, v.descricao, f.nome, f.sobrenome FROM vendas v
    JOIN funcionarios f 
    WHERE  f.id = v.funcionario_id 
    and v.cliente_id = (SELECT id FROM clientes WHERE  sobrenome="Arbinger");

data_venda   total   descricao   nome   sobrenome
10/12/2020   200   Filial 1   Levindo   Lopes
11/12/2020   100   Filial 1   Levindo   Lopes
13/12/2020   280   Filial 2   Pedro   Altusser
17/12/2020   290   Filial 4   Levindo   Lopes

Se, além disso quisermos saber quais os itens foram vendidos fazemos:

SELECT v.data_venda, v.total, v.descricao, f.nome, f.sobrenome, p.descricao as "Produto"
    FROM vendas v
    JOIN funcionarios f
    JOIN itens_vendas i
    JOIN produtos p 
    WHERE  f.id = v.funcionario_id and v.id = i.venda_id and i.produto_id = p.id
    and v.cliente_id = (SELECT id FROM clientes WHERE  sobrenome="Arbinger");

data_venda   total   descricao   nome       sobrenome    Produto
10/12/2020   200     Filial 1    Levindo    Lopes        Windows 11
10/12/2020   200     Filial 1    Levindo    Lopes        Teclado sem fio
11/12/2020   100     Filial 1    Levindo    Lopes        iPAD

Observe que dois itens foram listados relativos à venda do dia 10/12/2020 e um item para o dia 11/12. Nenhum item foi listado para as vendas dos dias 13 e 17/12.

sqlite3 e Python

O python traz um módulo instalado com a biblioteca padrão para integrar o SQLite com o código. Para usá-lo basta importar esse módulo chamado sqlite3.

import sqlite3
connection = sqlite3.connect("escola.db")
cursor = connection.cursor()

sql = """
      CREATE TABLE IF NOT EXISTS  alunos (
        id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
        nome TEXT,
        idade INTEGER
        );
      """
cursor.execute(sql)

sql = """
    INSERT INTO alunos (nome, idade) VALUES
    ('Alice', 21),
    ('Letícia', 22),
    ('Vinicius', 23),
    ('Guilherme', 12),
    ('Marcos', 37),
    ('Pedro', 6),
    ('Wanda', 20),
    ('Aluísio', 10);
    """
cursor.execute(sql)
connection.commit()

dados = cursor.execute("SELECT * FROM alunos WHERE idade <=' 22")
for linha in dados:
    print(linha[0], linha[1], linha[2])

1 Alice 21
4 Guilherme 12
6 Pedro 6
7 Wanda 20
8 Aluísio 10

Com o comando connection = sqlite3.connect("escola.db") o banco de dados é criado, se já não existe. Um cursor é um objeto usado para fazer a relação entre o código (e as queries) com o banco. O objeto dados é um iterável que retorna tuplas com os resultados da query.

Digamos que queremos fazer uma consulta ao banco de dados pre-existente phylos.db, usado nos exercícios anteriores. Para isso estabelecemos uma nova conecção com esse banco de dados.

connection = sqlite3.connect("vendas.db")
sql = """
   SELECT v.data_venda, v.total, v.descricao, f.nome, f.sobrenome, p.descricao as "Produto"
      FROM vendas v
      JOIN funcionarios f
      JOIN itens_vendas i
      JOIN produtos p 
      WHERE  f.id = v.funcionario_id and v.id = i.venda_id and i.produto_id = p.id
      and v.cliente_id = (SELECT id FROM clientes WHERE  sobrenome="Arbinger");
"""
cursor = connection.cursor()
data = cursor.execute(sql)
for row in data:
    print(row)
    
# o resultado é:
('10/12/2020', 200, 'Filial 1', 'Levindo', 'Lopes', 'Windows 11')
('10/12/2020', 200, 'Filial 1', 'Levindo', 'Lopes', 'Teclado sem fio')
('11/12/2020', 100, 'Filial 1', 'Levindo', 'Lopes', 'iPAD')

sqlite3 e pandas

Veja o artigo Pandas e Dataframes e artigos subsequentes.

Uma outra possibilidade interessante é a de integrar os dataframes do pandas com o SQLite. Para isso importamos o sqlite3 e o pandas e estabelecemos uma conexão com o banco de dados. Um dataframe pode ser carregado diretamente com o resultado da consulta com pandas.read_sql(sql, connection).

import sqlite3
import pandas as pd

connection = sqlite3.connect("vendas.db")
sql = """
   SELECT v.data_venda, v.total, v.descricao, f.nome, f.sobrenome
      FROM vendas v JOIN funcionarios f
      WHERE f.id = v.funcionario_id;
"""
cursor = connection.cursor()
# apenas para ver o resultado da consulta fazemos:
data = cursor.execute(sql)
for row in data:
    print(row)

# o resultado:
('10/12/2020', 200, 'Filial 1', 'Levindo', 'Lopes')
('11/12/2020', 100, 'Filial 1', 'Levindo', 'Lopes')
('12/12/2020', 120, 'Filial 2', 'Levindo', 'Lopes')
('13/12/2020', 280, 'Filial 2', 'Pedro', 'Altusser')
('17/12/2020', 290, 'Filial 4', 'Levindo', 'Lopes')

df = pd.read_sql(sql, connection)
print(df)

# o resultado:
   data_venda  total descricao     nome sobrenome
0  10/12/2020    200  Filial 1  Levindo     Lopes
1  11/12/2020    100  Filial 1  Levindo     Lopes
2  12/12/2020    120  Filial 2  Levindo     Lopes
3  13/12/2020    280  Filial 2    Pedro  Altusser
4  17/12/2020    290  Filial 4  Levindo     Lopes

df.to_sql("tabela_2", connection)

O último comando insere uma tabela tabela_2 no banco de dados vendas.db com os campos e valores desse dataframe.

Bibliografia

Sobre SQLite

Sobre o SQLite3, no Python

Instalações diversas do Python, pip e venv


Onde o Python está instalado

Muitas vezes precisamos saber em que diretório está nossa instalação do python. Representaremos por $ o prompt do terminal do sistema, e comentários por #. O sinal [...] indica linhas omitidas.

# Primeiro verificamos se o python está instalado
$ python --version
  Python 3.9.13
# Essa versão pode ser inicializada
$ python
  Python 3.9.13 (main, Aug 25 2022, 23:26:10) 
  [GCC 11.2.0] :: Anaconda, Inc. on linux
  Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
  ['', '/home/usuario/.anaconda3/lib/python39.zip', '/home/usuario/.anaconda3/lib/python3.9', '/home/usuario/.anaconda3/lib/python3.9/lib-dynload', '/home/usuario/.anaconda3/lib/python3.9/site-packages']
>>> exit()

O terminal interativo do python pode ser abandonado com exit(), quit() ou CTRL-D.

Não há problema em ter mais de uma versão instalada. Para ter um controle de qual versão estamos usando, e quais os módulos foram instalados para um projeto, devemos usar um ambiente virtual, como o venv.

Vemos que o Python 3.9.13, instalado com o Anaconda (usado para disparar o Jupyter Notebook) teve prioridade na chamada. Aproveitamos para importar o módulo sys e verificar os caminhos válidos para essa instalação.

No entanto, tenho outra versão, mais recente, do Python instalada. Versões diferentes podem ser usadas para abrir um terminal de comando de linha, como acima, ou para carregar um ambiente virtual, por exemplo. Temos, portanto, que encontrar quais são as versões instaladas.

Existem algumas maneiras de descobrir onde o Python está instalado, tipicamente com os comandos which, whereis e find.

which exibe o diretório de instalação do executável Python que está no PATH,
whereis exibe o diretórios de instalação do código fonte, binários e páginas man,
find procura no sistema onde estão os arquivos *.py.

Por exemplo:

$ which python
  /home/guilherme/.anaconda3/bin/python
$ whereis python
  python: /usr/bin/python /home/guilherme/.anaconda3/bin/python /usr/share/man/man1/python.1.gz
$ find ~/Projetos -name *.py
# uma lista de arquivos com extensão py é exibida.

No último comando usamos find ~/Projetos -name *.py para encontrar arquivos com extensão .py partindo do diretório ~/Projetos, lembrando que ~ significa a pasta do usuário, /home/usuario. O comando find é muito mais geral que o exemplo mostrado e pode ser usado de muitas formas diferentes. Nenhum desses comandos são específicos para o python.

O comando whereis python mostra que tenho outra versão instalada, além do python da Anaconda, em /usr/bin/python . Ela pode ser executada diretamente do terminal:

$ /usr/bin/python
  Python 3.11.0 (main, Oct 24 2022, 00:00:00) [GCC 12.2.1 20220819 (Red Hat 12.2.1-2)] on linux
  Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
  ['', '/usr/lib64/python311.zip', '/usr/lib64/python3.11', '/usr/lib64/python3.11/lib-dynload', '/usr/lib64/python3.11/site-packages', '/usr/lib/python3.11/site-packages']

Vimos assim que há uma versão mais recente do python instalada, o python3.11. Como uma ilustração do uso de versões diferentes do python vamos estender a discussão para a instalação e uso de módulos instalados.

Módulos em diferentes versões

Vimos acima que, no caso de meu computador o comando python está associado ao python3.9.3 da anaconda (porque é o caminho que ele encontra primeiro no PATH (exibido abaixo). Essa instalação já contém o pip. No entanto não há pip instalado para a versão do python3.11.

# exeminando o PATH
$ echo $PATH
  /home/usuario/.anaconda3/share/rubygems/bin:/home/usuario/.anaconda3/share/rubygems/bin:
  /home/usuario/.anaconda3/bin:/home/usuario/.anaconda3/condabin:/home/usuario/.local/bin:
  /home/usuario:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin

# pip está instalado no python3.9
$ python -m pip --version
pip 22.2.2 from /home/guilherme/.anaconda3/lib/python3.9/site-packages/pip (python 3.9)

# pip não está instalado no python3.11
$ python3.11 -m pip --version
/usr/bin/python3.11: No module named pip

O pip não está instalado para o python3.11. Para essa instalação vamos usar o ensurepip, descrito no artigo sobre ambientes virtuais.

$ python3.11 -m ensurepip
  [...]
  Successfully installed pip-22.2.2

# para verificar a instalação
$ python3.11 -m pip --version
  pip 22.2.2 from /home/usuario/.local/lib/python3.11/site-packages/pip (python 3.11)

# caso um upgrade do pip seja necessário
$ python3.11 -m ensurepip --upgrade

Vemos assim que essa instalação do pip está em diretório diferente da instalação do anaconda. Para instalar módulos fazemos:

# para instalar módulos no ambiente do python3.9
$ python -m pip install <modulo>
# ou    
$ pip install <modulo>
 
# para instalar módulos no ambiente do python3.11
$ python3.11 -m pip install <modulo>


Observe que essa diferenciação entre os nomes python e python3.11 ocorre no meu computador e pode ser diferente dependendo da forma de instalação usada. Um computador pode ter várias outras versões do python instaladas, como o python2. Da mesma forma a criação de um ambiente virtual depende da versão do python que se quer usar.

Por default o Python é instalado no Linux no diretório /usr/local/bin/ e no Windows em C:\Python39 ou C:\Users\YourUser\AppData\Local\Programs\Python\PythonX onde X é o número de versão. O Anaconda é instalado em diretório definido pelo usuário.

Usando o pip

Para instalar um módulo no python3.11:

$ python3.11 -m pip install django

Para verificar quais os módulos instalados usamos:

$ python3.11 -m pip freeze
  [..]
  beautifulsoup4==4.11.0
  Django==4.1.3
  Markdown==3.4.1
  Pillow==9.2.0
  [..]

Os módulos instalados junto com o Anaconda são outros:

$ python -m pip freeze
  [..]
  anaconda-client==1.11.0
  anaconda-navigator==2.3.2
  [..]

Em ambos os casos várias linhas foram omitidas.
Também podemos ver os módulos instalados de dentro de uma sessão do python.

$ python3.11
  Python 3.11.0 (main, Oct 24 2022, 00:00:00) [GCC 12.2.1 20220819 (Red Hat 12.2.1-2)] on linux
>>> help("modules")
  Please wait a moment while I gather a list of all available modules...
  /usr/lib/python3.11/site-packages/_distutils_hack/__init__.py:33: UserWarning: Setuptools is replacing distutils.
  warnings.warn("Setuptools is replacing distutils.")
  PIL                     bisect              glob                       re
  PyQt5               blivet               gnome_abrt           readline
  [..]

# para obter ajuda específica sobre um módulo digitamos help("nome_modulo")
>>> help("random")

Módulos em ambientes virtuais

Para criar um ambiente virtual em uma versão específica do python informamos qual versão deve ser usada. Por ex., para criar um ambiente virtual em ~/Projetos/.venv usamos:

$ python3.11 -m venv ~/Projetos/.venv

# para ativar esse ambiente
$ source ~/Projetos/.venv/bin/activate
# o prompt muda para refletir esse estado
(.venv) $ 

# para executar o python nesse ambiente
$ cd ~/Projetos/.venv
(.venv) (~/Projetos/.venv)$ python
  Python 3.11.0 

Como visto, toda a operação feita dentro do ambiente se refere à versão do python que instalou o ambiente. Nesse ambiente a biblioteca pandas não está instalada. Para instalar essa biblioteca saimos do python e usamos pip:

>>> import pandas
  [..]
  ModuleNotFoundError: No module named 'pandas'
# saimos do python
>>> exit()

# instalamos o módulo pandas
(.venv) (~/Projetos/.venv)$ python -m pip install pandas
  Collecting pandas
  [..]
  Successfully installed numpy-1.23.5 pandas-1.5.2 python-dateutil-2.8.2 pytz-2022.6 six-1.16.0

# o comando acima sugere um upgrade do pip
  [notice] A new release of pip available: 22.2.2 -> 22.3.1
  [notice] To update, run: pip install --upgrade pip

(.venv) (~/Projetos/.venv)$ pip install --upgrade pip
  [..]
  Successfully installed pip-22.3.1

O uso do pip acima, após a instalação da biblioteca pandas, informa a existência de um upgrade e fornece o comando para fazer essa atualização.

Bibliografia

Sites

todos acessados em dezembro de 2022.

Entidades Html

Entidades HTML são códigos usados para a exibição de símbolos que não estão diretamente acessíveis pelo teclado, dentro de uma página de HTML (páginas da WEB). Eles podem representar caracteres técnicos, matemáticos, símbolos de moedas, sinais gráficos e muitos outros. A tabela lista os códigos html hexadecimal e decimal. Por exemplo, a página:

<html>
<body>
<h1>Exibindo html </h1>
<p>Símbolo de um naipe espada de baralho: &clubs; - &clubsuit; - &♣; - &#9827;.</p>
<p>Símbolo matemático da integral de contorno: &conint; - &oint; - &#x0222e; - &#8750;.
</body>
</html>

seria exibida da seguinte forma (dependendo, é claro, do browser e das instruções css da página):

Exibindo html

Símbolo de um naipe espada de baralho: ♣ – ♣ – ♣ – ♣.
Símbolo matemático, integral de contorno: ∮ – ∮ – ∮ – ∮.

Nem todos os browsers podem exibir corretamente todos os símbolos. Um código na primeira coluna da tabela representa uma falha de exibição. Passe o mouse sobre os caracteres da 1ª coluna para ampliar.

Caracteres comuns: Pontuação
Caracter HTML HEX DEC Descrição
&quot; &#x00022; &#34; aspas
, &comma; &#x0002c; &#44; vírgula
. &period; &#x0002e; &#46; ponto final
: &colon; &#x0003a; &#58; dois pontos
; &semi; &#x0003b; &#59; ponto e vírgula
? &quest; &#x0003f; &#63; ponto de interrogação
¿ &iquest; &#x000bf; &#191; ponto de interrogação invertido
! &excl; &#x00021; &#33; ponto de exclamação
¡ &iexcl; &#x000a1; &#161; ponto de exclamação invertido
Barras e Aspas
Caracter HTML HEX DEC Descrição
&hyphen; &dash; &#x02010; &#8208; hífen
&ndash; &#x02013; &#8211; en dash
&mdash; &#x02014; &#8212; travessão, metade de emdash
&horbar; &#x02015; &#8213; barra horizontal
| &verbar; &vert; &#x02016; &#8214; barra vertical
/ &sol; &#x0002f; &#47; barra, travessa ou tranca
\ &bsol; &#x0005c; &#92; barra reversa
_ &lowbar; &#x0005f; &#95; linha baixa
¦ &brvbar; &#x000a6; &#166; barra quebrada
&lsquo; &opencurlyquote; &#x02018; &#8216; aspas simples esquerda
&rsquo; &rsquor; &closecurlyquote; &#x02019; &#8217; aspas simples direitas
&lsquor; &sbquo; &#x0201a; &#8218; aspas baixas simples
&ldquo; &opencurlydoublequote; &#x0201c; &#8220; aspas duplas esquerdas
&rdquo; &rdquor; &closecurlydoublequote; &#x0201d; &#8221; aspas duplas direitas
&ldquor; &bdquo; &#x0201e; &#8222; aspas baixas duplas
« &laquo; &#x000ab; &#171; aspa dupla angular esquerda
» &raquo; &#x000bb; &#187; aspa dupla angular direita
Sinais Tipográficos
Caracter HTML HEX DEC Descrição
§ &sect; &#x000a7; &#167; seção (parágrafo)
&para; &#x000b6; &#182; marca de parágrafo, alínea
&blank; &#x02423; &#9251; caixa aberta
Abreviaturas
Caracter HTML HEX DEC Descrição
# &num; &#x00023; &#35; número
$ &dollar; &#x00024; &#36; cifrão
% &percnt; &#x00025; &#37; porcentagem
@ &commat; &#x00040; &#64; comercial em (arroba)
® &reg; &#x000ae; &#174; marca registrada
° &deg; &#x000b0; &#176; grau
¢ &cent; &#x000a2; &#162; 1 centésimo
¤ &curren; &#x000a4; &#164; moeda
&euro; &#x020ac; &#8364; sinal do euro
£ &pound; &#x000a3; &#163; libra
¥ &yen; &#x000a5; &#165; iene
& &amp; &#x00026; &#38; e comercial
* &ast; &midast; &#x0002a; &#42; asterisco
¬ &not; &#x000ac; &#172; negação (not)
­­ &shy; &#x000ad; &#173; hífen suave
¯ &macr; &strns; &#x000af; &#175; mácron
· &middot; &#x000b7; &#183; ponto central
Espaçamento e controles
Caracter HTML HEX DEC Descrição
tab &tab; &#x00009; &#9; caracter de tabulação
newline &newline; &#x0000a; &#10; alimentação de linha (lf)
&nbsp; &nonbreakingspace; &#x000a0; &#160; espaço sem quebra | |
&ensp; &#x02002; &#8194; espaço | |
&emsp; &#x02003; &#8195; espaço | |
&emsp13; &#x02004; &#8196; espaço 1 terço | |
&emsp14; &#x02005; &#8197; espaço 1 quarto | |
&numsp; &#x02007; &#8199; espaço de figura | |
&puncsp; &#x02008; &#8200; espaço de pontuação | |
&thinsp; &thinspace; &#x02009; &#8201; espaço pequeno | |
&hairsp; &verythinspace; &#x0200a; &#8202; espaço muito pequeno | |
&zerowidthspace; &negativethickspace; &#x0200b; &#8203; espaço de largura zero |​|
&zwnj; &#x0200c; &#8204; largura zero não grudado |‌|
&zwj; &#x0200d; &#8205; largura zero grudado |‍|
&mediumspace; &#x0205f; &#8287; espaço matemático médio | |
&nobreak; &#x02060; &#8288; juntador de palavras
&invisibletimes; &it; &#x02062; &#8290; multiplicação invisível
&invisiblecomma; &ic; &#x02063; &#8291; vírgula invisível
&lrm; &#x0200e; &#8206; marca da esquerda para a direita
&rlm; &#x0200f; &#8207; marca da direita para a esquerda
Ícones, Símbolos
Caracter HTML HEX DEC Descrição
&starf; &bigstar; &#x02605; &#9733; estrela preta
&star; &#x02606; &#9734; estrela branca
&spades; &spadesuit; &#x02660; &#9824; naipe paus preto
&clubs; &clubsuit; &#x02663; &#9827; naipe espada preto
&hearts; &heartsuit; &#x02665; &#9829; naipe copa preto
&diams; &diamondsuit; &#x02666; &#9830; naipe ouro preto
&loz; &#9674; &#x25CA; losango branco
&sung; &#x0266a; &#9834; oitava nota
&flat; &#x0266d; &#9837; bemol musical
&natur; &natural; &#x0266e; &#9838; natural de música
&sharp; &#x0266f; &#9839; sharp, sustenido músical
&check; &checkmark; &#x02713; &#10003; marca de verificação
&cross; &#x02717; &#10007; seleção x
&malt; &maltese; &#x02720; &#10016; cruz maltesa
&sext; &#x02736; &#10038; estrela preta de seis pontas
&lozf; &blacklozenge; &#x029eb; &#10731; losango preto
© &copy; &#x000a9; &#169; direitos autorais
&os; &circleds; &#x024c8; &#9416; S maiúsculo circulado
&phone; &#x0260e; &#9742; telefone preto
&female; &#x02640; &#9792; signo feminino
&male; &#x02642; &#9794; signo masculino
&elinters; &#x023e7; &#9191; intersecção elétrica
Símbolos Matemáticos: Operadores
Caracter HTML HEX DEC Descrição
+ &plus; &#x0002b; &#43; soma, mais
± &plusmn; &pm; &#x000b1; &#177; mais ou menos
× &times; &#x000d7; &#215; multiplicação
÷ &divide; &div; &#x000f7; &#247; divisão
= &equals; &#x0003d; &#61; igual
< &lt; &#x0003c; &#60; menor que
> &gt; &#x0003e; &#62; maior que
&minus; &#x02212; &#8722; menos (subtração)
&mnplus; &mp; &minusplus; &#x02213; &#8723; menos ou mais
&plusdo; &dotplus; &#x02214; &#8724; ponto mais
&radic; &sqrt; &#x0221a; &#8730; raiz quadrada
&setmn; &setminus; &ssetmn; &#x02216; &#8726; menos de conjunto
&lowast; &#x02217; &#8727; operador asterisco
&compfn; &smallcircle; &#x02218; &#8728; operador anel
&oplus; &circleplus; &#x02295; &#8853; mais circulado
&ominus; &circleminus; &#x02296; &#8854; menos circulado
&otimes; &circletimes; &#x02297; &#8855; vezes circulado
&osol; &#x02298; &#8856; divisão circulada
&odot; &circledot; &#x02299; &#8857; ponto circulado
&ocir; &circledcirc; &#x0229a; &#8858; operador de anel circular
&oast; &circledast; &#x0229b; &#8859; asterisco circulado
&odash; &circleddash; &#x0229d; &#8861; traço circulado
&plusb; &boxplus; &#x0229e; &#8862; mais quadrado
&minusb; &boxminus; &#x0229f; &#8863; menos quadrado
&timesb; &boxtimes; &#x022a0; &#8864; vezes quadrado
&sdotb; &dotsquare; &#x022a1; &#8865; ponto quadrado
&xodot; &bigodot; &#x02a00; &#10752; operador ponto circulado
&xoplus; &bigoplus; &#x02a01; &#10753; mais círculo
&xotime; &bigotimes; &#x02a02; &#10754; vezes circulado
&xuplus; &biguplus; &#x02a04; &#10756; operador de união com mais
&xsqcup; &bigsqcup; &#x02a06; &#10758; operador de união quadrado
&forall; &#x02200; &#8704; para todos
&comp; &complement; &#x02201; &#8705; complemento
&part; &partiald; &#x02202; &#8706; diferencial parcial
&exist; &exists; &#x02203; &#8707; existe
&nexist; &nexists; &#x02204; &#8708; não existe
&empty; &emptyset; &emptyv; &#x02205; &#8709; conjunto vazio
&nabla; &del; &#x02207; &#8711; nabla
&isin; &isinv; &element; &in; &#x02208; &#8712; elemento de
&notin; &notelement; &notinva; &#x02209; &#8713; não é elemento de
&niv; &reverseelement; &ni; &suchthat; &#x0220b; &#8715; contém como membro
&notni; &notniva; &notreverseelement; &#x0220c; &#8716; não contém como membro
&prod; &product; &#x0220f; &#8719; produtório
&coprod; &coproduct; &#x02210; &#8720; coprodutório
&sum; &#x02211; &#8721; somatório
Operações e Relações em Conjuntos
Caracter HTML HEX DEC Descrição
&uplus; &unionplus; &#x0228e; &#8846; união de conjuntos
&xcap; &intersection; &bigcap; &#x022c2; &#8898; interseção de conjuntos
&xcup; &union; &bigcup; &#x022c3; &#8899; união de conjuntos
&sub; &subset; &#x02282; &#8834; subconjunto de
&sup; &supset; &superset; &#x02283; &#8835; superconjunto de
&nsub; &#x02284; &#8836; não é um subconjunto de
&nsup; &#x02285; &#8837; não um superconjunto de
&sube; &subsetequal; &subseteq; &#x02286; &#8838; subconjunto de ou igual a
&supe; &supseteq; &supersetequal; &#x02287; &#8839; superconjunto de ou igual a
&nsube; &nsubseteq; &notsubsetequal; &#x02288; &#8840; nem um subconjunto de nem igual a
&nsupe; &nsupseteq; &notsupersetequal; &#x02289; &#8841; nem um superconjunto de nem igual a
&subne; &subsetneq; &#x0228a; &#8842; subconjunto de com não igual a
&supne; &supsetneq; &#x0228b; &#8843; superconjunto de com não igual a
&cupdot; &#x0228d; &#8845; multiplicação de conjuntos
Caracteres Especiais
Caracter HTML HEX DEC Descrição
ı &imath; &inodot; &#x00131; &#305; i sem ponto (mat.)
ȷ &jmath; &#x00237; &#567; j sem ponto (mat)
ƒ &fnof; &#x00192; &#402; f cortado
¹ &sup1; &#x000b9; &#185; um sobrescrito
&iinfin; &#x029dc; &#10716; infinito incompleto
&infintie; &#x029dd; &#10717; ligadura sobre infinito
&nvinfin; &#x029de; &#10718; infinito negado com barra vertical
µ &micro; &#x000b5; &#181; micro
Frações
Caracter HTML HEX DEC Descrição
¼ &frac14; &#x000bc; &#188; fração um quarto
½ &frac12; &half; &#x000bd; &#189; fração metade
¾ &frac34; &#x000be; &#190; fração três quartos
&frac13; &#x02153; &#8531; fração um terço
&frac23; &#x02154; &#8532; fração dois terços
&frac15; &#x02155; &#8533; fração um quinto
&frac25; &#x02156; &#8534; fração dois quintos
&frac35; &#x02157; &#8535; fração três quintos
&frac45; &#x02158; &#8536; fração quatro quintos
&frac16; &#x02159; &#8537; fração um sexto
&frac56; &#x0215a; &#8538; fração cinco sextos
&frac18; &#x0215b; &#8539; fração um oitavo
&frac38; &#x0215c; &#8540; fração três oitavos
&frac58; &#x0215d; &#8541; fração cinco oitavos
&frac78; &#x0215e; &#8542; fração sete oitavos
Sobrescritos
Caracter HTML HEX DEC Descrição
² &sup2; &#x000b2; &#178; dois sobrescrito
³ &sup3; &#x000b3; &#179; três sobrescrito
ª &ordf; &#x000aa; &#170; ordinal feminino
º &ordm; &#x000ba; &#186; ordinal masculino
Delimitadores
Caracter HTML HEX DEC Descrição
( &lpar; &#x00028; &#40; parêntese esquerdo
) &rpar; &#x00029; &#41; parêntese direito
[ &lsqb; &lbrack; &#x0005b; &#91; colchete esquerdo
] &rsqb; &rbrack; &#x0005d; &#93; colchete direito
{ &lcub; &lbrace; &#x0007b; &#123; chave esquerda
} &rcub; &rbrace; &#x0007d; &#125; chave direita
| &verbar; &vert; &#x0007c; &#124; linha vertical
&lang; &leftanglebracket; &langle; &#x027e8; &#10216; colchete angular esquerdo
&rang; &rightanglebracket; &rangle; &#x027e9; &#10217; colchete angular direito
&lang; &#x027ea; &#10218; colchete angular esquerdo duplo
&rang; &#x027eb; &#10219; colchete angular direito duplo
&verticalseparator; &#x02758; &#10072; barra vertical leve
Delimitadores Decorativos
Caracter HTML HEX DEC Descrição
&lbbrk; &#x02772; &#10098; chave ornamental esquerda
&rbbrk; &#x02773; &#10099; chave ornamental direita
&lobrk; &leftdoublebracket; &#x027e6; &#10214; colchete branco esquerdo
&robrk; &rightdoublebracket; &#x027e7; &#10215; colchete vazado direito
&loang; &#x027ec; &#10220; colchete vazado esquerdo
&roang; &#x027ed; &#10221; colchete vazado direito
&ufisht; &#x0297e; &#10622; rabo de peixe
⥿ &dfisht; &#x0297f; &#10623; rabo de peixe invertido
&lopar; &#x02985; &#10629; parêntese vazado esquerdo
&ropar; &#x02986; &#10630; parêntese vazado direito
&lbrke; &#x0298b; &#10635; colchete esquerdo com barra inferior
&rbrke; &#x0298c; &#10636; colchete direito com barra inferior
&lbrkslu; &#x0298d; &#10637; colchete esquerdo com marca no canto superior
&rbrksld; &#x0298e; &#10638; colchete direito com marca no canto inferior
&lbrksld; &#x0298f; &#10639; colchete esquerdo com marca no canto inferior
&rbrkslu; &#x02990; &#10640; colchete direito com marca no canto superior
&langd; &#x02991; &#10641; colchete angular esquerdo com ponto
&rangd; &#x02992; &#10642; colchete angular direito com ponto
&lparlt; &#x02993; &#10643; arco esquerdo menor que colchete
&rpargt; &#x02994; &#10644; arco direito maior que colchete
&gtlpar; &#x02995; &#10645; arco esquerdo duplo maior que colchete
&ltrpar; &#x02996; &#10646; arco direito duplo menor que colchete
Integrais
Caracter HTML HEX DEC Descrição
&int; &integral; &#x0222b; &#8747; integral
&int; &#x0222c; &#8748; integral dupla
&tint; &iiint; &#x0222d; &#8749; integral triplo
&conint; &oint; &contourintegral; &#x0222e; &#8750; integral de contorno
&conint; &doublecontourintegral; &#x0222f; &#8751; integral de superfície
&cwint; &#x02231; &#8753; integral no sentido horário
&awint; &#x02a11; &#10769; integração anti-horário
&cwconint; &#x02232; &#8754; integral de contorno no sentido horário
&awconint; &#x02233; &#8755; integral de contorno anti-horário
&qint; &iiiint; &#x02a0c; &#10764; operador quádruplo integral
&fpartint; &#x02a0d; &#10765; parte finita integral
&cirfnint; &#x02a10; &#10768; função de circulação
&rppolint; &#x02a12; &#10770; integral de caminho quadrado em torno do pólo
&scpolint; &#x02a13; &#10771; integral de caminho semicircular em torno do pólo
&npolint; &#x02a14; &#10772; integral de linha excluindo pólo
&pointint; &#x02a15; &#10773; integral circular em torno do polo
&quatint; &#x02a16; &#10774; operador integral de quatérnio
&intlarhk; &#x02a17; &#10775; integral com seta esquerda com gancho
Setas
Caracter HTML HEX DEC Descrição
&larr; &leftarrow; &leftarrow; &slarr; &#x02190; &#8592; seta esquerda
&uarr; &uparrow; &uparrow; &shortuparrow; &#x02191; &#8593; seta para cima
&rarr; &rightarrow; &rightarrow; &srarr; &#x02192; &#8594; seta direita
&darr; &downarrow; &downarrow; &shortdownarrow; &#x02193; &#8595; seta para baixo
&harr; &leftrightarrow; &leftrightarrow; &#x02194; &#8596; seta esquerda-direita
&nlarr; &#x0219a; &#8602; seta esquerda cortada
&nrarr; &nrightarrow; &#x0219b; &#8603; seta direita cortada
&rarrw; &rightsquigarrow; &#x0219d; &#8605; seta ondulada para a direita
&larr; &twoheadleftarrow; &#x0219e; &#8606; seta de duas pontas esquerda
&uarr; &#x0219f; &#8607; seta de duas pontas para cima
&rarr; &twoheadrightarrow; &#x021a0; &#8608; seta de duas pontas para a direita
&darr; &#x021a1; &#8609; seta de duas pontas para baixo
&cudarrr; &#x02935; &#10549; seta para a direita e curvando para baixo
&larrhk; &hookleftarrow; &#x021a9; &#8617; seta esquerda com gancho
&rarrhk; &hookrightarrow; &#x021aa; &#8618; seta direita com gancho
&varr; &updownarrow; &updownarrow; &#x02195; &#8597; seta para cima e para baixo
&nwarr; &nwarrow; &#x02196; &#8598; seta noroeste
&nearr; &nearrow; &#x02197; &#8599; seta nordeste
&searr; &searrow; &#x02198; &#8600; seta sudeste
&swarr; &swarrow; &#x02199; &#8601; seta sudoeste
&larrtl; &leftarrowtail; &#x021a2; &#8610; seta esquerda com cauda
&rarrtl; &rightarrowtail; &#x021a3; &#8611; seta direita com cauda
&leftteearrow; &mapstoleft; &#x021a4; &#8612; seta esquerda barra
&upteearrow; &mapstoup; &#x021a5; &#8613; seta para cima, barra
&map; &rightteearrow; &mapsto; &#x021a6; &#8614; seta direita, barra
&ruledelayed; &#x029f4; &#10740; dois pontos, seta direita; rule-delayed
&larrlp; &looparrowleft; &#x021ab; &#8619; seta esquerda com laço
&rarrlp; &looparrowright; &#x021ac; &#8620; seta direita com laço
&harrw; &leftrightsquigarrow; &#x021ad; &#8621; seta onda esquerda direita
&nharr; &nleftrightarrow; &#x021ae; &#8622; seta traço esquerda direita
&lsh; &#x021b0; &#8624; seta acima, ponta à esquerda
&rsh; &#x021b1; &#8625; seta acima, ponta à direita
&ldsh; &#x021b2; &#8626; seta abaixo, ponta à esquerda
&rdsh; &#x021b3; &#8627; seta abaixo, ponta à direita
&crarr; &#x021b5; &#8629; seta para baixo com canto esquerdo
&cularr; &curvearrowleft; &#x021b6; &#8630; seta semicírculo superior anti-horária
&curarr; &curvearrowright; &#x021b7; &#8631; seta de semicírculo superior horária
&olarr; &circlearrowleft; &#x021ba; &#8634; seta circular aberta anti-horária
&orarr; &circlearrowright; &#x021bb; &#8635; seta circular aberta horária
&lharu; &leftvector; &leftharpoonup; &#x021bc; &#8636; arpão esquerda, farpa para cima
&lhard; &downleftvector; &#x021bd; &#8637; arpão esquerda, farpa para baixo
&uharr; &rightupvector; &#x021be; &#8638; arpão para cima, farpa para a direita
&uharl; &leftupvector; &#x021bf; &#8639; arpão para cima, farpa para a esquerda
&rharu; &rightvector; &#x021c0; &#8640; arpão direita, farpa para cima
&rhard; &downrightvector; &#x021c1; &#8641; arpão direita, farpa para baixo
&dharr; &rightdownvector; &#x021c2; &#8642; arpão para baixo, farpa para a direita
&dharl; &leftdownvector; &#x021c3; &#8643; arpão para baixo, farpa para a esquerda
&rlarr; &rightleftarrows; &#x021c4; &#8644; seta direita sobre seta esquerda
&udarr; &uparrowdownarrow; &#x021c5; &#8645; seta para cima para a esquerda da seta para baixo
&lrarr; &leftrightarrows; &#x021c6; &#8646; seta esquerda sobre seta direita
&llarr; &leftleftarrows; &#x021c7; &#8647; duas setas, esquerda
&uuarr; &upuparrows; &#x021c8; &#8648; duas setas para cima
&rrarr; &rightrightarrows; &#x021c9; &#8649; duas setas para a direita
&ddarr; &downdownarrows; &#x021ca; &#8650; duas setas para baixo
&lrhar; &reverseequilibrium; &leftrightharpoons; &#x021cb; &#8651; arpão esquerda sobre o arpão direita
&rlhar; &rightleftharpoons; &equilibrium; &#x021cc; &#8652; arpão da direita sobre arpão da esquerda
&nlarr; &nleftarrow; &#x021cd; &#8653; seta dupla para a esquerda cortada
&nharr; &nleftrightarrow; &#x021ce; &#8654; seta dupla esquerda direita cortada
&nrarr; &nrightarrow; &#x021cf; &#8655; seta dupla para a direita com traço
&larr; &leftarrow; &doubleleftarrow; &#x021d0; &#8656; seta dupla para a esquerda
&uarr; &uparrow; &doubleuparrow; &#x021d1; &#8657; seta dupla para cima
&rarr; &rightarrow; &implies; &doublerightarrow; &#x021d2; &#8658; seta dupla para a direita
&darr; &downarrow; &doubledownarrow; &#x021d3; &#8659; seta dupla para baixo
&harr; &leftrightarrow; &doubleleftrightarrow; &iff; &#x021d4; &#8660; seta dupla esquerda direita
&laarr; &lleftarrow; &#x021da; &#8666; seta tripla para a esquerda
&raarr; &rrightarrow; &#x021db; &#8667; seta tripla para a direita
&zigrarr; &#x021dd; &#8669; seta zigzag para a direita
&larrb; &leftarrowbar; &#x021e4; &#8676; seta esquerda para barra
&rarrb; &rightarrowbar; &#x021e5; &#8677; seta direita para barra
&duarr; &downarrowuparrow; &#x021f5; &#8693; seta para baixo para a esquerda da seta para cima
&loarr; &#x021fd; &#8701; seta de cabeça aberta para a esquerda
&roarr; &#x021fe; &#8702; seta de cabeça aberta para a direita
&hoarr; &#x021ff; &#8703; seta esquerda direita aberta
&xlarr; &longleftarrow; &longleftarrow; &#x027f5; &#10229; seta longa para a esquerda
&xlarr; &longleftarrow; &doublelongleftarrow; &#x027f8; &#10232; seta dupla longa para a esquerda
&xrarr; &longrightarrow; &longrightarrow; &#x027f6; &#10230; seta longa para a direita
&xharr; &longleftrightarrow; &longleftrightarrow; &#x027f7; &#10231; seta longa esquerda direita
&xrarr; &longrightarrow; &doublelongrightarrow; &#x027f9; &#10233; seta dupla longa para a direita
&xharr; &longleftrightarrow; &#x027fa; &#10234; seta dupla longa esquerda direita
&xmap; &longmapsto; &#x027fc; &#10236; seta longa para a direita da barra
&dzigrarr; &#x027ff; &#10239; seta longa para a direita
&nvlarr; &#x02902; &#10498; seta dupla para a esquerda com traço vertical
&nvrarr; &#x02903; &#10499; seta dupla para a direita com traço vertical
&nvharr; &#x02904; &#10500; seta dupla esquerda direita com traço vertical
&map; &#x02905; &#10501; seta de duas pontas para a direita da barra
&lbarr; &#x0290c; &#10508; seta esquerda com traço duplo
&rbarr; &bkarow; &#x0290d; &#10509; seta direita com traço duplo
&lbarr; &#x0290e; &#10510; seta de traço triplo para a esquerda
&rbarr; &dbkarow; &#x0290f; &#10511; seta de traço triplo para a direita
&rbarr; &drbkarow; &#x02910; &#10512; seta de duas pontas para a direita com traço triplo
&ddotrahd; &#x02911; &#10513; seta direita com haste pontilhada
&uparrowbar; &#x02912; &#10514; seta para cima para barra
&downarrowbar; &#x02913; &#10515; seta para baixo para barra
&rarrtl; &#x02916; &#10518; seta de duas pontas para a direita com cauda
&latail; &#x02919; &#10521; cauda de seta esquerda
&ratail; &#x0291a; &#10522; cauda de seta direita
&latail; &#x0291b; &#10523; cauda de seta dupla para a esquerda
&ratail; &#x0291c; &#10524; cauda de seta dupla para a direita
&larrfs; &#x0291d; &#10525; seta esquerda para diamante preto
&rarrfs; &#x0291e; &#10526; seta direita para diamante preto
&larrbfs; &#x0291f; &#10527; seta esquerda da barra ao diamante preto
&rarrbfs; &#x02920; &#10528; seta direita da barra para o diamante preto
&nwarhk; &#x02923; &#10531; seta noroeste com gancho
&nearhk; &#x02924; &#10532; flecha nordeste com gancho
&searhk; &hksearow; &#x02925; &#10533; seta sudeste com gancho
&swarhk; &hkswarow; &#x02926; &#10534; seta sudoeste com gancho
&nwnear; &#x02927; &#10535; seta noroeste e seta nordeste
&nesear; &toea; &#x02928; &#10536; seta nordeste e seta sudeste
&seswar; &tosa; &#x02929; &#10537; seta sudeste e seta sudoeste
&swnwar; &#x0292a; &#10538; seta sudoeste e seta noroeste
&rarrc; &#x02933; &#10547; seta ondulada para a direita
&ldca; &#x02936; &#10550; seta para baixo e curvando para a esquerda
&rdca; &#x02937; &#10551; seta para baixo e curvando para a direita
&cudarrl; &#x02938; &#10552; arco do lado direito seta no sentido horário
&larrpl; &#x02939; &#10553; arco do lado esquerdo no sentido anti-horário
&curarrm; &#x0293c; &#10556; arco superior seta no sentido horário com menos
&cularrp; &#x0293d; &#10557; seta do arco superior no sentido anti-horário com mais
&rarrpl; &#x02945; &#10565; seta direita com mais abaixo
&harrcir; &#x02948; &#10568; seta esquerda direita através de círculo
&uarrocir; &#x02949; &#10569; seta de duas pontas para cima do círculo
&lurdshar; &#x0294a; &#10570; farpa esquerda para cima direita farpa para baixo arpão
&ldrushar; &#x0294b; &#10571; farpa esquerda para baixo direita farpa para cima arpão
&leftrightvector; &#x0294e; &#10574; farpa esquerda para cima direita farpa para arpão
&rightupdownvector; &#x0294f; &#10575; para cima farpa direita para baixo farpa arpão direito
&downleftrightvector; &#x02950; &#10576; farpa esquerda para baixo direita farpa para baixo arpão
&leftupdownvector; &#x02951; &#10577; cima farpa esquerda baixo farpa esquerda arpão
&leftvectorbar; &#x02952; &#10578; arpão esquerda com farpa até a barra
&rightvectorbar; &#x02953; &#10579; arpão direita com farpa até a barra
&rightupvectorbar; &#x02954; &#10580; arpão para cima com farpa direita na barra
&rightdownvectorbar; &#x02955; &#10581; arpão para baixo com farpa à direita da barra
&downleftvectorbar; &#x02956; &#10582; arpão esquerda com farpa até a barra
&downrightvectorbar; &#x02957; &#10583; arpão direita com farpa até a barra
&leftupvectorbar; &#x02958; &#10584; arpão para cima com farpa da esquerda para a barra
&leftdownvectorbar; &#x02959; &#10585; arpão para baixo com farpa da esquerda para a barra
&leftteevector; &#x0295a; &#10586; arpão esquerda com farpa para cima da barra
&rightteevector; &#x0295b; &#10587; arpão direita com farpa para cima da barra
&rightupteevector; &#x0295c; &#10588; arpão para cima com farpa direto da barra
&rightdownteevector; &#x0295d; &#10589; arpão para baixo com farpa direto da barra
&downleftteevector; &#x0295e; &#10590; arpão esquerda com farpa para baixo da barra
&downrightteevector; &#x0295f; &#10591; arpão direita com farpa para baixo da barra
&leftupteevector; &#x02960; &#10592; arpão para cima com farpa esquerda da barra
&leftdownteevector; &#x02961; &#10593; arpão para baixo com farpa esquerda da barra
&lhar; &#x02962; &#10594; arpão esquerda com farpa para cima acima do arpão esquerda com farpa para baixo
&uhar; &#x02963; &#10595; arpão para cima com farpa esquerda ao lado arpão para cima com farpa direita
&rhar; &#x02964; &#10596; arpão direita com farpa para cima acima para arpão direita com farpa para baixo
&dhar; &#x02965; &#10597; arpão para baixo com farpa esquerda ao lado arpão para baixo com farpa direita
&luruhar; &#x02966; &#10598; arpão esquerda com farpa para cima acima do arpão direita com farpa para cima
&ldrdhar; &#x02967; &#10599; arpão esquerda com farpa para baixo acima do arpão direita com farpa para baixo
&ruluhar; &#x02968; &#10600; arpão direita com farpa para cima acima do arpão esquerda com farpa para cima
&rdldhar; &#x02969; &#10601; arpão direita com farpa para baixo acima do arpão esquerda com farpa para baixo
&lharul; &#x0296a; &#10602; arpão esquerda com farpa acima do longo traço
&llhard; &#x0296b; &#10603; arpão esquerda com farpa abaixo do longo traço
&rharul; &#x0296c; &#10604; arpão direita com farpa acima do longo traço
&lrhard; &#x0296d; &#10605; arpão direita com farpa abaixo do longo traço
&udhar; &upequilibrium; &#x0296e; &#10606; arpão p/ cima com farpa esquerda ao lado arpão para baixo com farpa direita
&duhar; &reverseupequilibrium; &#x0296f; &#10607; arpão para baixo com farpa esquerda ao lado arpão para cima com farpa direita
&roundimplies; &#x02970; &#10608; seta dupla direita com cabeça arredondada
&erarr; &#x02971; &#10609; igual acima da seta direita
&simrarr; &#x02972; &#10610; operador til acima da seta direita
&larrsim; &#x02973; &#10611; seta esquerda acima do operador til
&rarrsim; &#x02974; &#10612; seta direita acima do operador til
&rarrap; &#x02975; &#10613; seta direita acima quase igual a
&ltlarr; &#x02976; &#10614; seta esquerda menor que acima
&gtrarr; &#x02978; &#10616; maior do que acima da seta direita
&subrarr; &#x02979; &#10617; subconjunto acima da seta direita
&suplarr; &#x0297b; &#10619; superconjunto acima da seta esquerda
&lfisht; &#x0297c; &#10620; cauda de peixe esquerda
&rfisht; &#x0297d; &#10621; cauda de peixe direita
Caracteres Gráficos: Desenho
Caracter HTML HEX DEC Descrição
&boxh; &horizontalline; &#x02500; &#9472; parte de caixa: linha horizontal
&boxv; &#x02502; &#9474; parte de caixa: linha vertical
&boxdr; &#x0250c; &#9484; parte de caixa: canto superior esquerdo
&boxdl; &#x02510; &#9488; desenho da caixa canto superior direito
&boxur; &#x02514; &#9492; parte de caixa: canto inferior esquerdo
&boxul; &#x02518; &#9496; parte de caixa: canto inferior direito
&boxvr; &#x0251c; &#9500; parte de caixa: linha central direita
&boxvl; &#x02524; &#9508; parte de caixa: linha central esquerda
&boxhd; &#x0252c; &#9516; parte de caixa: linha para baixo e horizontal
&boxhu; &#x02534; &#9524; parte de caixa: iluminados e horizontais
&boxvh; &#x0253c; &#9532; parte de caixa: de linha vertical e horizontal
&boxh; &#x02550; &#9552; parte de caixa: dupla horizontal
&boxv; &#x02551; &#9553; parte de caixa: dupla vertical
&boxdr; &#x02552; &#9554; parte de caixa: para baixo simples e duplo direito
&boxdr; &#x02553; &#9555; parte de caixa: para baixo duplo e único direito
&boxdr; &#x02554; &#9556; os desenhos da caixa dobram para baixo e para a direita
&boxdl; &#x02555; &#9557; parte de caixa: para baixo simples e duplo esquerdo
&boxdl; &#x02556; &#9558; parte de caixa: para baixo duplo e único esquerdo
&boxdl; &#x02557; &#9559; desenhos da caixa dobrar para baixo e para a esquerda
&boxur; &#x02558; &#9560; parte de caixa: s simples e duplas à direita
&boxur; &#x02559; &#9561; caixa de desenhos dupla e direita simples
&boxur; &#x0255a; &#9562; os desenhos da caixa dobram e correm
&boxul; &#x0255b; &#9563; parte de caixa: para cima simples e duplo esquerdo
&boxul; &#x0255c; &#9564; parte de caixa: duplo e esquerdo simples
&boxul; &#x0255d; &#9565; os desenhos da caixa dobram e saem
&boxvr; &#x0255e; &#9566; parte de caixa: vertical simples e duplo direito
&boxvr; &#x0255f; &#9567; parte de caixa: vertical duplo e direito simples
&boxvr; &#x02560; &#9568; parte de caixa: dupla vertical e direita
&boxvl; &#x02561; &#9569; parte de caixa: vertical simples e duplo esquerdo
&boxvl; &#x02562; &#9570; parte de caixa: dupla vertical e esquerda simples
&boxvl; &#x02563; &#9571; parte de caixa: duplo vertical e esquerdo
&boxhd; &#x02564; &#9572; parte de caixa: para baixo simples e duplo horizontal
&boxhd; &#x02565; &#9573; parte de caixa: para baixo duplo e horizontal simples
&boxhd; &#x02566; &#9574; parte de caixa: dobrar para baixo e horizontal
&boxhu; &#x02567; &#9575; parte de caixa: para cima simples e duplo horizontal
&boxhu; &#x02568; &#9576; parte de caixa: duplo e horizontal simples
&boxhu; &#x02569; &#9577; parte de caixa: dobrados e horizontais
&boxvh; &#x0256a; &#9578; parte de caixa: vertical simples e horizontal duplo
&boxvh; &#x0256b; &#9579; parte de caixa: dupla vertical e simples horizontal
&boxvh; &#x0256c; &#9580; parte de caixa: duplo vertical e horizontal
&uhblk; &#x02580; &#9600; meio bloco superior
&lhblk; &#x02584; &#9604; meio bloco inferior
&block; &#x02588; &#9608; bloco completo
&blk14; &#x02591; &#9617; sombra clara
&blk12; &#x02592; &#9618; tom médio
&blk34; &#x02593; &#9619; sombra escura
Figuras Geométricas
Caracter HTML HEX DEC Descrição
&squ; &square; &square; &#x025a1; &#9633; quadrado branco
&squf; &squarf; &blacksquare; &filledverysmallsquare; &#x025aa; &#9642; quadrado pequeno preto
&emptyverysmallsquare; &#x025ab; &#9643; quadrado pequeno branco
&rect; &#x025ad; &#9645; retângulo branco
&marker; &#x025ae; &#9646; retângulo preto vertical
&fltns; &#x025b1; &#9649; paralelogramo branco
&xutri; &bigtriangleup; &#x025b3; &#9651; triângulo branco para cima
&utrif; &blacktriangle; &#x025b4; &#9652; triângulo pequeno preto para cima
&utri; &triangle; &#x025b5; &#9653; triângulo pequeno branco para cima
&rtrif; &blacktriangleright; &#x025b8; &#9656; triângulo pequeno preto para a direita
&rtri; &triangleright; &#x025b9; &#9657; triângulo pequeno branco para a direita
&xdtri; &bigtriangledown; &#x025bd; &#9661; triângulo branco para baixo
&dtrif; &blacktriangledown; &#x025be; &#9662; triângulo pequeno preto para baixo
&dtri; &triangledown; &#x025bf; &#9663; ponto baixo branco
&ltrif; &blacktriangleleft; &#x025c2; &#9666; triângulo pequeno preto para a esquerda
&ltri; &triangleleft; &#x025c3; &#9667; triângulo pequeno branco para a esquerda
&loz; &lozenge; &#x025ca; &#9674; pastilha
&cir; &#x025cb; &#9675; círculo branco
&tridot; &#x025ec; &#9708; triângulo branco para cima com ponto
&xcirc; &bigcirc; &#x025ef; &#9711; grande círculo
&ultri; &#x025f8; &#9720; triângulo superior esquerdo
&urtri; &#x025f9; &#9721; triângulo superior direito
&lltri; &#x025fa; &#9722; triângulo inferior esquerdo
&emptysmallsquare; &#x025fb; &#9723; quadrado médio branco
&filledsmallsquare; &#x025fc; &#9724; quadrado médio preto
Acentos
Caracter HTML HEX DEC Descrição
´ &acute; &diacriticalacute; &#x000b4; &#180; acento agudo
^ &hat; &#x0005e; &#94; acento circunflexo
ˆ &circ; &#x002c6; &#710; acento circunflexo
` &grave; &diacriticalgrave; &#x00060; &#96; acento grave
˘ &breve; &#x002d8; &#728; breve
¸ &cedil; &cedilla; &#x000b8; &#184; cedilha
¨ &dot; &die; &uml; &#x000a8; &#168; trema
&apos; &#x00027; &#39; apóstrofe
˜ &tilde; &#x002dc; &#732; til pequeno
˙ &dot; &#x002d9; &#729; ponto acima
ˇ &caron; &#x002c7; &#711; caron
˚ &ring; &#x002da; &#730; anel acima
˛ &ogon; &#x002db; &#731; ogonek
˝ &dblac; &#x002dd; &#733; acento agudo duplo
_ &UnderBar; &#x00332; &#818; traço subescrito
Caracteres Acentuados
Caracter HTML HEX DEC Descrição
À &Agrave; &#x000c0; &#192; A grave
Á &Aacute; &#x000c1; &#193; A agudo
 &Acirc; &#x000c2; &#194; A circunflexo
à &Atilde; &#x000c3; &#195; A til
Ä &Auml; &#x000c4; &#196; A trema
Å &Aring; &#x000c5; &#197; A anel
Æ &Aelig; &#x000c6; &#198; Ae
à &agrave; &#x000e0; &#224; a grave
á &aacute; &#x000e1; &#225; a agudo
â &acirc; &#x000e2; &#226; a circunflexo
ã &atilde; &#x000e3; &#227; a til
ä &auml; &#x000e4; &#228; a trema
å &aring; &#x000e5; &#229; a anel acima
æ &aelig; &#x000e6; &#230; ae
Ā &Amacr; &#x00100; &#256; A mácron
ā &amacr; &#x00101; &#257; a mácron
Ă &Abreve; &#x00102; &#258; A breve
ă &abreve; &#x00103; &#259; a breve
Ą &Aogon; &#x00104; &#260; A ogonek
ą &aogon; &#x00105; &#261; a ogonek
Ć &Cacute; &#x00106; &#262; C com agudo
ć &cacute; &#x00107; &#263; c com agudo
Ĉ &Ccirc; &#x00108; &#264; C com circunflexo
ĉ &ccirc; &#x00109; &#265; c com circunflexo
Ċ &Cdot; &#x0010a; &#266; C com ponto acima
ċ &cdot; &#x0010b; &#267; c com ponto acima
č &ccaron; &#x0010c; &#268; maiúscula c com caron
č &ccaron; &#x0010d; &#269; minúscula c com caron
ç &ccedil; &#x000c7; &#199; c com cedilha
Ď &Dcaron; &#x0010e; &#270; D caron
ď &dcaron; &#x0010f; &#271; d caron
Đ &Dstrok; &#x00110; &#272; D com traço
đ &dstrok; &#x00111; &#273; d com traço
È &Egrave; &#x000e8; &#232; E grave
è &egrave; &#x000c8; &#200; e com grave
é &eacute; &#x000c9; &#201; e com agudo
é &eacute; &#x000e9; &#233; e agudo
ê &ecirc; &#x000ea; &#234; e circunflexo
ê &ecirc; &#x000ca; &#202; e com circunflexo
Ë &Euml; &#x000cb; &#203; E com trema
ë &euml; &#x000eb; &#235; e trema
Ē &Emacr; &#x00112; &#274; E mácron
ē &emacr; &#x00113; &#275; e mácron
Ė &Edot; &#x00116; &#278; E ponto
ė &edot; &#x00117; &#279; e ponto
Ę &Eogon; &#x00118; &#280; E com ogonek
ę &eogon; &#x00119; &#281; e com ogonek
Ě &Ecaron; &#x0011a; &#282; E caron
ě &ecaron; &#x0011b; &#283; e caron
ð &eth; &#x000f0; &#240; eth minúsculo
Ð &Eth; &#x000d0; &#208; eth maiúsculo
Ĝ &Gcirc; &#x0011c; &#284; G circunflexo
ĝ &gcirc; &#x0011d; &#285; g circunflexo
Ğ &Gbreve; &#x0011e; &#286; G breve
ğ &gbreve; &#x0011f; &#287; g breve
Ġ &Gdot; &#x00120; &#288; G com ponto acima
ġ &gdot; &#x00121; &#289; g com ponto acima
ǵ &gacute; &#x001f5; &#501; g com agudo
Ĥ &Hcirc; &#x00124; &#292; H circunflexo
ĥ &hcirc; &#x00125; &#293; h circunflexo
Ħ &Hstrok; &#x00126; &#294; H com traço
ħ &hstrok; &#x00127; &#295; h com traço
ì &igrave; &#x000ec; &#236; i grave
í &iacute; &#x000ed; &#237; i agudo
î &icirc; &#x000ee; &#238; i circunflexo
ï &iuml; &#x000ef; &#239; i trema
Ĩ &itilde; &#x00129; &#297; I til
ĩ &itilde; &#x00128; &#296; i til
Ī &Imacr; &#x0012a; &#298; I com mácron
ī &imacr; &#x0012b; &#299; i com mácron
Į &Iogon; &#x0012e; &#302; I com ogonek
į &iogon; &#x0012f; &#303; i com ogonek
İ &Idot; &#x00130; &#304; I com ponto
IJ &IJlig; &#x00132; &#306; ligadura IJ
ij &ijlig; &#x00133; &#307; ligadura ij
Ĵ &Jcirc; &#x00134; &#308; J circunflexo
ĵ &jcirc; &#x00135; &#309; j circunflexo
Ķ &Kcedil; &#x00136; &#310; K com cedilha
ķ &kcedil; &#x00137; &#311; k com cedilha
ĸ &kgreen; &#x00138; &#312; kra minúsculo
Ĺ &Lacute; &#x00139; &#313; L agudo
ĺ &lacute; &#x0013a; &#314; l agudo
Ļ &Lcedil; &#x0013b; &#315; L com cedilha
ļ &lcedil; &#x0013c; &#316; l com cedilha
Ľ &Lcaron; &#x0013d; &#317; L com caron
ľ &lcaron; &#x0013e; &#318; l com caron
Ŀ &Lmidot; &#x0013f; &#319; L com ponto
ŀ &lmidot; &#x00140; &#320; l com ponto
Ł &Lstrok; &#x00141; &#321; L com traço
ł &lstrok; &#x00142; &#322; l com traço
Ń &Nacute; &#x00143; &#323; N com agudo
ń &nacute; &#x00144; &#324; n com agudo
Ņ &Ncedil; &#x00145; &#325; N com cedilha
ņ &ncedil; &#x00146; &#326; n com cedilha
Ň &Ncaron; &#x00147; &#327; N com caron
ň &ncaron; &#x00148; &#328; n com caron
ʼn &napos; &#x00149; &#329; n precedida de apóstrofo
ñ &ntilde; &#x000d1; &#209; n com til
ò &ograve; &#x000f2; &#242; o grave
ó &oacute; &#x000f3; &#243; o agudo
ô &ocirc; &#x000f4; &#244; o circunflexo
õ &otilde; &#x000f5; &#245; o til
ö &ouml; &#x000f6; &#246; o com trema
ø &oslash; &#x000f8; &#248; o com traço
Ō &Omacr; &#x0014c; &#332; O com macron
ō &omacr; &#x0014d; &#333; o com mácron
Ő &Odblac; &#x00150; &#336; O com duplo agudo
ő &odblac; &#x00151; &#337; o com duplo agudo
Œ &Oelig; &#x00152; &#338; ligadura OE maiúscula
œ &oelig; &#x00153; &#339; ligadura oe minúscula
Ŕ &Racute; &#x00154; &#340; R com agudo
ŕ &racute; &#x00155; &#341; r com agudo
Ŗ &Rcedil; &#x00156; &#342; R com cedilha
ŗ &rcedil; &#x00157; &#343; r com cedilha
Ř &Rcaron; &#x00158; &#344; R com caron
ř &rcaron; &#x00159; &#345; r minúscula com caron
Ś &Sacute; &#x0015a; &#346; S com agudo
ś &sacute; &#x0015b; &#347; s com agudo
Ŝ &Scirc; &#x0015c; &#348; S com circunflexo
ŝ &scirc; &#x0015d; &#349; s com circunflexo
Ş &Scedil; &#x0015e; &#350; S com cedilha
ş &scedil; &#x0015f; &#351; s com cedilha
Š &Scaron; &#x00160; &#352; S com caron
š &scaron; &#x00161; &#353; s com caron
Ţ &Tcedil; &#x00162; &#354; T com cedilha
ţ &tcedil; &#x00163; &#355; t com cedilha
Ť &Tcaron; &#x00164; &#356; T com caron
ť &tcaron; &#x00165; &#357; t com caron
Ŧ &Tstrok; &#x00166; &#358; T com traço
ŧ &tstrok; &#x00167; &#359; t com traço
ù &ugrave; &#x000f9; &#249; u grave
ú &uacute; &#x000fa; &#250; u agudo
û &ucirc; &#x000fb; &#251; u circunflexo
ü &uuml; &#x000fc; &#252; u com trema
Ũ &Utilde; &#x00168; &#360; U com til
ũ &utilde; &#x00169; &#361; u com til
Ū &Umacr; &#x0016a; &#362; U com mácron
ū &umacr; &#x0016b; &#363; u com mácron
Ŭ &Ubreve; &#x0016c; &#364; U breve
ŭ &ubreve; &#x0016d; &#365; u breve
Ů &Uring; &#x0016e; &#366; U com anel
ů &uring; &#x0016f; &#367; u com anel
Ű &Udblac; &#x00170; &#368; U com duplo agudo
ű &udblac; &#x00171; &#369; u com duplo agudo
Ų &Uogon; &#x00172; &#370; U com ogonek
ų &uogon; &#x00173; &#371; u com ogonek
Ŵ &Wcirc; &#x00174; &#372; W com circunflexo
ŵ &wcirc; &#x00175; &#373; w com circunflexo
Ŷ &Ycirc; &#x00176; &#374; Y com circunflexo
ŷ &ycirc; &#x00177; &#375; y com circunflexo
Ÿ &Yuml; &#x00178; &#376; Y com trema
ÿ &yuml; &#x000ff; &#255; y com trema
Ý &Yacute; &#x000dd; &#221; Y com agudo
ý &yacute; &#x000fd; &#253; y com agudo
Ź &Zacute; &#x00179; &#377; Z com agudo
ź &zacute; &#x0017a; &#378; z com agudo
Ż &Zdot; &#x0017b; &#379; Z com ponto acima
ż &zdot; &#x0017c; &#380; z com ponto acima
Ž &Zcaron; &#x0017d; &#381; Z com caron
ž &zcaron; &#x0017e; &#382; z com caron
Ƶ &imped; &#x001b5; &#437; Z cortado
Ligaduras de Carateres Latinos
Caracter HTML HEX DEC Descrição
IJ &IJlig; &#x00132; &#306; ligadura IJ (maiúsculo)
ij &ijlig; &#x00133; &#307; ligadura ij (minúsculo)
Œ &OElig; &#x00152; amp;Œ; ligadura OE maiúscula
œ &oelig; &#x00153; &#339; ligadura oe minúscula
&fflig; &#x0fb00; &#64256; ligadura ff
&filig; &#x0fb01; &#64257; ligadura fi
&fllig; &#x0fb02; &#64258; ligadura fl
&ffilig; &#x0fb03; &#64259; ligadura ffi
&ffllig; &#x0fb04; &#64260; ligadura ffl
Carateres Não Latinos
Caracter HTML HEX DEC Descrição
ŋ &eng; &#x0014b; &#331; minúscula eng
Þ &Thorn; &#x000de; &#222; ‘thorn’ maiúsculo
þ &thorn; &#x000fe; &#254; ‘thorn’ minúsculo
ß &szlig; &#x000df; &#223; ‘eszett’ alemão
Caracteres script
Caracter HTML HEX DEC Descrição
𝒶 &ascr; &#x1d49c; &#119964; a minúsculo
𝒷 &bscr; &#x1d49D; &#119965; b
𝒸 &cscr; &#x1d49e; &#119966; c
𝒹 &dscr; &#x1d49f; &#119967; d
&escr; &#x1d4a0; &#119968; e
𝒻 &fscr; &#x1d4a1; &#119969; e
&gscr; &#x1d4a2; &#119970; g
𝒽 &hscr; &#x1d4a3; &#119971; h
𝒾 &iscr; &#x1d4a4; &#119972; i
𝒿 &jscr; &#x1d4a5; &#119973; j
𝓀 &kscr; &#x1d4a6; &#119974; k
𝓁 &lscr; &#x1d4a7; &#119975; l
𝓂 &mscr; &#x1d4a8; &#119976; m
𝓃 &nscr; &#x1d4a9; &#119977; n
&oscr; &#x1d4aa; &#119978; o
𝓅 &pscr; &#x1d4ab; &#119979; p
𝓆 &qscr; &#x1d4ac; &#119980; q
𝓇 &rscr; &#x1d4ad; &#119981; r
𝓈 &sscr; &#x1d4ae; &#119982; s
𝓉 &tscr; &#x1d4af; &#119983; t
𝓊 &uscr; &#x1d4b0; &#119984; u
𝓋 &vscr; &#x1d4b1; &#119985; v
𝓌 &wscr; &#x1d4b2; &#119986; w
𝓍 &xscr; &#x1d4b3; &#119987; x
𝓎 &yscr; &#x1d4b4; &#119988; y
𝓏 &zscr; &#x1d4b5; &#119989; z
Caracter HTML HEX DEC Descrição
𝒜 &Ascr; &#x1d4b6; &#119989; A maiúsculo
&Bscr; &#x1d4b7; &#119990; B
𝒞 &Cscr; &#x1d4b8; &#119991; C
𝒟 &Dscr; &#x1d4b9; &#119992; D
&Escr; &#x1d4ba; &#119993; E
&Fscr; &#x1d4bb; &#119994; F
𝒢 &Gscr; &#x1d4bc; &#119995; G
&Hscr; &#x1d4bd; &#119996; H
&Iscr; &#x1d4be; &#119997; I
𝒥 &Jscr; &#x1d4bf; &#119998; J
𝒦 &Kscr; &#x1d4c0; &#119999; K
&Lscr; &#x1d4c1; &#120000; L
&Mscr; &#x1d4c2; &#120001; M
𝒩 &Nscr; &#x1d4c3; &#120002; N
𝒪 &Oscr; &#x1d4c4; &#120003; O
𝒫 &Pscr; &#x1d4c5; &#120004; P
𝒬 &Qscr; &#x1d4c6; &#120005; Q
&Rscr; &#x1d4c7; &#120006; R
𝒮 &Sscr; &#x1d4c8; &#120007; S
𝒯 &Tscr; &#x1d4c9; &#120008; T
𝒰 &Uscr; &#x1d4ca; &#120009; U
𝒱 &Vscr; &#x1d4cb; &#120010; V
𝒲 &Wscr; &#x1d4cc; &#120011; W
𝒳 &Xscr; &#x1d4cd; &#120012; X
𝒴 &Yscr; &#x1d4ce; &#120013; Y
𝒵 &Zscr; &#x1d4ce; &#120014; Z
Caracteres Fraktur
Caracter HTML HEX DEC Descrição
𝔞 &afr; &#x1d504; &#120068; a minúsculo
𝔟 &bfr; &#x1d505; &#120069; b
𝔠 &cfr; &#x1d506; &#120070; c
𝔡 &dfr; &#x1d507; &#120071; d
𝔢 &efr; &#x1d508; &#120072; e
𝔣 &ffr; &#x1d509; &#120073; f
𝔤 &gfr; &#x1d50a; &#120074; g
𝔥 &hfr; &#x1d50b; &#120075; h
𝔦 &ifr; &#x1d50c; &#120076; i
𝔧 &jfr; &#x1d50d; &#120077; j
𝔨 &kfr; &#x1d50e; &#120078; k
𝔩 &lfr; &#x1d50f; &#120079; l
𝔪 &mfr; &#x1d510; &#120080; m
𝔫 &nfr; &#x1d511; &#120081; n
𝔬 &ofr; &#x1d512; &#120082; o
𝔭 &pfr; &#x1d513; &#120083; p
𝔮 &qfr; &#x1d514; &#120084; q
𝔯 &rfr; &#x1d515; &#120085; r
𝔰 &sfr; &#x1d516; &#120086; s
𝔱 &tfr; &#x1d517; &#120087; t
𝔲 &ufr; &#x1d518; &#120088; u
𝔳 &vfr; &#x1d519; &#120089; v
𝔴 &wfr; &#x1d51a; &#120090; w
𝔵 &xfr; &#x1d51b; &#120091; x
𝔶 &yfr; &#x1d51c; &#120092; y
𝔷 &zfr; &#x1d51d; &#120093; z
Caracter HTML HEX DEC Descrição
𝔄 &Afr; &#x1d51e; &#120094; A maiúsculo
𝔅 &Bfr; &#x1d51f; &#120095; B
&Cfr; &#x1d520; &#120096; C
𝔇 &Dfr; &#x1d521; &#120097; D
𝔈 &Efr; &#x1d522; &#120098; E
𝔉 &Ffr; &#x1d523; &#120099; F
𝔊 &Gfr; &#x1d524; &#120100; G
&Hfr; &#x1d525; &#120101; H
&Ifr; &#x1d526; &#120102; I
𝔍 &Jfr; &#x1d527; &#120103; J
𝔎 &Kfr; &#x1d528; &#120104; K
𝔏 &Lfr; &#x1d529; &#120105; L
𝔐 &Mfr; &#x1d52a; &#120106; M
𝔑 &Nfr; &#x1d52b; &#120107; N
𝔒 &Ofr; &#x1d52c; &#120108; O
𝔓 &Pfr; &#x1d52d; &#120109; P
𝔔 &Qfr; &#x1d52e; &#120110; Q
&Rfr; &#x1d52f; &#120111; R
𝔖 &Sfr; &#x1d530; &#120112; S
𝔗 &Tfr; &#x1d531; &#120113; T
𝔘 &Ufr; &#x1d532; &#120114; U
𝔙 &Vfr; &#x1d533; &#120115; V
𝔚 &Wfr; &#x1d534; &#120116; W
𝔛 &Xfr; &#x1d535; &#120117; X
𝔜 &Yfr; &#x1d536; &#120118; Y
&Zfr; &#x1d537; &#120119; Z
Caracteres de traço duplo
Caracter HTML HEX DEC Descrição
𝕒 &aopf; &#x1d538; &#120120; a minúsculo
𝕓 &bopf; &#x1d539; &#120121; b
𝕔 &copf; &#x1d539; &#120122; c
𝕕 &dopf; &#x1d53b; &#120123; d
𝕖 &eopf; &#x1d53c; &#120124; e
𝕗 &fopf; &#x1d53d; &#120125; f
𝕘 &gopf; &#x1d53e; &#120126; g
𝕙 &hopf; &#x1d53e; &#120127; h
𝕚 &iopf; &#x1d540; &#120128; i
𝕛 &jopf; &#x1d541; &#120129; j
𝕜 &kopf; &#x1d542; &#120130; k
𝕝 &lopf; &#x1d543; &#120131; l
𝕞 &mopf; &#x1d544; &#120132; m
𝕟 &nopf; &#x1d545; &#120133; n
𝕠 &oopf; &#x1d546; &#120134; o
𝕡 &popf; &#x1d547; &#120135; p
𝕢 &qopf; &#x1d548; &#120136; q
𝕣 &ropf; &#x1d549; &#120137; r
𝕤 &sopf; &#x1d54a; &#120138; s
𝕥 &topf; &#x1d54b; &#120139; t
𝕦 &uopf; &#x1d54c; &#120140; u
𝕧 &vopf; &#x1d54d; &#120141; v
𝕨 &wopf; &#x1d54e; &#120142; w
𝕩 &xopf; &#x1d54f; &#120143; x
𝕪 &yopf; &#x1d550; &#120144; y
𝕫 &zopf; &#x1d551; &#120145; z
Caracter HTML HEX DEC Descrição
𝔸 &Aopf; &#x1d552; &#120146; A maiúsculo
𝔹 &Bopf; &#x1d553; &#120147; B
&Copf; &#x1d554; &#120148; C
𝔻 &Dopf; &#x1d555; &#120149; D
𝔼 &Eopf; &#x1d556; &#120150; E
𝔽 &Fopf; &#x1d557; &#120151; F
𝔾 &Gopf; &#x1d558; &#120152; G
&Hopf; &#x1d559; &#120153; H
𝕀 &Iopf; &#x1d55a; &#120154; I
𝕁 &Jopf; &#x1d55b; &#120155; J
𝕂 &Kopf; &#x1d55c; &#120156; K
𝕃 &Lopf; &#x1d55d; &#120157; L
𝕄 &Mopf; &#x1d55e; &#120158; M
&Nopf; &#x1d55f; &#120159; N
𝕆 &Oopf; &#x1d560; &#120160; O
&Popf; &#x1d561; &#120161; P
&Qopf; &#x1d562; &#120162; Q
&Ropf; &#x1d563; &#120163; R
𝕊 &Sopf; &#x1d564; &#120164; S
𝕋 &Topf; &#x1d565; &#120165; T
𝕌 &Uopf; &#x1d566; &#120166; U
𝕍 &Vopf; &#x1d567; &#120167; V
𝕎 &Wopf; &#x1d568; &#120168; W
𝕏 &Xopf; &#x1d569; &#120169; X
𝕐 &Yopf; &#x1d56a; &#120170; Y
&Zopf; &#x1d56b; &#120171; Z
Caracteres Gregos
Caracter HTML HEX DEC Descrição
α &alpha; &#x00391; &#913; alfa maiúsculo grego
β &beta; &#x00392; &#914; beta maiúsculo grego
γ &gamma; &#x00393; &#915; gama grega maiúscula
δ &delta; &#x00394; &#916; letra maiúscula grega delta
ε &epsilon; &#x00395; &#917; epsilon com letra maiúscula grega
ζ &zeta; &#x00396; &#918; zeta letra maiúscula grega
η &eta; &#x00397; &#919; letra maiúscula grega eta
θ &theta; &#x00398; &#920; teta maiúscula grega
ι &iota; &#x00399; &#921; letra maiúscula iota
κ &kappa; &#x0039a; &#922; kappa maiúscula
λ &lambda; &#x0039b; &#923; lamda letra maiúscula
μ &mu; &#x0039c; &#924; letra maiúscula mu
ν &nu; &#x0039d; &#925; nu maiúsculo
ξ &xi; &#x0039e; &#926; letra maiúscula xi
ο &omicron; &#x0039f; &#927; omicron maiúsculo
π &pi; &#x003a0; &#928; pi maiúscula
ρ &rho; &#x003a1; &#929; rho maiúsculo
σ &sigma; &#x003a3; &#931; sigma maiúsculo
τ &tau; &#x003a4; &#932; tau maiúsculo
υ &upsilon; &#x003a5; &#933; upsilon maiúsculo
φ &phi; &#x003a6; &#934; letra maiúscula phi
χ &chi; &#x003a7; &#935; maiúsculo chi
ψ &psi; &#x003a8; &#936; letra maiúscula psi
ω &omega; &#x003a9; &#937; ômega letra maiúscula
ς &sigmav; &varsigma; &sigmaf; &#x003c2; &#962; final sigma minúsculo
ϑ &thetav; &vartheta; &thetasym; &#x003d1; &#977; letra minúscula theta
υ ϒ &upsi; &upsih; &#x003d2; &#978; Upsilon com símbolo de gancho
φ &straightphi; &#x003d5; &#981; letra minúscula phi
ϖ &piv; &varpi; &#x003d6; &#982; letra minúscula pi
ϝ &gammad; &#x003dc; &#988; letra digamma
ϰ &kappav; &varkappa; &#x003f0; &#1008; símbolo kappa
ϱ &rhov; &varrho; &#x003f1; &#1009; letra rho
ε &epsi; &straightepsilon; &#x003f5; &#1013; épsilon semilunar
϶ &bepsi; &backepsilon; &#x003f6; &#1014; épsilon semilunar invertido

Letras gregas maiúsculas podem ser obtidas com o primeiro caracter em maiúsculo no codigo html. Por exemplo:

&Lambda; = Λ, &Sigma; = Σ, &Omega; = Ω.

A maioria das letra gregas maiúsculas são parecida com as do alfabeto latino e, por isso, não são muito úteis na simbologia.

Caracteres Cirílicos
Caracter HTML HEX DEC Descrição
ё &iocy; &#x00401; &#1025; cirílico maiúsculo io
ђ &djcy; &#x00402; &#1026; dje cirílico maiúsculo
ѓ &gjcy; &#x00403; &#1027; cirílico maiúsculo gje
є &jukcy; &#x00404; &#1028; cirílico maiúsculo ucraniano, isto é
ѕ &dscy; &#x00405; &#1029; cirílico maiúsculo dze
і &iukcy; &#x00406; &#1030; cirílico maiúsculo bielorrussa-ucraniana i
ї &yicy; &#x00407; &#1031; cirílico maiúsculo yi
ј &jsercy; &#x00408; &#1032; cirílico maiúsculo je
љ &ljcy; &#x00409; &#1033; cirílico maiúsculo lje
њ &njcy; &#x0040a; &#1034; cirílico maiúsculo nje
ћ &tshcy; &#x0040b; &#1035; cirílico maiúsculo tshe
ќ &kjcy; &#x0040c; &#1036; cirílico maiúsculo kje
ў &ubrcy; &#x0040e; &#1038; cirílico maiúsculo curta u
џ &dzcy; &#x0040f; &#1039; cirílico maiúsculo dzhe
а &acy; &#x00410; &#1040; cirílico maiúsculo a
б &bcy; &#x00411; &#1041; cirílico maiúsculo seja
в &vcy; &#x00412; &#1042; cirílico maiúsculo ve
г &gcy; &#x00413; &#1043; cirílico maiúsculo ghe
д &dcy; &#x00414; &#1044; cirílico maiúsculo de
е &iecy; &#x00415; &#1045; cirílico maiúsculo , ou seja
ж &zhcy; &#x00416; &#1046; cirílico maiúsculo zhe
з &zcy; &#x00417; &#1047; cirílico maiúsculo ze
и &icy; &#x00418; &#1048; cirílico maiúsculo i
й &jcy; &#x00419; &#1049; cirílico maiúsculo curto i
к &kcy; &#x0041a; &#1050; cirílico maiúsculo ka
л &lcy; &#x0041b; &#1051; cirílico maiúsculo el
м &mcy; &#x0041c; &#1052; cirílico maiúsculo em
н &ncy; &#x0041d; &#1053; cirílico maiúsculo en
о &ocy; &#x0041e; &#1054; cirílico maiúsculo o
п &pcy; &#x0041f; &#1055; cirílico maiúsculo pe
р &rcy; &#x00420; &#1056; cirílico maiúsculo
с &scy; &#x00421; &#1057; letras maiúsculas cirílicas
т &tcy; &#x00422; &#1058; cirílico maiúsculo te
у &ucy; &#x00423; &#1059; cirílico maiúsculo u
ф &fcy; &#x00424; &#1060; cirílico maiúsculo ef
х &khcy; &#x00425; &#1061; cirílico maiúsculo ha
ц &tscy; &#x00426; &#1062; cirílico maiúsculo tse
ч &chcy; &#x00427; &#1063; cirílico maiúsculo che
ш &shcy; &#x00428; &#1064; cirílico maiúsculo sha
щ &shchcy; &#x00429; &#1065; cirílico maiúsculo shcha
ъ &hardcy; &#x0042a; &#1066; cirílico maiúsculo
ы &ycy; &#x0042b; &#1067; cirílico maiúsculo yeru
ь &softcy; &#x0042c; &#1068; signo suave em cirílico maiúsculo
э &ecy; &#x0042d; &#1069; cirílico maiúsculo e
ю &yucy; &#x0042e; &#1070; cirílico maiúsculo yu
я &yacy; &#x0042f; &#1071; cirílico maiúsculo ya
а &acy; &#x00430; &#1072; cirílico minúsculo a
б &bcy; &#x00431; &#1073; cirílico minúsculo seja
в &vcy; &#x00432; &#1074; cirílico minúsculo ve
г &gcy; &#x00433; &#1075; cirílico minúsculo ghe
д &dcy; &#x00434; &#1076; cirílico minúsculo de
е &iecy; &#x00435; &#1077; cirílico minúsculo , isto é
ж &zhcy; &#x00436; &#1078; cirílico minúsculo zhe
з &zcy; &#x00437; &#1079; letra minúscula ze cirílica
и &icy; &#x00438; &#1080; cirílico minúsculo i
й &jcy; &#x00439; &#1081; cirílico minúsculo curta i
к &kcy; &#x0043a; &#1082; letra minúscula ka cirílica
л &lcy; &#x0043b; &#1083; letra minúscula el cirílica
м &mcy; &#x0043c; &#1084; em minúsculo cirílico
н &ncy; &#x0043d; &#1085; cirílico minúsculo en
о &ocy; &#x0043e; &#1086; minúsculo cirílico o
п &pcy; &#x0043f; &#1087; pe minúsculo cirílico
р &rcy; &#x00440; &#1088; cirílico minúsculo er
с &scy; &#x00441; &#1089; letras minúsculas cirílicas
т &tcy; &#x00442; &#1090; cirílico minúsculo te
у &ucy; &#x00443; &#1091; letra minúscula você em cirílico
ф &fcy; &#x00444; &#1092; cirílico minúsculo ef
х &khcy; &#x00445; &#1093; cirílico minúsculo ha
ц &tscy; &#x00446; &#1094; cirílico minúsculo tse
ч &chcy; &#x00447; &#1095; cirílico minúsculo che
ш &shcy; &#x00448; &#1096; letra minúscula em cirílico sha
щ &shchcy; &#x00449; &#1097; cirílico minúsculo shcha
ъ &hardcy; &#x0044a; &#1098; letra minúscula cirílico duro
ы &ycy; &#x0044b; &#1099; cirílico minúsculo yeru
ь &softcy; &#x0044c; &#1100; signo macio de letra minúscula cirílico
э &ecy; &#x0044d; &#1101; letra minúscula e cirílica
ю &yucy; &#x0044e; &#1102; letra minúscula em cirílico, você
я &yacy; &#x0044f; &#1103; cirílico minúsculo sim
ё &iocy; &#x00451; &#1105; cirílico minúsculo io
ђ &djcy; &#x00452; &#1106; dje cirílico minúsculo
ѓ &gjcy; &#x00453; &#1107; cirílico minúsculo gje
є &jukcy; &#x00454; &#1108; cirílico minúsculo ucraniana, ie
ѕ &dscy; &#x00455; &#1109; cirílico minúsculo dze
і &iukcy; &#x00456; &#1110; cirílico minúsculo bielorrussa-ucraniana i
ї &yicy; &#x00457; &#1111; letra minúscula em cirílico yi
ј &jsercy; &#x00458; &#1112; je minúsculo cirílico
љ &ljcy; &#x00459; &#1113; cirílico minúsculo lje
њ &njcy; &#x0045a; &#1114; nje minúsculo cirílico
ћ &tshcy; &#x0045b; &#1115; cirílico minúsculo tshe
ќ &kjcy; &#x0045c; &#1116; kje minúsculo cirílico
ў &ubrcy; &#x0045e; &#1118; cirílico minúsculo curta u
џ &dzcy; &#x0045f; &#1119; dzhe minúsculo cirílico
Miscelânia de Caracteres
Caracter HTML HEX DEC Descrição
&vzigzag; &#x0299a; &#10650; linha em ziguezague vertical
&vangrt; &#x0299c; &#10652; variante de ângulo reto com quadrado
&angrtvbd; &#x0299d; &#10653; ângulo reto medido com ponto
&ange; &#x029a4; &#10660; ângulo com barra
&range; &#x029a5; &#10661; ângulo invertido com barra inferior
&dwangle; &#x029a6; &#10662; ângulo oblíquo abrindo
&uwangle; &#x029a7; &#10663; abertura de ângulo oblíquo para baixo
&angmsdaa; &#x029a8; &#10664; medida de ângulo, seta nordeste
&angmsdab; &#x029a9; &#10665; medida de ângulo, seta oeste
&angmsdac; &#x029aa; &#10666; medida de ângulo, seta sudeste
&angmsdad; &#x029ab; &#10667; medida de ângulo, seta sudoeste
&angmsdae; &#x029ac; &#10668; medida de ângulo, seta nordeste
&angmsdaf; &#x029ad; &#10669; medida de ângulo, seta oeste
&angmsdag; &#x029ae; &#10670; medida de ângulo, seta sudeste
&angmsdah; &#x029af; &#10671; medida de ângulo, seta nordeste
&bemptyv; &#x029b0; &#10672; conjunto vazio invertido
&demptyv; &#x029b1; &#10673; conjunto vazio com barra superior
&cemptyv; &#x029b2; &#10674; conjunto vazio com círculo acima
&raemptyv; &#x029b3; &#10675; conjunto vazio com a seta direita acima
&laemptyv; &#x029b4; &#10676; conjunto vazio com seta esquerda acima
&ohbar; &#x029b5; &#10677; círculo com barra horizontal
&omid; &#x029b6; &#10678; barra vertical circulada
&opar; &#x029b7; &#10679; circulado paralelo
&operp; &#x029b9; &#10681; circular perpendicular
&olcross; &#x029bb; &#10683; círculo com x sobreposto
&odsold; &#x029bc; &#10684; divisão girado no sentido anti-horário
&olcir; &#x029be; &#10686; bala branca circulada
⦿ &ofcir; &#x029bf; &#10687; bala circulada
&olt; &#x029c0; &#10688; circulou menos que
&ogt; &#x029c1; &#10689; circulado maior que
&cirscir; &#x029c2; &#10690; círculo com círculo menor à direita
&cire; &#x029c3; &#10691; círculo com dois traços horizontais para a direita
&solb; &#x029c4; &#10692; barra diagonal ascendente quadrada
&bsolb; &#x029c5; &#10693; quadrado caindo barra diagonal
&boxbox; &#x029c9; &#10697; dois quadrados unidos
&trisb; &#x029cd; &#10701; triângulo com serifas na parte inferior
&rtriltri; &#x029ce; &#10702; triângulo direito acima do triângulo esquerdo
&lefttrianglebar; &#x029cf; &#10703; triângulo esquerdo ao lado da barra vertical
&righttrianglebar; &#x029d0; &#10704; barra vertical ao lado do triângulo retângulo
∽̱ &race; &#x029da; &#10714; cerca esquerda dupla ondulada
&eparsl; &#x029e3; &#10723; é igual a sinal e paralelo inclinado
&smeparsl; &#x029e4; &#10724; é igual ao sinal e inclinado paralelo ao til acima
&eqvparsl; &#x029e5; &#10725; idêntico e inclinado paralelo
&dsol; &#x029f6; &#10742; barra com traço superior
&pluscir; &#x02a22; &#10786; mais com ponto acima
&plusacir; &#x02a23; &#10787; mais com circunflexo acima
&simplus; &#x02a24; &#10788; mais com til acima
&plusdu; &#x02a25; &#10789; mais com ponto abaixo
&plussim; &#x02a26; &#10790; mais com til abaixo
&plustwo; &#x02a27; &#10791; mais com subscrito dois
&mcomma; &#x02a29; &#10793; menos com vírgula acima
&minusdu; &#x02a2a; &#10794; menos com ponto abaixo
&loplus; &#x02a2d; &#10797; mais no semicírculo esquerdo
&roplus; &#x02a2e; &#10798; mais no semicírculo direito
&cross; &#x02a2f; &#10799; vetor ou produto vetorial
&timesd; &#x02a30; &#10800; multiplicação com ponto acima
&timesbar; &#x02a31; &#10801; multiplicação com barra inferior
&smashp; &#x02a33; &#10803; esmagar produto
&lotimes; &#x02a34; &#10804; multiplicação no semicírculo esquerdo
&rotimes; &#x02a35; &#10805; multiplicação no semicírculo direito
&otimesas; &#x02a36; &#10806; multiplicação circulado com acento circunflexo
&otimes; &#x02a37; &#10807; multiplicação em círculo duplo
&odiv; &#x02a38; &#10808; divisão circulado
&triplus; &#x02a39; &#10809; mais no triângulo
&triminus; &#x02a3a; &#10810; menos no triângulo
&tritime; &#x02a3b; &#10811; multiplicação em triângulo
⨼ ⨼ &iprod; &intprod; &#x02a3c; &#10812; produto interior
⨿ &amalg; &#x02a3f; &#10815; amalgamação ou coproduto
&capdot; &#x02a40; &#10816; intersecção com ponto
&ncup; &#x02a42; &#10818; união com barra superior
&ncap; &#x02a43; &#10819; intersecção com barra superior
&capand; &#x02a44; &#10820; intersecção com lógica e
&cupor; &#x02a45; &#10821; união com lógica ou
&cupcap; &#x02a46; &#10822; união acima da interseção
&capcup; &#x02a47; &#10823; intersecção acima da união
&cupbrcap; &#x02a48; &#10824; união acima da barra acima da interseção
&capbrcup; &#x02a49; &#10825; intersecção acima da barra acima da união
&cupcup; &#x02a4a; &#10826; união ao lado e juntou-se a união
&capcap; &#x02a4b; &#10827; intersecção ao lado e junta com intersecção
&ccups; &#x02a4c; &#10828; união fechada com serifas
&ccaps; &#x02a4d; &#10829; interseção fechada com serifas
&ccupssm; &#x02a50; &#10832; união fechada com serifas e produto esmagado
&and; &#x02a53; &#10835; duplo lógico e
&or; &#x02a54; &#10836; duplo lógico ou
&andand; &#x02a55; &#10837; dois lógicos que se cruzam e
&oror; &#x02a56; &#10838; dois lógicos que se cruzam ou
&orslope; &#x02a57; &#10839; grande inclinado ou
&andslope; &#x02a58; &#10840; inclinado grande e
&andv; &#x02a5a; &#10842; lógico e com haste intermediária
&orv; &#x02a5b; &#10843; lógico ou com haste intermediária
&andd; &#x02a5c; &#10844; lógico e com traço horizontal
&ord; &#x02a5d; &#10845; lógico ou com traço horizontal
&wedbar; &#x02a5f; &#10847; lógico e com barra inferior
&sdote; &#x02a66; &#10854; é igual ao sinal com o ponto abaixo
&simdot; &#x02a6a; &#10858; Operador til com ponto acima
&congdot; &#x02a6d; &#10861; congruente com o ponto acima
&easter; &#x02a6e; &#10862; é igual a asterisco
&apacir; &#x02a6f; &#10863; quase igual a com acento circunflexo
&ape; &#x02a70; &#10864; aproximadamente igual ou igual a
&eplus; &#x02a71; &#10865; igual acima do mais
&pluse; &#x02a72; &#10866; mais acima do igual
&esim; &#x02a73; &#10867; igual ao sinal acima do operador til
&colone; &#x02a74; &#10868; dois pontos duplos iguais
&equal; &#x02a75; &#10869; dois sinais de igual consecutivos
&eddot; &ddotseq; &#x02a77; &#10871; é igual ao sinal com dois pontos acima e dois pontos abaixo
&equivdd; &#x02a78; &#10872; equivalente com quatro pontos acima
&ltcir; &#x02a79; &#10873; menos do que com um círculo dentro
&gtcir; &#x02a7a; &#10874; maior do que com um círculo dentro
&ltquest; &#x02a7b; &#10875; menos do que com ponto de interrogação acima
&gtquest; &#x02a7c; &#10876; maior do que com ponto de interrogação acima
&les; &lessslantequal; &leqslant; &#x02a7d; &#10877; menor ou inclinado igual a
&ges; &greaterslantequal; &geqslant; &#x02a7e; &#10878; maior que ou inclinado igual a
⩿ &lesdot; &#x02a7f; &#10879; menor que ou inclinado igual a com ponto dentro
&gesdot; &#x02a80; &#10880; maior que ou inclinado igual a com ponto dentro
&lesdoto; &#x02a81; &#10881; menor que ou inclinado igual ao ponto acima
&gesdoto; &#x02a82; &#10882; maior que ou inclinado igual ao ponto acima
&lesdotor; &#x02a83; &#10883; menor que ou inclinado igual a com ponto acima à direita
&gesdotol; &#x02a84; &#10884; maior que ou inclinado igual ao ponto acima à esquerda
&lap; &lessapprox; &#x02a85; &#10885; menor ou aproximado
&gap; &gtrapprox; &#x02a86; &#10886; maior que ou aproximado
&lne; &lneq; &#x02a87; &#10887; menor que e linha única não igual a
&gne; &gneq; &#x02a88; &#10888; maior que e linha única diferente de
&lnap; &lnapprox; &#x02a89; &#10889; inferior e não aproximado
&gnap; &gnapprox; &#x02a8a; &#10890; maior que e não aproximado
&leg; &lesseqqgtr; &#x02a8b; &#10891; menor que acima linha dupla igual acima maior que
&gel; &gtreqqless; &#x02a8c; &#10892; maior que acima linha dupla igual acima menor que
&lsime; &#x02a8d; &#10893; menor que acima semelhante ou igual
&gsime; &#x02a8e; &#10894; maior do que acima semelhante ou igual
&lsimg; &#x02a8f; &#10895; menor que acima semelhante acima maior que
&gsiml; &#x02a90; &#10896; maior que acima semelhante acima menor que
&lge; &#x02a91; &#10897; menor que acima maior que acima linha dupla igual
&gle; &#x02a92; &#10898; maior que acima menor que acima linha dupla igual
&lesges; &#x02a93; &#10899; menor que acima inclinado igual acima maior que acima inclinado igual
&gesles; &#x02a94; &#10900; maior que acima inclinado igual acima menor que acima inclinado igual
&els; &eqslantless; &#x02a95; &#10901; inclinado igual ou menor que
&egs; &eqslantgtr; &#x02a96; &#10902; inclinado igual ou maior que
&elsdot; &#x02a97; &#10903; inclinado igual ou menor do que com ponto dentro
&egsdot; &#x02a98; &#10904; inclinado igual ou maior que com ponto dentro
&el; &#x02a99; &#10905; linha dupla igual ou menor que
&eg; &#x02a9a; &#10906; linha dupla igual ou maior que
&siml; &#x02a9d; &#10909; semelhante ou inferior
&simg; &#x02a9e; &#10910; semelhante ou maior que
&simle; &#x02a9f; &#10911; semelhante acima menos-que acima é igual ao sinal
&simge; &#x02aa0; &#10912; semelhante acima maior do que acima é igual ao sinal
&lessless; &#x02aa1; &#10913; duplo aninhado menor que
&greatergreater; &#x02aa2; &#10914; aninhado duplo maior que
&glj; &#x02aa4; &#10916; maior que sobreposição menor que
&gla; &#x02aa5; &#10917; maior que ao lado menor que
&ltcc; &#x02aa6; &#10918; menos que fechado por curva
&gtcc; &#x02aa7; &#10919; maior que fechado por curva
&lescc; &#x02aa8; &#10920; menos que fechado pela curva acima inclinado igual
&gescc; &#x02aa9; &#10921; maior que fechado por curva acima inclinado igual
&smt; &#x02aaa; &#10922; menor que
&lat; &#x02aab; &#10923; maior que
&smte; &#x02aac; &#10924; menor ou igual a
&late; &#x02aad; &#10925; maior ou igual a
&bumpe; &#x02aae; &#10926; é igual ao sinal com irregular acima
&pre; &preceq; &precedesequal; &#x02aaf; &#10927; precede acima do igual de linha única
&sce; &succeq; &succeedsequal; &#x02ab0; &#10928; sucede acima do igual de linha única
&pre; &#x02ab3; &#10931; precede o igual
&sce; &#x02ab4; &#10932; sucede acima do igual
&prne; &precneqq; &#x02ab5; &#10933; precede acima não é igual a
&scne; &succneqq; &#x02ab6; &#10934; sucede acima não igual a
&prap; &precapprox; &#x02ab7; &#10935; precede acima quase igual a
&scap; &succapprox; &#x02ab8; &#10936; sucede acima de quase igual a
&prnap; &precnapprox; &#x02ab9; &#10937; precede acima, não quase igual a
&scnap; &succnapprox; &#x02aba; &#10938; sucede acima, não quase igual a
&pr; &#x02abb; &#10939; o dobro precede
&sc; &#x02abc; &#10940; sucesso duplo
&subdot; &#x02abd; &#10941; subconjunto com ponto
&supdot; &#x02abe; &#10942; superconjunto com ponto
⪿ &subplus; &#x02abf; &#10943; subconjunto com mais abaixo
&supplus; &#x02ac0; &#10944; superconjunto com mais abaixo
&submult; &#x02ac1; &#10945; subconjunto com multiplicação abaixo
&supmult; &#x02ac2; &#10946; superconjunto com multiplicação abaixo
&subedot; &#x02ac3; &#10947; subconjunto de ou igual a com ponto acima
&supedot; &#x02ac4; &#10948; superconjunto de ou igual a com ponto acima
&sube; &subseteqq; &#x02ac5; &#10949; subconjunto de igual acima
&supe; &supseteqq; &#x02ac6; &#10950; superconjunto de igual acima
&subsim; &#x02ac7; &#10951; subconjunto do operador til acima
&supsim; &#x02ac8; &#10952; superconjunto do operador til acima
&subne; &subsetneqq; &#x02acb; &#10955; subconjunto de acima não igual a
&supne; &supsetneqq; &#x02acc; &#10956; superconjunto de acima não igual a
&csub; &#x02acf; &#10959; subconjunto fechado
&csup; &#x02ad0; &#10960; superconjunto fechado
&csube; &#x02ad1; &#10961; subconjunto fechado ou igual a
&csupe; &#x02ad2; &#10962; superconjunto fechado ou igual a
&subsup; &#x02ad3; &#10963; subconjunto acima do superconjunto
&supsub; &#x02ad4; &#10964; superconjunto acima do subconjunto
&subsub; &#x02ad5; &#10965; subconjunto acima do subconjunto
&supsup; &#x02ad6; &#10966; superconjunto acima do superconjunto
&suphsub; &#x02ad7; &#10967; superconjunto ao lado do subconjunto
&supdsub; &#x02ad8; &#10968; superconjunto ao lado e unido por traço com subconjunto
&forkv; &#x02ad9; &#10969; elemento de abertura para baixo
&topfork; &#x02ada; &#10970; forcado com camiseta
&mlcp; &#x02adb; &#10971; intersecção transversal
&dashv; &doublelefttee; &#x02ae4; &#10980; barra vertical dupla catraca esquerda
&vdashl; &#x02ae6; &#10982; traço longo do membro esquerdo da vertical dupla
&barv; &#x02ae7; &#10983; amura curta com barra superior
&vbar; &#x02ae8; &#10984; amura curta com barra inferior
&vbarv; &#x02ae9; &#10985; amura curta acima da amura curta
&vbar; &#x02aeb; &#10987; dobrar a aderência
¬ &not; &#x02aec; &#10988; traço duplo não sinal
&bnot; &#x02aed; &#10989; traço duplo invertido sem sinal
&rnmid; &#x02aee; &#10990; não divide com barra de negação invertida
&cirmid; &#x02aef; &#10991; linha vertical com círculo acima
&midcir; &#x02af0; &#10992; linha vertical com círculo abaixo
&topcir; &#x02af1; &#10993; rumo para baixo com círculo abaixo
&nhpar; &#x02af2; &#10994; paralelo com traço horizontal
&parsim; &#x02af3; &#10995; paralelo com operador til
&parsl; &#x02afd; &#11005; operador barra dupla
Sinais Gráficos
Caracter HTML HEX DEC Descrição
&dagger; &#x02020; &#8224; punhal
&dagger; &ddagger; &#x02021; &#8225; punhal duplo
&bull; &bullet; &#x02022; &#8226; bala
&nldr; &#x02025; &#8229; líder de dois pontos
&hellip; &mldr; &#x02026; &#8230; elipse horizontal
&permil; &#x02030; &#8240; por milhar
&pertenk; &#x02031; &#8241; por dez mil
&prime; &#x02032; &#8242; aspas
&prime; &#x02033; &#8243; aspas dupla
&tprime; &#x02034; &#8244; aspas tripla
&qprime; &#x02057; &#8279; aspas quádruplas
&bprime; &backprime; &#x02035; &#8245; aspas invertida
&lsaquo; &#x02039; &#8249; única aspa angular esquerda
&rsaquo; &#x0203a; &#8250; única aspa angular direita
&oline; &#x0203e; &#8254; overline
&caret; &#x02041; &#8257; ponto de inserção acento circunflexo
&hybull; &#x02043; &#8259; hífen marcador
&frasl; &#x02044; &#8260; barra de fração
&bsemi; &#x0204f; &#8271; ponto e vírgula invertido
&applyfunction; &af; &#x02061; &#8289; aplicação de função
&tdot; &tripledot; &#x020db; &#8411; combinando três pontos acima
&copf; &complexes; &#x02102; &#8450; c maiúsculo duplo
&incare; &#x02105; &#8453; aos cuidados de
&gscr; &#x0210a; &#8458; script g minúsculo
&hamilt; &hilbertspace; &hscr; &#x0210b; &#8459; script h maiúsculo
𝔥 &hfr; &poincareplane; &#x0210c; &#8460; letra preta h maiúsculo
𝕙 &quaternions; &hopf; &#x0210d; &#8461; H maiúsculo duplo
&planckh; &#x0210e; &#8462; constante de planck
&planck; &hbar; &plankv; &hslash; &#x0210f; &#8463; Planck cortado
&iscr; &imagline; &#x02110; &#8464; script I maiúsculo
&image; &im; &imagpart; &ifr; &#x02111; &#8465; letra preta i maiúscula
&lscr; &lagran; &laplacetrf; &#x02112; &#8466; script l maiúsculo
&ell; &#x02113; &#8467; roteiro pequeno l
&nopf; &naturals; &#x02115; &#8469; n maiúsculo duplo
&numero; &#x02116; &#8470; sinal numero
&copysr; &#x02117; &#8471; gravação de som copyright
&weierp; &wp; &#x02118; &#8472; script p
&popf; &primes; &#x02119; &#8473; p maiúsculo duplo
&rationals; &qopf; &#x0211a; &#8474; Q maiúsculo duplo
&rscr; &realine; &#x0211b; &#8475; script r maiúsculo
&real; &re; &realpart; &rfr; &#x0211c; &#8476; reais
&reals; &ropf; &#x0211d; &#8477; r maiúsculo duplo
&rx; &#x0211e; &#8478; receita
&trade; &trade; &#x02122; &#8482; marca registrada
&integers; &zopf; &#x02124; &#8484; z maiúsculo duplo
Ω &ohm; &#x02126; &#8486; ohm (resistência)
&mho; &#x02127; &#8487; ohm invertido
&zfr; &zeetrf; &#x02128; &#8488; preta z maiúscula
&iiota; &#x02129; &#8489; minúsculo iota
Å &angst; &#x0212b; &#8491; angstrom
&bernou; &bernoullis; &#x0212c; &#8492; script b maiúsculo
&cfr; &cayleys; &#x0212d; &#8493; letra preta c maiúscula
&escr; &#x0212f; &#8495; script e pequeno
&escr; &expectation; &#x02130; &#8496; script e maiúsculo
&fscr; &fouriertrf; &#x02131; &#8497; script f maiúsculo
&phmmat; &mellintrf; &mscr; &#x02133; &#8499; script m
&order; &orderof; &oscr; &#x02134; &#8500; script pequeno o
&alefsym; &aleph; &#x02135; &#8501; símbolo alef
&beth; &#x02136; &#8502; símbolo de aposta
&gimel; &#x02137; &#8503; símbolo gimel
&daleth; &#x02138; &#8504; símbolo dalet
&dD; &#x02145; &#8517; itálico duplo traçado d
&dd; &#x02146; &#8518; itálico duplo traçado pequeno d
&exponentiale; &exponentiale; &ee; &#x02147; &#8519; e pequeno itálico duplo traçado
&imaginaryi; &ii; &#x02148; &#8520; itálico duplo traçado pequeno i
&prop; &propto; &vprop; &#x0221d; &#8733; proporcional a
&infin; &#x0221e; &#8734; infinito
&angrt; &#x0221f; &#8735; ângulo reto
&ang; &#x02220; &#8736; ângulo
&angmsd; &measuredangle; &#x02221; &#8737; ângulo medido
&angsph; &#x02222; &#8738; ângulo esférico
&mid; &smid; &shortmid; &#x02223; &#8739; divide
&nmid; &nsmid; &nshortmid; &#x02224; &#8740; não divide
&par; &parallel; &spar; &#x02225; &#8741; paralelo a
&npar; &nparallel; &#x02226; &#8742; não paralelo a
&and; &wedge; &#x02227; &#8743; lógico e
&or; &vee; &#x02228; &#8744; lógico ou
&cap; &#x02229; &#8745; interseção
&cup; &#x0222a; &#8746; união
&there4; &therefore; &therefore; &#x02234; &#8756; portanto
&becaus; &because; &because; &#x02235; &#8757; porque
&ratio; &#x02236; &#8758; razão
: &colon; &proportion; &#x02237; &#8759; proporção
&minusd; &dotminus; &#x02238; &#8760; ponto menos
&homtht; &#x0223b; &#8763; homotético
&sim; &thksim; &thicksim; &#x0223c; &#8764; operador til
&bsim; &backsim; &#x0223d; &#8765; til invertido
&ac; &mstpos; &#x0223e; &#8766; S invertido
&acd; &#x0223f; &#8767; senoidal
&wreath; &verticaltilde; &wr; &#x02240; &#8768; produto de coroa
&nsim; &nottilde; &#x02241; &#8769; til cortado
&esim; &equaltilde; &eqsim; &#x02242; &#8770; menos til
&sime; &tildeequal; &simeq; &#x02243; &#8771; assintoticamente igual a
&nsime; &nsimeq; &nottildeequal; &#x02244; &#8772; não assintoticamente igual a
&cong; &tildefullequal; &#x02245; &#8773; aproximadamente igual a
&simne; &#x02246; &#8774; aproximadamente, mas não exatamente igual
&ncong; &nottildefullequal; &#x02247; &#8775; nem aproximado nem igual a
&asymp; &ap; &approx; &thkap; &thickapprox; &#x02248; &#8776; quase igual
&nap; &nottildetilde; &napprox; &#x02249; &#8777; não quase igual
&ape; &approxeq; &#x0224a; &#8778; quase igual ou igual
&apid; &#x0224b; &#8779; triplo till
&bcong; &backcong; &#x0224c; &#8780; todos iguais a
&asympeq; &cupcap; &#x0224d; &#8781; equivalente a
&bump; &humpdownhump; &bumpeq; &#x0224e; &#8782; geometricamente equivalente a
&bumpe; &humpequal; &bumpeq; &#x0224f; &#8783; diferença entre
&esdot; &dotequal; &doteq; &#x02250; &#8784; aproxima-se do limite
ė &edot; &doteqdot; &#x02251; &#8785; geometricamente igual a
&colone; &coloneq; &#x02254; &#8788; dois pontos igual
&ecolon; &eqcolon; &#x02255; &#8789; igual dois pontos
&ecir; &eqcirc; &#x02256; &#8790; anel igual a
&cire; &circeq; &#x02257; &#8791; anel igual a
&wedgeq; &#x02259; &#8793; estimativas
&veeeq; &#x0225a; &#8794; equiangular para
&trie; &triangleq; &#x0225c; &#8796; delta igual
&equest; &questeq; &#x0225f; &#8799; igual questionado
&ne; &notequal; &#x02260; &#8800; não é igual a (diferente)
&equiv; &congruent; &#x02261; &#8801; idêntico a
&nequiv; &notcongruent; &#x02262; &#8802; não idêntico a
&le; &leq; &#x02264; &#8804; menor ou igual
&ge; &greaterequal; &geq; &#x02265; &#8805; maior ou igual
&le; &lessfullequal; &leqq; &#x02266; &#8806; menor que, igual
&ge; &greaterfullequal; &geqq; &#x02267; &#8807; maior do, igual
&lne; &lneqq; &#x02268; &#8808; menor que, diferente
&gne; &gneqq; &#x02269; &#8809; maior que, diferente
< &lt; &nestedlessless; &ll; &#x0226a; &#8810; muito menor que
> &gt; &nestedgreatergreater; &gg; &#x0226b; &#8811; muito maior que
&twixt; &between; &#x0226c; &#8812; entre
&nlt; &notless; &nless; &#x0226e; &#8814; não menor que
&ngt; &notgreater; &ngtr; &#x0226f; &#8815; não maior que
&nle; &notlessequal; &nleq; &#x02270; &#8816; nem menor que nem igual a
&nge; &notgreaterequal; &ngeq; &#x02271; &#8817; nem maior que nem igual a
&lsim; &lesstilde; &lesssim; &#x02272; &#8818; menor ou equivalente a
&gsim; &gtrsim; &greatertilde; &#x02273; &#8819; maior que ou equivalente a
&nlsim; &notlesstilde; &#x02274; &#8820; nem menor que nem equivalente a
&ngsim; &notgreatertilde; &#x02275; &#8821; nem maior que nem equivalente a
&lg; &lessgtr; &lessgreater; &#x02276; &#8822; menor ou maior que
&gl; &gtrless; &greaterless; &#x02277; &#8823; maior ou menor que
&ntlg; &notlessgreater; &#x02278; &#8824; nem menor que nem maior que
&ntgl; &notgreaterless; &#x02279; &#8825; nem maior que nem menor que
&pr; &precedes; &prec; &#x0227a; &#8826; precede
&sc; &succeeds; &succ; &#x0227b; &#8827; sucede
&prcue; &precedesslantequal; &preccurlyeq; &#x0227c; &#8828; precede ou igual a
&sccue; &succeedsslantequal; &succcurlyeq; &#x0227d; &#8829; sucede ou igual
&prsim; &precsim; &precedestilde; &#x0227e; &#8830; precede ou equivalente a
&scsim; &succsim; &succeedstilde; &#x0227f; &#8831; sucede ou equivalente a
&npr; &nprec; &notprecedes; &#x02280; &#8832; não precede
&nsc; &nsucc; &notsucceeds; &#x02281; &#8833; não sucede
&sqsub; &squaresubset; &sqsubset; &#x0228f; &#8847; imagem quadrada de
&sqsup; &squaresuperset; &sqsupset; &#x02290; &#8848; quadrado original de
&sqsube; &squaresubsetequal; &sqsubseteq; &#x02291; &#8849; imagem quadrada de ou igual a
&sqsupe; &squaresupersetequal; &sqsupseteq; &#x02292; &#8850; quadrado original de ou igual a
&sqcap; &squareintersection; &#x02293; &#8851; chapeu quadrado
&sqcup; &squareunion; &#x02294; &#8852; copo quadrado
&vdash; &righttee; &#x022a2; &#8866; rumo direito
&dashv; &lefttee; &#x022a3; &#8867; rumo esquerdo
&top; &downtee; &#x022a4; &#8868; aderência
&bottom; &bot; &perp; &uptee; &#x022a5; &#8869; aderência
&models; &#x022a7; &#8871; modela
&vdash; &doublerighttee; &#x022a8; &#8872; verdadeiro
&vdash; &#x022a9; &#8873; forças
&vdash; &#x022ab; &#8875; barra vertical dupla catraca direita dupla
&nvdash; &#x022ac; &#8876; não prova
&nvdash; &#x022ad; &#8877; Não é verdade
&nvdash; &#x022ae; &#8878; não força
&nvdash; &#x022af; &#8879; torniquete duplo direito barra vertical dupla negada
&prurel; &#x022b0; &#8880; precede em relação
&vltri; &vartriangleleft; &lefttriangle; &#x022b2; &#8882; subgrupo normal de
&vrtri; &vartriangleright; &righttriangle; &#x022b3; &#8883; contém como subgrupo normal
&ltrie; &trianglelefteq; &lefttriangleequal; &#x022b4; &#8884; subgrupo normal ou igual a
&rtrie; &trianglerighteq; &righttriangleequal; &#x022b5; &#8885; contém como subgrupo normal ou igual a
&origof; &#x022b6; &#8886; original de
&imof; &#x022b7; &#8887; imagem de
&mumap; &multimap; &#x022b8; &#8888; multimapa
&hercon; &#x022b9; &#8889; matriz conjugada hermitiana
&intcal; &intercal; &#x022ba; &#8890; intercalar
&veebar; &#x022bb; &#8891; xor lógico
&barvee; &#x022bd; &#8893; nor lógico
&angrtvb; &#x022be; &#8894; ângulo reto com arco
&lrtri; &#x022bf; &#8895; triângulo retângulo
&xwedge; &wedge; &bigwedge; &#x022c0; &#8896; n-ário lógico e
&xvee; &vee; &bigvee; &#x022c1; &#8897; n-ário lógico ou
&diam; &diamond; &diamond; &#x022c4; &#8900; operador de diamante
&sdot; &#x022c5; &#8901; operador de ponto
&sstarf; &star; &#x022c6; &#8902; operador estrela
&divonx; &divideontimes; &#x022c7; &#8903; vezes divisão
&bowtie; &#x022c8; &#8904; gravata-borboleta
&ltimes; &#x022c9; &#8905; produto semidireto de fator normal esquerdo
&rtimes; &#x022ca; &#8906; produto semidireto de fator normal correto
&lthree; &leftthreetimes; &#x022cb; &#8907; produto semidireto esquerdo
&rthree; &rightthreetimes; &#x022cc; &#8908; produto semidireto certo
&bsime; &backsimeq; &#x022cd; &#8909; til invertido é igual a
&cuvee; &curlyvee; &#x022ce; &#8910; ou lógico curvo
&cuwed; &curlywedge; &#x022cf; &#8911; e lógico curvo
&sub; &subset; &#x022d0; &#8912; subconjunto duplo
&sup; &supset; &#x022d1; &#8913; superconjunto duplo
&cap; &#x022d2; &#8914; cruzamento duplo
&cup; &#x022d3; &#8915; união dupla
&fork; &pitchfork; &#x022d4; &#8916; forquilha
&epar; &#x022d5; &#8917; igual e paralelo a
&ltdot; &lessdot; &#x022d6; &#8918; menos do que com ponto
&gtdot; &gtrdot; &#x022d7; &#8919; maior do que com ponto
&ll; &#x022d8; &#8920; muito menos do que
&gg; &ggg; &#x022d9; &#8921; muito maior do que
&leg; &lessequalgreater; &lesseqgtr; &#x022da; &#8922; menor que igual ou maior que
&gel; &gtreqless; &greaterequalless; &#x022db; &#8923; maior que igual ou menor que
&cuepr; &curlyeqprec; &#x022de; &#8926; igual a ou precede
&cuesc; &curlyeqsucc; &#x022df; &#8927; igual ou sucede
&nprcue; &notprecedesslantequal; &#x022e0; &#8928; não precede ou é igual
&nsccue; &notsucceedsslantequal; &#x022e1; &#8929; não sucede ou igual
&nsqsube; &notsquaresubsetequal; &#x022e2; &#8930; imagem não quadrada ou igual a
&nsqsupe; &notsquaresupersetequal; &#x022e3; &#8931; não quadrado original ou igual a
&lnsim; &#x022e6; &#8934; menor que, mas não equivalente a
&gnsim; &#x022e7; &#8935; maior que, mas não equivalente a
&prnsim; &precnsim; &#x022e8; &#8936; precede, mas não é equivalente a
&scnsim; &succnsim; &#x022e9; &#8937; sucede, mas não equivalente a
&nltri; &ntriangleleft; &notlefttriangle; &#x022ea; &#8938; subgrupo não normal de
&nrtri; &ntriangleright; &notrighttriangle; &#x022eb; &#8939; não contém um subgrupo normal
&nltrie; &ntrianglelefteq; &notlefttriangleequal; &#x022ec; &#8940; subgrupo não normal ou igual a
&nrtrie; &ntrianglerighteq; &notrighttriangleequal; &#x022ed; &#8941; não contém subgrupo normal ou igual
&vellip; &#x022ee; &#8942; elipse vertical
&ctdot; &#x022ef; &#8943; elipse horizontal da linha média
&utdot; &#x022f0; &#8944; elipse diagonal direita
&dtdot; &#x022f1; &#8945; elipse diagonal direita para baixo
&disin; &#x022f2; &#8946; elemento de com longo curso horizontal
&isinsv; &#x022f3; &#8947; elemento de com barra vertical no final do curso horizontal
&isins; &#x022f4; &#8948; pertence com traço vertical
&isindot; &#x022f5; &#8949; elemento de com ponto acima
&notinvc; &#x022f6; &#8950; elemento de com barra superior
&notinvb; &#x022f7; &#8951; pertence com barra superior
&nisd; &#x022fa; &#8954; contém com longo curso horizontal
&xnis; &#x022fb; &#8955; contém com barra vertical
&nis; &#x022fc; &#8956; pertence à esquerda com barra vertical
&notnivc; &#x022fd; &#8957; contém com barra superior
&notnivb; &#x022fe; &#8958; pequeno contém com barra superior
&barwed; &barwedge; &#x02305; &#8965; projetivo
&barwed; &doublebarwedge; &#x02306; &#8966; perspectiva
&lceil; &leftceiling; &#x02308; &#8968; teto esquerdo
&rceil; &rightceiling; &#x02309; &#8969; teto direito
&lfloor; &leftfloor; &#x0230a; &#8970; chão esquerdo
&rfloor; &rightfloor; &#x0230b; &#8971; chão certo
&drcrop; &#x0230c; &#8972; corte inferior direito
&dlcrop; &#x0230d; &#8973; corte inferior esquerdo
&urcrop; &#x0230e; &#8974; corte superior direito
&ulcrop; &#x0230f; &#8975; corte superior esquerdo
&bnot; &#x02310; &#8976; invertido, não assinar
&profline; &#x02312; &#8978; arco
&profsurf; &#x02313; &#8979; segmento
&telrec; &#x02315; &#8981; gravador de telefone
&target; &#x02316; &#8982; indicador de posição
&ulcorn; &ulcorner; &#x0231c; &#8988; canto superior esquerdo
&urcorn; &urcorner; &#x0231d; &#8989; canto superior direito
&dlcorn; &llcorner; &#x0231e; &#8990; canto inferior esquerdo
&drcorn; &lrcorner; &#x0231f; &#8991; canto inferior direito
&frown; &sfrown; &#x02322; &#8994; carranca
&smile; &ssmile; &#x02323; &#8995; sorriso
&cylcty; &#x0232d; &#9005; cilindricidade
&profalar; &#x0232e; &#9006; perfil generalizado
&topbot; &#x02336; &#9014; símbolo funcional apl i-beam
&ovbar; &#x0233d; &#9021; estilo de círculo de símbolo funcional apl
&solbar; &#x0233f; &#9023; barra de barra de símbolo funcional apl
&angzarr; &#x0237c; &#9084; ângulo reto com seta em zigue-zague para baixo
&lmoust; &lmoustache; &#x023b0; &#9136; seção superior esquerda ou inferior direita das chaves
&rmoust; &rmoustache; &#x023b1; &#9137; seção superior direita ou inferior esquerda das chaves
&tbrk; &overbracket; &#x023b4; &#9140; colchete superior
&bbrk; &underbracket; &#x023b5; &#9141; colchete inferior
&bbrktbrk; &#x023b6; &#9142; colchete inferior sobre colchete superior
&trpezium; &#x023e2; &#9186; trapézio branco
Emoticons
Símbolo HEX DEC Símbolo HEX DEC Símbolo HEX DEC Símbolo HEX DEC
#8986 #231A 💠 #128160 #1F4A0 🎈 #127880 #1F388 😩 #128553 #1F629
#8987 #231B 💡 #128161 #1F4A1 🎉 #127881 #1F389 😪 #128554 #1F62A
#9193 #23E9 💢 #128162 #1F4A2 🎊 #127882 #1F38A 😫 #128555 #1F62B
#9194 #23EA 💣 #128163 #1F4A3 🎋 #127883 #1F38B 😬 #128556 #1F62C
#9195 #23EB 💤 #128164 #1F4A4 🎌 #127884 #1F38C 😭 #128557 #1F62D
#9196 #23EC 💥 #128165 #1F4A5 🎍 #127885 #1F38D 😮 #128558 #1F62E
#9197 #23ED 💦 #128166 #1F4A6 🎎 #127886 #1F38E 😯 #128559 #1F62F
#9198 #23EE 💧 #128167 #1F4A7 🎏 #127887 #1F38F 😰 #128560 #1F630
#9199 #23EF 💨 #128168 #1F4A8 🎐 #127888 #1F390 😱 #128561 #1F631
#9200 #23F0 💩 #128169 #1F4A9 🎑 #127889 #1F391 😲 #128562 #1F632
#9201 #23F1 💪 #128170 #1F4AA 🎒 #127890 #1F392 😳 #128563 #1F633
#9202 #23F2 💫 #128171 #1F4AB 🎓 #127891 #1F393 😴 #128564 #1F634
#9203 #23F3 💬 #128172 #1F4AC 🎖 #127894 #1F396 😵 #128565 #1F635
#9208 #23F8 💭 #128173 #1F4AD 🎗 #127895 #1F397 😶 #128566 #1F636
#9209 #23F9 💮 #128174 #1F4AE 🎙 #127897 #1F399 😷 #128567 #1F637
#9210 #23FA 💯 #128175 #1F4AF 🎚 #127898 #1F39A 😸 #128568 #1F638
#9410 #24C2 💰 #128176 #1F4B0 🎛 #127899 #1F39B 😹 #128569 #1F639
#9748 #2614 💱 #128177 #1F4B1 🎞 #127902 #1F39E 😺 #128570 #1F63A
#9749 #2615 💲 #128178 #1F4B2 🎟 #127903 #1F39F 😻 #128571 #1F63B
#9757 #261D 💳 #128179 #1F4B3 🎠 #127904 #1F3A0 😼 #128572 #1F63C
#9800 #2648 💴 #128180 #1F4B4 🎡 #127905 #1F3A1 😽 #128573 #1F63D
#9801 #2649 💵 #128181 #1F4B5 🎢 #127906 #1F3A2 😾 #128574 #1F63E
#9802 #264A 💶 #128182 #1F4B6 🎣 #127907 #1F3A3 😿 #128575 #1F63F
#9803 #264B 💷 #128183 #1F4B7 🎤 #127908 #1F3A4 🙀 #128576 #1F640
#9804 #264C 💸 #128184 #1F4B8 🎥 #127909 #1F3A5 🙁 #128577 #1F641
#9805 #264D 💹 #128185 #1F4B9 🎦 #127910 #1F3A6 🙂 #128578 #1F642
#9806 #264E 💺 #128186 #1F4BA 🎧 #127911 #1F3A7 🙃 #128579 #1F643
#9807 #264F 💻 #128187 #1F4BB 🎨 #127912 #1F3A8 🙄 #128580 #1F644
#9808 #2650 💼 #128188 #1F4BC 🎩 #127913 #1F3A9 🙅 #128581 #1F645
#9809 #2651 💽 #128189 #1F4BD 🎪 #127914 #1F3AA 🙆 #128582 #1F646
#9810 #2652 💾 #128190 #1F4BE 🎫 #127915 #1F3AB 🙇 #128583 #1F647
#9811 #2653 💿 #128191 #1F4BF 🎬 #127916 #1F3AC 🙈 #128584 #1F648
#9823 #265F 📀 #128192 #1F4C0 🎭 #127917 #1F3AD 🙉 #128585 #1F649
#9855 #267F 📁 #128193 #1F4C1 🎮 #127918 #1F3AE 🙊 #128586 #1F64A
#9875 #2693 📂 #128194 #1F4C2 🎯 #127919 #1F3AF 🙋 #128587 #1F64B
#9889 #26A1 📃 #128195 #1F4C3 🎰 #127920 #1F3B0 🙌 #128588 #1F64C
#9898 #26AA 📄 #128196 #1F4C4 🎱 #127921 #1F3B1 🙍 #128589 #1F64D
#9899 #26AB 📅 #128197 #1F4C5 🎲 #127922 #1F3B2 🙎 #128590 #1F64E
#9917 #26BD 📆 #128198 #1F4C6 🎳 #127923 #1F3B3 🙏 #128591 #1F64F
#9918 #26BE 📇 #128199 #1F4C7 🎴 #127924 #1F3B4 🚀 #128640 #1F680
#9924 #26C4 📈 #128200 #1F4C8 🎵 #127925 #1F3B5 🚁 #128641 #1F681
#9925 #26C5 📉 #128201 #1F4C9 🎶 #127926 #1F3B6 🚂 #128642 #1F682
#9934 #26CE 📊 #128202 #1F4CA 🎷 #127927 #1F3B7 🚃 #128643 #1F683
#9935 #26CF 📋 #128203 #1F4CB 🎸 #127928 #1F3B8 🚄 #128644 #1F684
#9937 #26D1 📌 #128204 #1F4CC 🎹 #127929 #1F3B9 🚅 #128645 #1F685
#9939 #26D3 📍 #128205 #1F4CD 🎺 #127930 #1F3BA 🚆 #128646 #1F686
#9940 #26D4 📎 #128206 #1F4CE 🎻 #127931 #1F3BB 🚇 #128647 #1F687
#9961 #26E9 📏 #128207 #1F4CF 🎼 #127932 #1F3BC 🚈 #128648 #1F688
#9962 #26EA 📐 #128208 #1F4D0 🎽 #127933 #1F3BD 🚉 #128649 #1F689
#9968 #26F0 📑 #128209 #1F4D1 🎾 #127934 #1F3BE 🚊 #128650 #1F68A
#9969 #26F1 📒 #128210 #1F4D2 🎿 #127935 #1F3BF 🚋 #128651 #1F68B
#9970 #26F2 📓 #128211 #1F4D3 🏀 #127936 #1F3C0 🚌 #128652 #1F68C
#9971 #26F3 📔 #128212 #1F4D4 🏁 #127937 #1F3C1 🚍 #128653 #1F68D
#9972 #26F4 📕 #128213 #1F4D5 🏂 #127938 #1F3C2 🚎 #128654 #1F68E
#9973 #26F5 📖 #128214 #1F4D6 🏃 #127939 #1F3C3 🚏 #128655 #1F68F
#9975 #26F7 📗 #128215 #1F4D7 🏄 #127940 #1F3C4 🚐 #128656 #1F690
#9976 #26F8 📘 #128216 #1F4D8 🏅 #127941 #1F3C5 🚑 #128657 #1F691
#9977 #26F9 📙 #128217 #1F4D9 🏆 #127942 #1F3C6 🚒 #128658 #1F692
#9978 #26FA 📚 #128218 #1F4DA 🏇 #127943 #1F3C7 🚓 #128659 #1F693
#9981 #26FD 📛 #128219 #1F4DB 🏈 #127944 #1F3C8 🚔 #128660 #1F694
#9986 #2702 📜 #128220 #1F4DC 🏉 #127945 #1F3C9 🚕 #128661 #1F695
#9989 #2705 📝 #128221 #1F4DD 🏊 #127946 #1F3CA 🚖 #128662 #1F696
#9992 #2708 📞 #128222 #1F4DE 🏋 #127947 #1F3CB 🚗 #128663 #1F697
#9993 #2709 📟 #128223 #1F4DF 🏌 #127948 #1F3CC 🚘 #128664 #1F698
#9994 #270A 📠 #128224 #1F4E0 🏍 #127949 #1F3CD 🚙 #128665 #1F699
#9995 #270B 📡 #128225 #1F4E1 🏎 #127950 #1F3CE 🚚 #128666 #1F69A
#9996 #270C 📢 #128226 #1F4E2 🏏 #127951 #1F3CF 🚛 #128667 #1F69B
#9997 #270D 📣 #128227 #1F4E3 🏐 #127952 #1F3D0 🚜 #128668 #1F69C
#9999 #270F 📤 #128228 #1F4E4 🏑 #127953 #1F3D1 🚝 #128669 #1F69D
#10002 #2712 📥 #128229 #1F4E5 🏒 #127954 #1F3D2 🚞 #128670 #1F69E
#10004 #2714 📦 #128230 #1F4E6 🏓 #127955 #1F3D3 🚟 #128671 #1F69F
#10006 #2716 📧 #128231 #1F4E7 🏔 #127956 #1F3D4 🚠 #128672 #1F6A0
#10013 #271D 📨 #128232 #1F4E8 🏕 #127957 #1F3D5 🚡 #128673 #1F6A1
#10017 #2721 📩 #128233 #1F4E9 🏖 #127958 #1F3D6 🚢 #128674 #1F6A2
#10024 #2728 📪 #128234 #1F4EA 🏗 #127959 #1F3D7 🚣 #128675 #1F6A3
#10035 #2733 📫 #128235 #1F4EB 🏘 #127960 #1F3D8 🚤 #128676 #1F6A4
#10036 #2734 📬 #128236 #1F4EC 🏙 #127961 #1F3D9 🚥 #128677 #1F6A5
#10052 #2744 📭 #128237 #1F4ED 🏚 #127962 #1F3DA 🚦 #128678 #1F6A6
#10055 #2747 📮 #128238 #1F4EE 🏛 #127963 #1F3DB 🚧 #128679 #1F6A7
#10060 #274C 📯 #128239 #1F4EF 🏜 #127964 #1F3DC 🚨 #128680 #1F6A8
#10062 #274E 📰 #128240 #1F4F0 🏝 #127965 #1F3DD 🚩 #128681 #1F6A9
#10067 #2753 📱 #128241 #1F4F1 🏞 #127966 #1F3DE 🚪 #128682 #1F6AA
#10068 #2754 📲 #128242 #1F4F2 🏟 #127967 #1F3DF 🚫 #128683 #1F6AB
#10069 #2755 📳 #128243 #1F4F3 🏠 #127968 #1F3E0 🚬 #128684 #1F6AC
#10071 #2757 📴 #128244 #1F4F4 🏡 #127969 #1F3E1 🚭 #128685 #1F6AD
#10083 #2763 📵 #128245 #1F4F5 🏢 #127970 #1F3E2 🚮 #128686 #1F6AE
#10084 #2764 📶 #128246 #1F4F6 🏣 #127971 #1F3E3 🚯 #128687 #1F6AF
#10133 #2795 📷 #128247 #1F4F7 🏤 #127972 #1F3E4 🚰 #128688 #1F6B0
#10134 #2796 📸 #128248 #1F4F8 🏥 #127973 #1F3E5 🚱 #128689 #1F6B1
#10135 #2797 📹 #128249 #1F4F9 🏦 #127974 #1F3E6 🚲 #128690 #1F6B2
#10145 #27A1 📺 #128250 #1F4FA 🏧 #127975 #1F3E7 🚳 #128691 #1F6B3
#10160 #27B0 📻 #128251 #1F4FB 🏨 #127976 #1F3E8 🚴 #128692 #1F6B4
#10175 #27BF 📼 #128252 #1F4FC 🏩 #127977 #1F3E9 🚵 #128693 #1F6B5
#10548 #2934 📽 #128253 #1F4FD 🏪 #127978 #1F3EA 🚶 #128694 #1F6B6
#10549 #2935 📿 #128255 #1F4FF 🏫 #127979 #1F3EB 🚷 #128695 #1F6B7
#11013 #2B05 🔀 #128256 #1F500 🏬 #127980 #1F3EC 🚸 #128696 #1F6B8
#11014 #2B06 🔁 #128257 #1F501 🏭 #127981 #1F3ED 🚹 #128697 #1F6B9
#11015 #2B07 🔂 #128258 #1F502 🏮 #127982 #1F3EE 🚺 #128698 #1F6BA
#11035 #2B1B 🔃 #128259 #1F503 🏯 #127983 #1F3EF 🚻 #128699 #1F6BB
#11036 #2B1C 🔄 #128260 #1F504 🏰 #127984 #1F3F0 🚼 #128700 #1F6BC
#11088 #2B50 🔅 #128261 #1F505 🏳 #127987 #1F3F3 🚽 #128701 #1F6BD
#11093 #2B55 🔆 #128262 #1F506 🏴 #127988 #1F3F4 🚾 #128702 #1F6BE
#12336 #3030 🔇 #128263 #1F507 🏵 #127989 #1F3F5 🚿 #128703 #1F6BF
#12349 #303D 🔈 #128264 #1F508 🏷 #127991 #1F3F7 🛀 #128704 #1F6C0
#12951 #3297 🔉 #128265 #1F509 🏸 #127992 #1F3F8 🛁 #128705 #1F6C1
#12953 #3299 🔊 #128266 #1F50A 🏹 #127993 #1F3F9 🛂 #128706 #1F6C2
🀄 #126980 #1F004 🔋 #128267 #1F50B 🏺 #127994 #1F3FA 🛃 #128707 #1F6C3
🃏 #127183 #1F0CF 🔌 #128268 #1F50C 🏻 #127995 #1F3FB 🛄 #128708 #1F6C4
🅰 #127344 #1F170 🔍 #128269 #1F50D 🏼 #127996 #1F3FC 🛅 #128709 #1F6C5
🅱 #127345 #1F171 🔎 #128270 #1F50E 🏽 #127997 #1F3FD 🛋 #128715 #1F6CB
🅾 #127358 #1F17E 🔏 #128271 #1F50F 🏾 #127998 #1F3FE 🛌 #128716 #1F6CC
🅿 #127359 #1F17F 🔐 #128272 #1F510 🏿 #127999 #1F3FF 🛍 #128717 #1F6CD
🆎 #127374 #1F18E 🔑 #128273 #1F511 🐀 #128000 #1F400 🛎 #128718 #1F6CE
🆑 #127377 #1F191 🔒 #128274 #1F512 🐁 #128001 #1F401 🛏 #128719 #1F6CF
🆒 #127378 #1F192 🔓 #128275 #1F513 🐂 #128002 #1F402 🛐 #128720 #1F6D0
🆓 #127379 #1F193 🔔 #128276 #1F514 🐃 #128003 #1F403 🛑 #128721 #1F6D1
🆔 #127380 #1F194 🔕 #128277 #1F515 🐄 #128004 #1F404 🛒 #128722 #1F6D2
🆕 #127381 #1F195 🔖 #128278 #1F516 🐅 #128005 #1F405 🛠 #128736 #1F6E0
🆖 #127382 #1F196 🔗 #128279 #1F517 🐆 #128006 #1F406 🛡 #128737 #1F6E1
🆗 #127383 #1F197 🔘 #128280 #1F518 🐇 #128007 #1F407 🛢 #128738 #1F6E2
🆘 #127384 #1F198 🔙 #128281 #1F519 🐈 #128008 #1F408 🛣 #128739 #1F6E3
🆙 #127385 #1F199 🔚 #128282 #1F51A 🐉 #128009 #1F409 🛤 #128740 #1F6E4
🆚 #127386 #1F19A 🔛 #128283 #1F51B 🐊 #128010 #1F40A 🛥 #128741 #1F6E5
🈁 #127489 #1F201 🔜 #128284 #1F51C 🐋 #128011 #1F40B 🛩 #128745 #1F6E9
🈂 #127490 #1F202 🔝 #128285 #1F51D 🐌 #128012 #1F40C 🛫 #128747 #1F6EB
🈚 #127514 #1F21A 🔞 #128286 #1F51E 🐍 #128013 #1F40D 🛬 #128748 #1F6EC
🈯 #127535 #1F22F 🔟 #128287 #1F51F 🐎 #128014 #1F40E 🛰 #128752 #1F6F0
🈲 #127538 #1F232 🔠 #128288 #1F520 🐏 #128015 #1F40F 🛳 #128755 #1F6F3
🈳 #127539 #1F233 🔡 #128289 #1F521 🐐 #128016 #1F410 🛴 #128756 #1F6F4
🈴 #127540 #1F234 🔢 #128290 #1F522 🐑 #128017 #1F411 🛵 #128757 #1F6F5
🈵 #127541 #1F235 🔣 #128291 #1F523 🐒 #128018 #1F412 🛶 #128758 #1F6F6
🈶 #127542 #1F236 🔤 #128292 #1F524 🐓 #128019 #1F413 🛷 #128759 #1F6F7
🈷 #127543 #1F237 🔥 #128293 #1F525 🐔 #128020 #1F414 🛸 #128760 #1F6F8
🈸 #127544 #1F238 🔦 #128294 #1F526 🐕 #128021 #1F415 🛹 #128761 #1F6F9
🈹 #127545 #1F239 🔧 #128295 #1F527 🐖 #128022 #1F416 🛺 #128762 #1F6FA
🈺 #127546 #1F23A 🔨 #128296 #1F528 🐗 #128023 #1F417 🤐 #129296 #1F910
🉐 #127568 #1F250 🔩 #128297 #1F529 🐘 #128024 #1F418 🤑 #129297 #1F911
🉑 #127569 #1F251 🔪 #128298 #1F52A 🐙 #128025 #1F419 🤒 #129298 #1F912
🌀 #127744 #1F300 🔫 #128299 #1F52B 🐚 #128026 #1F41A 🤓 #129299 #1F913
🌁 #127745 #1F301 🔬 #128300 #1F52C 🐛 #128027 #1F41B 🤔 #129300 #1F914
🌂 #127746 #1F302 🔭 #128301 #1F52D 🐜 #128028 #1F41C 🤕 #129301 #1F915
🌃 #127747 #1F303 🔮 #128302 #1F52E 🐝 #128029 #1F41D 🤖 #129302 #1F916
🌄 #127748 #1F304 🔯 #128303 #1F52F 🐞 #128030 #1F41E 🤗 #129303 #1F917
🌅 #127749 #1F305 🔰 #128304 #1F530 🐟 #128031 #1F41F 🤘 #129304 #1F918
🌆 #127750 #1F306 🔱 #128305 #1F531 🐠 #128032 #1F420 🤙 #129305 #1F919
🌇 #127751 #1F307 🔲 #128306 #1F532 🐡 #128033 #1F421 🤚 #129306 #1F91A
🌈 #127752 #1F308 🔳 #128307 #1F533 🐢 #128034 #1F422 🤛 #129307 #1F91B
🌉 #127753 #1F309 🔴 #128308 #1F534 🐣 #128035 #1F423 🤜 #129308 #1F91C
🌊 #127754 #1F30A 🔵 #128309 #1F535 🐤 #128036 #1F424 🤝 #129309 #1F91D
🌋 #127755 #1F30B 🔶 #128310 #1F536 🐥 #128037 #1F425 🤞 #129310 #1F91E
🌌 #127756 #1F30C 🔷 #128311 #1F537 🐦 #128038 #1F426 🤟 #129311 #1F91F
🌍 #127757 #1F30D 🔸 #128312 #1F538 🐧 #128039 #1F427 🤠 #129312 #1F920
🌎 #127758 #1F30E 🔹 #128313 #1F539 🐨 #128040 #1F428 🤡 #129313 #1F921
🌏 #127759 #1F30F 🔺 #128314 #1F53A 🐩 #128041 #1F429 🤢 #129314 #1F922
🌐 #127760 #1F310 🔻 #128315 #1F53B 🐪 #128042 #1F42A 🤣 #129315 #1F923
🌑 #127761 #1F311 🔼 #128316 #1F53C 🐫 #128043 #1F42B 🤤 #129316 #1F924
🌒 #127762 #1F312 🔽 #128317 #1F53D 🐬 #128044 #1F42C 🤥 #129317 #1F925
🌓 #127763 #1F313 🕉 #128329 #1F549 🐭 #128045 #1F42D 🤦 #129318 #1F926
🌔 #127764 #1F314 🕊 #128330 #1F54A 🐮 #128046 #1F42E 🤧 #129319 #1F927
🌕 #127765 #1F315 🕋 #128331 #1F54B 🐯 #128047 #1F42F 🤨 #129320 #1F928
🌖 #127766 #1F316 🕌 #128332 #1F54C 🐰 #128048 #1F430 🤩 #129321 #1F929
🌗 #127767 #1F317 🕍 #128333 #1F54D 🐱 #128049 #1F431 🤪 #129322 #1F92A
🌘 #127768 #1F318 🕎 #128334 #1F54E 🐲 #128050 #1F432 🤫 #129323 #1F92B
🌙 #127769 #1F319 🕐 #128336 #1F550 🐳 #128051 #1F433 🤬 #129324 #1F92C
🌚 #127770 #1F31A 🕑 #128337 #1F551 🐴 #128052 #1F434 🤭 #129325 #1F92D
🌛 #127771 #1F31B 🕒 #128338 #1F552 🐵 #128053 #1F435 🤮 #129326 #1F92E
🌜 #127772 #1F31C 🕓 #128339 #1F553 🐶 #128054 #1F436 🤯 #129327 #1F92F
🌝 #127773 #1F31D 🕔 #128340 #1F554 🐷 #128055 #1F437 🤰 #129328 #1F930
🌞 #127774 #1F31E 🕕 #128341 #1F555 🐸 #128056 #1F438 🤱 #129329 #1F931
🌟 #127775 #1F31F 🕖 #128342 #1F556 🐹 #128057 #1F439 🤲 #129330 #1F932
🌠 #127776 #1F320 🕗 #128343 #1F557 🐺 #128058 #1F43A 🤳 #129331 #1F933
🌡 #127777 #1F321 🕘 #128344 #1F558 🐻 #128059 #1F43B 🤴 #129332 #1F934
🌤 #127780 #1F324 🕙 #128345 #1F559 🐼 #128060 #1F43C 🤵 #129333 #1F935
🌥 #127781 #1F325 🕚 #128346 #1F55A 🐽 #128061 #1F43D 🤶 #129334 #1F936
🌦 #127782 #1F326 🕛 #128347 #1F55B 🐾 #128062 #1F43E 🤷 #129335 #1F937
🌧 #127783 #1F327 🕜 #128348 #1F55C 🐿 #128063 #1F43F 🤸 #129336 #1F938
🌨 #127784 #1F328 🕝 #128349 #1F55D 👀 #128064 #1F440 🤹 #129337 #1F939
🌩 #127785 #1F329 🕞 #128350 #1F55E 👁 #128065 #1F441 🤺 #129338 #1F93A
🌪 #127786 #1F32A 🕟 #128351 #1F55F 👂 #128066 #1F442 🤼 #129340 #1F93C
🌫 #127787 #1F32B 🕠 #128352 #1F560 👃 #128067 #1F443 🤽 #129341 #1F93D
🌬 #127788 #1F32C 🕡 #128353 #1F561 👄 #128068 #1F444 🤾 #129342 #1F93E
🌭 #127789 #1F32D 🕢 #128354 #1F562 👅 #128069 #1F445 🥀 #129344 #1F940
🌮 #127790 #1F32E 🕣 #128355 #1F563 👆 #128070 #1F446 🥁 #129345 #1F941
🌯 #127791 #1F32F 🕤 #128356 #1F564 👇 #128071 #1F447 🥂 #129346 #1F942
🌰 #127792 #1F330 🕥 #128357 #1F565 👈 #128072 #1F448 🥃 #129347 #1F943
🌱 #127793 #1F331 🕦 #128358 #1F566 👉 #128073 #1F449 🥄 #129348 #1F944
🌲 #127794 #1F332 🕧 #128359 #1F567 👊 #128074 #1F44A 🥅 #129349 #1F945
🌳 #127795 #1F333 🕯 #128367 #1F56F 👋 #128075 #1F44B 🥇 #129351 #1F947
🌴 #127796 #1F334 🕰 #128368 #1F570 👌 #128076 #1F44C 🥈 #129352 #1F948
🌵 #127797 #1F335 🕳 #128371 #1F573 👍 #128077 #1F44D 🥉 #129353 #1F949
🌶 #127798 #1F336 🕴 #128372 #1F574 👎 #128078 #1F44E 🥊 #129354 #1F94A
🌷 #127799 #1F337 🕵 #128373 #1F575 👏 #128079 #1F44F 🥋 #129355 #1F94B
🌸 #127800 #1F338 🕶 #128374 #1F576 👐 #128080 #1F450 🥌 #129356 #1F94C
🌹 #127801 #1F339 🕷 #128375 #1F577 👑 #128081 #1F451 🥍 #129357 #1F94D
🌺 #127802 #1F33A 🕸 #128376 #1F578 👒 #128082 #1F452 🥎 #129358 #1F94E
🌻 #127803 #1F33B 🕹 #128377 #1F579 👓 #128083 #1F453 🥏 #129359 #1F94F
🌼 #127804 #1F33C 🕺 #128378 #1F57A 👔 #128084 #1F454 🥐 #129360 #1F950
🌽 #127805 #1F33D 🖇 #128391 #1F587 👕 #128085 #1F455 🥑 #129361 #1F951
🌾 #127806 #1F33E 🖊 #128394 #1F58A 👖 #128086 #1F456 🥒 #129362 #1F952
🌿 #127807 #1F33F 🖋 #128395 #1F58B 👗 #128087 #1F457 🥓 #129363 #1F953
🍀 #127808 #1F340 🖌 #128396 #1F58C 👘 #128088 #1F458 🥔 #129364 #1F954
🍁 #127809 #1F341 🖍 #128397 #1F58D 👙 #128089 #1F459 🥕 #129365 #1F955
🍂 #127810 #1F342 🖐 #128400 #1F590 👚 #128090 #1F45A 🥖 #129366 #1F956
🍃 #127811 #1F343 🖕 #128405 #1F595 👛 #128091 #1F45B 🥗 #129367 #1F957
🍄 #127812 #1F344 🖖 #128406 #1F596 👜 #128092 #1F45C 🥘 #129368 #1F958
🍅 #127813 #1F345 🖤 #128420 #1F5A4 👝 #128093 #1F45D 🥙 #129369 #1F959
🍆 #127814 #1F346 🖥 #128421 #1F5A5 👞 #128094 #1F45E 🥚 #129370 #1F95A
🍇 #127815 #1F347 🖨 #128424 #1F5A8 👟 #128095 #1F45F 🥛 #129371 #1F95B
🍈 #127816 #1F348 🖱 #128433 #1F5B1 👠 #128096 #1F460 🥜 #129372 #1F95C
🍉 #127817 #1F349 🖲 #128434 #1F5B2 👡 #128097 #1F461 🥝 #129373 #1F95D
🍊 #127818 #1F34A 🖼 #128444 #1F5BC 👢 #128098 #1F462 🥞 #129374 #1F95E
🍋 #127819 #1F34B 🗂 #128450 #1F5C2 👣 #128099 #1F463 🥟 #129375 #1F95F
🍌 #127820 #1F34C 🗃 #128451 #1F5C3 👤 #128100 #1F464 🥠 #129376 #1F960
🍍 #127821 #1F34D 🗄 #128452 #1F5C4 👥 #128101 #1F465 🥡 #129377 #1F961
🍎 #127822 #1F34E 🗑 #128465 #1F5D1 👦 #128102 #1F466 🥢 #129378 #1F962
🍏 #127823 #1F34F 🗒 #128466 #1F5D2 👧 #128103 #1F467 🥣 #129379 #1F963
🍐 #127824 #1F350 🗓 #128467 #1F5D3 👨 #128104 #1F468 🥤 #129380 #1F964
🍑 #127825 #1F351 🗜 #128476 #1F5DC 👩 #128105 #1F469 🥥 #129381 #1F965
🍒 #127826 #1F352 🗝 #128477 #1F5DD 👪 #128106 #1F46A 🥦 #129382 #1F966
🍓 #127827 #1F353 🗞 #128478 #1F5DE 👫 #128107 #1F46B 🥧 #129383 #1F967
🍔 #127828 #1F354 🗡 #128481 #1F5E1 👬 #128108 #1F46C 🥨 #129384 #1F968
🍕 #127829 #1F355 🗣 #128483 #1F5E3 👭 #128109 #1F46D 🥩 #129385 #1F969
🍖 #127830 #1F356 🗨 #128488 #1F5E8 👮 #128110 #1F46E 🥪 #129386 #1F96A
🍗 #127831 #1F357 🗯 #128495 #1F5EF 👯 #128111 #1F46F 🥫 #129387 #1F96B
🍘 #127832 #1F358 🗳 #128499 #1F5F3 👰 #128112 #1F470 🦀 #129408 #1F980
🍙 #127833 #1F359 🗺 #128506 #1F5FA 👱 #128113 #1F471 🦁 #129409 #1F981
🍚 #127834 #1F35A 🗻 #128507 #1F5FB 👲 #128114 #1F472 🦂 #129410 #1F982
🍛 #127835 #1F35B 🗼 #128508 #1F5FC 👳 #128115 #1F473 🦃 #129411 #1F983
🍜 #127836 #1F35C 🗽 #128509 #1F5FD 👴 #128116 #1F474 🦄 #129412 #1F984
🍝 #127837 #1F35D 🗾 #128510 #1F5FE 👵 #128117 #1F475 🦅 #129413 #1F985
🍞 #127838 #1F35E 🗿 #128511 #1F5FF 👶 #128118 #1F476 🦆 #129414 #1F986
🍟 #127839 #1F35F 😀 #128512 #1F600 👷 #128119 #1F477 🦇 #129415 #1F987
🍠 #127840 #1F360 😁 #128513 #1F601 👸 #128120 #1F478 🦈 #129416 #1F988
🍡 #127841 #1F361 😂 #128514 #1F602 👹 #128121 #1F479 🦉 #129417 #1F989
🍢 #127842 #1F362 😃 #128515 #1F603 👺 #128122 #1F47A 🦊 #129418 #1F98A
🍣 #127843 #1F363 😄 #128516 #1F604 👻 #128123 #1F47B 🦋 #129419 #1F98B
🍤 #127844 #1F364 😅 #128517 #1F605 👼 #128124 #1F47C 🦌 #129420 #1F98C
🍥 #127845 #1F365 😆 #128518 #1F606 👽 #128125 #1F47D 🦍 #129421 #1F98D
🍦 #127846 #1F366 😇 #128519 #1F607 👾 #128126 #1F47E 🦎 #129422 #1F98E
🍧 #127847 #1F367 😈 #128520 #1F608 👿 #128127 #1F47F 🦏 #129423 #1F98F
🍨 #127848 #1F368 😉 #128521 #1F609 💀 #128128 #1F480 🦐 #129424 #1F990
🍩 #127849 #1F369 😊 #128522 #1F60A 💁 #128129 #1F481 🦑 #129425 #1F991
🍪 #127850 #1F36A 😋 #128523 #1F60B 💂 #128130 #1F482 🦒 #129426 #1F992
🍫 #127851 #1F36B 😌 #128524 #1F60C 💃 #128131 #1F483 🦓 #129427 #1F993
🍬 #127852 #1F36C 😍 #128525 #1F60D 💄 #128132 #1F484 🦔 #129428 #1F994
🍭 #127853 #1F36D 😎 #128526 #1F60E 💅 #128133 #1F485 🦕 #129429 #1F995
🍮 #127854 #1F36E 😏 #128527 #1F60F 💆 #128134 #1F486 🦖 #129430 #1F996
🍯 #127855 #1F36F 😐 #128528 #1F610 💇 #128135 #1F487 🦗 #129431 #1F997
🍰 #127856 #1F370 😑 #128529 #1F611 💈 #128136 #1F488 🧀 #129472 #1F9C0
🍱 #127857 #1F371 😒 #128530 #1F612 💉 #128137 #1F489 🧐 #129488 #1F9D0
🍲 #127858 #1F372 😓 #128531 #1F613 💊 #128138 #1F48A 🧑 #129489 #1F9D1
🍳 #127859 #1F373 😔 #128532 #1F614 💋 #128139 #1F48B 🧒 #129490 #1F9D2
🍴 #127860 #1F374 😕 #128533 #1F615 💌 #128140 #1F48C 🧓 #129491 #1F9D3
🍵 #127861 #1F375 😖 #128534 #1F616 💍 #128141 #1F48D 🧔 #129492 #1F9D4
🍶 #127862 #1F376 😗 #128535 #1F617 💎 #128142 #1F48E 🧕 #129493 #1F9D5
🍷 #127863 #1F377 😘 #128536 #1F618 💏 #128143 #1F48F 🧖 #129494 #1F9D6
🍸 #127864 #1F378 😙 #128537 #1F619 💐 #128144 #1F490 🧗 #129495 #1F9D7
🍹 #127865 #1F379 😚 #128538 #1F61A 💑 #128145 #1F491 🧘 #129496 #1F9D8
🍺 #127866 #1F37A 😛 #128539 #1F61B 💒 #128146 #1F492 🧙 #129497 #1F9D9
🍻 #127867 #1F37B 😜 #128540 #1F61C 💓 #128147 #1F493 🧚 #129498 #1F9DA
🍼 #127868 #1F37C 😝 #128541 #1F61D 💔 #128148 #1F494 🧛 #129499 #1F9DB
🍽 #127869 #1F37D 😞 #128542 #1F61E 💕 #128149 #1F495 🧜 #129500 #1F9DC
🍾 #127870 #1F37E 😟 #128543 #1F61F 💖 #128150 #1F496 🧝 #129501 #1F9DD
🍿 #127871 #1F37F 😠 #128544 #1F620 💗 #128151 #1F497 🧞 #129502 #1F9DE
🎀 #127872 #1F380 😡 #128545 #1F621 💘 #128152 #1F498 🧟 #129503 #1F9DF
🎁 #127873 #1F381 😢 #128546 #1F622 💙 #128153 #1F499 🧠 #129504 #1F9E0
🎂 #127874 #1F382 😣 #128547 #1F623 💚 #128154 #1F49A 🧡 #129505 #1F9E1
🎃 #127875 #1F383 😤 #128548 #1F624 💛 #128155 #1F49B 🧢 #129506 #1F9E2
🎄 #127876 #1F384 😥 #128549 #1F625 💜 #128156 #1F49C 🧣 #129507 #1F9E3
🎅 #127877 #1F385 😦 #128550 #1F626 💝 #128157 #1F49D 🧤 #129508 #1F9E4
🎆 #127878 #1F386 😧 #128551 #1F627 💞 #128158 #1F49E 🧥 #129509 #1F9E5
🎇 #127879 #1F387 😨 #128552 #1F628 💟 #128159 #1F49F 🧦 #129510 #1F9E6

Links úteis relacionados:

Tecnologias Web

Plataformas WEB

Uma plataforma WEB é uma coleção de tecnologias desenvolvidas para a criação, desenvolvimento e distribuição de páginas e aplicativos na internet.

Front End

Elas envolvem tecnologias de níveis variados de complexidade sendo que as mais básicas, e mais amplamente empregadas, são a linguagem HTML, Hyper Text Markup Language e CSS, Cascade Style Sheets. HTML é responsável pela estrutura de uma página, marcando títulos, parágrafos, links, blocos de texto, inserção de imagens, etc. CSS são arquivos de formatação, usados para definir tamanhos e tipos de fontes, cores, espaçamentos, etc. A linguagem javascript é usada pela maioria dos sites para executar ações do lado do navegador, tais como atualização dinâmica de conteúdo, envio de respostas do usuário para o servidor e controle de arquivos multimídia. Diversas novas tecnologias são baseadas no javascript, como AngularJS, Node.js e React.

Back End

Além das tecnologias usadas no navegador quase sempre é necessário usar linguagens de programação que alteram a construção de páginas e aplicativos no lado do servidor. O acesso a um banco de dados é a utilização mais comum. As mais empregadas são o PHP, Java, Python, Ruby on Rails e ASP.Net. Cada uma delas oferece um número de plataformas, cada em com suas vantagens e desvantagens, como agilidade de desenvolvimento e velocidade de resposta das páginas. As plataformas são basicamente ambientes desenvolvidos para resolver problemas determinados. Por ex., o PHP é a base de CMSs como o WordPress, útil para a construção de blogs e exibição de textos, ou desenvolvimento de sites de vendas online. O Python é usado na plataforma Django, criado e muito empregado nos sites de jornalismo, onde as atualizações devem ser rápidas e constantes. Todas essas tecnologias demandam algum tempo de aprendizado.

A Arquitetura básica da Web

Partindo do usuário, a web é vista primeiro em um navegador, como o Firefox, Chrome ou Safari. O navegador envia uma URL para a rede e recebe em resposta páginas HTML, arquivos de estilo CSS, arquivos Javascript e de multimídia, como áudios e vídeos. O navegador é um aplicativo que pode agrupar esse conteúdo e apresentá-los de forma compreensível. Navegadores modernos podem exibir outros formatos de arquivo, como pdf e planilhas, nativamente ou por meio de plugins. Além disso eles podem interpretar e executar código javascript, também recebidos do servidor.

 

A requisição do cliente (o navegador) contém o endereço do servidor que são computadores dedicados à esse serviço em diversas partes do mundo. O servidor recebe a solicitação e monta uma resposta. Para isso ele pode usar diversas tecnologias diferentes, acesso a banco de dados, código de frameworks montadores de páginas, etc. O servidor pode, por sua vez, acessar outros servidores para montar o conteúdo que será enviado para o cliente. O resultado será uma página em formato HTML gerada pelo servidor e apropriada para ser exibida.

 

Para a comunicação entre cliente e servidor é usado o HTTP, um protocolo de transferência de hipertexto que define como dados devem trafegar na rede. O HTTPS é um aperfeiçoamento que adiciona uma camada de segurança entre a emissão e resposta. Páginas HTML enviadas pelo servidor podem ser estáticas, enviadas como estão gravadas, sem processamento. O mais comum hoje é que as páginas sejam dinâmicas, montadas por solicitação, processadas por código de programa, geralmente ASP ou .NET, Java, Rails ou  Python. Um servidor de aplicativos é o mecanismo que executa a lógica de negócios no lado do servidor que, em geral, está separado do servidor de páginas.

Containers e Docker



Um histórico sobre containers

Embora exista a palavra contêiner, (plural: contêineres) nos dicionários de português do Brasil, manterei nesse texto a terminologia container (plural: containers) para me referir aos objetos de software tratado neste texto.

Uma máquina virtual, ou uma VM (Virtual Machine) é uma simulação em software de um computador, incluindo CPU, memória, área de armazenamento de arquivos e conexão com a internet. Um aplicativo como o VMware, VirtualBox ou Gnome Boxes cria um arquivo no computador (a imagem) que se comporta como sendo outro computador, no sistema operacional escolhido. Essa imagem se torna um novo ambiente que pode ser isolado ou ter suas interações bem definidas com o computador hospedeiro. Máquinas virtuais foram a primeira resposta dada para o problema de executar diversos aplicativos simultaneamente e em isolamento nos servidores.

Containers são pacotes de software que contém um ambiente completo para a execução de um aplicativo. Isso inclui suas dependências, bibliotecas de sistemas, configurações e outros binários, além dos arquivos de configuração necessários para essa operação. Eles estão disponíveis no Linux e Windows e são extremamente úteis para executar aplicativos da mesma forma, independentemente do ambiente usado. Containers são especialmente usados na fase de desenvolvimento, reduzindo os conflitos entre as equipes de TI por favorecerem a montagem de ambiente idêntico para todos os envolvidos no projeto. Além disso eles facilitam a portabilidade de um sistema operacional para outro, ou mesmo entre diferentes modelos de arquitetura de hardware.

Nas máquinas virtuais um pacote de software inclui, além do aplicativo que se quer executar, um sistema operacional inteiro. No caso dos containers, o kernel (o núcleo) do sistema operacional é compartilhado com outros containers que, por isso, usam menos recursos do que as máquinas virtuais. Um servidor físico executando três máquinas virtuais, por exemplo, teria que rodar três sistemas operacionais separados. O mesmo servidor executando três aplicativos em containers roda em um único sistema operacional, sendo que cada container tem acesso ao núcleo do sistema operacional hospedeiro. Dessa forma a máquina pode rodar um número muito superior de containers. Além disso, aplicativos de container são iniciados mais rapidamente porque não precisam inicializar todo o sistema operacional, liberando recursos que não estão em uso.

Outro problema que os containers buscam resolver é questão dos diversos aplicativos instalados em uma máquina que partilham bibliotecas comuns. Não é raro que um dos aplicativos necessite de versão diferente da biblioteca, o que pode fazer com que partes do software instalado deixe de funcionar. Containers são, portanto, úteis para o desenvolvimento, a distribuição e instalação, e uso em produção de softwares.

Finalmente, containers introduzem um nível adicional de segurança aos computadores. Sabemos que existem programas deliberadamente projetados para atacar, invadir, roubar dados e tempo de máquina no computador hospedeiro. Mesmo programas bem-intencionados podem conter bugs perigosos para o sistema e usuário. Um programa rodando em um container só tem acesso ao seu próprio ambiente, a menos que tenha sido projetado de outra forma.

Todas essas caraterísticas tornam os containers especialmente úteis para a implementação de aplicativos e serviços na nuvem, usando ambientes tais como o Azure, AWS, Google Cloud, etc.

Docker

Docker é o formato de implementação de container mais conhecido. Ele foi desenvolvido primeiro para o uso no Linux, depois levado até plataformas como o MacOS e Windows. Associado ao uso de docker temos o Kubernetes, um projeto de código aberto do Google que serve como orquestrador de aplicativos em container. Ele é um dos gerenciadores de containers e imagens do docker disponíveis.

O projeto é open-source e mantido pela empresa Docker Inc., EUA. Empresas e indivíduos contribuem para o projeto que é construído sobre uma arquitetura de plug-ins, onde componentes podem ser acrescentados ou removidos. A Open Container Iniciative, OCI, é um conselho fundado para zelar pela manutenção e observância de padrões, e é mantida pela Linux Foundation com a colaboração de Docker Inc. e outras.

Atualmente (em 2022) o docker roda nativamente no Linux. Para MacOs e Windows ele instala uma única máquina virtual que pode executar todos os containers. Em sistemas modernos é possível rodar dentro de containers aplicativos do Windows e macOS. Docker não é uma linguagem de programação nem um ambiente de desenvolvimento mas sim uma ferramenta de comando de linha que ajuda na solução de diversos problemas comuns ao processo de desenvolvimento, tais como a construção de projetos, sua distribuição, instalação, remoção, atualização e execução. Existem hoje diversos aplicativos GUI (com interfaces gráficas) para o gerenciamento do Docker.

Resumindo

Podemos pensar um container como uma caixa que contém aplicações. Dentro dela existe tudo o que é necessário para a execução das aplicações. O container se assemelha a um computador, com seu próprio nome de máquina, endereço de IP address e até seus próprios discos, todos eles virtuais e criados pelo docker.

Um aplicativo dentro do container não tem contato com nada fora dele, exceto o que for explicitamente indicado. Um exemplo disso seriam os casos de interação com o usuário via teclado, ou o acesso do aplicativo a um banco de dados externo.

Terminologia

Images (imagens do Docker) contêm o código-fonte do aplicativo executável, junto com as ferramentas, bibliotecas e dependências que o código precisa para ser executado. Elas podem ser construídas pelo desenvolvedor ou baixadas prontas de repositórios como o Docker Hub. Geralmente uma imagem é composta de várias outras imagens base que são empilhadas em camadas. Vários containers podem ser criados a partir de uma imagem.

Containers são as instâncias ativas e em execução das imagens. Enquanto imagens são arquivos de leitura somente (read-only) os containers são processos ativos que podem interagir com outros processos. Administradores podem ajustar suas configurações e condições usando comandos do Docker.

Dockerfiles (arquivos Docker) são arquivos de texto com as instruções sobre como criar imagens. Um Dockerfile automatiza o processo de criação de containers, contendo todos os passos na sequência que devem ser executados para a construção da imagem. Ele é similar a um script, declarando qual é a imagem base inicial a usar e com instruções para instalar os demais programas necessários, em camadas posteriores.

Build é a ação de criar de uma imagem usando as informações fornecidas pelo Dockerfile, desde que os arquivos adicionais estejam disponíveis no diretório onde a imagem é criada.

Tag (marcação) é uma marca ou rótulo aplicado em imagens para diferenciar versões. As tags podem ser usadas para, por exemplo, baixar uma imagem em versão específica.

Layers (camadas) são modificações aplicadas à uma imagem por meio de instruções do dockerfile. Por exemplo, dentro de uma imagem do Ubuntu Linux, que está na primeira camada, pode-se instalar outro aplicativo qualquer que será construído em uma segunda camada. Layers são identificadas por ids (hashes) que são usados para identificá-las.

Repository (repositório) é uma coleção de imagens do Docker rotuladas com marcação (tags) que indicam a versão da imagem. Podem conter imagem de versões diversas diferenciadas por tags, e variantes para as diversas plataformas.

Docker Hub é o repositório público padrão de imagens do Docker considerado a “maior biblioteca para imagens de containers”. Ele contém mais de 100.000 imagens de containers provenientes de fornecedores de software comercial ou de código aberto, e desenvolvedores individuais. Usuários do Docker Hub podem compartilhar suas imagens livremente ou baixar imagens base para usar no desenvolvimento de outros projetos. Existem outros repositórios, como o GitHub. Acesse o site Docker Hub.

Registro do Docker é um sistema de armazenamento e distribuição de imagens do Docker. Ele permite rastrear versões de imagens em repositórios usando o git, uma ferramenta de controle de versão. O registro padrão para imagens mais frequentemente usado é o Docker Hub (propriedade da Docker como uma organização).

Daemon Docker é um serviço que cria e gerencia imagens Docker usando os comandos do cliente. Ele serve como central de controle da implementação do Docker. O servidor onde o daemon é executado é chamado de host do Docker, que pode ser o computador local ou um serviço em nuvem.

Docker Desktop é um aplicativo que facilita a criação e compartilhamento de aplicativos e microsserviços em containers. Junto com o Desktop são instaladas ferramentas como Kubernetes, Docker Compose, BuildKit e verificação de vulnerabilidades. Inicialmente disponível apenas para Windows e MacOS, foi disponibilizado para desenvolvedores no Linux em Maio de 2022. Docker Desktop também inclui extensões do Docker para facilitar a integração com ferramentas de outros desenvolvedores da equipe ou de terceiros.

Docker Dashboard é um aplicativo GUI para gerenciamento de imagens, containers e volumes do Docker. Ele é um aplicativo WEB que roda em Node, abrindo uma página no navegador e pode ser usado para gerenciar visualmente os recursos de container.

Docker Compose é uma ferramenta utilizada na definição e compartilhamento de aplicações multi-container. Com Compose podemos criar um arquivo YAML para definir os serviços usados. Dessa forma um aplicativo pode ser iniciado ou finalizado com um único comando. Ele também facilita a colaboração entre desenvolvedores.

Instalando o Docker


Existem duas versões disponíveis para instalação:

  • Community Edition (CE), edição da comunidade,
  • Enterprise Edition (EE), edição comercial.

Docker CE é gratuita. Docker EE é o mesmo pacote de software, mas com suporte técnico e acesso à outros produtos da empresa.

Instalação: Fedora Linux, Ubuntu Linux, Archlinux, Mac, Windows.

Como exemplo, listamos o procedimento de instalação no Fedora Linux:

# Primeiro habilitamos o repositório    
$ sudo dnf -y install dnf-plugins-core

$ sudo dnf config-manager \
     --add-repo \
     https://download.docker.com/linux/fedora/docker-ce.repo

# instalamos o docker engine
$ sudo dnf install docker-ce docker-ce-cli containerd.io docker-compose-plugin

Para iniciar o serviço do docker via systemctl usamos start docker. Depois podemos testar se a instalação foi bem sucedida verificando a versão instalada e o status do serviço.

# para iniciar o Docker
sudo systemctl start docker

# para verificar a versão instalada
$ docker --version
  Docker version 20.10.17, build 100c701

# verificando o status do serviço
$ sudo service docker status
Redirecting to /bin/systemctl status docker.service
● docker.service - Docker Application Container Engine
     Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
     Active: active (running) since Fri 2022-08-12 18:52:19 -03; 1min 10s ago
TriggeredBy: ● docker.socket
       Docs: https://docs.docker.com
   Main PID: 32787 (dockerd)
      Tasks: 13
     Memory: 111.8M
        CPU: 344ms     # (saída truncada)

Nessa instalação é criado um grupo de usuários que podem rodar o Docker. Por default o Docker exige privilégios de root para rodar containers, o que pode ser problemático para a segurança do sistema. Para rodar o Docker sem necessidade de emitir comandos como superuser adicionamos um usuário ao grupo do Docker.

$ sudo usermod -aG docker <nome_usuario>
# verificando
$ cat /etc/group | grep docker
  docker:x:973:nome_usuario

Temos dois componentes principais com a instalação do docker: o cliente e o docker daemon (também chamado de docker engine). Uma versão alternativa de docker version (sem os hífens) fornece mais dados sobre a instalação:

$ docker version
  Client: Docker Engine - Community
   Version:           20.10.17
   API version:       1.41
   Go version:        go1.17.11
   Git commit:        100c701
   Built:             Mon Jun  6 23:03:59 2022
   OS/Arch:           linux/amd64
   Context:           default
   Experimental:      true

Server: Docker Engine - Community (saída truncada)

Para instalar e verificar o docker-compose usamos:

# instalar o docker
$ sudo dnf install docker-compose  # (no fedora) ou
$ sudo apt install docker-compose  # (no ubuntu)

# Para verificar a instalação
$ docker-compose version 
  docker-compose version 1.29.2, build unknown
  docker-py version: 5.0.3
  CPython version: 3.10.5
  OpenSSL version: OpenSSL 3.0.5 5 Jul 2022

Desinstalação e atualização: Para atualizar o docker é necessário remover os pacotes antigos e realizar uma nova instalação.

As desinstalações podem ser feitas da seguinte maneira:

# para desinstalar o docker
$ sudo dnf remove docker-desktop

# para desinstalar Docker Engine, CLI, containerd, e Docker Compose
$ sudo dnf remove docker-ce docker-ce-cli containerd.io docker-compose-plugin

Docker Desktop

Docker Desktop é um aplicativo (disponível para Mac, Linux ou Windows) que permite criar, compartilhar e executar aplicativos e microsserviços em containers. Ele fornece uma GUI (interface gráfica do usuário) simples que permite gerenciar seus containers, aplicativos e imagens diretamente de sua máquina.

Para instalar o Docker desktop faça o download do pacote mais recente: Docker desktop e o instale, por exemplo com o comando dnf. Isso criará um ícone no desktop que pode ser clicado para iniciar o aplicativo.


$ sudo dnf install ./docker-desktop--.rpm

# para iniciar o docker-desktop clique no ícone no desktop ou
# alternativamente use, para iniciar e finalizar o docker-desktop
$ systemctl --user start docker-desktop
$ systemctl --user stop docker-desktop

O Docker-desktop é iniciado dando as alternativas de criação de uma conta no Docker, de login em contas já criadas, ou de prosseguir sem esse login.

Docker Dashboard (GUI)

Um aplicativo web pode ser usado com Docker Dashboard. O Dashboard roda com Node.js que, portanto, precisa estar instalado em seu sistema. Depois você deve clonar um repositório do Github.

# clone repositório
$ git clone git@github.com:rakibtg/docker-web-gui.git

# mude para o novo directório criado
$ cd ./docker-web-gui

# execute o app app.js usando o node. Essa ação instalará os módulos necessários ainda não instalados.
$ node app.js

# agora você pode abrir no navegador a página http://localhost:3230/

O navegador abrirá uma página exibindo as imagens instaladas, containers em execução ou parados, visualização dos logs, criação e vizualização de grupos de containers.

Para baixar o código do aplicativo usando git clone git@github.com:rakibtg/docker-web-gui.git, você deve ter uma conta do GitHub com chaves certificadas de ssh. Instruções em Connecting to GitHub with ssh. Alternativamente você pode entrar no site do GitHub para esse aplicativo, baixar e instalar o código.

Imagens do Docker

Uma imagem do docker é um objeto que contém um aplicativo e arquivos de sistema, suficientes para rodar esse aplicativo. Essas imagens podem ser construídas pelo desenvolvedor ou baixadas pela internet de algum repositório, onde outros desenvolvedores as disponibilizaram. Por default as imagens são procuradas no GitHub. Um exemplo simples pode ser dado com a imagem de hello_world. Digitamos no prompt de comando do terminal:

# o primeiro passo é baixar uma imagem
$ docker pull hello-world
# o container pode ser agora executado
$ docker run hello-world
  Hello from Docker!
  This message shows that your installation appears to be working correctly.
  (saída truncada)


O uso de docker pull <imagem> não é estritamente necessário. O comando docker run procura primeiro a imagem na máquina local e, se ela não foi previamente baixada, ele a procura em Docker Hub. Se a imagem for encontrada ele a baixa e constroi um container à partir dela. No caso desse exemplo a imagem contém um aplicativo simples de demonstração que imprime a mensagem listada (que foi aqui truncada) no console. A cada execução de run um novo container é criado e seu aplicativo subjacente é rodado. Imagens baixadas são previamente compiladas (built).

Se você está com o Docker-Desktop aberto a imagem baixada aparecerá no painel do aplicativo.

O comando docker image ls permite ver quais são as imagens já baixadas:

$ docker image ls
  REPOSITORY                   TAG       IMAGE ID       CREATED        SIZE
  hello-world                  latest    feb5d9fea6a5   9 months ago   13.3kB

A tag latest indica que a última versão disponível foi baixada. Em outros casos, a tag é a versão da imagem. A imagem hello-world foi preparada para teste dos iniciantes e contém instruções interessantes. Para apagar um imagem baixada usamos docker rm imagem ou cicando no painels do Docker-Desktop. Mesmo sem a imagem baixada podemos rodar o container. O output da execução será em inglês (para essa imagem) mas a exibiremos aqui com partes traduzidas.

# para apagar a imagem
$ docker rm hello-world

# para rodar novamente essa imagem
$ docker container run hello-world

  Hello from Docker!
  Essa messagem mostra que sua instalação parece estar funcionando corretamente.
  
  Para gerar essa mensagem Docker seguiu os seguintes passos:
   1. O cliente Docker fez contato com o Docker daemon.
   2. O Docker daemon baixou (pulled) a imagem "hello-world" do Docker Hub. (amd64)
   3. O Docker daemon criou um novo container à partir dessa imagem. O container
      executou código que produziu o presente output.
   4. O Docker daemon transmitiu esse output para o Docker client, que o enviou para
      o terminal.
  
  Você pode tentar algo mais ambicioso, por ex. rodando o container do Ubuntu usando:
   $ docker run -it ubuntu bash
  
  Partilhe imagens, fluxos de trabalho automatizados e mais coisas usando um Docker ID gratuito:
    https://hub.docker.com/
  
  Para ver mais exemplos e ideias visite:
        https://docs.docker.com/get-started/

Aproveite a oportunidade para entrar no Docker Hub e criar uma conta.

Comandos do Docker

Recapitulando, podemos baixar uma imagem do docker usando pull.

$ docker pull <nome_imagem>:latest

Como vimos latest significa que baixamos a última e mais atualizada versão da imagem. Outra versão pode ser especificada se necessário como, por exemplo, fazendo $ docker pull <nome_imagem>:8.0. Para executar essa imagem usamos:

$ docker run -d --name=<nome_alternativo> -p m:n -<nome_imagem>

Além do comando run para criar e rodar um container usamos os parâmetros:

  • --name: para dar um nome alternativo para o container criado,
  • -p: para tornar a porta do docker visível fora do container,
  • <nome_imagem>: especifica a imagem a ser executada,
  • m:n: mapeia portas do container em portas do host. (porta_host:porta_container)

Podemos ver quais são os containers em execução:

docker ps
container ID  IMAGE               COMMAND                 CREATED         STATUS                 
642881d63879  mysql/mysql-server  "/entrypoint.sh mysq…"  16 minutes ago  Up 16 minutes (healthy)

PORTS                                                       NAMES
0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060-33061/tcp  mysql1

Para encerrar a execução, e remover o container usamos, respectivamente:

# encerrar
$ docker stop <nome_imagem>
# remover container
$ docker rm <nome_imagem>

No linux, os arquivos de configuração do docker e as imagens baixadas ficam no diretório /var/lib/docker. No Windows o endereço desses arquivos varia, de acordo com o modo de instalação. Esses arquivos nunca devem ser manipulados diretamente pelo usuário.

Uma página de ajuda é exibida com docker help, sobre a sintaxe de docker. Uma lista completa da saída do help pode ser vista na seção Docker Help.

# ajuda geral
$ docker help

# ajuda sobre um comando específico
$ docker <comando> help
# por exemplo, para o comando cp (copy)
$ docker cp help

Podemos ver todas as imagens baixadas no computador e apagá-las, se assim for desejado.

# para listar as imagens
$ docker image ls
REPOSITORY                   TAG       IMAGE ID       CREATED         SIZE
mysql/mysql-server           latest    5a9594052aec   3 months ago    438MB
hello-world                  latest    feb5d9fea6a5   10 months ago   13.3kB

# para apagar as imagens usamos os primeiros caracteres do id para identificá-las
$ docker image rm -f 5a
$ docker image rm -f fe

#alternativamente, o seguinte comando lista todos os containers
$ docker container ls --all --quiet

# podemos apagar todos os containers usando (veja descrição abaixo)
$ docker container rm --force $(docker container ls --all --quiet)

A sintaxe $(), usada no último comando, faz com que a saída de um comando seja enviada para o comando externo. Nesse caso o comando interno gera uma lista de containers instalado, que é enviada para o comando de apagamento forçado. A chave –force (ou -f) força o apagamento mesmo que exista uma imagem desse container em execução. Esse comando deve ser usado com cautela pois a remoção é feita sem nenhuma confirmação.

Várias dessas operações, como inicializar o docker, apagar imagens, visualizar status e rodar uma imagem, podem ser executadas através do aplicativo gui, Docker Desktop. Você encontra mais ajuda no site do docker.

Imagem do MySQL

Como um exemplo mais completo e útil vamos carregar e executar uma imagem contendo o mysql. Começamos por baixar (pull) a imagem.


# baixar a imagem
$ docker pull mysql/mysql-server:latest
# um outpup  é exibido:
   What's Next?
     1. Sign in to your Docker account → docker login
     2. View a summary of image vulnerabilities and recommendations → docker scout quickview mysql/mysql-server:latest

A mensagem (aqui truncada) sugere o login na conta do docker ou a visualização de recomendações. O login também pode ser feito na janela do Docker Desktop.

Como vimos, latest significa que baixamos a última e mais atualizada versão do mysql. Outra versão pode ser especificada se necessário, como em $ docker pull mysql/mysql-server:8.0. Para executar essa imagem usamos:

# executar a imagem do mysql
$ docker run --name=mysql1 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=meupassword -d mysql/mysql-server
# um id é exibido

Pode ocorrer que a porta designada esteja ocupada por outro serviço do computador host, o que provoca a emissão de uma mensagem de erro. Nesse caso podemos usar outra porta, tal como docker run --name=mysql1 -p 8080:3306....

Além do comando run para criar e rodar um container usamos os parâmetros:

  • --name: para dar um nome alternativo para o container criado,
  • -p: para tornar a porta do docker visível fora do container,
  • -e: para inserir um password de gerenciamento do mysql,
  • mysql/mysql-server: especifica a imagem a ser executada,
  • 3306:3306: mapeia portas do container em portas do host, (porta_host:porta_container).

Podemos ver quais são os containers em execução:

docker ps
container ID  IMAGE               COMMAND                 CREATED         STATUS                 
642881d63879  mysql/mysql-server  "/entrypoint.sh mysq…"  16 minutes ago  Up 16 minutes (healthy)

PORTS                                                       NAMES
0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060-33061/tcp  mysql1

A linha foi quebrada para facilitar a visualização. Para conectar com o servidor do mysql executamos:

$ docker exec -it mysql1 mysql -uroot -p
Enter password: meupassword

Welcome to the MySQL monitor.  ... (saída truncada)
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> 


Um prompt de comandos fica aberto para receber comandos do mysql. Operações usuais sobre o banco de dados ficam disponíveis. Você pode encontrar mais informações sobre a sintaxe básica do SQL no artigo Linguagem de Consulta SQL.

Por exemplo, no caso abaixo criamos um banco de dados, uma tabela, inserimos valores, fazemos uma consulta, finalizando depois o uso do MySQL.

# cria um banco de dados
mysql> CREATE DATABASE teste;
# use esse BD
mysql> USE teste;
# cria uma tabela 
mysql> CREATE TABLE cidades(nome varchar(60),id varchar(4));

# insere valores na tabela
mysql> INSERT INTO cidades SET nome='Belo Horizonte', id='1';
mysql> INSERT INTO cidades SET nome='Salvador', id='2';
mysql> INSERT INTO cidades SET nome='Curitiba', id='3';

mysql> SELECT * FROM cidades;
+----------------+------+
| nome           | id   |
+----------------+------+
| Belo Horizonte | 1    |
| Salvador       | 2    |
| Curitiba       | 3    |
+----------------+------+
3 rows in set (0.00 sec)

# para abandonar o programa
mysql> exit

Para encerrar a execução, e remover o container usamos, respectivamente:

# encerrar    
$ docker stop mysql1
# remover container
$ docker rm mysql1

Conectando com o container

A interação com um container se dá de modo similar à execução de comandos em um computador remoto. Para mostrar isso executaremos uma imagem do docker chamada diamol/base, criada e disponibilizada no Dockerhub por E. Stoneman para exemplificar um trecho de seu livro (veja bibliografia). Para interagir com o aplicativo em execução usamos as flags –interactive e –tty (ou simplesmente -i e -t) para fazer essa interação através do terminal. O primeiro comando abaixo baixa a imagem e a executa, criando um container que expõe um prompt de terminal para o usuário. Os comandos aceitos no prompt dependem do sistema operacional. Por exemplo, no linux usamos ls para listar os arquivos do diretório atual; no Windows usamos dir.

# para baixar e rodar a imagem    
$ docker container run --interactive --tty diamol/base
  Unable to find image 'diamol/base:latest' locally
  latest: Pulling from diamol/base
  31603596830f: Pull complete 
  792f5419a843: Pull complete 
  Digest: sha256:787fe221a14f46b55e224ea0436aca77d345c3ded400aaf6cd40125e247f35c7
  Status: Downloaded newer image for diamol/base:latest

# O aplicativo expõe o prompt do terminal (usando os sinais / ⧣ para indicar o prompt)
/ ⧣
# no linux podemos listar os arquivos na pasta ativa
/ ⧣ ls
  bin    etc    lib    mnt    proc   run    srv    tmp    var
  dev    home   media  opt    root   sbin   sys    usr

# o nome da máquina em execução é o mesmo que o id da imagem
/ ⧣ hostname
2c33c99fcaf7

# para ver a data
/ ⧣ date
Sat Aug  6 15:38:21 UTC 2022

# uma lista de comandos disponíveis pode ser vista com help
/ # help
Built-in commands:
------------------
	. : [ [[ alias bg break cd chdir command continue echo eval exec
	exit export false fg getopts hash help history jobs kill let
	local printf pwd read readonly return set shift source test times
	trap true type ulimit umask unalias unset wait

# para terminar a execução
/ ⧣ exit

Mantendo esse terminal aberto, podemos abrir outro terminal e listar detalhes dos containers em execução:

$ docker container ls
container ID  IMAGE        COMMAND    CREATED        STATUS       PORTS  NAMES
2c33c99fcaf7  diamol/base  "/bin/sh"  3 minutes ago  Up 3 minutes        strange_lehmann

# para listar os processos dentro desse container (notando que 2c são os primeiros dígitos do container ID)
$ docker container top 2c
UID      PID      PPID      C    STIME    TTY     TIME      CMD
root     13436    13410     0    12:36    pts/0   00:00:00  /bin/sh

# para listar as ações (log) executadas nesse container
$ docker container logs 2c
# ls
bin    etc    lib    mnt    proc   run    srv    tmp    var
dev    home   media  opt    root   sbin   sys    usr

(retorna uma lista de comandos já utilizados no container, aqui truncada)

# inspect retorna uma descrição detalhada do container
$ docker container inspect 2c
  [
    {
        "Id": "2c33c99fcaf7fb94393ce1e7e31ce05ab6f5bcc914fc5eac8c90e600cf711995",
        "Created": "2022-08-06T15:36:14.327115583Z",
        "Path": "/bin/sh",
        "Args": [],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,                        ... (saída truncada)
        },
    }
]        

Os comandos usam o atalho 2c para se referir ao ID do container 2c33c99fcaf7. inspect retorna dados no formato JSON, aqui truncados, incluindo informações sobre caminhos usados pelo filesystem virtual e muitos outros.

Podemos ver uma lista de imagens instaladas usando docker image list ou docker image ls, e apagar uma imagem que não será mais necessária com docker image rm <id>, onde <id> é o id listado no passo anterior. No nosso caso, se usamos:

# para listar imagens baixadas
$ docker image list
  REPOSITORY                   TAG       IMAGE ID       CREATED         SIZE
  mysql/mysql-server           latest    5a9594052aec   3 months ago    438MB
  hello-world                  latest    feb5d9fea6a5   10 months ago   13.3kB
  diamol/base                  latest    9fc3f74c8b53   17 months ago   7.13MB

# para apagar a imagem referente à diamol/base (a imagem está em uso no container 5f73d74675a7)
$ docker image rm 9fc3f74c8b53
  Error response from daemon: conflict: unable to delete 9fc3f74c8b53 (must be forced)
  - image is being used by stopped container 5f73d74675a7

# para forçar a interrupção desse container e apagar a imagem
$ docker image rm -f 9fc3f74c8b53
  Deleted: sha256:9fc3f74c8b533bed2ac40ccfc6a5235ebb626a26a230dc3731fcdf926d984106

# lista todos os containers (em execução ou não)
$ docker container ls --all
  container ID   IMAGE               COMMAND                  CREATED        STATUS                  PORTS    NAMES
  5f73d74675a7   9fc3f74c8b53        "/bin/sh"                3 hours ago    Exited (0) 2 hours ago            intelligent_wescoff
  2c33c99fcaf7   9fc3f74c8b53        "/bin/sh"                6 hours ago    Exited (0) 3 hours ago            strange_lehmann
  e0ca567900b9   nginx               "/docker-entrypoint.…"   25 hours ago   Created                           webserver
  5ccc8d7d5ff3   hello-world         "/hello"                 32 hours ago   Exited (0) 32 hours ago           hopeful_banach
  fb9dc473c7f5   hello-world         "/hello"                 32 hours ago   Exited (0) 32 hours ago           loving_swartz
  5e5c65bb00c4   hello-world         "/hello"                 32 hours ago   Exited (0) 32 hours ago           cool_cannon
  (... saída truncada)

Como nenhum container está ativo (no meu caso) eles têm o status Exited. Quando um container está em execução o mesmo ocorre com o aplicativo que ele contém. Quando o processo é terminado o container entra no estado de encerrado (Exited). Containers encerrados não usam recursos de memória nem tempo de CPU do computador mas são mantidos em disco e ocupam espaço. Containers não desaparecem quando são terminados mas continuam a existir e podem ser executados novamente. Nesse estado eles retêm logs e arquivos no sistema de arquivos. Para remover containers encerrados você deve emitir o controle específico para tal.

Outros comandos associados:

$ docker ps -a                  # Lista containers, informando a imagem que os gerou

$ docker images                 # Lista imagens 

$ docker rm <container_id>      # Remove um container interrompido (não em execução)

$ docker rm -f <container_id>   # Força a remoção de um container mesmo que em execução

$ docker rmi <image_id>         # Remove uma imagem que não esteja
                                # associada um container em execução

$ docker rmi -f <image_id>      # Força a remoção da imagem mesmo que esteja em uso

$ docker system prune -a        # remove: todos os containers parados,
                                # todas as redes não utilizadas 
                                # todas as imagens não ativas
                                # todo o cache de docker                           


Claro que o comando docker system prune -a deve ser usada com bastante cuidado.
Uma lista de comandos está listada na seção Docker Help.

Rodando um webserver



Outro bom exemplo de funcionamento do docker consiste em rodar um container preparado para fins educacioanis pelo DockerHub, que roda um tutorial interativo para iniciantes. Outras instruções podem ser encontradas em Docker Docs: Getting started.

$ docker run -d -p 8080:80 docker/getting-started

# as flags podem ser unidas
docker run -dp 8080:80 docker/getting-started

# para acessar o tutorial abra o navegador em http://localhost:8080. 

As flags ou chaves significam o seguinte:

  • -d: execute o container em mode destacado (detached ou em background).
  • -p 8080:80: associa a porta 8080 do host à porta 80 do container. Se a porta 8080 já estiver em uso especifica outra porta, como 80 ou 3000.
  • docker/getting-started especifica o nome da imagem a ser usada.

Como já vimos, a imagem é baixada (se não estiver no computador) e rodada. Ela cria uma seção de um servidor que pode ser acessado em http://localhost:8080, contendo um tutorial de uso do docker com instruções iniciais para seu uso, preparada por seus desenvolvedores.

Se iniciarmos uma imagem em segundo plano e quisermos um terminal interativo podemos executar

$ docker exec -it <container> bash

Rodando Oracle Linux


Mostraremos outro exemplo rodando um container com Oracle Linux.

Verificamos primeiro o status do docker:

$ service docker status
  Redirecting to /bin/systemctl status docker.service
  ○ docker.service - Docker Application Container Engine
       Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
       Active: inactive (dead)
  TriggeredBy: ○ docker.socket
       Docs: https://docs.docker.com

Vemos que o docker está inativo, como exibido em Active: inactive (dead). Então reiniciamos o serviço:

$ sudo service docker start
  [sudo] password for guilherme: 
  Redirecting to /bin/systemctl start docker.service

$ service docker status
  Redirecting to /bin/systemctl status docker.service
  ● docker.service - Docker Application Container Engine
       Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
       Active: active (running) since Mon 2022-08-15 16:32:32 -03; 2s ago
       (saída truncada)

Podemos terminar um comando no prompt do bash usamos CTRL-C.
Para baixar a imagem do Oracle Linux usamos pull oraclelinux:9, sendo 9 a versão mais atual, na data em que esse texto foi escrito. Caso seja necessária uma versão anterior usamos pull oraclelinux:8 (ou a versão necessária). No caso do Oracle Linux a tag latest foi removida, como se pode ver na página docker: oraclelinux.

$ sudo docker pull oraclelinux:9
  9: Pulling from library/oraclelinux
  16c5055e8b97: Pull complete 
  Digest: sha256:93514816e192d51fbb34c008a479c789c43fb1fe0c2f91bd5be1ba7919dafa77
  Status: Downloaded newer image for oraclelinux:9
  docker.io/library/oraclelinux:9

# para visualizar as imagens baixadas
$ docker images
REPOSITORY               TAG       IMAGE ID       CREATED        SIZE
docker/getting-started   latest    cb90f98fd791   4 months ago   28.8MB
oraclelinux              9         61a922496eee   16 hours ago   236MB


Podemos rodar essa imagem, gerando um container, com o comando docker run <nome_imagem>. Por default a imagem é criada, se já não existe, e roda em primeiro plano (attached mode). Para rodá-la no segundo plano (em background ou dettached mode) usamos o marcador (flag) -d. Para usar o terminal de modo interativo use as opções –i, –t. As flags podem ser juntas, como em –dit. A flag –rm serve para apagar o container quando ele for encerrado. A opção --name é usada para dar um nome para a imagem sendo rodada. Esse nome deve ser único (não pode ter o mesmo nome de um que já está rodando).

# para rodar a imagem em primeiro plano (attached mode)    
$ sudo docker run  <nome_imagem>
    
# para rodar a imagem em segundo plano ou background (dettached mode)
$ sudo docker run -d  <nome_imagem>

No caso de nossa imagem para o oracle linux usaremos run com as flags já descritas. Um terminal é apresentado, onde podem ser digitados comandos apropriados.

$ docker run -i -t  --rm --name oraclelinux oraclelinux:9
  [root@723e7c1a71ea /]#
# se executado em outro terminal (para não fechar o terminal do oraclelinux)
# podemos ver que temos uma imagem rodando. 
$ docker images
  REPOSITORY    TAG       IMAGE ID       CREATED      SIZE
  oraclelinux   9         a0ad236ea4f0   6 days ago   218MB

# no prompt do Oracle digitamos comandos. Por exemplo:
[root@723e7c1a71ea /]# cat /etc/oracle-release
  Oracle Linux Server release 9.0

[root@03d35e4c35b1 teste_oracle]# cat /etc/os-release
  NAME="Oracle Linux Server"
  VERSION="9.0"
  <saída truncada>

O comando cat é usado para ler, concatenar e escrever em arquivos, para o output padrão. Outros comandos usuais do linux podem ser executados no prompt:

[root@723e7c1a71ea /]# mkdir teste_oracle      # cria um novo diretório
[root@723e7c1a71ea /]# cd teste_oracle         # move cursor para o novo diretório

# envia texto para arquivo.txt
[root@723e7c1a71ea teste_oracle]# echo "teste de docker no oraclelinux" > arquivo.txt
# exibe conteúdo do arquivo.txt
[root@723e7c1a71ea teste_oracle]# cat arquivo.txt
  teste de docker no oraclelinux
  
# para interromper a execução usamos exit [root@723e7c1a71ea teste_oracle]# exit # retornamos ao prompt do sistema hospedeiro $

Uma vez rodando o Oracle Linux todas as operações válidas no sistema podem ser executadas. Mais detalhes sobre o Oracle Linux 9 pode ser lido no site da Oracle.

O Docker é normalmente usado para rodar aplicativos em segundo plano e programas CLI (command-line interface). Mas ele também pode ser usado para executar programas com interface gráfico, desde que você faça o setup necessário. Informações podem ser vistas por exemplo em Baeldung.com: Running GUI Applications in a Linux Docker Container.

Docker Help

A execução de docker help resulta em uma lista de comandos e parâmetros, mostrados na tabela.
Para ajuda sobre comando específico digite docker COMMAND --help. Para mais informações visite Docker Guides.

O Docker é executado como a seguinte sintaxe:
docker [OPÇÕES] COMANDO

 Comandos comuns:
run Cria e executa um novo container de uma imagem
exec Executa comando em container em execução
ps Lista containers
build Cria imagem de um Dockerfile
pull Download imagem de um repositório
push Upload imagem para repositório
images Lista imagens
login Login no repositório
logout Logout do repositório
search Pesquisa imagens no Docker Hub
version Exibe informações de versão do Docker
info Exibe informações gerais de sistema
 Comandos de gerenciamento:
builder Gerencia builds
buildx* Docker Buildx
compose* Docker Compose
container Gerencia containers
context Gerencia contexts
debug* Abre uma shell dentro de qualquer imagem ou container
dev* Ambiente de Docker Dev
extension* Gerencia extensões do Docker
feedback* Fornece feedback direto no terminal
imagem Gerencia imagens
init* Cria arquivos de inicialização do Docker para o projeto
manifest Gerencia manifestos de imagem e lists do Docker
network Gerencia networks
plugin Gerencia plugins
sbom* Visualiza Software Bill Of Materials (SBOM) de uma imagem
scout* Docker Scout
system Gerencia Docker
trust Gerencia confiança sobre imagens do Docker
volume Gerencia volumes
 Comandos de grupo:
attach Anexa entrada, saída e mensagens de erro padrões local a container em execução
commit Cria nova imagem após alteração em container
cp Copia arquivos/pastas entre um container e sistema local
create Cria novo container
diff Inspeciona alterações em arquivos e pastas no container
events Recebe eventos no servidor em tempo real
export Exporta aqruivos de sistema em um container para arquivo tar
history Exibe o histórico de uma imagem
import Importa conteúdo de tarball para criat sistema na imagem
inspect Retorna informação de baixo nível sobre objetos do Docker
kill Extingue um ou mais containersem execução
load Carrega uma imagem de arquivo tar ou STDIN
logs Captura os logs de um container
pause Pausa todos os processos dentro de um ou mais containers
port Lista mapeamento de portas para o container
rename Renomeia um container
restart Reinicia um ou mais containers
rm Remove um ou mais containers
rmi Remove uma ou mais imagems
save Grava uma ou mais imagems para arquivo tar (para STDOUT, por default)
start Inicializa um ou mais containers parados
stats Exibe estísticas de containers em tempo real
stop Para um ou mais containers em execução
tag Cria uma tag TARGET_IMAGE em referência à SOURCE_IMAGE
top Exibe os processos ativos de um container
unpause Recomeça todos os processos dentro de um ou mais containers
update Atualiza a configuração de um ou mais containers
wait Paraliza a execução até que um ou mais containers são interrompidos, depois exibe códigos de saída
 Opções globais:
–config string Local dos arquivos de configuração do cliente (default “/home/usuario/.docker”)
-c, –context string Nome do contexto a ser usado para conectar com o daemon (sobrescreve DOCKER_HOST e ajusta default para “docker context use”)
-D, –debug Liga modo de debug
-H, –host list Daemon socket a conectar
-l, –log-level string Ajusta nível de logging (“debug”, “info”, “warn”, “error”, “fatal”) (default é “info”)
–tls Usa TLS; implícito por –tlsverify
–tlscacert string Aceita apenas certificados assinado por esse CA (default “/home/usuario/.docker/ca.pem”)
–tlscert string Caminho para arquivo de certificado (default “/home/usuario/.docker/cert.pem”)
–tlskey string Caminho para arquivo de chave (default “/home/guilherme/.docker/key.pem”)
–tlsverify Use TLS e verifique o remoto
-v, –version Exibe informação de versão e termina

: Na tabela usamos a expressão repositório no lugar de registry. O Registry é a implementação de código aberto para armazenamento e distribuição de imagens de containers e outros conteúdos. A implementação do registro Docker Hub é baseada em Distribuição. Docker Hub implementa especificação de distribuição OCI versão 1.0.1. Mais informações em Docker Hub Registry.

Bibliografia


Livros:

  • Ashley, David: Foundation Dynamic Web Pages with Python, Apress, 2020,
  • Miel, Ian: Sayers, Aidan H.: Docker in Practice, 2 Ed., Manning, 2019,
  • Nickoloff, Jeff; Kuenzli, Stephen: Docker in Action, 2 Ed., Manning, 2019,
  • Poulton, Nigel: Docker Deep Dive, disponível em Leanpub.com, 2018,
  • Stoneman, Elton: Learn Docker in a Month of Lunches, Manning, 2020,
  • Vohra, Deepak: Pro Docker, Apress, 2016.

Sites: