Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Artikel werden die verfügbaren Methoden zum Abfragen von Dataverse-Daten mithilfe des SDK für Python beschrieben. Sie können Daten mithilfe von STRUCTURED Query Language (SQL) und OData-basierten APIs abfragen.
Python Entwickler sollten sich zunächst über das SDK für Python informieren, indem Sie Getting started lesen, bevor Sie mit diesem Artikel fortfahren.
Querybuilder
QueryBuilder ist die empfohlene Methode zum Abfragen von Datensätzen. Es stellt eine typsichere, leicht verwendbare Schnittstelle bereit, die automatisch korrekte OData-Abfragen generiert. Sie müssen sich nicht an die OData-Filtersyntax erinnern.
# 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']}")
QueryBuilder übernimmt die Wertformatierung, die Groß-/Kleinschreibung von Spaltennamen und die OData-Syntax automatisch. Erstellen Sie Filterausdrücke mit col() und Standardoperatoren Python.
# 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
)
Für komplexe Logik (OR, NOT, Gruppierung) verfassen Sie Ausdrücke mit &, , |: ~
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"])
Formatierte Werte und Anmerkungen
In diesem Beispiel wird veranschaulicht, wie lokalisierte Bezeichnungen, Währungssymbole und Anzeigenamen angefordert werden.
# 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"))
Erweitern von Navigationseigenschaften
Verwenden Sie geschachtelte „Expand“-Optionen, um Navigationseigenschaften mit $select, $filter, $orderby und $top zu erweitern.
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
Verwenden Sie execute_pages(), um große Ergebnismengen mit allen Builder-Optionen zu streamen, wie Filterung, Sortierung und formatierte Werte. Verwenden Sie für einfachere zeichenfolgenbasierte OData-Filterabfragen records.list() und records.list_pages() als Kurzformen.
# 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
Sowohl execute(by_page=True) als auch execute(by_page=False) sind veraltet und geben ein UserWarning aus. Ersetzen Sie sie durch execute_pages() (Streaming) oder einfach execute() (eifrig).
QueryBuilder.to_dataframe() ist auch veraltet – stattdessen verwenden .execute().to_dataframe() .
Das Migrationstool schreibt alle diese Aufrufe automatisch um. Installieren Sie das Migrationstool, indem Sie pip install PowerPlatform-Dataverse-Client[migration] ausführen, und führen Sie dataverse-migrate path/to/your/scripts/ aus. Alternativ führen Sie python -m PowerPlatform.Dataverse.migration.migrate_v0_to_v1 für Development-Checkouts aus.
Datensatzanzahl
Um die Anzahl der Datensätze abzurufen, fügen Sie $count=true in die Anforderung ein.
# 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)
FetchXML-Abfragen
Durch Aufrufen client.query.fetchxml() wird ein inert-Objekt FetchXmlQuery zurückgegeben. Es wird keine HTTP-Anforderung ausgeführt, bis Sie aufrufen .execute() oder .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"])
Einfache Listen-Tastenkombination
Der records.list() Aufruf akzeptiert eine unformatierte OData-Filterzeichenfolge für grundlegende Abfragen. Für alles, was über einfache Filter+Auswahl hinausgeht, verwenden Sie bevorzugt client.query.builder(), das kombinierbare Filter, formatierte Werte und geschachtelte Expandierungen bereitstellt.
# 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})
Abfragen von Daten mit SQL
Dataverse stellt eine schreibgeschützte Schnittstelle für eine begrenzte Anzahl von SQL-Befehlen SELECT bereit. Unterstützung für SQL JOINs, Aggregate, GROUP BY, DISTINCT und OFFSET FETCH-Paginierung wird bereitgestellt.
Sie können auch mithilfe des ?sql=-Parameters der Dataverse-Web-API auf die SQL-Abfragefunktion zugreifen, sodass code in anderen Sprachen als Python auf Dataverse-Daten zugreifen kann. Weitere Informationen finden Sie unter Verwenden von SQL zum Abfragen von Daten mit der Dataverse-Web-API.
Wichtig
Die SQL-Unterstützung ist auf schreibgeschützte Abfragen beschränkt. Komplexe Verknüpfungen, Unterabfragen und bestimmte SQL-Funktionen werden möglicherweise nicht unterstützt. Die SQL-Abfrage muss der unterstützten Teilmenge folgen:
- WHERE kann nur ein Strukturbaum für boolesche Ausdrücke sein, wobei die Blättern binäre Operatoren (=, >, wie, usw.) sind. Eines der Argumente ist dabei ein direkter Spaltenverweis und das andere eine Konstante.
- TOP lässt nur ein ganzzahliges Literal zu.
- ORDERBY kann nur auf Spalten verweisen und lässt keine komplexen Ausdrücke zu.
Der folgende Beispielcode veranschaulicht eine SQL-Abfrage in 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)