Ler dados com a API BigQuery usando paginação

Neste documento, descrevemos como ler dados de tabela e resultados de consulta com a API BigQuery usando paginação.

Analisar os resultados usando a API

Em todos os métodos *collection*.list são retornadas páginas de resultados sob certas circunstâncias. A propriedade maxResults limita o número de resultados por página.

Método Critérios de paginação Valor padrão de maxResults Valor máximo de maxResults Valor máximo de maxFieldValues
tabledata.list Retorna resultados paginados se o tamanho da resposta tiver mais que 10 MB1 de dados ou mais que maxResults linhas. Ilimitado Ilimitado Ilimitado
Todos os outros métodos *collection*.list Retorna resultados paginados se a resposta tiver mais do que maxResults linhas e também menos que os limites máximos. 10.000 Ilimitado 300.000

Se o resultado for maior que o limite de bytes ou campos, o resultado será cortado para se ajustar ao limite. Se uma linha for maior que o limite de bytes ou campos, tabledata.list pode retornar até 100 MB de dados1, o que é consistente com o limite máximo de tamanho de linha para os resultados da consulta. Não há um tamanho mínimo por página, e algumas páginas podem retornar mais linhas do que outras.

1O tamanho da linha é aproximado, porque ele é baseado na representação interna dos dados da linha. Esse limite é aplicado durante determinados estágios da execução do job de consulta.

jobs.getQueryResults pode retornar 20 MB de dados, a menos que seja explicitamente solicitado mais do que isso pelo suporte.

Cada página é um subconjunto do número total de linhas. Quando os resultados são compostos por mais de uma página de dados, eles têm uma propriedade pageToken. Para recuperar a próxima página de resultados, faça outra chamada de list e inclua o valor do token como um parâmetro de URL chamado pageToken.

O método tabledata.list, usado para fazer a paginação dos dados da tabela, usa um valor de ajuste de linha ou token de página. Consulte Como procurar dados da tabela para mais informações.

Iterar nos resultados das bibliotecas de cliente

As bibliotecas de cliente na nuvem processam os detalhes de baixo nível da paginação da API e fornecem uma experiência mais semelhante à de iteração, que simplifica a interação com os elementos individuais nas respostas da página.

Os exemplos a seguir demonstram a paginação por meio de dados da tabela do BigQuery.

C#

Antes de testar esta amostra, siga as instruções de configuração do C# no Guia de início rápido do BigQuery: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API BigQuery em C#.

Para autenticar no BigQuery, configure o Application Default Credentials. Para mais informações, acesse Configurar a autenticação para bibliotecas de cliente.


using Google.Api.Gax;
using Google.Apis.Bigquery.v2.Data;
using Google.Cloud.BigQuery.V2;
using System;
using System.Linq;

public class BigQueryBrowseTable
{
    public void BrowseTable(
        string projectId = "your-project-id"
    )
    {
        BigQueryClient client = BigQueryClient.Create(projectId);
        TableReference tableReference = new TableReference()
        {
            TableId = "shakespeare",
            DatasetId = "samples",
            ProjectId = "bigquery-public-data"
        };
        // Load all rows from a table
        PagedEnumerable<TableDataList, BigQueryRow> result = client.ListRows(
            tableReference: tableReference,
            schema: null
        );
        // Print the first 10 rows
        foreach (BigQueryRow row in result.Take(10))
        {
            Console.WriteLine($"{row["corpus"]}: {row["word_count"]}");
        }
    }
}

Java

Antes de testar esta amostra, siga as instruções de configuração do Java no Guia de início rápido do BigQuery: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API BigQuery em Java.

Para autenticar no BigQuery, configure o Application Default Credentials. Para mais informações, acesse Configurar a autenticação para bibliotecas de cliente.

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQuery.TableDataListOption;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.TableId;
import com.google.cloud.bigquery.TableResult;

// Sample to directly browse a table with optional paging
public class BrowseTable {

  public static void runBrowseTable() {
    // TODO(developer): Replace these variables before running the sample.
    String table = "MY_TABLE_NAME";
    String dataset = "MY_DATASET_NAME";
    browseTable(dataset, table);
  }

