我在下面的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个单独调用表存储然后再进行连接?
答案 0 :(得分:8)
是的,你不能加入。你有几个选择。
1)多次扫描 - 在加入之前拍打几个.ToArray()语句,以便它在应用程序的内存中进行连接。这不是高性能,但表存储非常快。真的归结为这会产生多少行。
2)对表进行反规范化,以便在单个表中引用所需的所有键。这将使您在1个查询中获得结果,但意味着需要更新所有插入/更新逻辑。
答案 1 :(得分:2)
您的查询中有3件事情不受Azure表存储(AZT,我的缩写,其他人通常不使用)查询的支持。
简短版本是如果你想在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。但是如果您仍然想要使用它,则需要对数据进行非规范化。