---
slug: agentes/sql
title: Planilhas com SQL
description: Consulte as células de todas as planilhas com uma instrução SQL e preencha relatórios sem que os dados passem pelo agente.
sidebar:
  order: 3
---

Cada planilha `.xlsx`, `.xlsm` ou `.csv` enviada vira fatos que o agente consulta com SQL. Uma pergunta sobre dez planilhas cabe em uma chamada `sql`, em vez de dez leituras de arquivo. `sql`, `sql_schema`, `sql_write` e `sheet_write` são ferramentas [MCP](/docs/agentes/mcp).

## Quando os fatos ficam prontos [#readiness]

Todo arquivo tem um `status`, que `get`, `list`, `file_read` e `document_search` informam; os que não estão `ready` trazem em `statusMessage` o que fazer:

| Status | Significado |
| :-- | :-- |
| `processing` | O arquivo ainda está sendo lido. |
| `ready` | As células ou o texto já podem ser consultados. |
| `too_large` | O arquivo passa do limite de células ou de tamanho por planilha. Ele continua disponível para download; divida-o em partes menores e envie de novo. |
| `limit_reached` | Os dados processados da organização chegaram ao limite da organização. O arquivo fica guardado e é lido sozinho quando houver espaço: quando a equipe da Siglata aumenta o limite, ou ao apagar arquivos ou tabelas SQL. |
| `failed` | O arquivo não pôde ser lido. |
| `stored` | Um tipo que a Siglata guarda, mas não lê, como um vídeo. |

Enquanto a versão atual de uma planilha está em `processing`, `sql` lê a última versão pronta e diz em `warnings` qual versão leu; uma planilha sem versão pronta aparece em `warnings` em vez de sumir do resultado.

A Siglata guarda apenas fatos determinísticos: nunca recalcula fórmulas, nunca adivinha versões nem localidades. A interpretação fica com o agente.

## Tabelas e funções [#facts]

Chame `sql_schema` para ver as colunas de cada tabela, as funções e os objetos que você pode ler.

| Tabela | Conteúdo |
| :-- | :-- |
| `sheet_files` | Um registro por planilha, com o estado dos fatos. |
| `cells` | Uma linha por célula: arquivo, aba, endereço, linha, coluna, texto bruto, valor tipado, fórmula e formato numérico. |
| `styles` | Fonte, preenchimento, borda e alinhamento de cada estilo. |
| `sheets` | Abas e intervalos usados. |
| `excel_tables` | Tabelas do Excel. |
| `defined_names` | Nomes definidos. |
| `merges` | Células mescladas. |

| Função | Uso |
| :-- | :-- |
| `sheet_rows(file, sheet)` | Linhas largas de uma aba, com as colunas por letra (`A`, `B`, `C`…). |
| `table_rows(target uuid)` | Revisão atual de cada registro legível da tabela `target`, com colunas de auditoria tipadas e o conteúdo em `data jsonb`. |
| `parse_number(text, locale)` | Converte texto como `1.234,56` (`pt-BR`) ou `1,234.56` (`en-US`) em número. |
| `parse_date(text, format)` | Converte texto em data com um padrão como `DD/MM/YYYY`. |
| `column_letters(col)` | Converte o número da coluna em letras. |

Valores de CSV ficam como texto; use `parse_number` e `parse_date` para interpretá-los.

Uma tabela da empresa também se lê como `tables.<key>`, com a chave que `table_list` e `table_get` mostram. Cada registro atual é uma linha: primeiro `record_id`, `revision`, `created_by`, `changed_by`, `created_at`, `source_file_id`, `transcript_file_id` e `unconfirmed_fields text[]`, depois uma coluna por propriedade de primeiro nível do esquema ativo, na ordem do esquema:

| Propriedade                                    | Coluna    |
| :--------------------------------------------- | :-------- |
| `string`                                       | `text`    |
| `string` com `format: date`                    | `date`    |
| `string` com `format: uuid`, ou uma referência | `uuid`    |
| `integer`                                      | `bigint`  |
| `number`                                       | `numeric` |
| `boolean`                                      | `boolean` |
| lista de opções em texto                       | `text[]`  |
| qualquer outra                                 | `jsonb`   |

```sql
SELECT v.visited_on, l.name AS loja, v.amount
FROM tables.visitas v
JOIN tables.lojas l ON l.record_id = v.store
WHERE 'loja' = ANY(v.types)
```

As colunas seguem o esquema ativo a cada chamada; colunas `text[]` voltam como listas JSON de textos. Uma propriedade com nome vazio, com mais de 63 bytes ou igual a uma coluna fixa não vira coluna, e `tables.<key>` é recusada nomeando-a; leia essa tabela com `table_rows`. Para que toda coluna tipada caiba no seu tipo, um registro com um inteiro além de ±9007199254740991, uma data no ano 0000 ou um UUID fora da forma canônica (`xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`) é recusado. Se um registro salvo antes dessa regra tiver um desses valores, `tables.<key>` é recusada nomeando a propriedade até que o registro seja corrigido. Se o esquema ativo da tabela mudar enquanto a consulta é preparada, `sql` falha com `conflict`; basta repetir a chamada. `sql_write` recusa views e snapshots que leem `tables.<key>`: consulte `tables.<key>` direto ou crie a view sobre `table_rows`.

