> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-detect-table-modification.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Usando a API nativa do ClickHouse com clickhouse-go: executando consultas, inserções em lote, inserções assíncronas e muito mais.

# API do ClickHouse

Todos os exemplos de código da API do ClickHouse podem ser encontrados [aqui](https://github.com/ClickHouse/clickhouse-go/tree/main/examples/clickhouse_api).

Para a configuração da conexão, consulte [Configuração](/pt-BR/integrations/language-clients/go/configuration).
Para ver os tipos de dados compatíveis e os mapeamentos de tipos em Go, consulte [Tipos de dados](/pt-BR/integrations/language-clients/go/data-types).

<div id="connecting">
  ## Conexão
</div>

O exemplo a seguir, que retorna a versão do servidor, demonstra como se conectar ao ClickHouse, supondo que o ClickHouse não esteja protegido e possa ser acessado com o usuário `default`.

Observe que usamos a porta nativa padrão para a conexão.

```go theme={null}
conn, err := clickhouse.Open(&clickhouse.Options{
    Addr: []string{fmt.Sprintf("%s:%d", env.Host, env.Port)},
    Auth: clickhouse.Auth{
        Database: env.Database,
        Username: env.Username,
        Password: env.Password,
    },
})
if err != nil {
    return err
}
v, err := conn.ServerVersion()
fmt.Println(v)
```

[Exemplo completo](https://github.com/ClickHouse/clickhouse-go/blob/main/examples/clickhouse_api/connect.go)

**Em todos os exemplos a seguir, salvo indicação em contrário, assumimos que a variável `conn` do ClickHouse já foi criada e está disponível.**

<div id="execution">
  ## Execução
</div>

Instruções arbitrárias podem ser executadas com o método `Exec`. Isso é útil para DDL e instruções simples. Ele não deve ser usado para inserts maiores nem para iterações de consultas.

```go theme={null}
conn.Exec(context.Background(), `DROP TABLE IF EXISTS example`)
err = conn.Exec(context.Background(), `
    CREATE TABLE IF NOT EXISTS example (
        Col1 UInt8,
        Col2 String
    ) engine=Memory
`)
if err != nil {
    return err
}
conn.Exec(context.Background(), "INSERT INTO example VALUES (1, 'test-1')")
```

[Exemplo completo](https://github.com/ClickHouse/clickhouse-go/blob/main/examples/clickhouse_api/exec.go)

Observe que é possível passar um Context para a consulta. Isso pode ser usado para definir configurações específicas no nível da consulta — veja [Using Context](#using-context).

<div id="batch-insert">
  ## Inserção em lote
</div>

Para inserir um grande número de linhas, o cliente oferece suporte a operações em lote. Isso exige a preparação de um lote ao qual é possível adicionar linhas. Em seguida, ele é enviado por meio do método `Send()`. Os lotes permanecem na memória até que `Send` seja executado.

Recomenda-se chamar `Close` no lote para evitar vazamento de conexões. Isso pode ser feito com a palavra-chave `defer` após preparar o lote. Isso liberará a conexão caso `Send` nunca seja chamado. Observe que, se nenhuma linha for adicionada, o log de consultas mostrará 0 linhas inseridas.

```go theme={null}
conn, err := GetNativeConnection(nil, nil, nil)
if err != nil {
    return err
}
ctx := context.Background()
defer func() {
    conn.Exec(ctx, "DROP TABLE example")
}()
conn.Exec(context.Background(), "DROP TABLE IF EXISTS example")
err = conn.Exec(ctx, `
    CREATE TABLE IF NOT EXISTS example (
            Col1 UInt8
        , Col2 String
        , Col3 FixedString(3)
        , Col4 UUID
        , Col5 Map(String, UInt8)
        , Col6 Array(String)
        , Col7 Tuple(String, UInt8, Array(Map(String, String)))
        , Col8 DateTime
    ) Engine = Memory
`)
if err != nil {
    return err
}

batch, err := conn.PrepareBatch(ctx, "INSERT INTO example")
if err != nil {
    return err
}
defer batch.Close()

for i := 0; i < 1000; i++ {
    err := batch.Append(
        uint8(42),
        "ClickHouse",
        "Inc",
        uuid.New(),
        map[string]uint8{"key": 1},             // Map(String, UInt8)
        []string{"Q", "W", "E", "R", "T", "Y"}, // Array(String)
        []interface{}{ // Tuple(String, UInt8, Array(Map(String, String)))
            "String Value", uint8(5), []map[string]string{
                {"key": "value"},
                {"key": "value"},
                {"key": "value"},
            },
        },
        time.Now(),
    )
    if err != nil {
        return err
    }
}

return batch.Send()
```

[Exemplo completo](https://github.com/ClickHouse/clickhouse-go/blob/main/examples/clickhouse_api/batch.go)

As recomendações para o ClickHouse estão [aqui](/pt-BR/concepts/features/operations/insert/inserting-data#best-practices-for-inserts). Os lotes não devem ser compartilhados entre go-routines — construa um lote separado para cada rotina.

A partir do exemplo acima, observe que os tipos das variáveis precisam estar alinhados com o tipo da coluna ao acrescentar linhas. Embora o mapeamento normalmente seja óbvio, essa interface busca ser flexível, e os tipos serão convertidos desde que não haja perda de precisão. Por exemplo, o trecho a seguir demonstra a inserção de uma string em um datetime64.

```go theme={null}
batch, err := conn.PrepareBatch(ctx, "INSERT INTO example")
if err != nil {
    return err
}
defer batch.Close()

for i := 0; i < 1000; i++ {
    err := batch.Append(
        "2006-01-02 15:04:05.999",
    )
    if err != nil {
        return err
    }
}

return batch.Send()
```

[Exemplo completo](https://github.com/ClickHouse/clickhouse-go/blob/main/examples/clickhouse_api/type_convert.go)

Para ver um resumo completo dos tipos em Go compatíveis com cada tipo de coluna, consulte [Conversões de tipos](/pt-BR/integrations/language-clients/go/data-types#type-conversions).

<div id="ephemeral-columns">
  ## Colunas efêmeras
</div>

[Colunas efêmeras](/pt-BR/reference/statements/create/table#ephemeral) são colunas apenas para escrita que existem somente durante a inserção — não são armazenadas e não podem ser selecionadas. Elas são úteis para calcular valores de colunas derivadas no momento da inserção.

```go theme={null}
ctx := context.Background()
ddl := `
CREATE OR REPLACE TABLE test
(
    id UInt64,
    unhexed String EPHEMERAL,
    hexed FixedString(4) DEFAULT unhex(unhexed)
)
ENGINE = MergeTree
ORDER BY id`

if err := conn.Exec(ctx, ddl); err != nil {
    return err
}

// Insira fornecendo o valor da coluna efêmera
if err := conn.Exec(ctx, "INSERT INTO test (id, unhexed) VALUES (1, '5a90b714')"); err != nil {
    return err
}

// Apenas colunas não efêmeras podem ser consultadas
rows, err := conn.Query(ctx, "SELECT id, hexed, hex(hexed) FROM test")
```

[Exemplo completo](https://github.com/ClickHouse/clickhouse-go/blob/main/examples/clickhouse_api/ephemeral_native.go)

<div id="querying-rows">
  ## Consultando linhas
</div>

Você pode consultar uma única linha usando o método `QueryRow` ou obter um cursor para iterar sobre um conjunto de resultados com `Query`. Enquanto o primeiro permite informar um destino no qual os dados serão serializados, o segundo exige chamar `Scan` para cada linha.

```go theme={null}
row := conn.QueryRow(context.Background(), "SELECT * FROM example")
var (
    col1             uint8
    col2, col3, col4 string
    col5             map[string]uint8
    col6             []string
    col7             []interface{}
    col8             time.Time
)
if err := row.Scan(&col1, &col2, &col3, &col4, &col5, &col6, &col7, &col8); err != nil {
    return err
}
fmt.Printf("row: col1=%d, col2=%s, col3=%s, col4=%s, col5=%v, col6=%v, col7=%v, col8=%v\n", col1, col2, col3, col4, col5, col6, col7, col8)
```

[Exemplo completo](https://github.com/ClickHouse/clickhouse-go/blob/main/examples/clickhouse_api/query_row.go)

```go theme={null}
rows, err := conn.Query(ctx, "SELECT Col1, Col2, Col3 FROM example WHERE Col1 >= 2")
if err != nil {
    return err
}
for rows.Next() {
    var (
        col1 uint8
        col2 string
        col3 time.Time
    )
    if err := rows.Scan(&col1, &col2, &col3); err != nil {
        return err
    }
    fmt.Printf("row: col1=%d, col2=%s, col3=%s\n", col1, col2, col3)
}
rows.Close()
return rows.Err()
```

[Exemplo completo](https://github.com/ClickHouse/clickhouse-go/blob/main/examples/clickhouse_api/query_rows.go)

Observe que, em ambos os casos, é necessário passar um ponteiro para as variáveis nas quais queremos armazenar os respectivos valores das colunas. Elas devem ser passadas na ordem especificada na instrução `SELECT` — por padrão, a ordem de declaração das colunas será usada no caso de um `SELECT *`, como mostrado acima.

Assim como na inserção, o método Scan exige que as variáveis de destino sejam de um tipo apropriado. Novamente, a ideia é ser flexível, convertendo tipos sempre que possível, desde que não haja perda de precisão; por exemplo, o exemplo acima mostra uma coluna UUID sendo lida em uma variável string. Para ver uma lista completa dos tipos Go compatíveis com cada tipo de coluna, consulte [Conversões de tipos](/pt-BR/integrations/language-clients/go/data-types#type-conversions).

Por fim, observe que é possível passar um `Context` para os métodos `Query` e `QueryRow`. Isso pode ser usado para configurações no nível da consulta — consulte [Usando Context](#using-context) para mais detalhes.

<div id="async-insert">
  ## Inserção assíncrona
</div>

As inserções assíncronas são suportadas pelo método Async. Isso permite que o usuário especifique se o cliente deve esperar que o servidor conclua a inserção ou responda assim que receber os dados. Isso controla efetivamente o parâmetro [wait\_for\_async\_insert](/pt-BR/reference/settings/session-settings#wait_for_async_insert).

```go theme={null}
conn, err := GetNativeConnection(nil, nil, nil)
if err != nil {
    return err
}
ctx := context.Background()
if err := clickhouse_tests.CheckMinServerServerVersion(conn, 21, 12, 0); err != nil {
    return nil
}
defer func() {
    conn.Exec(ctx, "DROP TABLE example")
}()
conn.Exec(ctx, `DROP TABLE IF EXISTS example`)
const ddl = `
    CREATE TABLE example (
            Col1 UInt64
        , Col2 String
        , Col3 Array(UInt8)
        , Col4 DateTime
    ) ENGINE = Memory
`
if err := conn.Exec(ctx, ddl); err != nil {
    return err
}
for i := 0; i < 100; i++ {
    if err := conn.AsyncInsert(ctx, fmt.Sprintf(`INSERT INTO example VALUES (
        %d, '%s', [1, 2, 3, 4, 5, 6, 7, 8, 9], now()
    )`, i, "Golang SQL database driver"), false); err != nil {
        return err
    }
}
```

[Exemplo completo](https://github.com/ClickHouse/clickhouse-go/blob/main/examples/clickhouse_api/async.go)

<div id="columnar-insert">
  ## Inserção colunar
</div>

As inserções podem ser feitas em formato colunar. Isso pode trazer ganhos de desempenho se os dados já estiverem organizados dessa forma, evitando a necessidade de pivotar para linhas.

```go theme={null}
batch, err := conn.PrepareBatch(context.Background(), "INSERT INTO example")
if err != nil {
    return err
}
defer batch.Close()

var (
    col1 []uint64
    col2 []string
    col3 [][]uint8
    col4 []time.Time
)
for i := 0; i < 1_000; i++ {
    col1 = append(col1, uint64(i))
    col2 = append(col2, "Golang SQL database driver")
    col3 = append(col3, []uint8{1, 2, 3, 4, 5, 6, 7, 8, 9})
    col4 = append(col4, time.Now())
}
if err := batch.Column(0).Append(col1); err != nil {
    return err
}
if err := batch.Column(1).Append(col2); err != nil {
    return err
}
if err := batch.Column(2).Append(col3); err != nil {
    return err
}
if err := batch.Column(3).Append(col4); err != nil {
    return err
}

return batch.Send()
```

[Exemplo completo](https://github.com/ClickHouse/clickhouse-go/blob/main/examples/clickhouse_api/columnar_insert.go)

<div id="using-structs">
  ## Usando structs
</div>

Para os usuários, as structs do Golang oferecem uma representação lógica de uma linha de dados no ClickHouse. Para facilitar isso, a interface nativa fornece várias funções úteis.

<div id="select-with-serialize">
  ### Select com serialização
</div>

O método Select permite serializar um conjunto de linhas de resposta em uma slice de structs com uma única chamada.

```go theme={null}
var result []struct {
    Col1           uint8
    Col2           string
    ColumnWithName time.Time `ch:"Col3"`
}

if err = conn.Select(ctx, &result, "SELECT Col1, Col2, Col3 FROM example"); err != nil {
    return err
}

for _, v := range result {
    fmt.Printf("row: col1=%d, col2=%s, col3=%s\n", v.Col1, v.Col2, v.ColumnWithName)
}
```

[Exemplo completo](https://github.com/ClickHouse/clickhouse-go/blob/main/examples/clickhouse_api/select_struct.go)

<div id="scan-struct">
  ### Scan em struct
</div>

`ScanStruct` permite mapear uma única linha de uma consulta para uma struct.

```go theme={null}
var result struct {
    Col1  int64
    Count uint64 `ch:"count"`
}
if err := conn.QueryRow(context.Background(), "SELECT Col1, COUNT() AS count FROM example WHERE Col1 = 5 GROUP BY Col1").ScanStruct(&result); err != nil {
    return err
}
```

[Exemplo completo](https://github.com/ClickHouse/clickhouse-go/blob/main/examples/clickhouse_api/scan_struct.go)

<div id="append-struct">
  ### Anexar struct
</div>

`AppendStruct` permite anexar uma struct a um [lote](#batch-insert) existente e interpretá-la como uma linha completa. Isso exige que as colunas da struct correspondam à tabela tanto no nome quanto no tipo. Embora todas as colunas devam ter um campo equivalente na struct, alguns campos da struct podem não ter uma coluna equivalente. Eles serão simplesmente ignorados.

```go theme={null}
batch, err := conn.PrepareBatch(context.Background(), "INSERT INTO example")
if err != nil {
    return err
}
defer batch.Close()

for i := 0; i < 1_000; i++ {
    err := batch.AppendStruct(&row{
        Col1:       uint64(i),
        Col2:       "Golang SQL database driver",
        Col3:       []uint8{1, 2, 3, 4, 5, 6, 7, 8, 9},
        Col4:       time.Now(),
        ColIgnored: "this will be ignored",
    })
    if err != nil {
        return err
    }
}
```

[Exemplo completo](https://github.com/ClickHouse/clickhouse-go/blob/main/examples/clickhouse_api/append_struct.go)

<div id="parameter-binding">
  ## Vinculação de parâmetros
</div>

O cliente oferece suporte à vinculação de parâmetros nos métodos `Exec`, `Query` e `QueryRow`. Como mostrado no exemplo abaixo, isso é compatível com parâmetros nomeados, numerados e posicionais. A seguir, apresentamos exemplos de cada um deles.

```go theme={null}
var count uint64
// bind posicional
if err = conn.QueryRow(ctx, "SELECT count() FROM example WHERE Col1 >= ? AND Col3 < ?", 500, now.Add(time.Duration(750)*time.Second)).Scan(&count); err != nil {
    return err
}
// 250
fmt.Printf("Positional bind count: %d\n", count)
// bind numérico
if err = conn.QueryRow(ctx, "SELECT count() FROM example WHERE Col1 <= $2 AND Col3 > $1", now.Add(time.Duration(150)*time.Second), 250).Scan(&count); err != nil {
    return err
}
// 100
fmt.Printf("Numeric bind count: %d\n", count)
// bind nomeado
if err = conn.QueryRow(ctx, "SELECT count() FROM example WHERE Col1 <= @col1 AND Col3 > @col3", clickhouse.Named("col1", 100), clickhouse.Named("col3", now.Add(time.Duration(50)*time.Second))).Scan(&count); err != nil {
    return err
}
// 50
fmt.Printf("Named bind count: %d\n", count)
```

[Exemplo completo](https://github.com/ClickHouse/clickhouse-go/blob/main/examples/clickhouse_api/bind.go)

<div id="special-cases">
  ### Casos especiais
</div>

Por padrão, slices são expandidos em uma lista de valores separados por vírgulas quando passados como parâmetro para uma consulta. Se você precisar que um conjunto de valores seja injetado com os delimitadores `[ ]`, use `ArraySet`.

Se forem necessários grupos/tuplas, com os delimitadores `( )`, por exemplo, para uso com operadores IN, você pode usar `GroupSet`. Isso é particularmente útil quando vários grupos são necessários, como mostrado no exemplo abaixo.

Por fim, campos DateTime64 exigem precisão para garantir que os parâmetros sejam renderizados corretamente. No entanto, o nível de precisão do campo é desconhecido para o cliente, então o usuário precisa informá-lo. Para isso, fornecemos o parâmetro `DateNamed`.

```go theme={null}
var count uint64
// arrays serão desdobrados
if err = conn.QueryRow(ctx, "SELECT count() FROM example WHERE Col1 IN (?)", []int{100, 200, 300, 400, 500}).Scan(&count); err != nil {
    return err
}
fmt.Printf("Array unfolded count: %d\n", count)
// arrays serão preservados com []
if err = conn.QueryRow(ctx, "SELECT count() FROM example WHERE Col4 = ?", clickhouse.ArraySet{300, 301}).Scan(&count); err != nil {
    return err
}
fmt.Printf("Array count: %d\n", count)
// Group sets nos permitem formar listas ( )
if err = conn.QueryRow(ctx, "SELECT count() FROM example WHERE Col1 IN ?", clickhouse.GroupSet{[]interface{}{100, 200, 300, 400, 500}}).Scan(&count); err != nil {
    return err
}
fmt.Printf("Group count: %d\n", count)
// Mais útil quando precisamos de aninhamento
if err = conn.QueryRow(ctx, "SELECT count() FROM example WHERE (Col1, Col5) IN (?)", []clickhouse.GroupSet{{[]interface{}{100, 101}}, {[]interface{}{200, 201}}}).Scan(&count); err != nil {
    return err
}
fmt.Printf("Group count: %d\n", count)
// Use DateNamed quando precisar de precisão no seu campo de tempo#
if err = conn.QueryRow(ctx, "SELECT count() FROM example WHERE Col3 >= @col3", clickhouse.DateNamed("col3", now.Add(time.Duration(500)*time.Millisecond), clickhouse.NanoSeconds)).Scan(&count); err != nil {
    return err
}
fmt.Printf("NamedDate count: %d\n", count)
```

[Exemplo completo](https://github.com/ClickHouse/clickhouse-go/blob/main/examples/clickhouse_api/bind_special.go)

<div id="using-context">
  ## Usando contexto
</div>

Os contextos do Go fornecem uma forma de passar prazos, sinais de cancelamento e outros valores com escopo de solicitação entre limites de API. Todos os métodos de uma conexão aceitam um contexto como primeiro parâmetro. Embora os exemplos anteriores tenham usado context.Background(), você pode usar esse recurso para passar configurações e prazos, além de cancelar consultas.

Passar um contexto criado com `withDeadline` permite definir limites de tempo de execução para as consultas. Observe que esse é um horário absoluto, e a expiração apenas liberará a conexão e enviará um sinal de cancelamento ao ClickHouse. Como alternativa, `WithCancel` pode ser usado para cancelar explicitamente uma consulta.

As funções auxiliares `clickhouse.WithQueryID` e `clickhouse.WithQuotaKey` permitem especificar um ID de consulta e uma chave de quota. IDs de consulta podem ser úteis para rastrear consultas nos logs e para fins de cancelamento. Uma chave de quota pode ser usada para impor limites ao uso do ClickHouse com base em um valor de chave exclusivo - consulte [Quotas Management](/pt-BR/concepts/features/security/access-rights#quotas-management) para mais detalhes.

Você também pode usar o contexto para garantir que uma configuração seja aplicada apenas a uma consulta específica, em vez de à conexão inteira, como mostrado em [Connection Settings](/pt-BR/integrations/language-clients/go/configuration#connection-settings).

Por fim, você pode controlar o tamanho do buffer de blocos por meio de `clickhouse.WithBlockSize`. Isso substitui a configuração de nível de conexão `BlockBufferSize` e controla o número máximo de blocos decodificados e mantidos na memória a qualquer momento. Valores maiores podem significar mais paralelismo, à custa de memória.

Exemplos do que foi descrito acima são mostrados abaixo.

```go theme={null}
dialCount := 0
conn, err := clickhouse.Open(&clickhouse.Options{
    Addr: []string{fmt.Sprintf("%s:%d", env.Host, env.Port)},
    Auth: clickhouse.Auth{
        Database: env.Database,
        Username: env.Username,
        Password: env.Password,
    },
    DialContext: func(ctx context.Context, addr string) (net.Conn, error) {
        dialCount++
        var d net.Dialer
        return d.DialContext(ctx, "tcp", addr)
    },
})
if err != nil {
    return err
}
if err := clickhouse_tests.CheckMinServerServerVersion(conn, 22, 6, 1); err != nil {
    return nil
}
// podemos usar o contexto para passar configurações a uma chamada de API específica
ctx := clickhouse.Context(context.Background(), clickhouse.WithSettings(clickhouse.Settings{
    "async_insert": "1",
}))

// consultas podem ser canceladas usando o contexto
ctx, cancel := context.WithCancel(context.Background())
go func() {
    cancel()
}()
if err = conn.QueryRow(ctx, "SELECT sleep(3)").Scan(); err == nil {
    return fmt.Errorf("expected cancel")
}

// define um prazo para uma consulta - isso cancelará a consulta após o tempo absoluto ser atingido.
// as consultas continuarão até a conclusão no ClickHouse
ctx, cancel = context.WithDeadline(context.Background(), time.Now().Add(-time.Second))
defer cancel()
if err := conn.Ping(ctx); err == nil {
    return fmt.Errorf("expected deadline exceeeded")
}

// define um id de consulta para auxiliar no rastreamento de consultas nos logs, ex.: veja system.query_log
var one uint8
queryId, _ := uuid.NewUUID()
ctx = clickhouse.Context(context.Background(), clickhouse.WithQueryID(queryId.String()))
if err = conn.QueryRow(ctx, "SELECT 1").Scan(&one); err != nil {
    return err
}

conn.Exec(context.Background(), "DROP QUOTA IF EXISTS foobar")
defer func() {
    conn.Exec(context.Background(), "DROP QUOTA IF EXISTS foobar")
}()
ctx = clickhouse.Context(context.Background(), clickhouse.WithQuotaKey("abcde"))
// define uma chave de quota - primeiro crie a quota
if err = conn.Exec(ctx, "CREATE QUOTA IF NOT EXISTS foobar KEYED BY client_key FOR INTERVAL 1 minute MAX queries = 5 TO default"); err != nil {
    return err
}

type Number struct {
    Number uint64 `ch:"number"`
}
for i := 1; i <= 6; i++ {
    var result []Number
    if err = conn.Select(ctx, &result, "SELECT number FROM numbers(10)"); err != nil {
        return err
    }
}
```

[Exemplo completo](https://github.com/ClickHouse/clickhouse-go/blob/main/examples/clickhouse_api/context.go)

<div id="progress-profile-log">
  ## Informações de Progress, Profile e Log
</div>

As informações de Progress, Profile e Log podem ser solicitadas para consultas. As informações de Progress relatam estatísticas sobre o número de linhas e bytes lidos e processados no ClickHouse. Já as informações de Profile fornecem um resumo dos dados retornados ao cliente, incluindo totais de bytes (não comprimidos), linhas e blocos. Por fim, as informações de Log fornecem estatísticas sobre threads, como uso de memória e taxa de processamento de dados.

A obtenção dessas informações exige o uso de [Context](#using-context), ao qual o usuário pode passar funções de callback.

```go theme={null}
totalRows := uint64(0)
// use o contexto para passar callbacks de progresso e informações de perfil
ctx := clickhouse.Context(context.Background(), clickhouse.WithProgress(func(p *clickhouse.Progress) {
    fmt.Println("progress: ", p)
    totalRows += p.Rows
}), clickhouse.WithProfileInfo(func(p *clickhouse.ProfileInfo) {
    fmt.Println("profile info: ", p)
}), clickhouse.WithLogs(func(log *clickhouse.Log) {
    fmt.Println("log info: ", log)
}))

rows, err := conn.Query(ctx, "SELECT number from numbers(1000000) LIMIT 1000000")
if err != nil {
    return err
}
for rows.Next() {
}

// NOTA: Não ignore a verificação de rows.Err()
if err := rows.Err(); err != nil {
    return err
}

fmt.Printf("Total Rows: %d\n", totalRows)
rows.Close()
```

[Exemplo completo](https://github.com/ClickHouse/clickhouse-go/blob/main/examples/clickhouse_api/progress.go)

<div id="dynamic-scanning">
  ## Varredura dinâmica
</div>

Pode ser necessário ler tabelas cujo esquema ou cujos tipos dos campos retornados sejam desconhecidos. Isso é comum em casos de análise de dados ad hoc ou no desenvolvimento de ferramentas genéricas. Para isso, informações sobre os tipos das colunas estão disponíveis nas respostas de consulta. Elas podem ser usadas com a reflexão do Go para criar, em tempo de execução, instâncias de variáveis com os tipos corretos, que podem ser passadas para `Scan`.

```go theme={null}
const query = `
SELECT
        1     AS Col1
    , 'Text' AS Col2
`
rows, err := conn.Query(context.Background(), query)
if err != nil {
    return err
}
defer rows.Close()
var (
    columnTypes = rows.ColumnTypes()
    vars        = make([]interface{}, len(columnTypes))
)
for i := range columnTypes {
    vars[i] = reflect.New(columnTypes[i].ScanType()).Interface()
}
for rows.Next() {
    if err := rows.Scan(vars...); err != nil {
        return err
    }
    for _, v := range vars {
        switch v := v.(type) {
        case *string:
            fmt.Println(*v)
        case *uint8:
            fmt.Println(*v)
        }
    }
}
// NOTA: Não ignore a verificação de rows.Err()
if err := rows.Err(); err != nil {
    return err
}
```

[Exemplo completo](https://github.com/ClickHouse/clickhouse-go/blob/main/examples/clickhouse_api/dynamic_scan_types.go)

<div id="external-tables">
  ## Tabelas externas
</div>

[Tabelas externas](/pt-BR/reference/engines/table-engines/special/external-data) permitem que o cliente envie dados ao ClickHouse junto com uma consulta SELECT. Esses dados são colocados em uma tabela temporária e podem ser usados na própria consulta para avaliação.

Para enviar dados externos junto com uma consulta, o usuário deve criar uma tabela externa com `ext.NewTable` antes de passá-la por meio do contexto.

```go theme={null}
table1, err := ext.NewTable("external_table_1",
    ext.Column("col1", "UInt8"),
    ext.Column("col2", "String"),
    ext.Column("col3", "DateTime"),
)
if err != nil {
    return err
}

for i := 0; i < 10; i++ {
    if err = table1.Append(uint8(i), fmt.Sprintf("value_%d", i), time.Now()); err != nil {
        return err
    }
}

table2, err := ext.NewTable("external_table_2",
    ext.Column("col1", "UInt8"),
    ext.Column("col2", "String"),
    ext.Column("col3", "DateTime"),
)

for i := 0; i < 10; i++ {
    table2.Append(uint8(i), fmt.Sprintf("value_%d", i), time.Now())
}
ctx := clickhouse.Context(context.Background(),
    clickhouse.WithExternalTable(table1, table2),
)
rows, err := conn.Query(ctx, "SELECT * FROM external_table_1")
if err != nil {
    return err
}
for rows.Next() {
    var (
        col1 uint8
        col2 string
        col3 time.Time
    )
    rows.Scan(&col1, &col2, &col3)
    fmt.Printf("col1=%d, col2=%s, col3=%v\n", col1, col2, col3)
}
// NOTA: Não ignore a verificação de rows.Err()
if err := rows.Err(); err != nil {
    return err
}
rows.Close()

var count uint64
if err := conn.QueryRow(ctx, "SELECT COUNT(*) FROM external_table_1").Scan(&count); err != nil {
    return err
}
fmt.Printf("external_table_1: %d\n", count)
if err := conn.QueryRow(ctx, "SELECT COUNT(*) FROM external_table_2").Scan(&count); err != nil {
    return err
}
fmt.Printf("external_table_2: %d\n", count)
if err := conn.QueryRow(ctx, "SELECT COUNT(*) FROM (SELECT * FROM external_table_1 UNION ALL SELECT * FROM external_table_2)").Scan(&count); err != nil {
    return err
}
fmt.Printf("external_table_1 UNION external_table_2: %d\n", count)
```

[Exemplo completo](https://github.com/ClickHouse/clickhouse-go/blob/main/examples/clickhouse_api/external_data.go)

<div id="open-telemetry">
  ## Open telemetry
</div>

O ClickHouse oferece suporte à [propagação de contexto de rastreamento](/pt-BR/guides/oss/deployment-and-scaling/monitoring/opentelemetry) tanto em transportes TCP quanto HTTP. Ao usar TCP, o cliente serializa o span no protocolo binário nativo. Use `clickhouse.WithSpan` para associar um span a uma consulta por meio do contexto.

<Info>
  **Limitação do transporte HTTP**

  Embora o servidor ClickHouse aceite os cabeçalhos HTTP padrão `traceparent` / `tracestate`, o transporte HTTP do clickhouse-go atualmente não os envia — `WithSpan` não tem efeito via HTTP. Como alternativa, você pode definir manualmente o cabeçalho por meio de `HttpHeaders` nas opções de conexão.
</Info>

```go theme={null}
var count uint64
rows := conn.QueryRow(clickhouse.Context(context.Background(), clickhouse.WithSpan(
    trace.NewSpanContext(trace.SpanContextConfig{
        SpanID:  trace.SpanID{1, 2, 3, 4, 5},
        TraceID: trace.TraceID{5, 4, 3, 2, 1},
    }),
)), "SELECT COUNT() FROM (SELECT number FROM system.numbers LIMIT 5)")
if err := rows.Scan(&count); err != nil {
    return err
}
// NOTA: Não omita a verificação de rows.Err()
if err := rows.Err(); err != nil {
    return err
}
fmt.Printf("count: %d\n", count)
```

[Exemplo completo](https://github.com/ClickHouse/clickhouse-go/blob/main/examples/clickhouse_api/open_telemetry.go)

Mais detalhes sobre como usar tracing podem ser encontrados em [suporte ao OpenTelemetry](/pt-BR/guides/oss/deployment-and-scaling/monitoring/opentelemetry).