  public static void browseTable(String dataset, String table) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      // Identify the table itself
      TableId tableId = TableId.of(dataset, table);

      // Page over 100 records. If you don't need pagination, remove the pageSize parameter.
      TableResult result = bigquery.listTableData(tableId, TableDataListOption.pageSize(100));

      // Print the records
      result
          .iterateAll()
          .forEach(
              row -> {
                row.forEach(fieldValue -> System.out.print(fieldValue.toString() + ", "));
                System.out.println();
              });

      System.out.println("Query ran successfully");
    } catch (BigQueryException e) {
      System.out.println("Query failed to run \n" + e.toString());
    }
  }
}

Go

Antes de testar esta amostra, siga as instruções de configuração do Go no Guia de início rápido do BigQuery: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API BigQuery em Go.

Para autenticar no BigQuery, configure o Application Default Credentials. Para mais informações, acesse Configurar a autenticação para bibliotecas de cliente.

Nas bibliotecas de cliente do Google Cloud para Go, a paginação dos resultados é feita automaticamente por padrão. Portanto, não é necessário implementá-la. Por exemplo:

import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/bigquery"
	"google.golang.org/api/iterator"
)

// browseTable demonstrates reading data from a BigQuery table directly without the use of a query.
// For large tables, we also recommend the BigQuery Storage API.
func browseTable(w io.Writer, projectID, datasetID, tableID string) error {
	// projectID := "my-project-id"
	// datasetID := "mydataset"
	// tableID := "mytable"
	ctx := context.Background()
	client, err := bigquery.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("bigquery.NewClient: %v", err)
	}
	defer client.Close()

	table := client.Dataset(datasetID).Table(tableID)
	it := table.Read(ctx)
	for {
		var row []bigquery.Value
		err := it.Next(&row)
		if err == iterator.Done {
			break
		}
		if err != nil {
			return err
		}
		fmt.Fprintln(w, row)
	}
	return nil
}

Node.js

Antes de testar esta amostra, siga as instruções de configuração do Node.js no Guia de início rápido do BigQuery: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API BigQuery em Node.js.

Para autenticar no BigQuery, configure o Application Default Credentials. Para mais informações, acesse Configurar a autenticação para bibliotecas de cliente.

Nas bibliotecas de cliente do Google Cloud para Node.js, a paginação dos resultados é feita automaticamente por padrão. Portanto, não é necessário implementá-la. Por exemplo:

// Import the Google Cloud client library using default credentials
const {BigQuery} = require('@google-cloud/bigquery');
const bigquery = new BigQuery();

async function browseTable() {
  // Retrieve a table's rows using manual pagination.

  /**
   * TODO(developer): Uncomment the following lines before running the sample.
   */
  // const datasetId = 'my_dataset'; // Existing dataset
  // const tableId = 'my_table'; // Table to create

  const query = `SELECT name, SUM(number) as total_people
    FROM \`bigquery-public-data.usa_names.usa_1910_2013\`
    GROUP BY name 
    ORDER BY total_people 
    DESC LIMIT 100`;

  // Create table reference.
  const dataset = bigquery.dataset(datasetId);
  const destinationTable = dataset.table(tableId);

  // For all options, see https://cloud.google.com/bigquery/docs/reference/rest/v2/Job#jobconfigurationquery
  const queryOptions = {
    query: query,
    destination: destinationTable,
  };

  // Run the query as a job
  const [job] = await bigquery.createQueryJob(queryOptions);

  // For all options, see https://cloud.google.com/bigquery/docs/reference/v2/jobs/getQueryResults
  const queryResultsOptions = {
    // Retrieve zero resulting rows.
    maxResults: 0,
  };

  // Wait for the job to finish.
  await job.getQueryResults(queryResultsOptions);

  function manualPaginationCallback(err, rows, nextQuery) {
    rows.forEach(row => {
      console.log(`name: ${row.name}, ${row.total_people} total people`);
    });

    if (nextQuery) {
      // More results exist.
      destinationTable.getRows(nextQuery, manualPaginationCallback);
    }
  }

  // For all options, see https://cloud.google.com/bigquery/docs/reference/v2/tabledata/list
  const getRowsOptions = {
    autoPaginate: false,
    maxResults: 20,
  };

  // Retrieve all rows.
  destinationTable.getRows(getRowsOptions, manualPaginationCallback);
}
browseTable();

