SELECT AI no Oracle 23ai e 26ai: conversando com o banco em português
🇬🇧 Read this post in English: SELECT AI on Oracle 23ai and 26ai: Talking to Your Database in Plain Language
Por Ricardo Rezende — Database & more
Sexta-feira, 17h45... Já sabe, né?
Você conhece a cena.
Sexta à tarde, o WhatsApp apita: "Ricardo, consegue me passar rapidinho quais clientes estão com contrato ativo e quanto cada um paga por hora?". "Rapidinho", claro.
Você abre o SQL*Plus (sim, ainda uso tela preta 😄), lembra de cabeça onde está a flag de contrato, faz o JOIN com a tabela de suporte, lembra que precisa pegar o valor do reajuste mais recente, e entrega.
Cinco minutos, talvez dez. Happy Friday!!! Final de semana salvo.
Agora multiplique isso por todas as perguntas que chegam numa semana.
A pergunta que me fiz montando o meu laboratório de IA em um Autonomous Database no OCI (Oracle Cloud Infrastructure) (utilizo um Oracle 23ai Always Free no meu tenancy 😄) foi simples: e se o chefe pudesse perguntar isso em português, e o próprio banco gerasse e rodasse o SQL?
É exatamente o que o SELECT AI entrega no Oracle 23ai e no 26ai.
Neste post vou direto ao que funciona, ao que me "deu trabalho", e ao que ninguém costuma contar.
O que é SELECT AI (e o que ele não é)
SELECT AI é o recurso do Oracle que traduz linguagem natural em linguagem SQL dentro do banco, usando um LLM (Large Language Model) como mecanismo de tradução. Quem orquestra a parafernália toda é o pacoteDBMS_CLOUD_AI.
Basta você cria um profile apontando para um provedor de IA (no meu caso, use o OCI Generative AI), diz quais objetos o modelo pode "enxergar", e passa a perguntar em português.
O ponto que costuma confundir é: o modelo não vê seus dados pra gerar a query. Ele vê o dicionário do schema — nomes de tabelas, colunas e, principalmente, os comentários (COMMENT ON). A partir disso ele escreve o SQL. Quem executa é o banco, não o LLM.
Guarde essa frase: "o modelo escreve a query; o Oracle roda a query". Isso muda tudo na hora de entender as quatro ações disponíveis.
Pré-requisitos: o profile e a credential
Antes de qualquer pergunta bonita em português, dois pré-requisitos:
1. Uma credencial para falar com o provedor de IA. Aqui mora a primeira diferença entre os dois mundos:
- No Autonomous Database 23ai (OCI), use Resource Principal. O ADB se autentica na OCI sem você guardar chave nenhuma:
-- Como usuário ADMIN
EXEC DBMS_CLOUD_ADMIN.ENABLE_RESOURCE_PRINCIPAL(username => 'RREZENDE');
- No Oracle 26ai on-premises (esta é a opção disponível para você "baixar" e instalar), não existe Resource Principal. Você gera um par de chaves RSA (API Key) no console da OCI e cria uma credencial com a chave privada:
-- Como usuário RREZENDE
BEGIN
DBMS_CLOUD.CREATE_CREDENTIAL(
credential_name => 'OCI_API_KEY_CRED',
user_ocid => 'ocid1.user.oc1..xxxxxxxx',
tenancy_ocid => 'ocid1.tenancy.oc1..xxxxxxxx',
fingerprint => 'aa:bb:cc:dd:...',
private_key => '-----BEGIN RSA PRIVATE KEY-----
... conteúdo da chave ...
-----END RSA PRIVATE KEY-----'
);
END;
/
(No on-premises ainda há o detalhe da ACL (Access Control List) de rede via DBMS_NETWORK_ACL_ADMIN liberando o endpoint do GenAI — o ADB já vem com isso resolvido. Mas isso é assunto pra outro post.)
2. O profile. É aqui que você diz o provedor, o modelo, e a lista de objetos visíveis:
-- Como usuário RREZENDE
BEGIN
DBMS_CLOUD_AI.CREATE_PROFILE(
profile_name => 'AI_PROFILE',
attributes => '{
"provider" : "oci",
"credential_name" : "OCI$RESOURCE_PRINCIPAL",
"model" : "cohere.command-r-plus-08-2024",
"oci_compartment_id" : "ocid1.compartment.oc1..xxxxxxxx",
"temperature" : 0,
"object_list" : [
{"owner": "RREZENDE", "name": "EMPRESA"},
{"owner": "RREZENDE", "name": "AMBIENTE"},
{"owner": "RREZENDE", "name": "CONTATO"},
{"owner": "RREZENDE", "name": "MANUTENCAO"},
{"owner": "RREZENDE", "name": "SUPORTE"},
{"owner": "RREZENDE", "name": "TAREFA"},
{"owner": "RREZENDE", "name": "DB_GROWTH"}
]
}'
);
END;
/
EXEC DBMS_CLOUD_AI.SET_PROFILE('AI_PROFILE');
Coloque o SET_PROFILE no setup de conexão da aplicação ou num LOGON trigger — assim toda sessão já nasce pronta.
Um detalhe que poupa cabelo branco (não no me caso 😅): no provider oci, atributos como url, endpoint e system_prompt não são válidos e disparam ORA-20046. O Oracle resolve o endpoint do GenAI internamente; você não aponta "na mão".
E sobre o modelo: testei o cohere.command-r-plus-08-2024 e ele entende português nativamente, o que para nós resolve metade do problema.
Outro aviso de quem já tropeçou: meta.llama-3.1-70b-instruct não existe — o nome correto é meta.llama-3.3-70b-instruct. Confira sempre os modelos disponíveis na sua região antes de cravar no profile.
A lição mais importante: comentário não é firula, é especificação
Vou repetir porque é o ponto que separa um SELECT AI que acerta de um que chuta: o modelo gera o SQL a partir dos comentários do schema.
Sem COMMENT ON, ele adivinha a semântica pelo nome da coluna — e adivinhação em banco de produção é o caminho mais curto pra um relatório errado entregue com cara de certo.
Um exemplo real do meu schema. Eu tenho duas formas de "contrato" que pareciam intercambiáveis para o modelo e não são:
EMPRESA.CONTRATO_ATIVO— flag'S'/'N', indica se o contrato está ativo.SUPORTE.TIPO_CONTRATO— a categoria do contrato (Database, Cloud), nada a ver com ativo/inativo.
Sem orientação, o modelo, ao ler "contrato ativo", filtrava em TIPO_CONTRATO. Resultado lindo, número errado.
A correção não foi melhorar o prompt, sim documentar o schema direito:
COMMENT ON COLUMN rrezende.empresa.contrato_ativo IS
'Flag que indica se o contrato de serviço está ativo. Valores: S = Ativo, N = Inativo. Este é o único campo correto para filtrar contratos ativos. Ex: WHERE CONTRATO_ATIVO = ''S''.';
COMMENT ON COLUMN rrezende.suporte.tipo_contrato IS
'Categoria do contrato (ex: Database, Cloud). NÃO indica se o contrato está ativo ou inativo — para status, use EMPRESA.CONTRATO_ATIVO.';
Repare que o comentário faz mais do que descrever, ele instrui. Diz qual coluna usar, qual evitar, e dá o valor exato esperado no WHERE. Faço isso para todo campo enumerado (status, função, tipo), para unidades (tamanhos em MB, com a regra de converter para GB), e para armadilhas estruturais — por exemplo, uma tabela minha tem a primary key desabilitada (preciso rever isso 😃) e pode ter duplicatas; o comentário avisa o modelo para tomar cuidado com COUNT e SUM.
IMPORTANTE: Depois de mexer nos comentários, recrie o profile (DROP_PROFILE + CREATE_PROFILE). O SELECT AI lê esse dicionário na criação; sem recriar, ele continua com a visão antiga.
Se você só vai levar uma coisa deste post pra casa, leve esta: bom comentário de schema é engenharia de prompt persistente, versionada e que serve a toda a equipe — não a apenas uma sessão.
As quatro ações: showsql, runsql, narrate e chat
O SELECT AI tem quatro modos, e entender a diferença evita 90% das frustrações:
| Ação | O que faz | Consulta o banco? | Retorna |
|---|---|---|---|
showsql |
Traduz a pergunta em SQL e mostra a query, sem rodar | Não | Texto SQL |
runsql |
Gera o SQL e executa | Sim | Linhas / JSON |
narrate |
Gera, executa e o LLM resume em texto | Sim | Texto em linguagem natural |
chat |
Conversa pura com o LLM, zero acesso ao banco | Não | Conhecimento do modelo |
A confusão clássica é com o chat. Ele é, de propósito, desconectado do seu schema — é como abrir o ChatGPT ou Gemini dentro do banco. Se você perguntar ao chat "Quais clientes têm contrato ativo?", ele responde, com toda honestidade, que não tem acesso aos seus dados. E está certo: ele realmente não tem.

