この記事では、sdk for Pythonを使用して Dataverse データに対してクエリを実行する方法について説明します。 構造化クエリ言語 (SQL) と OData ベースの API を使用して、データのクエリを実行できます。
Python開発者は、この記事に進む前に、作業の開始を読んで、Python用 SDK について学習する必要があります。
QueryBuilder
QueryBuilder は、レコードのクエリを実行するための推奨される方法です。 これは、正しい OData クエリを自動的に生成する、直感的で型安全なインターフェイスを提供します。 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']}")
QueryBuilder では、値の書式設定、列名の大文字と小文字の区別、OData 構文が自動的に処理されます。
col()演算子と標準の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
)
複雑なロジック (OR、NOT、グループ化) の場合は、 &、 |、 ~を使用して式を作成します。
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"])
書式設定された値と注釈
この例では、ローカライズされたラベル、通貨記号、表示名を要求する方法を示します。
# 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"))
ナビゲーション プロパティを展開する
入れ子になった展開とオプションを使用して、 $select、 $filter、 $orderby、および $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
execute_pages()を使用して、フィルター処理、並べ替え、書式設定された値などの完全なビルダー オプションを使用して大規模な結果セットをストリーミングします。 より単純な文字列ベースの OData フィルター クエリの場合は、ショートカットとして records.list() と records.list_pages() を使用します。
# 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
execute(by_page=True)とexecute(by_page=False)の両方が非推奨となり、UserWarningが出力されます。
execute_pages() (ストリーミング) またはプレーン execute() (熱心) に置き換えます。
QueryBuilder.to_dataframe() も非推奨です。代わりに .execute().to_dataframe() を使用してください。
移行ツールは、これらの呼び出しをすべて自動的に書き換えます。
pip install PowerPlatform-Dataverse-Client[migration]を実行して移行ツールをインストールし、dataverse-migrate path/to/your/scripts/実行します。 代わりに、開発用チェックアウトでは python -m PowerPlatform.Dataverse.migration.migrate_v0_to_v1 を実行します。
レコード数
レコード数を取得するには、要求に $count=true を含めます。
# 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 クエリ
client.query.fetchxml()を呼び出すと、不活性なFetchXmlQueryオブジェクトが返されます。
.execute()または.execute_pages()を呼び出すまで HTTP 要求は行われません。
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"])
単純なリストショートカット
records.list()呼び出しは、基本的なクエリの生の OData フィルター文字列を受け入れます。 単純なフィルターと選択以外の場合は、構成可能なフィルター、書式設定された値、入れ子になった展開を提供する client.query.builder() を使用することをお勧めします。
# 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})
SQL を使用してデータのクエリを実行する
Dataverse は、SQL SELECT コマンドの限られたセットに対する読み取り専用インターフェイスを提供します。 SQL JOIN、集計、GROUP BY、DISTINCT、および OFFSET FETCH によるページネーションをサポートしています。
Dataverse Web API の ?sql= パラメーターを使用して SQL クエリ機能にアクセスして、Python以外の言語で記述されたコードが Dataverse データにアクセスできるようにすることもできます。 詳細については、「 SQL を使用して Dataverse Web API を使用してデータにクエリを実行する」を参照してください。
Important
SQL のサポートは、読み取り専用クエリに限定されます。 複雑な結合、サブクエリ、および特定の SQL 関数はサポートされない場合があります。 SQL クエリは、サポートされているサブセットに従う必要があります。
- WHERE にはブール式ツリーのみを指定できます。リーフは 2 項演算子 (=、 >など) で、引数の 1 つが直接列参照であり、もう 1 つは定数です。
- TOP では整数リテラルのみを使用できます
- ORDERBY では列のみを参照でき、複雑な式は許可されません
次のコード例は、Python の SQL クエリを示しています。
# 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)