【问题标题】:WHERE IN with Azure DocumentDB (CosmosDB) .Net SDK在哪里使用 Azure DocumentDB (CosmosDB) .Net SDK
【发布时间】:2017-06-19 17:53:22
【问题描述】:

看到 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() 的变体

将我 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")

最终抛出“语法错误,'@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 最佳实践,坦率地说,不应该成为一个解决方案.


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

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

谢谢!

【问题讨论】:

    标签: azure azure-cosmosdb


    【解决方案1】:

    如果使用参数化的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)

    【讨论】:

      猜你喜欢
      • 2010-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多