azure table storage partitionkey/row key background? - azure

I'm reviewing Azure tables in an existing implementation. Here's an example of data from 1 row:
partitionkey (string):
6b348096-e6cb-4126-ba3c-cd0c9e8ba9c9
rowkey (string):
02519452888782521547_c1a98e0f-1b25-4d38-bd96-d72b30a97bf0
Obviously, rowkey does not have a proper guid and both column names are native to Azure and required per entity. There does not appear to be an identity or default insert for these columns. Can someone please provide context around these columns and the implementation style differences and considerations between these Azure columns vs a SQL Server style implementation?

I have no idea about how the partition key and row key are constructed in your table, please turn to someone who created the table. :)
About the considerations on the Azure Table design, you can refer to this post (which is very complete and helpful).

Partitionkey and Rowkey are just two properties of entitis in Azure table. Rowkey is the "primary key" within one partition. Within one PartitionKey, you can only have unique RowKeys. If you use multiple partitions, the same RowKey can be reused in every partition. PartitionKey + RowKey form the unique identifier(Primary key) for an entity.
In your table, the partitionkey and rowkey are just assigned with a random string. I'm not sure whether you designed this table or somebody else, but these two properties can be assigned with other values through Azure Storage .NET client libary and Rest API. As the example below, you can design the rowkey and partitionkey, and assign whatever valid value you want, here lastname for Partitionkey and firstname for rowkey:
public class CustomerEntity : TableEntity
{
public CustomerEntity(string lastName, string firstName)
{
this.PartitionKey = lastName;
this.RowKey = firstName;
}
public CustomerEntity() { }
public string Email { get; set; }
public string PhoneNumber { get; set; }
}
It’s better to think about both properties and your partitioning strategy. Don’t just assign them a guid or a random string as it does matter for performance. I recommend you go through Designing a Scalable Partitioning Strategy for Azure Table Storage, the most commom used is Range Partitions, but you can choose whatever you want.
This is a great blog help you understand how partitionkey and rowkey work http://blog.maartenballiauw.be/post/2012/10/08/What-PartitionKey-and-RowKey-are-for-in-Windows-Azure-Table-Storage.aspx

Related

How to apply filter in Azure table client

I am working on Azure storage table. Below is the partitionKey and rowkey
partitionKey = Product Type
rowKey = Region
I have used below filter to get the data (see the code below). If rowkey (region) is null then it should get product Type from all region. However, It get empty data. If I pass valid Partitionkey(ProductType) and RowKey (region) it's get the data.
AsyncPageable<T> result =TableClient.QueryAsync<T>(filter: $"PartitionKey eq '{partitionKey}' and RowKey eq '{rowKey}'");
Note: Partition-key is mandatory
Any advice.
Thanks
If rowkey (region) is null then it should get product Type from all
region.
If this is what you want, you would need to change the code to something like following:
AsyncPageable<T> result = TableClient.QueryAsync<T>(filter: $"PartitionKey eq '{partitionKey}'");
UPDATE
What I would like is If user has provided ProductType and Region then
filter using both. In case user only provide ProductType then include
all regions.
In this case, you will need to use two different queries - one when both PartitionKey and RowKey are present and other when only PartitionKey is present.
Your code would be something like:
var query = string.IsNullOrWhiteSpace(rowKey) ?
$"PartitionKey eq '{partitionKey}'" : $"PartitionKey eq '{partitionKey}' and RowKey eq '{rowKey}'";
AsyncPageable<T> result = TableClient.QueryAsync<T>(filter: query);

Querying PartitionKey and RowKey syntax in Azure Table Storage service

Meanwhile reading querying tables and entities in Azure Table Storage service on Microsoft docs I found PartitionKey and RowKeys can be filtered in 2 different ways in the myaccount.table.core.windows.net URL as the following:
https://<service-url>/Customers(PartitionKey='MyPartition',RowKey='MyRowKey1')
And using $filter to achieve the same:
https://<service-url>/Customers()?$filter=PartitionKey%20eq%20'MyPartitionKey'%20and%20RowKey%20eq%20'MyRowKey1'
I understand from the documentation PartitionKey and RowKey properties are forming the entity's primary key so the first syntax can be used as well as the Filtering on the PartitionKey and RowKey Properties part states:
Because the PartitionKey and RowKey properties form an entity's primary key, you can use a special syntax to identify the entity.
Questions:
Is there any drawbacks, disadvantages using $filter instead of that special syntax what has been mentioned?
Does it matter which one I use in my solution?
Any clarification is appreciated, thank you!
Interesing question! So I tried to perform both the operations to fetch an entity using Postman and the time it took to get the data was almost identical (between 250ms - 300ms).
So as long as you're using REST API, I don't think it would matter.
When you use the SDK (WindowsAzure.Storage version 9.3.3 for example), there're different methods that uses these features. Take a look at the sample code below:
var account = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
var tableClient = account.CreateCloudTableClient();
var table = tableClient.GetTableReference("TableName");
//This uses https://account.table.core.windows.net/table(PartitionKey='pk', RowKey='rk')
TableOperation op = TableOperation.Retrieve("pk", "rk");
var entity = table.Execute(op).Result as DynamicTableEntity;
//This uses https://account.table.core.windows.net/table?$filter=PartitionKey eq 'pk' and RowKey eq 'rk'
TableQuery query = new TableQuery();
query.FilterString = "PartitionKey eq 'pk' and RowKey eq 'rk'";
var entity = table.ExecuteQuery(query).FirstOrDefault();

