在 TableStorage 中查找第一个匹配条件的元素的最聪明和最有效的方法
Smartest and Most Performant Way to find the First Element Matching a Condition in a TableStorage
假设我有一个巨大的 table 存储客户的存储空间。假设 Partition Key 是他们的邮政编码,RowKey 是他们的注册时间戳。
现在,找到给定(邮政编码)区域的第一个客户(在给定日期后注册(早起的鸟儿:-))的最聪明、最有效的方法是什么?假设条目在写入 table 存储时未排序。
我最初的想法是有一个像这样的辅助方法(无论如何我都需要它用于其他目的):
public IEnumerable<Customer> GetCustomers(string zip, long stampStart, long stampEnd)
{
if (_table == null) return new List<Customer>();
return query = (from entry in _table.CreateQuery<Customer>()
where entry.PartitionKey == zip
&& entry.RowKey.CompareTo(stampStart) <= 0
&& entry.RowKey.CompareTo(stampEnd) >= 0
select entry);
}
然后用它来触发这样的请求:
public Customer GetEarlyBird(string zip, long stamp)
{
if (_table == null) return null;
return
GetCustomers(zip, stamp, stamp + 31536000) //covers a one year period
.OrderBy(x => x.SignupStamp)
.FirstOrDefault();
}
最后调用
var zip = //some zip code;
var lookupStamp = //some long timestamp;
var earlyBird = GetEarlyBird(zip, lookupStamp);
但是,由于 OrderBy 调用,必须对整个查询结果进行评估,这需要很长时间。另一方面,在不对查询结果进行排序的情况下,FirstOrDefault 不一定 return 在 stamp 之后注册最近的客户,而是列表中的第一个(可以是该地区的任何客户,因为他们存储在 table 中时不一定被排序)。
我错过了什么? "outsource" 向数据库排序而不是在内存中排序的最聪明的方法是什么?或者我的方法是否还有其他一些我遗漏的主要缺陷?
如果将注册时间戳转换为 DateTime.Ticks,然后从 DateTime.Max.Ticks 中减去并将其用作行键,Azure Table 存储服务将自然地对最新条目进行排序在顶部,因为它将具有最小的行键。因此,如果您使用 Take(1) 使用特定分区键进行查询,您将检索该分区键的最新条目。这样就没有分区扫描,客户端和服务都没有过滤。
正如 Dogu Arslan 所说,您可以使用 DateTime.Ticks 成为您的行键并使用 take 方法获取第一个值。
更多详情,您可以参考以下代码:
DateTime d1 = new DateTime(2016, 11, 1);
DateTime d2 = new DateTime(2016, 12, 1);
var query = (from ent in query2
where
ent.PartitionKey == "ZIP"
&& ent.RowKey.CompareTo(string.Format("{0:D19}", d1.Ticks)) > 0
&& ent.RowKey.CompareTo(string.Format("{0:D19}", d2.Ticks)) < 0
select ent).Take(1).FirstOrDefault() ;
我建议你也可以注意以下事项:
1.If 您想要获得在给定日期之后首先注册的人。
我建议您可以直接使用 DateTime.Now.Ticks,因为 azure table 会自动按分区键和行键升序对实体进行排序。
早期时间刻度将小于现在时间刻度。
2.You 必须用前导零填充反向刻度值,以确保字符串值按预期排序。
更多详情,您可以参考下图:
假设我有一个巨大的 table 存储客户的存储空间。假设 Partition Key 是他们的邮政编码,RowKey 是他们的注册时间戳。
现在,找到给定(邮政编码)区域的第一个客户(在给定日期后注册(早起的鸟儿:-))的最聪明、最有效的方法是什么?假设条目在写入 table 存储时未排序。
我最初的想法是有一个像这样的辅助方法(无论如何我都需要它用于其他目的):
public IEnumerable<Customer> GetCustomers(string zip, long stampStart, long stampEnd)
{
if (_table == null) return new List<Customer>();
return query = (from entry in _table.CreateQuery<Customer>()
where entry.PartitionKey == zip
&& entry.RowKey.CompareTo(stampStart) <= 0
&& entry.RowKey.CompareTo(stampEnd) >= 0
select entry);
}
然后用它来触发这样的请求:
public Customer GetEarlyBird(string zip, long stamp)
{
if (_table == null) return null;
return
GetCustomers(zip, stamp, stamp + 31536000) //covers a one year period
.OrderBy(x => x.SignupStamp)
.FirstOrDefault();
}
最后调用
var zip = //some zip code;
var lookupStamp = //some long timestamp;
var earlyBird = GetEarlyBird(zip, lookupStamp);
但是,由于 OrderBy 调用,必须对整个查询结果进行评估,这需要很长时间。另一方面,在不对查询结果进行排序的情况下,FirstOrDefault 不一定 return 在 stamp 之后注册最近的客户,而是列表中的第一个(可以是该地区的任何客户,因为他们存储在 table 中时不一定被排序)。
我错过了什么? "outsource" 向数据库排序而不是在内存中排序的最聪明的方法是什么?或者我的方法是否还有其他一些我遗漏的主要缺陷?
如果将注册时间戳转换为 DateTime.Ticks,然后从 DateTime.Max.Ticks 中减去并将其用作行键,Azure Table 存储服务将自然地对最新条目进行排序在顶部,因为它将具有最小的行键。因此,如果您使用 Take(1) 使用特定分区键进行查询,您将检索该分区键的最新条目。这样就没有分区扫描,客户端和服务都没有过滤。
正如 Dogu Arslan 所说,您可以使用 DateTime.Ticks 成为您的行键并使用 take 方法获取第一个值。
更多详情,您可以参考以下代码:
DateTime d1 = new DateTime(2016, 11, 1);
DateTime d2 = new DateTime(2016, 12, 1);
var query = (from ent in query2
where
ent.PartitionKey == "ZIP"
&& ent.RowKey.CompareTo(string.Format("{0:D19}", d1.Ticks)) > 0
&& ent.RowKey.CompareTo(string.Format("{0:D19}", d2.Ticks)) < 0
select ent).Take(1).FirstOrDefault() ;
我建议你也可以注意以下事项:
1.If 您想要获得在给定日期之后首先注册的人。 我建议您可以直接使用 DateTime.Now.Ticks,因为 azure table 会自动按分区键和行键升序对实体进行排序。 早期时间刻度将小于现在时间刻度。
2.You 必须用前导零填充反向刻度值,以确保字符串值按预期排序。 更多详情,您可以参考下图: