Consultar dados

Este artigo descreve os métodos disponíveis para consultar dados do Dataverse usando o SDK para Python. Você pode consultar dados usando SQL (Structured Query Language) e APIs baseadas em OData.

Os desenvolvedores Python devem primeiro conhecer o SDK para Python lendo Primeiros passos antes de continuar com este artigo.

QueryBuilder

QueryBuilder é a maneira recomendada de consultar registros. Ele fornece uma interface fluente com segurança de tipos que gera consultas OData corretas automaticamente. Você não precisa se lembrar da sintaxe do filtro OData.

# Fluent query builder (recommended)
from PowerPlatform.Dataverse.models.filters import col

for record in (client.query.builder("account")
               .select("name", "revenue")
               .where(col("statecode") == 0)
               .where(col("revenue") > 1000000)
               .order_by("revenue", descending=True)
               .top(100)
               .page_size(50)
               .execute()):
    print(f"{record['name']}: {record['revenue']}")

O QueryBuilder gerencia automaticamente a formatação de valores, o uso de maiúsculas e minúsculas nos nomes das colunas e a sintaxe OData. Crie expressões de filtro com operadores col() e Python padrão.

# Get results as a pandas DataFrame (consolidates all pages)
df = (client.query.builder("account")
      .select("name", "telephone1")
      .where(col("statecode") == 0)
      .top(100)
      .execute()
      .to_dataframe())
print(f"Got {len(df)} accounts")
# Comparison filters using col() expressions
query = (client.query.builder("contact")
         .where(col("statecode") == 0)                        # statecode eq 0
         .where(col("revenue") > 1000000)                     # revenue gt 1000000
         .where(col("name").contains("Corp"))                 # contains(name, 'Corp')
         .where(col("statecode").in_([0, 1]))                 # Microsoft.Dynamics.CRM.In(...)
         .where(col("revenue").between(100000, 500000))       # revenue ge 100000 and revenue le 500000
         .where(col("telephone1").is_null())                  # telephone1 eq null
         )

Para lógica complexa (OR, NOT, agrupamento), redigir expressões com &, , |: ~

from PowerPlatform.Dataverse.models.filters import col

# OR conditions: (statecode = 0 OR statecode = 1) AND revenue > 100k
for record in (client.query.builder("account")
               .select("name", "revenue")
               .where(((col("statecode") == 0) | (col("statecode") == 1))
                      & (col("revenue") > 100000))
               .execute()):
    print(record["name"])

# NOT, between, and in operators
for record in (client.query.builder("account")
               .where(col("statecode") != 2)                       # NOT inactive
               .where(col("revenue").between(100000, 500000))      # revenue in range
               .execute()):
    print(record["name"])

Valores formatados e anotações

Este exemplo demonstra como solicitar rótulos localizados, símbolos de moeda e nomes de exibição.

# Get formatted values (choice labels, currency, lookup names) — via query builder
for record in (client.query.builder("account")
               .select("name", "statecode", "revenue")
               .include_formatted_values()
               .execute()):
    status = record["statecode@OData.Community.Display.V1.FormattedValue"]
    print(f"{record['name']}: {status}")

# Get formatted values — via records.list() / records.retrieve() include_annotations param
result = client.records.list(
    "account",
    select=["name", "statecode"],
    include_annotations="OData.Community.Display.V1.FormattedValue",
)
for record in result:
    label = record.get("statecode@OData.Community.Display.V1.FormattedValue")
    print(f"{record['name']}: {label}")

record = client.records.retrieve(
    "account", account_id,
    select=["name", "statuscode"],
    include_annotations="OData.Community.Display.V1.FormattedValue",
)
if record:
    print(record.get("statuscode@OData.Community.Display.V1.FormattedValue"))

Expandir propriedades de navegação

Use a expansão aninhada com opções para expandir as propriedades de navegação usando $select, $filter, $orderby e $top.

from PowerPlatform.Dataverse.models.query_builder import ExpandOption

# Expand related tasks with filtering and sorting
for record in (client.query.builder("account")
               .select("name")
               .expand(ExpandOption("Account_Tasks")
                       .select("subject", "createdon")
                       .filter("contains(subject,'Task')")
                       .order_by("createdon", descending=True)
                       .top(5))
               .execute()):
    print(record["name"], record.get("Account_Tasks"))

Paging

Use execute_pages() para transmitir grandes conjuntos de resultados com opções completas do construtor, como filtragem, classificação e valores formatados. Para consultas de filtro OData baseadas em cadeia de caracteres mais simples, use records.list() e records.list_pages() como atalhos.

# Preferred: query.builder().execute_pages() — stream one page at a time, memory stays flat
# Supports composable filters, sorting, formatted values, and expand with nested selects
for page_num, page in enumerate(
    client.query.builder("account")
    .select("accountid", "name", "revenue")
    .where(col("statecode") == 0)
    .order_by("name")
    .page_size(500)        # optional: override Dataverse default (~5000/page)
    .execute_pages()
):
    print(f"Page {page_num + 1}: {len(page)} records")
    for record in page:
        print(f"  {record['name']}")

# Simple shortcut: records.list() — automatic paging, all records in memory
# Use for basic filter+select queries; string OData filter only (no composable expressions)
result = client.records.list(
    "account",
    filter="statecode eq 0",
    select=["name", "revenue"],
    orderby=["name asc"],          # optional sort
    top=500,                       # bounds total records returned and number of HTTP round-trips
    page_size=200,                 # optional: hint Dataverse default page size
)
for record in result:
    print(record["name"])

