04 Executar as consultas de mercado
Sete consultas sobre 26,5 milhões de ticks, cada uma substituindo uma página de SQL: contagens aproximadas, topK, candles OHLC, percentis, combinadores -If, argMax e o desafio final da chave de ordenação.
Estas são as "funções que substituem uma página de SQL" do ClickHouse. Cole e execute cada bloco e
leia a caixa verde para saber o que esperar. Todas as consultas usam a tabela forex que você
acabou de carregar.
4.1 Aproximado ou exato — o grande truque
Quantos timestamps de cotação distintos existem em 26,5 milhões de ticks? Três funções respondem à mesma pergunta com diferentes compromissos. Execute uma de cada vez e observe o cronômetro nas estatísticas da consulta — executá-las separadamente é essencial para a comparação.
-- Approximate count of distinct timestamps (HyperLogLog)
SELECT uniq(datetime) AS distinct_ts FROM forex;-- Exact count — precise, but scans every value
SELECT uniqExact(datetime) AS distinct_ts FROM forex;-- Adaptive + tunable accuracy vs memory
SELECT uniqCombined(datetime) AS distinct_ts FROM forex;Você deve ver
Cerca de 24,6 milhões de timestamps distintos nas três consultas. Porém, uniq retorna em
aproximadamente 0,2 s; uniqExact leva cerca de 3 s (quase 17 vezes mais lento) para obter o
número preciso; e uniqCombined fica a cerca de 0,4% do resultado em bem menos de um segundo. Em
bilhões de linhas, essa diferença separa uma resposta imediata de uma pausa para o café.
4.2 Top-N com uma única função
Os pares com maior volume de cotações — sem precisar de GROUP BY / ORDER BY / LIMIT:
-- One row: an array of the 8 most actively quoted pairs (by tick count)
SELECT topK(8)(pair) AS most_active FROM forex;Você deve ver
Uma única célula com um array de 8 pares, em ordem do mais cotado para o menos cotado (o ouro,
XAU/USD, fica em primeiro). topK reduz toda a tabela a essa lista ordenada em uma passagem —
substituindo um GROUP BY … ORDER BY count() DESC LIMIT 8.
4.3 Série temporal em uma passagem — candles OHLC
Abertura, máxima, mínima e fechamento diários do ouro — a consulta essencial do mercado:
-- One row per day: gold (XAU/USD) open, high, low, close — a candlestick chart
SELECT
toDate(datetime) AS day,
argMin(bid, datetime) AS open, -- bid at the day's first tick
max(bid) AS high,
min(bid) AS low,
argMax(bid, datetime) AS close -- bid at the day's last tick
FROM forex
WHERE base = 'XAU' AND quote = 'USD'
GROUP BY day
ORDER BY day;Você deve ver
Uma linha por dia de janeiro de 2020. argMin(bid, datetime) é o bid do primeiro tick do dia
(a abertura), enquanto argMax é o último (o fechamento) — sem funções de janela nem
autojoins. Observe o ouro subir de cerca de 1.520 para 1.610 ao longo do mês.
4.4 Percentis sem malabarismo com janelas
O spread entre bid e ask indica liquidez — e a média esconde a cauda, então use percentis:
-- One row per pair: median and 99th-percentile bid/ask spread (tighter = more liquid)
SELECT
pair,
round(quantile(0.5)(ask - bid), 6) AS median_spread,
round(quantile(0.99)(ask - bid), 6) AS p99_spread
FROM forex
GROUP BY pair
ORDER BY median_spread ASC;Você deve ver
Uma linha por par, começando pelo menor spread. EUR/USD aparece como o mais líquido (cerca de
0,00002, menos de um pip); o ouro tem o maior spread. quantile é calculado de forma aproximada
em uma única passagem — sem ordenar a coluna inteira.
4.5 Combinadores — uma leitura, duas respostas
Compare lado a lado o spread médio durante o horário ativo e o período de menor atividade, em uma única leitura:
-- One row per pair: avg spread during the active window vs quiet hours
SELECT
pair,
count() AS ticks,
round(avgIf(ask - bid, toHour(datetime) BETWEEN 7 AND 20), 6) AS spread_active,
round(avgIf(ask - bid, toHour(datetime) NOT BETWEEN 7 AND 20), 6) AS spread_quiet
FROM forex
GROUP BY pair
ORDER BY ticks DESC;Você deve ver
Os spreads dos períodos ativo e menos movimentado no mesmo resultado. O combinador -If
acrescenta uma condição a qualquer agregação: avgIf(x, cond) calcula a média de x apenas onde
cond é verdadeiro — sem consulta em duas passagens e sem CASE WHEN. Quase toda agregação o
aceita (countIf, sumIf, quantileIf, …).
4.6 O valor mais recente — argMax
A cotação mais recente de cada par, em uma única passagem:
-- One row per pair: the latest quoted bid and the timestamp it was seen
SELECT
pair,
argMax(bid, datetime) AS last_bid,
max(datetime) AS as_of
FROM forex
GROUP BY pair
ORDER BY pair;Você deve ver
O bid mais recente de cada par. argMax(bid, datetime) retorna o bid da linha que contém o
maior datetime — a consulta cotidiana de "último preço / status mais recente", sem função de
janela ou autojoin.
4.7 O desafio final — com e sem chave de ordenação
As duas consultas têm o mesmo formato — contar os ticks que atendem a uma condição — mas um filtro usa a chave de ordenação e o outro não. Primeiro, execute as duas contagens:
-- Optimized: base+quote ARE the leading sort key — the index skips to that pair
SELECT count() FROM forex WHERE base = 'XAU' AND quote = 'USD';
-- Non-optimized: bid is NOT in the sort key — no index, scans all 26.5M ticks.
-- (cache off so the full scan shows on every run)
SELECT count() FROM forex WHERE bid > 1.5
SETTINGS use_query_condition_cache = 0;Difícil perceber a diferença?
As duas contagens retornam em poucos milissegundos; olhando apenas o tempo, quase não há mudança —
count() é rápido nos dois casos. A diferença real está em quantos dados cada consulta precisa
acessar. Para enxergá-la, peça ao ClickHouse que mostre o plano com EXPLAIN indexes = 1, que
informa quantos grânulos (blocos de aproximadamente 8.192 linhas) serão lidos.
-- Optimized: the primary index skips straight to the XAU/USD rows
EXPLAIN indexes = 1
SELECT count() FROM forex WHERE base = 'XAU' AND quote = 'USD';
-- Non-optimized: bid isn't in the sort key, so nothing can be skipped
EXPLAIN indexes = 1
SELECT count() FROM forex WHERE bid > 1.5;Você deve ver
No plano otimizado, a seção Indexes → PrimaryKey mostra apenas uma parte dos grânulos
selecionada — cerca de 492 / 3.234 (aproximadamente 16 mil linhas). No plano não
otimizado, não há índice aplicável; ele lê 3.234 / 3.234 grânulos — todas as linhas. Mesmo
formato de consulta, volume de trabalho totalmente diferente. A lição: coloque as colunas mais
usadas nos filtros no início do seu ORDER BY.
Mantenha esta consulta à mão
Você entregará a consulta não otimizada WHERE bid > 1.5 à IA no
próximo módulo.
03 Carregar os dados — duas formas
Os mesmos 26,5 milhões de ticks carregados duas vezes: primeiro com ClickPipes, o pipeline gerenciado que você usaria em produção; depois, com uma única chamada a s3() — e quando escolher cada opção.
05 Pedir à IA que corrija uma consulta
Entregue a consulta que faz uma varredura completa, criada no módulo 04, ao ClickHouse Assistant; veja-o diagnosticar a ausência da chave de ordenação e sugerir um skip index e uma projection com SQL pronto para executar.