Extend Generic Inquiry to Show the Number of records

Is it possible to extend the Generic Inquiry screen so that it shows the number of records retrieved? Or perhaps is it possible to use PXGenericInqGrph to get the number of records of a Generic Inquiry?
However, it is important, for performance reasons that I only retrieve one record with the total from the Database. and not getting all records from the database and doing a Count at the Application layer.
At least up until Acumatica 7.207.0029 there is no method to extend the Generic Inquiry results screen.
If you only need the record count, what you can do is edit your GI or create a copy to get the total and use the special <Count> field to get the record count.
Of course this requires you to set a GroupBy field and you need this to be the same for all records if you want a total record count.
If your query has a field you know to be equal to all records, you can use that field in the GroupBy tab. If not, there is a way to do this by adding a join to an number table.
Number Table Workaround
This technique uses a table with numbers to create specific queries. In this case we can join it to your query to add a known common value to all rows.
Here is the XML for a Customization Project that creates this table and makes it available as the Is.Objects.Core.ISNumbers DAC.
<Customization level="200" description="Number utility table" product-version="17.207">
<Graph ClassName="ISNumbers" Source="#CDATA" IsNew="True" FileType="NewDac">
<CDATA name="Source"><![CDATA[using System;
using PX.Data;
namespace IS.Objects.Core
{
[Serializable]
public class ISNumbers: IBqlTable
{
#region Number
[PXDBInt(IsKey = true)]
[PXUIField(DisplayName = "Number", IsReadOnly = true)]
public int? Number { get; set; }
public class number : IBqlField{}
#endregion
}
}]]></CDATA>
</Graph>
<Sql TableName="ISNumbers" CustomScript="#CDATA">
<CDATA name="CustomScript"><![CDATA[IF OBJECT_ID('ISNumbers', 'U') IS NOT NULL DROP TABLE ISNumbers;
SELECT TOP 10000 IDENTITY(int,1,1) AS Number
INTO ISNumbers
FROM sys.objects s1
CROSS JOIN sys.objects s2
ALTER TABLE ISNumbers ADD CONSTRAINT PK_ISNumbers PRIMARY KEY CLUSTERED (Number)]]></CDATA>
</Sql>
</Customization>
Just add the table to the GI and crate an INNER JOIN relation where the value of the number field equals 1:
Then you can use this field in the GroupBy condition.
Then you can add the Numbers field and set its value to <Count>. Leave all your other result fields to keep the logic but hide them if you don't need them (they will be automatically grouped by max value).
All queries performed by GIs are executed in the DB so you don't need to worry about it running in the App side.

Getting PK columns from ColumnFamily columnFamily, ByteBuffer key Cassandra triggers

I am new to cassandra triggers. I am still ramping up. I could find a way to extract value out for a given ByteBuffer key, but do not know how to get the "name" of the actual primary key column
public static String getKeyText(ColumnFamily columnFamily, ByteBuffer key) {
CFMetaData cfm = columnFamily.metadata();
String key_data = cfm.getKeyValidator().getString(key);
}
Any idea on how to get just the key column name?
Any pointers are highly appreciated
Thanks
not sure if this what you mean, but you can get the name of the partition keys from columnFamily.partitionKeyColumns() the ColumnDefinition's have a name field thats readable. There may be more than one depending on schema
https://github.com/apache/cassandra/blob/cassandra-2.1/src/java/org/apache/cassandra/config/CFMetaData.java#L797

Astyanax getKey with compound key

I would like to run the following code with a compound primary key.
Column<String> result = keyspace.prepareQuery(CF_COUNTER1)
.getKey(rowKey)
.getColumn("Column1")
.execute().getResult();
Long counterValue = result.getLongValue();
Research seems to show that it can be a string that represents a key (if it's not a compound primary key). The documentation says that it is of type K, alas, I am not very experience with Java, and have no idea what that means. Is it just a base type that lots of stuff inherits from? If so, I'm not really any closer to knowing what getKey(K) needs in order to handle a compound key (am I?).
You just need to write a class that fits the columns in your data model. You can then give this class to Astyanax in your mutations or queries.
For example, if you had a data model like this
CREATE TABLE fishblogs (
userid varchar,
when timestamp,
fishtype varchar,
blog varchar,
image blob,
PRIMARY KEY (userid, when, fishtype)
);
you would create a class like this:
public class FishBlog {
#Component(ordinal = 0)
public long when;
#Component(ordinal = 1)
public String fishtype;
#Component(ordinal = 2)
public String field;
public FishBlog() {
}
}
When and fishtype form your composite column key and are represented by the FishBlog class. Userid would be your row/partition key and can be of the simple "string" type.
Have a look at this blog explaining in great detail how to insert data with composite keys (where I took this example from).
Hope that helps.

Resources