2011-03-07 37 views
3

我有连续编号的实体,我想持续与Azure表服务,但RowKey列的类型是有问题的。 的实体的数量应存放在RowKey列,这样我就可以快速查询(PK = '..' && RowKey = 5)实体,获得最新的实体(RowKey > 10)和查询某组实体(RowKey > 5 && RowKey < 10)。如何在Azure表存储中使用整数RowKeys?

由于RowKey必须是字符串,低于比较是有问题的("100" < "11")。 我想过把数字预置为零(所以"100" > "011"),但我无法预测实体的数量(从而也就是零的数量)。

我知道我可以只创建一个整数列,但我会失去索引RowKey列的性能优势(加上我没有适合RowKey任何其他信息)。 以前有没有人有这个问题?

回答

1

我通过创建一个自定义RowKey类,环绕一个字符串,并提供了一个Increment方法解决了这个问题。

我现在可以在此范围内定义一系列有效字符(例如0-9 + a-z + A-Z)和“count”(例如az9 + 1 = azaazZ + 1 = aA0)。与仅使用数字相比,此优点是我有更大范围的可能键(62^n而不是10^n)。

我还是要事先定义字符串的长度,不能改变它,但现在我可以存储几乎任何数量的实体,同时保持字符串本身要短得多。例如,对于10位数字,我可以存储~8*10^17密钥和20位数字~7*10^35

的有效字符的数目当然可以进一步增加能够更有效地使用的位数,但在我的情况在上述范围内是足够的,仍然是用于调试目的足够可读。

我希望这个答案能够帮助遇到同样问题的其他人。

编辑:正如一个方面说明的情况下,任何人都希望实现类似的东西:你必须创建自定义字符范围,不能仅仅从0开始计数向上,因为有非法之间的字符(例如/\)数字(0-9)和小写字母。

+4

后您的代码,节省重新发明轮子 – 2011-05-03 02:09:24

0

我发现了一个潜在的解决方案,如果你正在使用Linq to query against Azure Table Storage

您添加像这样到模型中的表...

public int ID 
{ 
    get 
    { 
     return int.Parse(RowKey); 
    } 
} 

然后你就可以在你的Linq查询做到这一点...

.Where(e => e.ID > 1 && e.ID < 10); 

有了这个技术,你”因为它没有“设置”操作,所以实际上并没有将“ID”列添加到表中。

我不确定的一件事是幕后发生了什么。我想知道Azure Table Storage对最终形式的查询是什么样的,但我不知道如何找到它。在调试和使用quickwatch时,我一直无法找到这些信息。

UPDATE

我还没有想出发生了什么,但我有一个强烈的感觉,这是不是很有效。我正在想办法是像OP那样创建一个sortable string。然后,可以使用Linq where子句中的RowKey.CompareTo()函数按范围进行过滤。

3

我发现一个简单的方法,但以前的解决方案更有效率(关于密钥长度)。 而不是使用所有的字母,我们可以只使用数字,关键的是使长度固定的(0000,0001,0002,......):

public class ReadingEntity : TableEntity 
{ 
    public static string KeyLength = "000000000000000000000"; 
    public ReadingEntity(string partitionId, int keyId) 
    { 
     this.PartitionKey = partitionId; 
     this.RowKey = keyId.ToString(KeyLength); ; 


    } 
    public ReadingEntity() 
    { 
    } 
} 


public IList<ReadingEntity> Get(string partitionName,int date,int enddate) 
{ 
     CloudTableClient tableClient = storageAccount.CreateCloudTableClient(); 

     // Create the CloudTable object that represents the "people" table. 
     CloudTable table = tableClient.GetTableReference("Record"); 

     // Construct the query operation for all customer entities where PartitionKey="Smith". 
     TableQuery<ReadingEntity> query = new TableQuery<ReadingEntity>().Where(TableQuery.CombineFilters(
    TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, partitionName), 
    TableOperators.And,TableQuery.CombineFilters(
    TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.LessThan, enddate.ToString(ReadingEntity.KeyLength)), TableOperators.And, 
    TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.GreaterThanOrEqual, date.ToString(ReadingEntity.KeyLength))))); 
     return table.ExecuteQuery(query).ToList(); 
} 

希望这有助于。

5

我有一个类似的问题,增加了警告,我也想支持RowKey按降序排序。在我的情况下,我并不在乎支持数万亿的可能值,因为我正确使用了PartitionKey,并且还需要在需要进一步细分RowKey时使用范围前缀(如“scope-id” - >“12-8374”)。最后我决定了enzi提出的一般方法的具体实现。我使用了Base64编码的修改版本,生成一个四个字符的字符串,它支持超过1600万个值,并可按升序或降序排序。这是代码,它已经过单元测试,但缺少范围检查/验证。

/// <summary> 
/// Gets the four character string representation of the specified integer id. 
/// </summary> 
/// <param name="number">The number to convert</param> 
/// <param name="ascending">Indicates whether the encoded number will be sorted ascending or descending</param> 
/// <returns>The encoded string representation of the number</returns> 
public static string NumberToId(int number, bool ascending = true) 
{ 
    if (!ascending) 
     number = 16777215 - number; 

    return new string(new[] { 
     SixBitToChar((byte)((number & 16515072) >> 18)), 
     SixBitToChar((byte)((number & 258048) >> 12)), 
     SixBitToChar((byte)((number & 4032) >> 6)), 
     SixBitToChar((byte)(number & 63)) }); 
} 

/// <summary> 
/// Gets the numeric identifier represented by the encoded string. 
/// </summary> 
/// <param name="id">The encoded string to convert</param> 
/// <param name="ascending">Indicates whether the encoded number is sorted ascending or descending</param> 
/// <returns>The decoded integer id</returns> 
public static int IdToNumber(string id, bool ascending = true) 
{ 
    var number = ((int)CharToSixBit(id[0]) << 18) | ((int)CharToSixBit(id[1]) << 12) | ((int)CharToSixBit(id[2]) << 6) | (int)CharToSixBit(id[3]); 

    return ascending ? number : -1 * (number - 16777215); 
} 

/// <summary> 
/// Converts the specified byte (representing 6 bits) to the correct character representation. 
/// </summary> 
/// <param name="b">The bits to convert</param> 
/// <returns>The encoded character value</returns> 
[MethodImplAttribute(MethodImplOptions.AggressiveInlining)] 
static char SixBitToChar(byte b) 
{ 
    if (b == 0) 
     return '!'; 
    if (b == 1) 
     return '$'; 
    if (b < 12) 
     return (char)((int)b - 2 + (int)'0'); 
    if (b < 38) 
     return (char)((int)b - 12 + (int)'A'); 
    return (char)((int)b - 38 + (int)'a'); 
} 

/// <summary> 
/// Coverts the specified encoded character into the corresponding bit representation. 
/// </summary> 
/// <param name="c">The encoded character to convert</param> 
/// <returns>The bit representation of the character</returns> 
[MethodImplAttribute(MethodImplOptions.AggressiveInlining)] 
static byte CharToSixBit(char c) 
{ 
    if (c == '!') 
     return 0; 
    if (c == '$') 
     return 1; 
    if (c <= '9') 
     return (byte)((int)c - (int)'0' + 2); 
    if (c <= 'Z') 
     return (byte)((int)c - (int)'A' + 12); 
    return (byte)((int)c - (int)'a' + 38); 
} 

您可以将false传递给升序参数以确保编码值将按相反方向排序。我选择了!和$来完成Base64集合,因为它们对RowKey值有效。这个算法可以很容易地修改以支持额外的字符,但我坚信,对于RowKey值来说,较大的数字是没有意义的,因为表格存储键必须被有效地分割。以下是一些输出示例:

0 - > !!!! ASC & ZZZZ降序

1000 - > !!直流ASC & zzkL降序

2000 - > !! TE ASC & zzUj降序

3000 - > !!是ASC & zzF5降序

4000 - >!宇ASC & ZZ $ T递减

5000 - > $ C6 ASC & zylr递减

6000 - > $ RK ASC & zyWD递减

7000! - > $ HM ASC & zyGb递减

8000 - > $ X! ASC & zy0z递减

9000 - > 0AC ASC & zxnL递减

+0

就像一个魅力!托马斯托马斯,你是一个天才! – 2014-06-07 20:22:34

相关问题