这可能使用Azure表吗?

时间:2012-02-10 00:26:40

标签: c# azure group-by azure-table-storage

我在下面的linq查询中收到错误消息“不支持方法'join':

tableServiceContext = new CustomTableServiceContext(storageAccount.TableEndpoint.AbsoluteUri, storageAccount.Credentials);
tableServiceContext.RetryPolicy = RetryPolicies.Retry(3, TimeSpan.FromSeconds(1));
var results = (from c in tableServiceContext.CreateQuery<ChannelEntry>("Channels").AsTableServiceQuery<ChannelEntry>()
    join v in tableServiceContext.CreateQuery<VideoEntry>("Videos").AsTableServiceQuery<VideoEntry>() on c.PartitionKey equals v.ChannelID
    join h in tableServiceContext.CreateQuery<HitEntry>("Hits").AsTableServiceQuery<HitEntry>() on v.PartitionKey equals h.VideoID
    where c.RowKey.Equals(UserID)
    group h by h.RowKey into g
    select new BiggestFan { UserID = g.Key, Hits = g.Count() }).AsTableServiceQuery().Execute().OrderByDescending(b => b.Hits).Take(1);

如果在此上下文中不支持“join”,那么最有效的查询方式是什么?

我的频道由视频组成,视频又有视频。我正在尝试找到当前登录用户的最大粉丝(最高点击率)。

在不使用连接的情况下,这种类型的最有效方法是什么?我是否必须获取所有频道然后视频,然后点击3个单独调用表存储然后再进行连接?

4 个答案:

答案 0 :(得分:8)

是的,你不能加入。你有几个选择。

1)多次扫描 - 在加入之前拍打几个.ToArray()语句,以便它在应用程序的内存中进行连接。这不是高性能,但表存储非常快。真的归结为这会产生多少行。

2)对表进行反规范化,以便在单个表中引用所需的所有键。这将使您在1个查询中获得结果,但意味着需要更新所有插入/更新逻辑。

答案 1 :(得分:2)

您的查询中有3件事情不受Azure表存储(AZT,我的缩写,其他人通常不使用)查询的支持。

  1. 加入
  2. 分组
  3. 汇总功能
  4. 简短版本是如果你想在AZT中运行一个有效的查询,那么你需要只针对一个表运行它并查询分区键或分区键和行键。

    这并不意味着您的基础数据必须只存储在这一个表中,您可以保留当前的结构,但是您可能需要构建一个基本上是一个索引的表,以便您获得你想要的信息。它可能有类似的结构:

    PartitionKey = ChannelUserId.PadWithLeadingZeros() + "-" + (int.MaxValue - NumberOfHits).PadWithLeadingZeros();
    RowKey = Fan User Id;
    

    您的查询将如下所示:

    tableServiceContext = new CustomTableServiceContext(storageAccount.TableEndpoint.AbsoluteUri, storageAccount.Credentials);
    tableServiceContext.RetryPolicy = RetryPolicies.Retry(3, TimeSpan.FromSeconds(1));
    var results = (from i in tableServiceContext.CreateQuery<BiggestFansIndex>("BiggestFansIndex").AsTableServiceQuery<BiggestFansIndex>()
        where i.PartitionKey.CompareTo(UserId.PaddedWithLeadingZeros()) >= 0
            && i.PartitionKey.CompareTo((UserId + 1).PaddedWithLeadingZeros()) < 0
        select i}).Take(1).Execute();
    

    我怀疑你最大的问题是保持这个索引表的最新状态,因为我确信命中率会有合理的规律性变化。

答案 2 :(得分:1)

其他人对于无法在Azure表中执行JOIN的说法是正确的。您可以将它移动到SQL Azure,其中JOIN按预期工作,但它比Azure表更昂贵,更慢。但是,假设您坚持使用Azure表:

在查看此特定查询时,您可以将Hits表的分区键设置为:

点击表:
PartitionKey = UserId(频道所有者)
RowKey =时间戳(或其他独特的东西)
UserId(执行命中的用户)
渠道ID
视频ID
(以及Hits表中您想要的其他字段)

正如其他人所说,你无法对Azure表存储查询进行聚合,因此你必须将所有数据拉回本地内存(通过调用Execute),然后你可以在内存中进行聚合。以下是从表存储中提取数据的方法(此查询在Azure表存储服务器上运行):

var allHits = 
    (
      from h in tableServiceContext.CreateQuery("Hits")
        .AsTableServiceQuery()
        where h.PartitionKey == CurrentUserId  // The currently logged in user
    ).Execute();

接下来是你如何聚合它(这个查询在本地内存中运行):

var result = 
    (
      from h in allHits
      group h by h.UserId into g  // The User that performed the Hit
      select new BiggestFan { UserID = g.Key, Hits = g.Count() }
    )
    .OrderByDescending(b => b.Hits).FirstOrDefault();

这在技术上可行,但不会扩展。一旦各种用户变得流行,将所有用户的命中下拉到本地内存以运行此查询将是不切实际的。另外,一旦数据变得太大而无法一次性下载,您可能最终不得不对数据进行分页。

您可以进一步对数据进行非规范化并计算和存储各种总计,这样当您需要运行此Biggest-Fan查询时,您需要检索的是各种预先计算的总计。

但是,这只是一个查询。在设计Azure表结构时,您需要考虑可能要对其执行的所有查询,运行的频率以及它们将要运行的数据量。然后,您可以在Azure表中找出数据的最佳结构。我建议不要围绕单个查询设计Azure表,因为将来可能需要更多查询。

答案 3 :(得分:0)

Azure表存储不适合此类聚合查询。我建议你研究一些No-SQL文档数据库,比如CouchDB,MongoDB和RavenDB。但是如果您仍然想要使用它,则需要对数据进行非规范化。