`tables.<key>` e `table_rows` retornam seus próprios registros, ou todos quando você criou a tabela, suas permissões atuais permitem ler todos os registros ou a tabela mostra os registros a toda a empresa. Projete campos escalares com `data->>'campo'`, `#>>` ou as funções de extração de JSON; `revision` e `schema_revision` são inteiros. `source_file_id` fica nulo quando o arquivo de origem não está no seu escopo de leitura. Views consultam novamente as permissões de cada chamada. Snapshots SQL de tabelas, inclusive por meio de views, não são aceitos; use uma view ou uma exportação de tabela com verificação de permissões.

## Consultar [#query]

`sql` (`files:read`) executa um único `SELECT` na sintaxe do Postgres:

```js
return await sql({
  query: `SELECT raw AS filial, count(*) AS celulas
FROM cells
WHERE sheet = 'Vendas' AND col = 2 AND row >= 2
GROUP BY raw`,
});
```

- Devolve as linhas que cabem em 23.000 bytes de JSON, sem limite de linhas, e roda por no máximo 10 s. Quando algo fica de fora, a resposta traz `truncated: true` e `next`, que explica como paginar com `ORDER BY` e `LIMIT`/`OFFSET` ou estreitar o `SELECT`.
- Nomeie tabelas sem esquema; só uma tabela da empresa leva um, como `tables.<key>`. Só as funções e os tipos listados são aceitos; catálogos do sistema e comandos que alteram dados são recusados.
- Antes de um comando rodar, a Siglata limita a memória do banco que ele poderia usar no pior caso, pelos valores mais longos das tabelas que ele lê e por uma regra de tamanho para cada função, operador e conversão, através de cada CTE, subconsulta e view. Um comando acima de 128 MiB é recusado com `too_large`, antes de rodar, em `SELECT`, `CREATE VIEW` e `CREATE TABLE AS`: monte valores menores (menos `concat`, `replace`, conversões ou construtores de JSON e de array aninhados), selecione menos colunas ou divida o trabalho. `WITH RECURSIVE` é recusado (use `generate_series`), e um padrão de expressão regular ou de `SIMILAR TO` precisa ser uma constante de texto de no máximo 2.000 estados compilados. No máximo 4 comandos rodam ao mesmo tempo; um comando que encontra todos ocupados falha com `conflict`, e basta tentar de novo.
- Cada recusa tem um código, como `relation_not_allowed`, `function_not_allowed`, `timeout` ou `conflict` (repita a chamada).

## Views e snapshots [#views-and-snapshots]

`sql_write` (`files:write`) cria e remove objetos seus:

| Instrução | Resultado |
| :-- | :-- |
| `CREATE VIEW nome AS SELECT …` | Uma view compartilhada com a organização. Cada membro a lê com o próprio acesso. |
| `CREATE TABLE nome AS SELECT …` | Um snapshot que só você lê, sobre `sources` (os ids dos arquivos que ele pode ler; se omitido, todos os que você lê quando a consulta lê as tabelas de fatos, `sheet_rows` ou uma view, e só as fontes dos snapshots que ela lê nos outros casos). Conta no limite de planilhas que o agente consulta. |
| `DROP VIEW nome` ou `DROP TABLE nome` | Remove um objeto que você criou. |

```js
return await sql_write({
  query:
    "CREATE TABLE vendas_agosto AS SELECT raw FROM cells WHERE sheet = 'Agosto'",
  sources: ["<id da planilha>"],
});
```

Um snapshot segue os arquivos de origem: fica oculto enquanto uma origem está na lixeira, volta quando ela é restaurada e é removido quando ela é excluída definitivamente.

## Preencher um relatório [#fill]

`sheet_write` aceita um patch de consulta `{ sheet, anchor, query }` (exige também `files:read`). Ele roda o `SELECT` e escreve todas as linhas a partir da célula `anchor` de uma nova versão do modelo, mantendo a formatação:

```js
return await sheet_write({
  fileId: "<id do modelo>",
  baseVersion: 1,
  requestId: "vendas-setembro-2026",
  patches: [
    {
      sheet: "Relatório",
      anchor: "A2",
      query: "SELECT filial, receita FROM vendas_mes",
    },
  ],
});
```

As linhas não passam pelo contexto do agente; a resposta traz apenas o arquivo na nova versão e o intervalo escrito. Um resultado acima de 5.000 linhas, 8 MiB ou 1.000.000 de células falha com `too_large`, sem acrescentar uma versão.

## Permissões [#permissions]

O SQL vê apenas as células dos arquivos ativos que o usuário da conexão pode ler. Um arquivo restrito exige uma permissão; outra organização nunca aparece. Arquivos na lixeira ficam ocultos até serem restaurados.