PHP

Antes de testar esta amostra, siga as instruções de configuração do PHP no Guia de início rápido do BigQuery: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API BigQuery em PHP.

Para autenticar no BigQuery, configure o Application Default Credentials. Para mais informações, acesse Configurar a autenticação para bibliotecas de cliente.

A paginação acontece automaticamente nas bibliotecas de cliente do Cloud para PHP usando a função do gerador rows, que busca a próxima página de resultados durante a iteração.

use Google\Cloud\BigQuery\BigQueryClient;

/** Uncomment and populate these variables in your code */
// $projectId = 'The Google project ID';
// $datasetId = 'The BigQuery dataset ID';
// $tableId   = 'The BigQuery table ID';
// $maxResults = 10;

$maxResults = 10;
$startIndex = 0;

$options = [
    'maxResults' => $maxResults,
    'startIndex' => $startIndex
];
$bigQuery = new BigQueryClient([
    'projectId' => $projectId,
]);
$dataset = $bigQuery->dataset($datasetId);
$table = $dataset->table($tableId);
$numRows = 0;
foreach ($table->rows($options) as $row) {
    print('---');
    foreach ($row as $column => $value) {
        printf('%s: %s' . PHP_EOL, $column, $value);
    }
    $numRows++;
}

Python

Antes de testar esta amostra, siga as instruções de configuração do Python no Guia de início rápido do BigQuery: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API BigQuery em Python.

Para autenticar no BigQuery, configure o Application Default Credentials. Para mais informações, acesse Configurar a autenticação para bibliotecas de cliente.

Nas bibliotecas de cliente do Google Cloud para Python, a paginação dos resultados é feita automaticamente por padrão. Portanto, não é necessário implementá-la. Por exemplo:


from google.cloud import bigquery

# Construct a BigQuery client object.
client = bigquery.Client()

# TODO(developer): Set table_id to the ID of the table to browse data rows.
# table_id = "your-project.your_dataset.your_table_name"

# Download all rows from a table.
rows_iter = client.list_rows(table_id)  # Make an API request.

# Iterate over rows to make the API requests to fetch row data.
rows = list(rows_iter)
print("Downloaded {} rows from table {}".format(len(rows), table_id))

# Download at most 10 rows.
rows_iter = client.list_rows(table_id, max_results=10)
rows = list(rows_iter)
print("Downloaded {} rows from table {}".format(len(rows), table_id))

# Specify selected fields to limit the results to certain columns.
table = client.get_table(table_id)  # Make an API request.
fields = table.schema[:2]  # First two columns.
rows_iter = client.list_rows(table_id, selected_fields=fields, max_results=10)
rows = list(rows_iter)
print("Selected {} columns from table {}.".format(len(rows_iter.schema), table_id))
print("Downloaded {} rows from table {}".format(len(rows), table_id))

# Print row data in tabular format.
rows = client.list_rows(table, max_results=10)
format_string = "{!s:<16} " * len(rows.schema)
field_names = [field.name for field in rows.schema]
print(format_string.format(*field_names))  # Prints column headers.
for row in rows:
    print(format_string.format(*row))  # Prints row data.

Ruby

Antes de testar esta amostra, siga as instruções de configuração do Ruby no Guia de início rápido do BigQuery: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API BigQuery em Ruby.

Para autenticar no BigQuery, configure o Application Default Credentials. Para mais informações, acesse Configurar a autenticação para bibliotecas de cliente.

A paginação acontece automaticamente nas bibliotecas de cliente do Cloud para Ruby usando Table#data e Data#next.

require "google/cloud/bigquery"