# Simple streaming shortcut: records.list_pages() — same params as records.list(), yields one page at a time
for page_num, page in enumerate(
    client.records.list_pages("account", filter="statecode eq 0", select=["name"], orderby=["name asc"])
):
    print(f"Page {page_num + 1}: {len(page)} records")
    for record in page:
        print(record["name"])

Note

Ambos execute(by_page=True) e execute(by_page=False) são preteridos e emitem um UserWarning. Substitua-os por execute_pages() (streaming) ou simples execute() (ansioso). QueryBuilder.to_dataframe() também é preterido – use .execute().to_dataframe() em vez disso.

A ferramenta de migração reescreve todas essas chamadas automaticamente. Instale a ferramenta de migração executando pip install PowerPlatform-Dataverse-Client[migration] e execute dataverse-migrate path/to/your/scripts/. Como alternativa, execute python -m PowerPlatform.Dataverse.migration.migrate_v0_to_v1 para checkouts de desenvolvimento.

Contagem de registros

Para obter uma contagem de registros, inclua $count=true na solicitação.

# Via query builder
results = (client.query.builder("account")
           .where(col("statecode") == 0)
           .count()
           .execute())

# Via records.list() — count=True adds $count=true to the OData request
results = client.records.list("account", filter="statecode eq 0", count=True)

Consultas FetchXML

A chamada client.query.fetchxml() retorna um objeto inert FetchXmlQuery . Nenhuma solicitação HTTP é feita até você ligar .execute() ou .execute_pages().

xml = """
<fetch>
  <entity name="account">
    <attribute name="name"/>
    <attribute name="revenue"/>
    <filter><condition attribute="statecode" operator="eq" value="0"/></filter>
  </entity>
</fetch>
"""

# .execute() — blocking, fetches all pages and returns a single QueryResult
result = client.query.fetchxml(xml).execute()
df = result.to_dataframe()

# .execute_pages() — streaming, yields one QueryResult per HTTP page
# Use count="N" in the FetchXML <fetch> element to set page size
for page_num, page in enumerate(client.query.fetchxml(xml).execute_pages()):
    print(f"Page {page_num + 1}: {len(page)} records")
    for record in page:
        print(record["name"])

Atalho para lista simples

A records.list() chamada aceita uma cadeia de caracteres de filtro OData bruta para consultas básicas. Para qualquer caso que vá além de filtrar e selecionar de forma simples, prefira usar client.query.builder(), que oferece filtros composáveis, valores formatados e expansão aninhada.

# records.list() shortcut — raw OData filter string, all records loaded into memory
# Column names in filter must be lowercase logical names
for record in client.records.list(
    "account",
    select=["name"],
    filter="statecode eq 0",
    top=100,
):
    print(record["name"])

# Discover navigation property names for $expand (metadata-discovery helper, kept at GA)
nav_props = client.query.odata_expands("account")  # → list of navigation property metadata

# Expand navigation properties using the query builder
from PowerPlatform.Dataverse.models.query_builder import ExpandOption
for record in (client.query.builder("contact")
               .select("fullname")
               .expand(ExpandOption("parentcustomerid_account").select("name"))
               .execute()):
    acct = record.get("parentcustomerid_account") or {}
    print(f"{record['fullname']} -> {acct.get('name')}")

# Build @odata.bind for lookup fields (deprecated helper, still functional with DeprecationWarning)
bind = client.query.odata_bind("contact", "account", account_id)
# Returns: {"parentcustomerid_account@odata.bind": "/accounts(guid)"}
client.records.create("contact", {"firstname": "Jane", **bind})

Consultar dados usando SQL

O Dataverse fornece uma interface de somente leitura para um conjunto limitado de comandos SQL SELECT. Há suporte para JOINs SQL, funções de agregação, GROUP BY, DISTINCT e paginação OFFSET FETCH.

Você também pode acessar a funcionalidade de consulta SQL usando o parâmetro ?sql= da API Web do Dataverse para que o código escrito em idiomas diferentes de Python possa acessar dados do Dataverse. Para obter mais informações, consulte Usar o SQL para consultar dados com a API Web do Dataverse.

Importante

O suporte ao SQL é limitado a consultas de leitura apenas. Não há suporte para junções complexas, subconsultas e determinadas funções SQL. A consulta SQL deve seguir o subconjunto com suporte:

  • WHERE só pode ser uma árvore de expressão booliana em que as folhas são operadores binários (=, >, como etc.) com um dos argumentos sendo uma referência de coluna direta e outro é uma constante
  • TOP permite apenas um literal inteiro
  • ORDERBY só pode referenciar colunas e não permite expressões complexas

O código de exemplo a seguir demonstra uma consulta SQL no Python.

# Basic query
results = client.query.sql(
    "SELECT TOP 10 accountid, name FROM account WHERE statecode = 0"
)

# JOINs and aggregates work
results = client.query.sql(
    "SELECT a.name, COUNT(c.contactid) as cnt "
    "FROM account a "
    "JOIN contact c ON a.accountid = c.parentcustomerid "
    "GROUP BY a.name"
)

# SQL results directly as a DataFrame
df = client.dataframe.sql(
    "SELECT name, revenue FROM account ORDER BY revenue DESC"
)

# Discover columns from metadata (schema-discovery helper, kept at GA)
cols_meta = client.query.sql_columns("account")
col_names = [c["LogicalName"] for c in cols_meta]

# Build queries using the discovered column names
sql = f"SELECT TOP 10 {', '.join(col_names[:5])} FROM account"
df = client.dataframe.sql(sql)

Consulte também