Então a regra de bolso é:
- Pergunta sobre conhecimento geral de Oracle/DBA (exemplo: "O que é um índice HNSW?") →
chat. - Pergunta sobre os seus dados (exemplo: "Qual cliente teve mais falha de backup em 2024?") →
narrate. - Quer só conferir o SQL antes de executar →
showsql. - Executar o SQL →
runsql.
Meu fluxo de trabalho é sempre: showsql para inspecionar, runsql para os dados crus, narrate para a versão legível que vai pro gestor.
Exemplo prático: do português ao resultado
No Database Actions (a worksheet web do ADB), a sintaxe SELECT AI funciona nativa:
-- Mostra o SQL gerado, sem rodar
SELECT AI SHOWSQL
Quantas atividades de manutenção foram concluídas no último mês?;
-- Roda e devolve os dados
SELECT AI
Quais empresas têm contrato de suporte ativo?;
-- Resposta em texto corrido
SELECT AI NARRATE
Quantas empresas existem por segmento de mercado?;

Já no SQL Developer tem uma pegadinha: a sintaxe SELECT AI dispara ORA-17041, porque o cliente intercepta os bind parameters internos.

A solução é usar a função DBMS_CLOUD_AI.GENERATE, que funciona em qualquer cliente:
SELECT DBMS_CLOUD_AI.GENERATE(
prompt => 'Quais empresas têm contrato de ativo?',
profile_name => 'AI_PROFILE',
action => 'runsql'
) AS resultado
FROM dual;