def browse_table
  bigquery = Google::Cloud::Bigquery.new project_id: "bigquery-public-data"
  dataset  = bigquery.dataset "samples"
  table    = dataset.table "shakespeare"

  # Load all rows from a table
  rows = table.data

  # Load the first 10 rows
  rows = table.data max: 10

  # Print row data
  rows.each { |row| puts row }
end

Solicitar páginas arbitrárias e evitar chamadas de lista redundantes

Quando você volta ou avança para uma página arbitrária usando os valores de pageToken em cache, os dados exibidos nas suas páginas talvez não sejam os mesmos visualizados no último acesso, mas não há uma indicação clara disso. Para minimizar esse problema, use a propriedade etag.

Cada método collection.list (exceto para Tabledata) retorna uma propriedade etag no resultado. Essa propriedade é um hash dos resultados da página que pode ser usado para verificar se a página foi alterada desde a última solicitação. Quando você fizer uma solicitação ao BigQuery com um valor de ETag, o BigQuery compara o valor de ETag com o valor de ETag retornado pela API e responde com base na correspondência dos valores de ETag. É possível usar ETags para evitar chamadas de lista redundantes da seguinte maneira:

  • Para retornar valores de lista se os valores tiverem sido alterados.

    Para retornar uma página de valores de lista se os valores tiverem sido alterados, faça uma chamada de lista com uma ETag armazenada anteriormente usando o cabeçalho HTTP "if-none-match". Se a ETag fornecida não corresponder à ETag no servidor, será retornada uma página dos novos valores da lista no BigQuery. Se as ETags corresponderem, o BigQuery retornará um código de status HTTP 304 Not Modified e nenhum valor. Um exemplo disso seria uma página da Web que os usuários periodicamente preenchem com informações armazenadas no BigQuery. Se não houver alterações nos seus dados, será possível evitar chamadas redundantes de lista ao BigQuery usando o cabeçalho if-none-match com ETags.

  • Para retornar valores de lista se os valores não tiverem sido alterados.

    Para retornar uma página de valores de lista se os valores não tiverem sido mudados, use o cabeçalho HTTP "if-match". O BigQuery fará a comparação dos valores de ETag e retornará a página de resultados se os valores não tiverem mudado ou retornará um resultado 412 "Pré-requisito falhou" se a página tiver mudado.

Observação: as ETags são uma boa maneira de evitar chamadas redundantes de lista, mas é possível aplicar esses mesmos métodos para identificar se houve alteração em quaisquer objetos. Por exemplo, é possível realizar uma solicitação Get de uma tabela específica e usar ETags para determinar se a tabela foi modificada antes de retornar a resposta completa.

Ver os resultados da consulta

Cada consulta é gravada em uma tabela de destino. Se nenhuma tabela de destino for fornecida, a API do BigQuery preencherá automaticamente a propriedade da tabela de destino com uma referência a uma tabela anônima temporária.

API

Leia o campo jobs.config.query.destinationTable para determinar a tabela na qual os resultados da consulta foram gravados. Chame tabledata.list para ler os resultados da consulta.

Java

Antes de testar esta amostra, siga as instruções de configuração do Java no Guia de início rápido do BigQuery: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API BigQuery em Java.

Para autenticar no BigQuery, configure o Application Default Credentials. Para mais informações, acesse Configurar a autenticação para bibliotecas de cliente.

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.QueryJobConfiguration;
import com.google.cloud.bigquery.TableId;
import com.google.cloud.bigquery.TableResult;

// Sample to run query with pagination.
public class QueryPagination {

  public static void main(String[] args) {
    String datasetName = "MY_DATASET_NAME";
    String tableName = "MY_TABLE_NAME";
    String query =
        "SELECT name, SUM(number) as total_people"
            + " FROM `bigquery-public-data.usa_names.usa_1910_2013`"
            + " GROUP BY name"
            + " ORDER BY total_people DESC"
            + " LIMIT 100";
    queryPagination(datasetName, tableName, query);
  }

  public static void queryPagination(String datasetName, String tableName, String query) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      TableId tableId = TableId.of(datasetName, tableName);
      QueryJobConfiguration queryConfig =
          QueryJobConfiguration.newBuilder(query)
              // save results into a table.
              .setDestinationTable(tableId)
              .build();

