| external help file | Rnwood.Dataverse.Data.PowerShell.Cmdlets.dll-Help.xml |
|---|---|
| Module Name | Rnwood.Dataverse.Data.PowerShell |
| online version | |
| schema | 2.0.0 |
Invokes a Dataverse SQL query using Sql4Cds and writes any resulting rows to the pipeline.
Invoke-DataverseSql [-Sql] <String> [-UseTdsEndpoint] [-Timeout <Int32>] [-Parameters <PSObject>]
[-BatchSize <Int32>] [-MaxDegreeOfParallelism <Int32>] [-BypassCustomPluginExecution] [-UseBulkDelete]
[-ReturnEntityReferenceAsGuid] [-UseLocalTimezone] [-AdditionalConnections <Hashtable>]
[-DataSourceName <String>] [-Connection <ServiceClient>] [-ProgressAction <ActionPreference>] [-WhatIf]
[-Confirm] [<CommonParameters>]
Sql4Cds is a powerfull engine which can translate many SELECT, INSERT, UPDATE and DELETE Sql queries and execute them against Dataverse. This Cmdlet uses Sql4Cds to execute such queries.
If the query returns a result set, it will output to the pipeline with an object per row having a property per column in the result set. If applicable (e.g. for UPDATE), the affected row count is written to verbose output.
By default, queries run through SQL4CDS compatibility mode (-UseTdsEndpoint is off). Some SQL Server-specific functions (for example DATEFROMPARTS or EOMONTH) may not be available in that mode. If you need those functions and your connection supports it, use -UseTdsEndpoint.
SQL Parameters
The cmdlet supports parameterized queries using the @paramname syntax. Parameters help avoid SQL injection and improve query readability.
Use the -Parameters parameter to provide values as a hashtable or PSObject:
Invoke-DataverseSql -Sql "SELECT * FROM contact WHERE lastname = @name AND createdon > @date" -Parameters @{ name = 'Smith'; date = '2024-01-01' }
When piping objects to the cmdlet, each object's properties become available as parameters, and the query executes once per input object.
Cross-Datasource Queries
The cmdlet supports querying across multiple Dataverse environments using the datasourcename..tablename syntax (double-dot notation).
Syntax: datasourcename..tablename where:
datasourcename= the name assigned to a connection (via-AdditionalConnectionsor-DataSourceName)..= double-dot separator (NOT a single dot.)tablename= logical name of the table
Setting datasource names:
- Primary connection defaults to organization unique name (e.g.,
org12345abc) - Use
-DataSourceNameto override the primary connection's name - Use
-AdditionalConnectionsto register named secondary connections
Why use -DataSourceName?
Organization unique names differ across environments (dev/test/prod). By setting explicit datasource names, your SQL queries remain portable:
Without -DataSourceName: Environment-specific - Invoke-DataverseSql -Sql "SELECT * FROM org12345abc..account" (Only works in one environment)
With -DataSourceName: Portable across all environments - Invoke-DataverseSql -DataSourceName "primary" -Sql "SELECT * FROM primary..account" (Works everywhere)
PS C:\> Get-DataverseConnection -Url https://myorg.crm.dynamics.com -Interactive -SetAsDefault
PS C:\> Invoke-DataverseSql -Sql "SELECT TOP 1 createdon FROM Contact WHERE lastname=@lastname" -Parameters @{
lastname = "Wood"
}
createdon
---------
28/11/2024 16:28:12Executes a SELECT query using the @lastname parameter. The @paramname syntax in SQL queries is mapped to properties in the -Parameters hashtable.
PS C:\> Get-DataverseConnection -Url https://myorg.crm.dynamics.com -Interactive -SetAsDefault
PS C:\> Invoke-DataverseSql "SELECT TOP 1 createdon FROM Contact WHERE lastname='Wood'"
createdon
---------
28/11/2024 16:28:12The -Sql parameter is positional (position 0), allowing you to omit the parameter name for cleaner syntax.
PS C:\> Get-DataverseConnection -Url https://myorg.crm.dynamics.com -Interactive -SetAsDefault
PS C:\> @(
@{ lastname = "Wood" },
@{ lastname = "Smith" }
) | Invoke-DataverseSql -Sql "SELECT TOP 1 lastname, createdon FROM Contact WHERE lastname=@lastname"
lastname createdon
-------- ---------
Wood 28/11/2024 16:28:12
Smith 28/11/2024 16:42:30Pipelines multiple parameter sets to the cmdlet. The query executes once for each input object, with that object's properties becoming available as @parameter values.
PS C:\> $primaryConn = Get-DataverseConnection -Url "https://primary.crm.dynamics.com" -ClientId $id -ClientSecret $secret
PS C:\> $secondaryConn = Get-DataverseConnection -Url "https://secondary.crm.dynamics.com" -ClientId $id -ClientSecret $secret
PS C:\> $additionalConnections = @{
"secondary" = $secondaryConn
}
PS C:\> # Query using double-dot syntax: datasourcename..tablename
PS C:\> Invoke-DataverseSql -Connection $primaryConn -AdditionalConnections $additionalConnections -Sql "
SELECT p.fullname AS primary_user, s.email AS secondary_email
FROM org12345abc..systemuser p
INNER JOIN secondary..systemuser s ON p.domainname = s.domainname
WHERE p.isdisabled = 0
"
primary_user secondary_email
------------ ---------------
John Smith jsmith@company.com
Jane Doe jdoe@company.comExecutes a cross-datasource query that joins data from two Dataverse environments:
org12345abc..systemuserreferences the primary connection's datasource (organization unique name)secondary..systemuserreferences the datasource named "secondary" from-AdditionalConnections
Note the double-dot (..) separator - this is the correct syntax, not a single dot.
PS C:\> $devConn = Get-DataverseConnection -Url "https://dev-org123.crm.dynamics.com" -ClientId $id -ClientSecret $secret
PS C:\> $prodConn = Get-DataverseConnection -Url "https://prod-org789.crm.dynamics.com" -ClientId $id -ClientSecret $secret
PS C:\> $additionalConnections = @{
"production" = $prodConn
}
PS C:\> # Set explicit datasource name for primary connection
PS C:\> Invoke-DataverseSql -Connection $devConn -DataSourceName "primary" -AdditionalConnections $additionalConnections -Sql "
SELECT p.name, prod.revenue
FROM primary..account p
LEFT JOIN production..account prod ON p.accountnumber = prod.accountnumber
WHERE p.statecode = 0
"
name revenue
---- -------
Contoso Ltd 1500000.00
Fabrikam Inc 2350000.00Uses -DataSourceName "primary" to assign a stable, environment-independent name to the main connection. This makes the SQL query portable:
- Without
-DataSourceName: Would use "org123" (dev) or "org789" (prod) - query changes per environment - With
-DataSourceName "primary": Same query works in all environments
This is essential for CI/CD pipelines and deployment scripts that run across multiple environments.
PS C:\> Get-DataverseConnection -Url https://myorg.crm.dynamics.com -Interactive -SetAsDefault
PS C:\> $params = @{
minRevenue = 100000
maxRevenue = 500000
industry = 'Technology'
city = 'Seattle'
}
PS C:\> Invoke-DataverseSql -Sql "
SELECT name, revenue, address1_city
FROM account
WHERE revenue BETWEEN @minRevenue AND @maxRevenue
AND industrycode = @industry
AND address1_city = @city
" -Parameters $params
name revenue address1_city
---- ------- -------------
TechCorp 250000.00 Seattle
InnoSoft 350000.00 SeattleDemonstrates using multiple parameters in a single query for complex filtering. All @paramname references in the SQL are replaced with values from the -Parameters hashtable.
PS C:\> $stagingConn = Get-DataverseConnection -Url "https://staging.crm.dynamics.com" -ClientId $id -ClientSecret $secret
PS C:\> $prodConn = Get-DataverseConnection -Url "https://prod.crm.dynamics.com" -ClientId $id -ClientSecret $secret
PS C:\> $additional = @{ "prod" = $prodConn }
PS C:\> # Compare record counts between staging and production
PS C:\> Invoke-DataverseSql -Connection $stagingConn -DataSourceName "staging" -AdditionalConnections $additional -Sql "
SELECT
'Contacts' AS entity_name,
(SELECT COUNT(*) FROM staging..contact) AS staging_count,
(SELECT COUNT(*) FROM prod..contact) AS prod_count
UNION ALL
SELECT
'Accounts',
(SELECT COUNT(*) FROM staging..account),
(SELECT COUNT(*) FROM prod..account)
"
entity_name staging_count prod_count
----------- ------------- ----------
Contacts 1523 1498
Accounts 847 842Uses cross-datasource queries for environment comparison reports. The double-dot syntax allows querying multiple environments in a single SQL statement.
Hashtable of additional Dataverse connections for cross-datasource queries. Keys are datasource names (strings), values are ServiceClient connections.
When specified, these connections can be referenced in SQL queries using datasourcename..tablename syntax (double-dot).
Example:
$additionalConnections = @{ "secondary" = $secondaryConnection "archive" = $archiveConnection }
SQL usage:
SELECT a.name, s.email FROM primary..account a JOIN secondary..contact s ON a.primarycontactid = s.contactid LEFT JOIN archive..auditlog al ON a.accountid = al.objectid
Type: Hashtable
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: FalseControls the batch size used by Sql4Cds.
Type: Int32
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: FalseBypasses custom plugins. See Sql4Cds docs.
Type: SwitchParameter
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: FalseDataverseConnection instance obtained from Get-DataverseConnection cmdlet
Type: ServiceClient
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: FalseOverrides the default datasource name for the primary connection. If not specified, defaults to the organization unique name (e.g., "org12345abc").
Use this to assign a stable, environment-independent name for portable SQL queries that work across dev/test/prod.
Why use this: Organization unique names differ between environments. Setting an explicit datasource name ensures SQL queries remain identical across all environments.
Example without -DataSourceName:
-- Environment-specific (breaks when moving between environments) SELECT * FROM org12345abc..account -- Only works in one environment
Example with -DataSourceName "primary":
-- Portable (works in all environments) SELECT * FROM primary..account -- Works everywhere
Used in combination with -AdditionalConnections and the datasourcename..tablename syntax.
Type: String
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: Organization unique name
Accept pipeline input: False
Accept wildcard characters: FalseMaximum number of threads to use. See Sql4Cds docs.
Type: Int32
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: FalseProvides values for @parameters referenced in the SQL query. Accepts a Hashtable or any PSObject with properties.
Parameter syntax in SQL: Use @paramname to reference parameters:
SELECT * FROM contact WHERE lastname = @name AND createdon > @date
Providing values:
-Parameters @{ name = 'Smith'; date = '2024-01-01' }
Pipeline usage: When reading from the pipeline, the query executes once per input object with that object's properties as parameter values:
@( @{ name = 'Smith'; dept = 'Sales' }, @{ name = 'Jones'; dept = 'Marketing' } ) | Invoke-DataverseSql -Sql "UPDATE contact SET department = @dept WHERE lastname = @name"
Type: PSObject
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: True (ByValue)
Accept wildcard characters: False{{ Fill ProgressAction Description }}
Type: ActionPreference
Parameter Sets: (All)
Aliases: proga
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: FalseReturns lookup column values as simple Guid as opposed to SqlEntityReference type. See Sql4Cds docs.
Type: SwitchParameter
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: FalseSQL to execute. See Sql4Cds docs for supported queries.
Supported features:
- Standard SQL: SELECT, INSERT, UPDATE, DELETE
@paramnamesyntax for parameters (values from-Parameters)datasourcename..tablenamesyntax for cross-datasource queries (requires-AdditionalConnections)- SQL4CDS compatibility mode by default; SQL Server-specific functions may require
-UseTdsEndpoint
The -Sql parameter is positional (position 0), allowing you to omit the parameter name:
Invoke-DataverseSql "SELECT * FROM contact WHERE lastname = @name" -Parameters @{ name = 'Smith' }
Type: String
Parameter Sets: (All)
Aliases:
Required: True
Position: 0
Default value: None
Accept pipeline input: False
Accept wildcard characters: FalseTimeout for query to execute. See Sql4Cds docs.
Type: Int32
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: FalseUses bulk delete for supported DELETE operations. See Sql4Cds docs.
Type: SwitchParameter
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: FalseWhen working with date values, this property indicates the local time zone should be used. See Sql4Cds docs.
Type: SwitchParameter
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: FalseLets Sql4Cds use the Dataverse TDS endpoint for compatible queries. By default this is off, so queries run in SQL4CDS compatibility mode for more predictable support across connections.
If a query fails because a SQL Server-specific function is not recognized (for example DATEFROMPARTS or EOMONTH), retry with -UseTdsEndpoint.
Type: SwitchParameter
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: FalsePrompts you for confirmation before running the cmdlet.
Type: SwitchParameter
Parameter Sets: (All)
Aliases: cf
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: FalseShows what would happen if the cmdlet runs. The cmdlet is not run. Does not apply to read only queries.
Type: SwitchParameter
Parameter Sets: (All)
Aliases: wi
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: FalseThis cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see about_CommonParameters.
A special thanks to Mark Carrington for his amazing open-source project that has enabled this.