WHERE IN 使用 Azure DocumentDB (CosmosDB) .Net SDK

WHERE IN with Azure DocumentDB (CosmosDB) .Net SDK

看过 this question 我不确定为什么 我的 DocDb 实例的以下代码不起作用:

var userApps = _docs.CreateDocumentQuery(UriFactory.CreateDocumentCollectionUri(Constants.Databases.Applications.ID, Constants.Databases.Applications.Collections.USER_APPS),
        new SqlQuerySpec(@"SELECT r.appId FROM ROOT r WHERE r.userId = @userId", (@"@userId", userId).ToSqlParameters()))
    .ToList()
    .Select(s => (string)s.appId);
var query = _docs.CreateDocumentQuery<Document>(UriFactory.CreateDocumentCollectionUri(Constants.Databases.Applications.ID, Constants.Databases.Applications.Collections.APP_DEFINITIONS),
        new SqlQuerySpec(@"SELECT r.id, r.appName FROM ROOT r WHERE r.appId IN (@userApps)", (@"@userApps", userApps.ToArray()).ToSqlParameters()),
        new FeedOptions { EnableCrossPartitionQuery = true })
    .AsDocumentQuery();

当我执行这个时,虽然我知道数据应该返回一个结果集,但每次返回都是空的。

到目前为止的故障排除

.Select()

的变体

Return 我 string.Join 到逗号分隔列表中的字符串。

例如:

var userApps = string.Join(@",", _docs.CreateDocumentQuery(UriFactory.CreateDocumentCollectionUri(Constants.Databases.Applications.ID, Constants.Databases.Applications.Collections.USER_APPS),
        new SqlQuerySpec(@"SELECT r.appId FROM ROOT r WHERE r.userId = @userId", (@"@userId", userId).ToSqlParameters()))
    .ToList()
    .Select(s => $@"'{s.appId}'");

不封装IN参数

删除查询中参数规范周围的 () 认为 SqlParameter 规范可能正在执行数组规范?

例如:@"SELECT r.id, r.appName FROM ROOT r WHERE r.appId IN @userApps")

最终抛出 "Syntax error, incorrect syntax near '@userApps'."

通过 Azure 门户查询验证

运行 这个代码应该是 运行.

的(预期的)SQL

我毫无问题地得到了我的预期结果(即:我知道这些查询有一个写成的结果集)。

查询 1 的调试器输出

AppIds 从查询 1 返回。

不令人满意的解决方法

将查询 2 更改为 参数化。相反,将查询 1 中以逗号分隔的 ID 列表注入其中:

var userApps = string.Join(@",", _docs.CreateDocumentQuery(UriFactory.CreateDocumentCollectionUri(Constants.Databases.Applications.ID, Constants.Databases.Applications.Collections.USER_APPS),
        new SqlQuerySpec(@"SELECT r.appId FROM ROOT r WHERE r.userId = @userId", (@"@userId", userId).ToSqlParameter()))
    .ToList()
    .Select(s => $@"'{s.appId}'"));
var query = _docs.CreateDocumentQuery<Document>(UriFactory.CreateDocumentCollectionUri(Constants.Databases.Applications.ID, Constants.Databases.Applications.Collections.APP_DEFINITIONS),
        new SqlQuerySpec($@"SELECT r.id, r.appName FROM ROOT r WHERE r.appId IN ({userApps})"),
        new FeedOptions { EnableCrossPartitionQuery = true })
    .AsDocumentQuery();

工作完美,但我不会接受它作为这个问题的答案,因为它违背了几十年的 SQL 最佳实践,坦率地说,不应该t 是一个解决方案。


这是我的 ToSqlParameters() 扩展方法,以防它是罪魁祸首(不过,这在我用过的其他任何地方都有效。数组可能需要一些特殊的东西?):

public static SqlParameterCollection ToSqlParameters(this (string, object) parmDef) => new SqlParameterCollection(new[] { new SqlParameter(parmDef.Item1, parmDef.Item2) });

谢谢!

如果你使用参数化的IN列表,那么当参数展开时它会被认为是一个单一的值。

例如,对于这个查询:

SELECT * FROM r WHERE r.item IN (@items) And @items is defined as "['val1', 'val2', 'val3']" 将被解释为:

SELECT * FROM r WHERE r.item IN (['val1', 'val2', 'val3']) 这基本上意味着您将 r.item 与一个包含三个元素的数组的单个值进行比较(即相当于 r.item = ['val1', 'val2', 'val3']).

要与多个项目进行比较,您需要为每个值使用一个参数。像这样:SELECT * FROM r WHERE r.item IN (@val1, @val2, @val3])

编写此查询的更方便的方法是改用 ARRAY_CONTAINS 并将项目数组作为单个参数传递。所以上面的查询会这样写:

SELECT * FROM r WHERE ARRAY_CONTAINS(@items, r.item)