      bigquery.query(queryConfig);

      TableResult results =
          bigquery.listTableData(tableId, BigQuery.TableDataListOption.pageSize(20));

      // First Page
      results
          .getValues()
          .forEach(row -> row.forEach(val -> System.out.printf("%s,\n", val.toString())));

      if (results.hasNextPage()) {
        // Next Page
        results
            .getNextPage()
            .getValues()
            .forEach(row -> row.forEach(val -> System.out.printf("%s,\n", val.toString())));
      }

      if (results.hasNextPage()) {
        // Remaining Pages
        results
            .getNextPage()
            .iterateAll()
            .forEach(row -> row.forEach(val -> System.out.printf("%s,\n", val.toString())));
      }

      System.out.println("Query pagination performed successfully.");
    } catch (BigQueryException | InterruptedException e) {
      System.out.println("Query not performed \n" + e.toString());
    }
  }
}

Para definir o número de linhas retornadas em cada página, use um job GetQueryResults e defina a opção pageSize do objeto QueryResultsOption que você transmite, conforme mostrado no exemplo a seguir:

TableResult result = job.getQueryResults();
QueryResultsOption queryResultsOption = QueryResultsOption.pageSize(20);

TableResult result = job.getQueryResults(queryResultsOption);

Node.js

Antes de testar esta amostra, siga as instruções de configuração do Node.js no Guia de início rápido do BigQuery: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API BigQuery em Node.js.

Para autenticar no BigQuery, configure o Application Default Credentials. Para mais informações, acesse Configurar a autenticação para bibliotecas de cliente.

// Import the Google Cloud client library using default credentials
const {BigQuery} = require('@google-cloud/bigquery');
const bigquery = new BigQuery();

async function queryPagination() {
  // Run a query and get rows using automatic pagination.

  const query = `SELECT name, SUM(number) as total_people
  FROM \`bigquery-public-data.usa_names.usa_1910_2013\`
  GROUP BY name
  ORDER BY total_people DESC
  LIMIT 100`;

  // Run the query as a job.
  const [job] = await bigquery.createQueryJob(query);

  // Wait for job to complete and get rows.
  const [rows] = await job.getQueryResults();

  console.log('Query results:');
  rows.forEach(row => {
    console.log(`name: ${row.name}, ${row.total_people} total people`);
  });
}
queryPagination();

Python

O método QueryJob.result retorna um iterável dos resultados da consulta. Como alternativa,

  1. Leia a propriedade QueryJob.destination. Se essa propriedade não estiver configurada, ela será definida pela API como uma referência a uma tabela anônima temporária.
  2. Receba o esquema da tabela com o método Client.get_table.
  3. Crie um iterável em todas as linhas na tabela de destino com o método Client.list_rows.

Antes de testar esta amostra, siga as instruções de configuração do Python no Guia de início rápido do BigQuery: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API BigQuery em Python.

Para autenticar no BigQuery, configure o Application Default Credentials. Para mais informações, acesse Configurar a autenticação para bibliotecas de cliente.


from google.cloud import bigquery

# Construct a BigQuery client object.
client = bigquery.Client()

query = """
    SELECT name, SUM(number) as total_people
    FROM `bigquery-public-data.usa_names.usa_1910_2013`
    GROUP BY name
    ORDER BY total_people DESC
"""
query_job = client.query(query)  # Make an API request.
query_job.result()  # Wait for the query to complete.

# Get the destination table for the query results.
#
# All queries write to a destination table. If a destination table is not
# specified, the BigQuery populates it with a reference to a temporary
# anonymous table after the query completes.
destination = query_job.destination

# Get the schema (and other properties) for the destination table.
#
# A schema is useful for converting from BigQuery types to Python types.
destination = client.get_table(destination)

# Download rows.
#
# The client library automatically handles pagination.
print("The query data:")
rows = client.list_rows(destination, max_results=20)
for row in rows:
    print("name={}, count={}".format(row["name"], row["total_people"]))