Uma dica: Para garantir resposta em português no narrate, prefixe o prompt — 'Responda em português: ...'. Em respostas curtas, na maioria das vezes nem precisa, mas em texto longo o prefixo evita o modelo escorregar pro inglês.
O que me mordeu (pra você não apanhar igual)
Algumas lições que vieram na marra:
Não use nomes de tabela no prompt. Palavras como "suporte", "empresa" ou "ambiente" mapeiam direto para nomes de tabela (no meu caso) e levam o modelo a filtrar na coluna errada. Fale a linguagem do negócio: "contrato ativo", não "suporte ativo". O modelo é melhor com conceito do que com nome de objeto.
narrate aberto estoura o limite de tokens. Pedir "resuma a tabela de manutenção" faz o modelo tentar trazer todas as linhas — e cai num ORA-20400: too many tokens. Procure sempre definir: um agregado, um intervalo de datas, uma empresa específica. narrate é para resposta, não para despejo de dados.
Sempre confira com showsql antes de confiar no runsql. Em produção, "parece certo" não é certificado de qualidade. Dois segundos olhando o SQL gerado já te dizem se o JOIN foi no lugar certo.
Recrie o profile depois de mexer nos comentários. Já disse, mas repito porque eu mesmo esqueci e fiquei dez minutos achando que o modelo era teimoso. Não era — eu que não tinha recarregado o dicionário.
23ai ou 26ai: a única diferença que importa aqui
Para SELECT AI, a lógica é idêntica nos dois. O que muda é como o banco se autentica no OCI:
- ADB 23ai → Resource Principal (
OCI$RESOURCE_PRINCIPAL), zero gestão de chave. - 26ai on-premises → credencial de API Key (
OCI_API_KEY_CRED) + liberação de ACL de rede e firewall para o endpoint do GenAI na porta 443.
Trocada a credencial no CREATE_PROFILE, o resto — profile, comentários, as quatro ações, os prompts — é tudo igualzinho. Quem aprende num, opera no outro.
O que levar pra casa
SELECT AI não é mágica nem substituto do DBA — é uma camada de tradução dentro do banco e respeita seus privilégios e seus dados (nada sai pra treinar modelo de ninguém).
O trabalho de verdade não está em escrever prompts espertos; está em documentar o schema com comentários que instruem. Quem sempre tratou COMMENT ON como burocracia opcional agora tem o melhor dos motivos para levá-lo a sério: ele virou a interface entre o português do usuário e o SQL do banco.
Comece pequeno: um profile, meia dúzia de tabelas bem comentadas, e o ciclo showsql → runsql → narrate. Em uma tarde você tem o gestor perguntando em português e o banco respondendo com SQL que você consegue auditar.
E você?
Já colocou SELECT AI pra rodar em algum ambiente? Ficou com a impressão de que o modelo "chuta" ou ele acertou de primeira? Aposto que, se chutou, o problema estava nos comentários — e adoraria ouvir o caso de vocês nos comentários aqui embaixo, ou lá no próximo GUOB Tech Day. Esse assunto vai render muita conversa boa na comunidade.
Nos próximos posts pretendo avançar para Vector Search e RAG em cima desse mesmo schema — porque, convenhamos, traduzir português pra SQL é só o aquecimento.
Até lá.
— Ricardo Rezende (@ricarezende)