<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Data Otaku</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/data_otaku/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/data_otaku/feed.xml</link>
    <description>A seemingly random collection of data- and database-related posts</description>
    <lastBuildDate>Mon, 30 Oct 2017 14:04:53 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Load Azure Storage Data into Cloudera HDFS</title>
      <link>https://docs.microsoft.com/archive/blogs/data_otaku/load-azure-storage-data-into-cloudera-hdfs</link>
      <pubDate>Mon, 30 Oct 2017 14:04:53 GMT</pubDate>
      <dc:creator><![CDATA[Bryan C Smith]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/data_otaku/?p=1566</guid>
      <description><![CDATA[How do I get data from an Azure Storage account into an Azure-deployed Cloudera cluster running...]]></description>
      <content:encoded><![CDATA[How do I get data from an Azure Storage account into an Azure-deployed Cloudera cluster running HDFS?  This question seems to be coming up with quite a bit of frequency lately, so that I thought I might answer it with a post.

Paige Liu has done an excellent job documenting how to configure Cloudera to read from an Azure Storage account.  You can read her step-by-step instructions <a href="https://blogs.msdn.microsoft.com/pliu/2016/06/19/backup-cloudera-data-to-azure-storage/">here</a>.

So assuming you’ve already moved data into your Azure Storage account using either the <a href="https://docs.microsoft.com/en-us/azure/vs-azure-tools-storage-explorer-blobs">Azure Storage Explorer</a>, <a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-azure-cli">CLI</a>, <a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-powershell-guide-full">PowerShell</a>, <a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-use-azcopy">AZCopy</a>, the <a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-import-export-service">Import/Export Service</a>, or some other data transfer mechanism, once you’ve completed the configuration changes Paige details, you are ready to transfer data using a command like one of the following:

hadoop fs -cp wasbs://<em>mydata</em>@<em>brysmi</em>.blob.core.windows.net/<em>myfiles</em>/ /<em>myhdfsfiles</em>/

hadoop distcp wasbs://<em>mydata</em>@<em>brysmi</em>.blob.core.windows.net/<em>myfiles</em>/ /<em>myhdfsfiles</em>/

In these two examples, <em>brysmi</em> is the name of my Azure Storage account, <em>mydata</em> is the name of the container in that Storage account which holds my files, and <em>myfiles</em> is the folder path under that container where the files (blobs) have been landed.  (The second path, <em>i.e.</em> /<em>myhdfsfiles</em>/, represents the path in HDFS where I want to files copied to.)

Finally, its important to note that distcp works much faster when you have numerous files with a large total storage volume as it copies files in parallel.]]></content:encoded>
    </item>
    <item>
      <title>Working with the HBase Import and Export Utility</title>
      <link>https://docs.microsoft.com/archive/blogs/data_otaku/working-with-the-hbase-import-and-export-utility</link>
      <pubDate>Wed, 21 Dec 2016 23:14:52 GMT</pubDate>
      <dc:creator><![CDATA[Bryan C Smith]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/data_otaku/?p=1505</guid>
      <description><![CDATA[As mentioned in a couple other posts, I am working with a customer to move data between two Hadoop...]]></description>
      <content:encoded><![CDATA[As mentioned in a couple other posts, I am working with a customer to move data between two Hadoop clusters. This includes data in several HBase tables which has led me to make use of the HBase Import and Export utilities. To help others who may have a similar need, I’m going to use this post to document some of the options you might use with these utilities. But before doing this, I’ll create a simple HBase table on which I can safely perform these operations.

<strong>The Sample Table</strong>

To illustrate some of the capabilities of the HBase Import and Export utilities, I’ll create a table with a single column family. That family will be configured to retain 10 versions of each row. I’ll then insert 10 rows with 10 versions per row. From the shell prompt:

<code>hbase shell
create 'mytable', {NAME =&gt; 'myfam', VERSIONS =&gt; 10}
for i in '0'..'9' do
for j in '0'..'9' do
put 'mytable', "#{j}", "myfam:mycol", "#{i}#{j}"
end
end</code>

I can verify I have 10 rows with the COUNT statement:

<code>count 'mytable'</code>

If I retrieve a single row, I’ll see the latest version of that row:

<code>get 'mytable', '0'</code>

But if I indicate I want all 10 versions of that row, I can see that HBase is storing these:

<code>get 'mytable', '0', {COLUMN =&gt; "myfam:mycol", VERSIONS =&gt; 10}</code>

Here is a screenshot of the results of these last few statements from my session:

<a href="https://msdnshared.blob.core.windows.net/media/2016/12/img_sampletable.png"><img width="1024" height="446" class="alignnone wp-image-1515 size-large" alt="img_sampletable" src="https://msdnshared.blob.core.windows.net/media/2016/12/img_sampletable-1024x446.png" /></a>

I can now exit the HBase shell:

<code>quit</code>

<strong>The HBase Export Utility</strong>

The basic syntax of the HBase Export utility looks something like this:

<code>hbase org.apache.hadoop.hbase.mapreduce.Export "table name" "targeted export location"</code>

The command is called from the (Linux) shell, not the HBase shell used earlier. In the directory identified as the targeted export location, you will find a part-m-00000 file representing the results of the map job created by the utility. You will also find an empty file indicating the success or failure of the map job.

When called using this simple syntax, the utility will export the latest version of each row in the table. Doing this for the sample table created earlier, we should see 10 records in the result set:

<code>hbase org.apache.hadoop.hbase.mapreduce.Export "mytable" "/export/mytable"</code>
<code>hdfs dfs -ls /export/mytable</code>
<code>hdfs dfs -cat /export/mytable/part-m-00000 | more</code>

<a href="https://msdnshared.blob.core.windows.net/media/2016/12/img_export1.png"><img width="1024" height="555" class="alignnone size-large wp-image-1525" alt="img_export1" src="https://msdnshared.blob.core.windows.net/media/2016/12/img_export1-1024x555.png" /></a>

If I’d like to retrieve more than the latest version of the records in a table, I can add a version count after the targeted export location. This value can be higher than the configured version retention on the table without triggering an error. For example, here I am extracting all 10 versions of each record by setting an arbitrarily high version count of 1000:

<code>hbase org.apache.hadoop.hbase.mapreduce.Export "mytable" "/export/mytable" 1000</code>
<code>hdfs dfs -ls /export/mytable</code>
<code>hdfs dfs -cat /export/mytable/part-m-00000 | more</code>

<a href="https://msdnshared.blob.core.windows.net/media/2016/12/img_export2.png"><img width="1024" height="673" class="alignnone size-large wp-image-1535" alt="img_export2" src="https://msdnshared.blob.core.windows.net/media/2016/12/img_export2-1024x673.png" /></a>

<strong>NOTE</strong> I am deleting the /export/mytable directory prior to re-running the Export utility.

If I need prior versions that fall within a select time range, I can add a starting Epoch timestamp and ending Epoch timestamp to the command call. To calculate an Epoch timestamp for a date of interest, calculate the number of days between that date and January 1, 1970 and then multiple that value by 86,400,000, the number of milliseconds in a day.

For example, the Epoch timestamp for December 1, 2016 and January 1, 2017 are 1480550400000 and 1483228800000, respectively. If I wanted the all versions of a record that were created in that range, again I’d set an arbitrarily high version count but then box it in by these timestamp values:

<code>hbase org.apache.hadoop.hbase.mapreduce.Export "mytable" "/export/mytable" 1000 1480550400000 1483228800000</code>

If I need to extract a subset of the rows in a table, <em>e.g. </em>to break up a large extract, I can do this by specifying starting row identifier. If I specify an ending row identifier, I will extract all rows starting with the specified starting row up to but not inclusive of the ending row. If I do not specify an ending row identifier, I will get all rows starting with the specified starting row through the remainder of the table.   For example, here I am extracting the latest version of all rows from 1 up to but not including 6:

<code>hbase org.apache.hadoop.hbase.mapreduce.Export -Dhbase.mapreduce.scan.row.start=0 -Dhbase.mapreduce.scan.row.stop=6 "mytable" "/export/mytable"</code>

<code>hdfs dfs -ls /export/mytable</code>
<code>hdfs dfs -cat /export/mytable/part-m-00000 | more </code>

<a href="https://msdnshared.blob.core.windows.net/media/2016/12/img_export3.png"><img width="1024" height="397" class="alignnone size-large wp-image-1545" alt="img_export3" src="https://msdnshared.blob.core.windows.net/media/2016/12/img_export3-1024x397.png" /></a>

Should I have needed to extract more versions or box my results around timestamp values, those options are supported as before.

While there are many more options I can employ with the Export utility, the last one I want to explore is the use of compression. By indicating that the results should be compressed and specifying the compression codec to use, I can extract a file with a smaller footprint. For example, here I am extracting the all versions of all rows in the sample table with and then without GZip compression so that I can compare extract file sizes:

<code>hbase org.apache.hadoop.hbase.mapreduce.Export -Dmapreduce.output.fileoutputformat.compress=true -Dmapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.GzipCodec "mytable" "/export/mytable/compressed" 1000</code>
<code>hbase org.apache.hadoop.hbase.mapreduce.Export "mytable" "/export/mytable/uncompressed" 1000</code>
<code>hdfs dfs -ls /export/mytable/compressed/part-m-00000</code>
<code>hdfs dfs -ls /export/mytable/uncompressed/part-m-00000</code>

<a href="https://msdnshared.blob.core.windows.net/media/2016/12/img_export4.png"><img width="1024" height="93" class="alignnone size-large wp-image-1555" alt="img_export4" src="https://msdnshared.blob.core.windows.net/media/2016/12/img_export4-1024x93.png" /></a>

If you compare the size of the part-m-00000 files, you can see the compressed file provides some space savings (though not much in absolute terms given the small nature of the sample data set).

There are a few more options available with the Export utility. You can see these by simply calling the utility with no parameters or arguments at the command prompt.

<strong>The HBase Import Utility</strong>

In comparison to the Export utility, the Import utility is very straightforward:

<code>hbase org.apache.hadoop.hbase.mapreduce.Import 'tablename' 'target import location'</code>

The key consideration when using the utility is that the targeted table must exist prior to the import. The table name does not need to match that of the exported table but it does need to have the same column families as the exported table. It’s also important to note that if you exported multiple versions of the row, the targeted import table will employ its own versioning control:

<code># assuming this is my extract:</code>
<code># hbase org.apache.hadoop.hbase.mapreduce.Export "mytable" "/export/mytable" 1000</code>
<code>hbase shell</code>
<code>create 'mytable_import', {NAME =&gt; 'myfam', VERSIONS =&gt; 10}</code>
<code>quit</code>
<code>hbase org.apache.hadoop.hbase.mapreduce.Import 'mytable_import' '/export/mytable'</code>

As with the Export utility, HBase Import supports many other options which you can see by simply calling the utility at the command prompt with no parameters or arguments.]]></content:encoded>
    </item>
    <item>
      <title>Exploring the Hive IMPORT and EXPORT Commands</title>
      <link>https://docs.microsoft.com/archive/blogs/data_otaku/exploring-the-hive-import-and-export-commands</link>
      <pubDate>Tue, 20 Dec 2016 15:54:54 GMT</pubDate>
      <dc:creator><![CDATA[Bryan C Smith]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/data_otaku/?p=1485</guid>
      <description><![CDATA[In a recent post, I mentioned I am working with a customer to migrate from a native Hortonworks...]]></description>
      <content:encoded><![CDATA[In a recent post, I mentioned I am working with a customer to migrate from a native Hortonworks (HDP) cluster to Azure HDInsight. As part of this work, I’ve had need to use the Hive IMPORT and EXPORT commands as part of the migration of tables from one cluster to another.

The Hive IMPORT and EXPORT commands aren’t terribly difficult to use, but still, I’ve found their documentation to be a bit sparse, leaving me to figure a few things out by trial and error. In addition, I’ve found that the use of the WASB file system, the default file system for Azure HDInsight, changes some of the commands’ behaviors.

In this post, I’ll document the basics of the commands, assuming the use of the HDFS file system, and then address some of the differences that arise when used with WASB. Be before doing this, I’ll introduce a few tables which I’ll use to illustrate some of the commands’ functionality.

&nbsp;

<strong>The Sample Hive Tables</strong>

To illustrate some differences in behavior between managed and external as well as partitioned and unpartitioned tables, I’ll create and populate four simple Hive tables, representing a combination of each of these elements:
<blockquote><code>CREATE TABLE ManagedUnpartitioned (
column1 STRING
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
STORED AS TEXTFILE
LOCATION '/demo/managedunpartitioned/';</code>

<code>CREATE TABLE ManagedPartitioned (
column1 STRING
)
PARTITIONED BY (partition1 STRING)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
STORED AS TEXTFILE
LOCATION '/demo/managedpartitioned/';</code>

<code>CREATE EXTERNAL TABLE ExternalUnpartitioned (
column1 STRING
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
STORED AS TEXTFILE
LOCATION '/demo/externalunpartitioned/';</code>

<code>CREATE EXTERNAL TABLE ExternalPartitioned (
column1 STRING
)
PARTITIONED BY (partition1 STRING)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
STORED AS TEXTFILE
LOCATION '/demo/externalpartitioned/';</code>

<code>INSERT INTO TABLE ManagedUnpartitioned
VALUES ('this is a test'), ('this is also a test');</code>

<code>INSERT INTO TABLE ManagedPartitioned PARTITION(partition1='A')
VALUES ('this is a test');
INSERT INTO table ManagedPartitioned PARTITION(partition1='B')
VALUES ('this is also a test');</code>

<code>INSERT INTO TABLE ExternalUnpartitioned
VALUES ('this is a test'), ('this is also a test');</code>

<code>INSERT INTO TABLE ExternalPartitioned PARTITION(partition1='A')
VALUES ('this is a test');
INSERT INTO TABLE ExternalPartitioned PARTITION(partition1='B')
VALUES ('this is also a test');</code></blockquote>
<strong>NOTE </strong>The code above should be executed as a block. Subsequent code samples include redundant statements that if executed as a block will produce an error. The redundant statements are provided below to illustrate supported variations in command syntax.

&nbsp;

<strong>The Hive EXPORT Command</strong>

The Hive EXPORT command is relatively simple. The basic form is:
<blockquote><code>EXPORT TABLE <em>tablename</em> TO '<em>targeted export location</em>';</code></blockquote>
Under the directory identified as the targeted export location, the export command will create a <em>_metadata</em> file containing table definition information. If the table is unpartitioned, a subdirectory named <em>data</em> will be created under the target export location and within it will be copied the data for the table. If the table is partitioned, then a subdirectory for each partition is created under the targeted export location to hold that partition’s data.

When submitting the command against a cluster using HDFS as its default file system, the syntax is very straightforward. You can use a fully-qualified targeted export location or a relative location path. The targeted export location does not need to be created in advance, but if it has, you only need to ensure the directory to which you are exporting data is empty. Otherwise, you will receive an error message specifying this as the problem.

Here are examples of the EXPORT command for each of the four sample tables on a cluster using HDFS as its default file system. Please note that the elements in the fully-qualified path will vary with the configuration of your cluster:
<blockquote><code>EXPORT TABLE ManagedUnpartitioned TO '/export/managedunpartitioned';
EXPORT TABLE ManagedUnpartitioned TO 'hdfs://master3:8020/export/managedunpartitioned';</code>

<code>EXPORT TABLE ManagedPartitioned TO '/export/managedpartitioned';
EXPORT TABLE ManagedPartitioned TO 'hdfs://master3:8020/export/managedpartitioned';</code>

<code>EXPORT TABLE ExternalUnpartitioned TO '/export/externalunpartitioned';
EXPORT TABLE ExternalUnpartitioned TO 'hdfs://master3:8020/export/externalunpartitioned';</code>

<code>EXPORT TABLE ExternalPartitioned TO '/export/externalpartitioned';
EXPORT TABLE ExternalPartitioned TO 'hdfs://master3:8020/export/externalpartitioned';</code></blockquote>
When working with a partitioned table, you have the option of exporting a single partition by adding a PARTITION clause to the EXPORT command. Here is each partition of the two partitioned tables being exported individually (using relative targeted location paths for brevity):
<blockquote><code>EXPORT TABLE ManagedPartitioned PARTITION (partition1='A') TO '/export/managedpartitioned/a';
EXPORT TABLE ManagedPartitioned PARTITION (partition1='B') TO '/export/managedpartitioned/b';</code>

<code>EXPORT TABLE ExternalPartitioned PARTITION (partition1='A') TO '/export/externalpartitioned/a';
EXPORT TABLE ExternalPartitioned PARTITION (partition1='B') TO '/export/externalpartitioned/b';</code></blockquote>
As before, the targeted export location should either not exist or should be an empty folder. Under this location, the command will create a <em>_metadata</em> file as well as a subdirectory for the targeted partition’s data. If individually exporting multiple partitions like this, it’s important that each partition have its own, unique targeted export location. On the import, you’ll need to bring these partitions into the target table one at a time.

&nbsp;

<strong>The Hive IMPORT Command </strong>

The basic form of the Hive IMPORT command identifies the target table and the directory containing the <em>_metadata</em> file and data subdirectories that will be used with the operation. This is the form of the command used when importing a table that has been exported in its entirety, <em>i.e.</em> not one partition at a time. The optional EXTERNAL clause indicates the newly created table should be created as an external table as opposed to a managed table:
<blockquote><code>IMPORT TABLE <em>tablename</em> FROM '<em>targeted import location</em>';
IMPORT EXTERNAL TABLE <em>tablename</em> FROM '<em>targeted import location</em>';
</code></blockquote>
A few points about the IMPORT command as presented here:
<ul>
 	<li>The table being created should not already exist as this command will attempt to create a new table using information in the <em>_metadata</em> file.</li>
 	<li>The name of the table being created does not need to match the name of the original (exported) table.</li>
 	<li>The table being created can be either managed or external, regardless of the status of the original table.</li>
</ul>
With that in mind, here are a few examples of the command illustrating some of its possible variations:
<blockquote><code>-- original export command
-- EXPORT TABLE ManagedPartitioned TO '/export/managedpartitioned';</code>

<code>IMPORT TABLE managedpartitioned_imported FROM '/export/managedpartitioned';</code>

<code>IMPORT EXTERNAL TABLE managedpartitioned_imported FROM '/export/managedpartitioned';</code>

<code>IMPORT TABLE managedpartitioned_imported FROM 'hdfs://master3:8020/export/managedpartitioned';</code>

<code>IMPORT EXTERNAL TABLE managedpartitioned_imported FROM 'hdfs://master3:8020/export/managedpartitioned';</code></blockquote>
To import individual partitions, a PARTITION clause can be added as demonstrated earlier. Again, here are a few examples of a partitioned table being imported by partition (using a relative path for the targeted import location for brevity):
<blockquote><code>-- original export commands
-- EXPORT TABLE ManagedPartitioned PARTITION (partition1='A') TO '/export/managedpartitioned/a';
-- EXPORT TABLE ManagedPartitioned PARTITION (partition1='B') TO '/export/managedpartitioned/b';</code>

<code>IMPORT TABLE managedpartitioned_imported PARTITION (partition1='A') FROM '/export/managedpartitioned/a';
IMPORT TABLE managedpartitioned_imported PARTITION (partition1='B') FROM '/export/managedpartitioned/b';</code>

<code>IMPORT EXTERNAL TABLE managedpartitioned_imported PARTITION (partition1='A') FROM '/export/managedpartitioned/a';
IMPORT EXTERNAL TABLE managedpartitioned_imported PARTITION (partition1='B') FROM '/export/managedpartitioned/b';</code></blockquote>
Unlike with the previous IMPORT statements, when importing a partition, the targeted table may already exist. IMPORT will throw an error if the <strong>targeted</strong> <strong>partition</strong> already exists in the targeted table or if the partition value specified with the PARTITION clause does not match the partition values of the original table as defined in the <em>_metadata</em> file. For example, if you attempt to import the partition with the partition1 value of ‘A’ to the partition with partition1 value of ‘B’ or a previously unknown partition with partition1 value of ‘C’, the IMPORT command will return an error.

Also interesting to note is that if you import the first partition as EXTERNAL, you can omit the EXTERNAL keyword on subsequent partition imports to that table and all partitions will be handled as part of an EXTERNAL table. However, if you omit the EXTERNAL keyword on the first partition import, then attempting to specify EXTERNAL with subsequent partition imports will generate an error complaining that the table is defined as managed.

Last thing to note about the IMPORT statement is that it allows you to copy data from the targeted import location to a new location which will serve as the go-forward path for the table. This is accomplished using the LOCATION clause:
<blockquote><code>IMPORT TABLE managedpartitioned_importedrelo FROM '/export/managedpartitioned' LOCATION '/relo/managedpartitioned';</code>

<code>IMPORT EXTERNAL TABLE managedpartitioned_importedrelo FROM '/export/managedpartitioned' LOCATION '/relo/managedpartitioned';</code></blockquote>
As with the previous statements, the path associated with the LOCATION clause should not exist prior to the import (or if it does, it should be empty).

When using LOCATION with a partition import, the path given should reference the path for the table and not the partition itself. The IMPORT command will create the partition folder automatically under this path:
<blockquote><code>IMPORT TABLE managedpartitioned_importedrelo PARTITION (partition1='A') FROM '/export/managedpartitioned/a' LOCATION '/relo/managedpartitioned_importedrelo';
IMPORT TABLE managedpartitioned_importedrelo PARTITION (partition1='B') FROM '/export/managedpartitioned/b' LOCATION '/relo/managedpartitioned_importedrelo';
</code></blockquote>
<strong>Hive IMPORT and EXPORT with the WASB File System</strong>

In the previous examples, directory path information could be provided using either a relative path or a fully-qualified path. When using the WASB file system, you must provide paths to the IMPORT and EXPORT command using the fully-qualified syntax only. If you do not, the Hive IMPORT and EXPORT commands will hang. Assuming you are using an Azure Storage account named <em>mystorageaccount </em>and a container named <em>mycontainer</em> for your WASB file system, a proper EXPORT and IMPORT command would look like this:
<blockquote><code>EXPORT TABLE ManagedUnpartitioned TO 'wasb://mycontainer@mystorageaccount.blob.core.windows.net/export/managedunpartitioned';</code>

<code>IMPORT TABLE ManagedUnpartitioned_imported FROM 'wasb://mycontainer@mystorageaccount.blob.core.windows.net/export/managedunpartitioned';</code></blockquote>
Surprisingly, when performing an IMPORT with the LOCATION clause, the path associated with the LOCATION clause does not need to be fully-qualified:
<blockquote><code>IMPORT TABLE ManagedUnpartitioned_imported FROM 'wasb://mycontainer@mystorageaccount.blob.core.windows.net/export/managedunpartitioned' LOCATION '/relo/managedunpartitioned_importedrelo';
</code></blockquote>]]></content:encoded>
    </item>
    <item>
      <title>Pushing Data from a Hortonworks Cluster to an Azure HDInsight Cluster</title>
      <link>https://docs.microsoft.com/archive/blogs/data_otaku/pushing-data-from-a-hortonworks-cluster-to-an-azure-hdinsight-cluster</link>
      <pubDate>Fri, 09 Dec 2016 21:00:20 GMT</pubDate>
      <dc:creator><![CDATA[Bryan C Smith]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/data_otaku/?p=1465</guid>
      <description><![CDATA[I have a scenario where a customer wishes to explore a move from an existing Hortonworks (HDP)...]]></description>
      <content:encoded><![CDATA[I have a scenario where a customer wishes to explore a move from an existing Hortonworks (HDP) cluster to an Azure HDInsight (HDI) cluster. The customer is interested in the lower administrative overhead of HDInsight’s Platform-as-a-Service offering as well as the ability to scale-out and scale-back cluster resources to match demand, something that’s challenging to do with the current architecture.

The first step in this is to move a subset of the HDP cluster’s data to the HDI environment to enable testing. Assuming none of the files I wish to transfer are individually over <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Understanding-Block-Blobs--Append-Blobs--and-Page-Blobs?redirectedfrom=MSDN#about-block-blobs">195 GB in size</a>, I can move this data into an Azure Storage account ahead of any HDI cluster provisioning. Once I’m ready for the HDI cluster, it’s a simple matter to <a href="https://docs.microsoft.com/en-us/azure/hdinsight/hdinsight-hadoop-provision-linux-clusters#basic-configuration-options">provision the cluster against the Azure Storage account</a> (which HDI will then interpret <a href="https://docs.microsoft.com/en-us/azure/hdinsight/hdinsight-hadoop-use-blob-storage">as part of its file system</a>).

To get started, I <a href="https://docs.microsoft.com/en-us/azure/storage/storage-create-storage-account">provision an Azure (Resource Manager) Storage account</a> in the Azure region (location) within which I intend to deploy the eventual HDI cluster. Once provisioned, I create a container within the Storage account to serve as the destination for the files from the HDP cluster. I record the Storage account and container names as well as <a href="https://docs.microsoft.com/en-us/azure/storage/storage-create-storage-account#manage-your-storage-account">one of the Account keys</a>.

<strong>NOTE</strong> If using the Azure Portal to create your Storage account, you can create a container by clicking on the Blobs item on the default Storage Account tile. In the resulting page, click the +Add item and provide the container a name. Leave the container configured as Private unless you have a reason to do otherwise.

With the target Storage account in place, I can now configure the HDP cluster to connect to it. The easiest way I have found to do this is using Ambari:
<ol>
 	<li>Login to the Ambari portal and navigate to the (default) dashboard</li>
 	<li>Select <em>HDFS </em>from the left-hand navigation</li>
 	<li>On the resulting HDFS page, click on the <em>Configs</em> tab</li>
 	<li>On the resulting page, select the <em>Advanced</em> option</li>
 	<li>Scroll down and expand the <em>Custom core-site</em> node</li>
 	<li>Select <em>Add Property…</em> from the bottom of the expanded node</li>
 	<li>Enter <em>fs.azure.account.key.&lt;account name&gt;.blob.core.windows.net</em>, substituting the name of the Storage account for <em>&lt;account name&gt;</em>, as the <em>Name </em>of the property</li>
 	<li>Enter the Account key as the <em>Value </em>of the property</li>
 	<li>Click the <em>Add</em> button and verify the new property appears under the <em>Custom core-site</em> node</li>
 	<li>Locate the notification bar at the top of the current page</li>
 	<li>Click the <em>Save</em> button on the notification bar to push changes to the cluster</li>
 	<li>Follow any remaining prompts to complete the Save process</li>
</ol>
Once the Save process is completed, Ambari will indicate a restart of some services is required. Click the <em>Restart</em> button and <em>Restart All Affected</em> from the resulting drop-down. Follow any remaining prompts and monitor the process until it is successfully completed.

Now that the HDP cluster is configured to speak to the Azure Storage, it can be referenced using the WASB (unencrypted) or WASBS (encrypted) protocol. The syntax for Azure Storage reference under these protocols is:
<ul>
 	<li>wasb://<em>&lt;container name&gt;</em>@<em>&lt;account name&gt;</em>.blob.core.windows.net/</li>
 	<li>wasbs://<em>&lt;container name&gt;</em>@<em>&lt;account name&gt;</em>.blob.core.windows.net/</li>
</ul>
where <em>&lt;container name&gt;</em> is the name of the container you previously created and <em>&lt;account name&gt;</em> is the name of the Storage account. More information on these protocols is found <a href="https://hadoop.apache.org/docs/r2.7.1/hadoop-azure/index.html">here</a>.

By opening an SSH connection on the name node of the HDP cluster, I can now use <a href="https://hadoop.apache.org/docs/r1.2.1/distcp.html">DISTCP</a> to copy files from HDFS to the Azure Storage account:

<span style="font-family: Courier">     hadoop distcp /mydir/mysubdir/ wasbs://mycontainer@mystorageaccount.blob.core.windows.net/mydir/mysubdir/</span>

<strong>NOTE</strong> Should I need to transfer Hive or HBase tables, I can export these using their associated EXPORT commands – <a href="https://cwiki.apache.org/confluence/display/Hive/LanguageManual+ImportExport">Hive EXPORT</a> and <a href="https://hbase.apache.org/book.html#tools">HBase EXPORT</a>. (For the HBase Export, scroll to item 131.9 in the referenced document.) HBase provides still more options, <em>e.g.</em> <a href="https://hbase.apache.org/book.html#snapshots_azure">ExportSnapshot</a> and <a href="https://hbase.apache.org/book.html#copy.table">CopyTable</a>.

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>Goofing around with the Cognitive Services Translator API</title>
      <link>https://docs.microsoft.com/archive/blogs/data_otaku/goofing-around-with-the-cognitive-services-translator-api</link>
      <pubDate>Sat, 05 Nov 2016 18:17:01 GMT</pubDate>
      <dc:creator><![CDATA[Bryan C Smith]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/data_otaku/?p=1355</guid>
      <description><![CDATA[Recently, I was asked to get familiar with the Translator API in Azure Cognitive Services.  Not...]]></description>
      <content:encoded><![CDATA[Recently, I was asked to get familiar with the Translator API in Azure Cognitive Services.  Not being a developer, I was a little leery of how I might approach this, but I was surprise how easy it was to tap into this functionality.  I thought I would share some of what I learned here in hopes that someone else tasked with using this or one of the other APIs in the Cognitive Services suite might find this helpful.  That said, what I've built here is in no way intended to represent a solution to a business problem; treat it as nothing more than a technical demonstration.

<strong>Deploying the API</strong>

The Translator API is part of a massive infrastructure managed by the Bing team.  In Azure, you provision a key which grants you access to this API.  This is done by logging into the Azure Portal (<a target="_blank" href="https://portal.azure.com">https://portal.azure.com</a>), clicking <strong>+New</strong> from the left-hand navigation, entering <em>Cognitive Services</em> in the resulting search, and selecting <strong>Cognitive Services APIs</strong> from the results.

<a href="https://msdnshared.blob.core.windows.net/media/2016/11/2016-11-05_9-59-28-e1478358049764.png"><img width="750" height="364" class="alignnone size-full wp-image-1365" alt="2016-11-05_9-59-28" src="https://msdnshared.blob.core.windows.net/media/2016/11/2016-11-05_9-59-28-e1478358049764.png" /></a>

Clicking the <strong>Create</strong> button takes you to a form where you enter a unique <strong>Account name</strong> and select your <strong>Subscription</strong>.  When you click <strong>API type</strong>, you're presented with a list of the available APIs in the Cognitive Services suite. Choose <strong>Translator Text API </strong>from this list. For <strong>Pricing Tier</strong>, you have several options to choose from, but if you're just playing around, the <em>Free tier</em> gives you a lot of mileage.  For <strong>Resource Group</strong>, you can choose to <em>Create new</em> or<em> Use Existing</em> and then choose a <strong>Location</strong> for the deployment.  Clicking <strong>Create</strong> deploys the API to your tenant.

<a href="https://msdnshared.blob.core.windows.net/media/2016/11/overview-e1478359300316.png"><img width="750" height="630" class="alignnone size-full wp-image-1375" alt="overview" src="https://msdnshared.blob.core.windows.net/media/2016/11/overview-e1478359300316.png" /></a>

Once provisioned, access the resource from within the Azure Portal. Click on the Keys item in the left-hand navigation of its default tile.  Copy one of the two keys to your clipboard for use in next steps.

<strong>Understanding the API Methods</strong>

The Translator API you've deployed supports a wide range of methods.  These are documented at the bottom of <a target="_blank" href="https://docs.microsofttranslator.com/text-translate.html">this page</a>.

As you explore each method, note whether it is employed via GET or POST.  When you expand the documentation of a method, notice the Response Content Type (identified just above the Parameters list) which typically indicates the method will return data as XML. In the Parameters list, take note of the various parameters, many of which are required, and their type.  Most parameters will be submitted via query string but most methods will require an Authorization parameter in the request header. (You can ignore the <em>required</em> appid parameter if you are submitting an Authorization parameter as shown below.)

<a href="https://msdnshared.blob.core.windows.net/media/2016/11/methods-e1478359944733.png"><img width="750" height="896" class="alignnone size-full wp-image-1385" alt="methods" src="https://msdnshared.blob.core.windows.net/media/2016/11/methods-e1478359944733.png" /></a>

<strong>Calling the Translate Method using PowerShell</strong>

At this point, I suspect the mechanics of the method call are a bit fuzzy.  Hopefully, jumping into some code will solve this problem.

To do this, let's use PowerShell.  Why PowerShell?  I can't imagine too many scenarios where PowerShell is the right solution for making calls to the Cognitive APIs, but I like the interactive nature of the PowerShell environment.  Instead of throwing all the demo code at you at once, I'll show it off in waves and PowerShell will allow you a bit of flexibility to play with it before moving on to the next step.

Launching PowerShell,  enter the following code:
<code>
$accountKey = "&lt;insert the key you copied to your clipboard here&gt;"
$tokenServiceURL = "https://api.cognitive.microsoft.com/sts/v1.0/issueToken"
$query = "?Subscription-Key=$accountKey"
$uri = $tokenServiceUrl+$query</code>
<code>
$token = Invoke-RestMethod -Uri $uri -Method Post
$token</code>

Running this, you should see a long token value returned from the API.

<a href="https://msdnshared.blob.core.windows.net/media/2016/11/psq-e1478361061371.png"><img width="750" height="168" class="alignnone size-full wp-image-1405" alt="psq" src="https://msdnshared.blob.core.windows.net/media/2016/11/psq-e1478361061371.png" /></a>

With this token value, you can now assemble the header for your requests:
<code>
$auth = "Bearer "+$token
$header = @{Authorization = $auth}
$header
</code>

<a href="https://msdnshared.blob.core.windows.net/media/2016/11/header.png"><img width="750" height="182" class="alignnone size-full wp-image-1415" alt="header" src="https://msdnshared.blob.core.windows.net/media/2016/11/header-e1478368684517.png" /></a>

<strong>NOTE</strong> If you receive an error your token has expired, be sure to rerun the previous code to retrieve a new token and assemble a new header.  The token expires every 10 minutes.

To translate text, the Translate method requires the text you want to translate as well as a code that indicates the language you want to translate it to.  Similarly, you can tell the method the original language in the submitted text but this is optional as you can opt to have the API determine the original language for you.  There is a method to return the list of language codes but I find its easier to just look them up <a target="_blank" href="https://msdn.microsoft.com/en-us/library/hh456380.aspx">here</a>.
<code>
$fromLang = "en" #English
$toLang = "es"   #Spanish
$text = Read-Host "Enter text to translate"
</code>
<a href="https://msdnshared.blob.core.windows.net/media/2016/11/mytext-e1478369032374.png"><img width="750" height="82" class="alignnone size-full wp-image-1425" alt="mytext" src="https://msdnshared.blob.core.windows.net/media/2016/11/mytext-e1478369032374.png" /></a>

Using this information, you now can call the method.  The text submitted needs to be encoded and the method needs to be called with the GET verb:
<code>
$translationURL = "https://api.microsofttranslator.com/v2/Http.svc/Translate"
$query = "?text=" + [System.Web.HttpUtility]::UrlEncode($text)
$query += "&amp;from=" + $fromLang
$query += "&amp;to=" + $toLang
$query += "&amp;contentType=text/plain"
$uri = $translationUrl+$query
</code><code>
$ret = Invoke-RestMethod -Uri $uri -Method Get -Headers $header
</code>
The $ret variable contains the XML output from the API service. PowerShell hands XML using the .<a target="_blank" href="https://msdn.microsoft.com/en-us/library/system.xml.xmldocument(v=vs.110).aspx">NET System.Xml.XmlDocument class</a> which exposes a bunch of properties and methods for extracting information from the XML document.  If you'd like to see the XML in its raw form, you can make use the OuterXML property on the variable:
<code>
$ret.OuterXml
</code>
<a href="https://msdnshared.blob.core.windows.net/media/2016/11/outerxml-e1478369433333.png"><img width="743" height="42" class="alignnone size-full wp-image-1445" alt="outerxml" src="https://msdnshared.blob.core.windows.net/media/2016/11/outerxml-e1478369433333.png" /></a>

You can see from this that the XML contains a single node named string.  To access the value assigned to this node, i.e. the translated text, you can reference it using:
<code>
$ret.string.'#text'
</code>
<a href="https://msdnshared.blob.core.windows.net/media/2016/11/string-e1478369551644.png"><img width="750" height="46" class="alignnone size-full wp-image-1455" alt="string" src="https://msdnshared.blob.core.windows.net/media/2016/11/string-e1478369551644.png" /></a>

<strong>Conclusion</strong>

As I said at the top, this isn't supposed to represent a solution but instead provide a way to explore this API.  I hope this has been useful to those of you interested in working with Cognitive Services.

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>A Fixed-Width Extractor for Azure Data Lake Analytics</title>
      <link>https://docs.microsoft.com/archive/blogs/data_otaku/a-fixed-width-extractor-for-azure-data-lake-analytics</link>
      <pubDate>Thu, 27 Oct 2016 22:18:15 GMT</pubDate>
      <dc:creator><![CDATA[Bryan C Smith]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/data_otaku/?p=1095</guid>
      <description><![CDATA[I have a fixed-width text file I'd like to use with Azure Data Lake Analytics (ADLA). ADLA reads...]]></description>
      <content:encoded><![CDATA[I have a fixed-width text file I'd like to use with <a href="https://azure.microsoft.com/en-us/documentation/services/data-lake-analytics/">Azure Data Lake Analytics</a> (ADLA). ADLA reads files using extractors.  As of today, ADLA comes out of the box with three extractors: one for comma-delimited text, another for tab-delimited text and a general purpose extractor for  delimited text.   <a href="https://github.com/Azure/usql">Examples on GitHub </a>demonstrate the mechanics for writing custom ADLA extractors and include working versions of JSON and XML extractors. Using these examples as a starting point, I've managed to hack together a custom extractor for fixed-width text files. I'll use this post to walk you through the code I've assembled in hopes that this helps someone else who needs to build a custom ADLA extractor of their own.

First, I'll walk you through my scenario in a bit more detail. I've setup an ADLA account (called <em>brysmi</em>) and have associated it with an Azure Data Lake Store (ADLS) account (also called <em>brysmi</em>). In the ADLS account, I've created a folder off the root called fixedwidth and in it I've placed my fixed-width file. (Details on creating an ADLS account and loading files to it are found <a href="https://azure.microsoft.com/en-us/documentation/articles/data-lake-store-get-started-portal/">here</a>.)  This file (<a href="https://msdnshared.blob.core.windows.net/media/2016/10/input.txt">downloadable here</a>) consists of three lines of fixed width text:
<pre><span style="font-family: Courier">   12345678
   12345678
   12345678</span></pre>
Admittedly, this isn't the most exciting file out there but the simple structure allows me to very easily verify my extractor is working correctly.  To do that, I need to submit a U-SQL script to ADLA which calls up my custom extractor, telling it how to read the fixed-width lines into columns, and then send that data to an output file using a delimited format.  The U-SQL code I'll use looks like this:
<pre><span style="font-family: Courier">   USE DATABASE [master];
   REFERENCE ASSEMBLY [FixedWidthExtractor];</span>
<span style="font-family: Courier">   @tbl =
      EXTRACT
         col1 int,
         col2 float,
         col3 decimal,
         col4 byte[]
      FROM "/fixedwidth/input.txt"
      USING new MyCustomExtractors.FixedWidthExtractor (
         new SQL.MAP&lt;string, string&gt; {
            {"col1","3"},
            {"col2","2"},
            {"col3","1"},
            {"col4","2"}
         }
      );</span>
<span style="font-family: Courier">   OUTPUT @tbl
   TO "/fixedwidth/output.txt"
   USING Outputters.Csv();
</span></pre>
The first line of this script tells ADLA to USE objects referenced in its master database.  You can think of this as a structure for keeping logical reference to objects in the ADLA/ADLS environment. In that master database, I've registered an assembly (DLL) which holds the extractor code - I'll cover that part towards the bottom of this post - so that the REFERENCE statement points ADLA at that registered object.

In the next block, I tell ADLA to extract four fields FROM my input file USING an extractor class that's defined in my referenced assembly. To that class, I am passing a parameter typed as an ADLA type called a SQL.MAP which holds ordered key-value pairs identifying the columns to read from a line of text and the number of characters associated with each.  Note that the columns in map are ordered the same as those in the EXTRACT statement's column list.  Note also that column widths are passed in as strings; while SQL.MAP appears to accept integer values for the value part of its pairs, passing anything other than a string was causing errors.

In the last block of U-SQL script, I have ADLA send the parsed data to a comma-delimited text file.  Nothing fancy here; I just want to see how the parsing worked.

If all that made sense, then let's <a href="https://msdnshared.blob.core.windows.net/media/2016/10/fixedwidthextractor.zip">take a look at the code for the assembly</a>.  I've written this using the<a href="https://www.microsoft.com/en-us/download/details.aspx?id=49504"> Azure Data Lake Tools for Visual Studio 2015</a>, leveraging the Class Library (For U-SQL Application) project type:

<a href="https://msdnshared.blob.core.windows.net/media/2016/10/class-library-for-u-sql-app-project-type-e1477601842971.png"><img width="600" height="416" class="alignnone size-full wp-image-1105" alt="class library for u-sql app project type" src="https://msdnshared.blob.core.windows.net/media/2016/10/class-library-for-u-sql-app-project-type-e1477601842971.png" /></a><a href="https://msdnshared.blob.core.windows.net/media/2016/10/class-library-for-u-sql-app-project-type-e1477601793951.png"></a>

Before going any further, I need to point out that I am not even a mediocre C# developer.  I'm going to screw up terminology and probably point out things that no true developer would either need pointing out or find interesting.  And, yes, there are probably a bazzilion ways the code could be written more efficiently. With that out of the way, let's proceed.

The first thing I want to point out is the namespace and class definitions. It's important to note these because when you call the extractor in the EXTRACT statement, you need to instantiate the extractor using the new keyword followed by namespace.class.
<pre><span style="font-family: Courier">   namespace MyCustomExtractors
   {
      public class FixedWidthExtractor : IExtractor
      {
         … //coding magic happens here
      }
   }</span></pre>
Under the class definition, I define the method by which the class is instantiated.  I have three parameters, two of which are optional.  The optional parameters, encoding and row_delim, are used to identify the encoding and row-delimiter for the text file.  These receive default values of UTF8 and carriage-return+line-feed if not otherwise specified. The SqlMap, <em>i.e.</em> col_widths, is the only required parameter and hopefully it is pretty well understood given the explanation earlier in this post.
<pre><span style="font-family: Courier">   public FixedWidthExtractor(SqlMap&lt;string, string&gt; col_widths, Encoding encoding = null, string row_delim = "\r\n")
   {
      this._encoding = ((encoding == null) ? Encoding.UTF8 : encoding);
      this._row_delim = this._encoding.GetBytes(row_delim);
      this._col_widths = col_widths;
   }</span></pre>
The class we're defining implements the IExtractor interface.  As such it exposes an IEnumerable method that ADLA depends upon to send file contents to it and receive structured data back.  By overriding this method, we now have the means to insert the custom logic needed to process the fixed-width file.
<pre><span style="font-family: Courier">   public override IEnumerable&lt;IRow&gt; Extract(IUnstructuredReader input, IUpdatableRow output)
   {
      ... //more code magic here
   }</span></pre>
The input variable passed into IEnumerable by ADLA is a reference to our input file.  To read data from that file, a stream is opened and information passing over that stream is split using our specified row-delimiter.  Each row is interpreted as a string using the specified encoding and assigned to the variable line.
<pre><span style="font-family: Courier">   public override IEnumerable&lt;IRow&gt; Extract(IUnstructuredReader input, IUpdatableRow output)
   {
      foreach (Stream currentline in input.Split(this._row_delim))
      {
         using (StreamReader lineReader = new StreamReader(currentline, this._encoding))
         {
            string line = lineReader.ReadToEnd();
            … //the good stuff is coming here
         }
      }
   }</span></pre>
With a line of text now assigned to the line variable, it can be parsed into columns.  Iterating over the SqlMap holding the column names and widths, each column is parsed from the line.  Based on data types read from the column-list in the U-SQL EXTRACT statement and imposed on the output object passed to the IEnumerable method by ADLA, strings parsed from the line are cast to appropriate types and assigned to the output.  Once the last column is parsed, the row in the output object is made read-only, freeing it to send data to ADLA.
<pre><span style="font-family: Courier">   //read new line of input
   int start_parse = 0;</span>
<span style="font-family: Courier">
   //for each column
   int i = 0;
   foreach (var col_width in this._col_widths)
   {
      //read chars associated with fixed-width column
      int chars_to_read = int.Parse(col_width.Value);
      string value = line.Substring(start_parse, chars_to_read);
</span>
<span style="font-family: Courier">
      //assign value to output (w/ appropriate type)
      switch (output.Schema[i].Type.Name)
      {
         case ("String"):
            output.Set(i, value);
            break;
         case ("Int32"):
            output.Set(i, Int32.Parse(value));
            break;
         ... //shortened for readability
         default:
            throw (new Exception("Unknown data type specified: "+output.Schema[i].Type.Name));
      }</span>
<span style="font-family: Courier">
      //move to start of next column
      start_parse += chars_to_read;
      i++;
   }
</span>
<span style="font-family: Courier">
   //send output
   yield return output.AsReadOnly();</span></pre>
With all the needed code in place, I build the project as I would any other.  Now I come to the challenge of registering it with ADLA.  To do this, I open Server Explorer in Visual Studio (found under the View menu if not already visible). I connect to my Azure account, expand the Data Lake Analytics node, and locate the ADLA account I've already created.

<a href="https://msdnshared.blob.core.windows.net/media/2016/10/server-explorer-adla-e1477603954648.png"><img width="300" height="209" class="alignnone size-medium wp-image-1125" alt="server explorer adla" src="https://msdnshared.blob.core.windows.net/media/2016/10/server-explorer-adla-300x209.png" /></a>

Expanding the ADLA account and then the U-SQL Databases node under it, I come to the master database. Expanding that database, I find a folder for Assemblies.  Right-clicking that folder and selecting Register Assembly, I launch a dialog for registering the assembly I just compiled.

<a href="https://msdnshared.blob.core.windows.net/media/2016/10/assembly-registration-e1477604202109.png"><img width="600" height="350" class="alignnone size-full wp-image-1135" alt="assembly registration" src="https://msdnshared.blob.core.windows.net/media/2016/10/assembly-registration-e1477604202109.png" /></a>

In the dialog, I click on the button next to the Load assembly from path: textbox.  In the pop-up, I select local click on the associated button to locate the DLL compiled with my project.  By default, this will be under the bin\Debug subfolder under the project folder. The .pdb file as well as the Microsoft.Analytics.Interfaces.dll and Microsoft.Analytics.Types.dll files are ignored.

Once the file is located, I click OK to return to the original dialog and then Submit to upload the assembly and register it with ADLA.  The process takes a little time to complete but I encounter a Job View screen displaying all green marks when successfully completed.

<a href="https://msdnshared.blob.core.windows.net/media/2016/10/job-view-e1477604679481.png"><img width="750" height="525" class="alignnone size-full wp-image-1136" alt="job view" src="https://msdnshared.blob.core.windows.net/media/2016/10/job-view-e1477604679481.png" /></a>

Refreshing and then expanding the Assemblies folder in Server Explorer, I now see the assembly registered.  Note the name as displayed in Server Explorer as this is the name used in the REFERENCE ASSEMBLY U-SQL statement.

<a href="https://msdnshared.blob.core.windows.net/media/2016/10/registered-e1477604868593.png"><img width="300" height="206" class="alignnone size-medium wp-image-1145" alt="registered" src="https://msdnshared.blob.core.windows.net/media/2016/10/registered-300x206.png" /></a>

At this point, everything is in place for a test run of the extractor.  To run the U-SQL script leveraging the Visual Studio interface, I simply create a new U-SQL project.  In the U-SQL Script view, I double check the script is pointed to my ADLA account (as highlighted in the image below) and click Submit.

<a href="https://msdnshared.blob.core.windows.net/media/2016/10/script-view-e1477605167765.png"><img width="500" height="429" class="alignnone wp-image-1155" alt="script view" src="https://msdnshared.blob.core.windows.net/media/2016/10/script-view-e1477605167765.png" /></a>

As before, I receive a Job View screen but this one will display a bit more info.  Once successfully completed, I use the Server Explorer to return to my ADLA account. I expand the Storage Accounts item, right-click the ADLS storage account and select Explorer. In the Explorer window, I navigate to the folder holding the output file. Right-clicking the output file and selecting Preview opens a window where I can see the file is parsed per my instructions.  (Note, I asked the extractor to parse the last two characters in the string as a byte array. Converting this back to text results in the characters shown in the screenshot below.)

<a href="https://msdnshared.blob.core.windows.net/media/2016/10/preview-e1477605675877.png"><img width="750" height="291" class="alignnone size-full wp-image-1165" alt="preview" src="https://msdnshared.blob.core.windows.net/media/2016/10/preview-e1477605675877.png" /></a>rere]]></content:encoded>
    </item>
    <item>
      <title>Data Sources for Business Analysts</title>
      <link>https://docs.microsoft.com/archive/blogs/data_otaku/data-sets-for-business-analysts</link>
      <pubDate>Mon, 04 Jul 2016 17:30:43 GMT</pubDate>
      <dc:creator><![CDATA[Bryan C Smith]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/data_otaku/?p=1086</guid>
      <description><![CDATA[I commonly get asked where to find data.  For most Business Analysts, your best data sources are...]]></description>
      <content:encoded><![CDATA[I commonly get asked where to find data.  For most Business Analysts, your best data sources are found within your enterprise,<em> i.e.</em> operational data in operational databases ideally transformed for easier, more consistent consumption in a data mart or data warehouse and exhaust data housed in a data lake. Locating these data can be tricky but ask around and you should be able to identify where this data resides and start the process of obtaining access to it.

Outside your enterprise, there exists numerous sources of potentially useful data.  Some of this is free but the best stuff is not.  In this post, I want to compile a list of potentially useful data sources based on my personal and therefore limited experience.  If you have suggestions for other useful data sources, please leave a comment and I will consider adding it here.

That said, I am not interested in academic data sets, <em>e.g. </em><a href="https://archive.ics.uci.edu/ml/datasets/Iris/">iris petal and sepal lengths</a>, which are commonly used by analysts to learn analytic techniques.  Most analytics tools will provide you quick access to this stuff.  I want to limit this list to data you might actually use to answer a business question.

<strong>Syndicated Data</strong>

Let's start with the for-fee data sets.  These are typically built using a combination of public and proprietary data.  These data sets are curated by private companies.  There quality is generally good but they may include derived information, <a href="https://www.thewrap.com/nbc-research-boss-endorses-3-nielsen-ratings-alternatives/">the accuracy or applicability of which may be questioned</a>.  Still, these tend to be some of the best quality data sets available to analysts:
<ul>
 	<li><a href="https://www.nielsen.com/us/en/solutions.html">Nielsen</a> (Consumer Data)</li>
 	<li><a href="https://www.iriworldwide.com/en-US/solutions/home">IRI</a> (Consumer Data)</li>
 	<li><a href="https://www.acxiom.com/data-packages/">Acxiom</a> (Consumer &amp; Online Data)</li>
 	<li><a href="https://bluekai.com/registry/index.php">BlueKai</a> (Online Data)</li>
 	<li><a href="https://www.melissadata.com/">Melissa Data</a> (Consumer Data)</li>
 	<li><a href="https://www.dnb.com">Dun &amp; Bradstreet</a> (Business Data)</li>
 	<li><a href="https://www.onterrasystems.com/">OnTerra Systems </a>(Geographical Data)</li>
 	<li><a href="https://data.trimble.com/market/">Trimble</a> (Geographical Data)</li>
</ul>
This list is very small compared to what is actually available.  More and more businesses are starting to deliver their data as a service to businesses.  You may find that searching the websites of companies you suspect may have interesting data can help you uncover quite a number of resources.

<strong>Web &amp; Social Media Data</strong>

Many web and social media sites provide APIs, against which developers may build a variety of applications.  <a href="https://www.programmableweb.com/apis">The Programmable Web </a>provides a registry for a very large number of these APIs though you might try trying to connect directly to the documentation for these by going to <em>dev.</em>, <em>developer.</em> or <em>developers.</em> and then the website name; this is kind of the normal location of this kind of thing. (The <a href="https://business.pinterest.com/en">Pintrest API</a>, frustratingly doesn't follow this norm and is not registered with the Programmable Web.)

Using these APIs can get very technical for most Business Analysts so that you might need to engage a developer to help you retrieve the data you are looking for.  Also, READ THE TERMS AND CONDITIONS associated with these APIs very carefully to ensure that how you are using the API and the extracted data is appropriate.  <a href="https://www.informationweek.com/software/social/linkedin-sues-after-scraping-of-user-data/d/d-id/1113362">Folks have been known to sue </a>when this data isn't used appropriately.

Finally, be aware that organizations throttle the use of their APIs.  You need to be mindful of this lest you find yourself locked out.  If you to consume data at a faster rate than the API can support, dig around to see if you can find a clearinghouse associated with the site.  Twitter for example provides full content access through <a href="https://gnip.com/">GNip</a> (for a fee).

<strong>Open Data</strong>

More and more government agencies are providing access to their data in the interest of transparency. In the US, <a href="https://www.data.gov/">data.gov </a>provides a clearing house for many of these data sets.  Other jurisdictions may post their data to similarly named web sites, e.g. the State of Texas provides data access at <a href="https://data.texas.gov/">data.texas.gov.</a>  Still others bury their data in poorly designed government websites.  The <a href="https://www.socrata.com/">Socrata web site </a>provides a good starting point for locating these data.

Returning to the US federal-level, I have found that going to specific agency websites to locate data can be easier than using data.gov which often has a bunch of broken links.  Specific sites I have found helpful include:
<ul>
 	<li><a href="https://www.census.gov/data.html">US Census Data</a></li>
 	<li><a href="https://www.irs.gov/uac/tax-stats-2">US Internal Revenue Service</a></li>
 	<li><a href="https://export.gov">US Dept of Commerce, International Trade Administration</a></li>
</ul>
Before focusing too much time on government data, please keep in mind that these data sets do not typically contain granular information.  Instead, these data sets often represent summarized subsets of data as found in a specific report.  The structure of these data can also be a bit jacked-up as in "who in their right mind would organize a data set like this?!" Complex data formats such as XML and JSON may also be problematic. Quite often, you will find yourself exerting a lot of effort to prep this data in order to answer a limited number of questions.  You've been warned :-)

<strong>Online Marketplaces</strong>

A number of cloud service providers are attempting to solve the problem of finding both public and syndicated data.  To this end, they have setup marketplaces where you can search for, consume and, if needed, pay for data access in a more consistent manner. Here are a few of these:
<ul>
 	<li><a href="https://datamarket.azure.com/browse/Data">Azure Data Marketplace</a></li>
 	<li><a href="https://aws.amazon.com/public-data-sets/">AWS Public Data Sets</a></li>
 	<li><a href="https://www.google.com/publicdata/directory">Google Public Data</a></li>
 	<li><a href="https://www.qlik.com/products/qlik-data-market">Qlik Data Market</a></li>
</ul>
<strong>Other Interesting Places to Look for Data</strong>

Most of the data sets I've listed in this post are data sets you could see yourself going back to on a regular basis.  Every now and then, you just need an interesting one-off data set in order to demonstrate a concept or prove a point. I have found these to be interesting starting points for finding this kind of data:
<ul>
 	<li><a href="https://www.reddit.com/r/dataisbeautiful/">Reddit, Data is Beautiful</a></li>
 	<li><a href="https://www.kdnuggets.com/datasets/kddcup.html">KDD Cup Datasets</a></li>
 	<li><a href="https://www.kaggle.com/datasets">Kaggle</a></li>
 	<li><a href="https://github.com/datasets">Open Data Sets on GitHub</a></li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title>Split a Large Row-Formatted Text File using PowerShell</title>
      <link>https://docs.microsoft.com/archive/blogs/data_otaku/split-a-large-row-formatted-text-file-using-powershell</link>
      <pubDate>Fri, 24 Jun 2016 14:43:18 GMT</pubDate>
      <dc:creator><![CDATA[Bryan C Smith]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/data_otaku/?p=1045</guid>
      <description><![CDATA[I move a lot of large, row-formatted text files into Azure Storage for work I do with HDInsight and...]]></description>
      <content:encoded><![CDATA[I move a lot of large, row-formatted text files into Azure Storage for work I do with HDInsight and other technologies.  I land these files as block blobs which means that individual files must stick below the 200 GB block blob size limit.  Azure Data Lake Store does not have this limitation and most of the technologies I work with support compression, but let's assume these are not options for the specific work I want to perform.

The way I typically work around this limitation is to split my files into smaller files that are below the max allowed size. There are several freely available file splitters on the Internet for this but they typically split the files based on a number of bytes which means that the split will likely occur in the middle of a row.  As these splitters do not recognize rows, they also do not recognize that my file may have a header which I would like replicated in the output files. For these reasons, I wrote a <a href="https://msdnshared.blob.core.windows.net/media/2016/06/splitfile.zip">custom script in PowerShell to split my files</a>.

The script requires you to target an input file, define a max number of bytes per output file, and identify whether the file has a header row. It then reads the input file line by line and writes these to a series of output files, preserving the original order of the rows, and making sure each file stays below the specified max size. If a header row is present, it is written to each output file.  The encoding of the original file is detected and preserved in the output files.

There are two things I'd like to add to the script but will simply need to return to at a latter date.  First, I'd like the script to detect the row-delimiter used by the original text file and then use this in the output files. As it currently stands, you must tell the script what row-delimiter to use in your output files.  Second, I'd like the script to assess the size of the original file and attempt to create output files that are more consistently sized.  This one is tricky if your rows are highly variable in size so I decided to keep the script simple for now.

One other thing about the script .... Because I am using this with very large files, I made use of the .NET Framework to read and write lines of data. If you will do a quick search, you'll find a ton of posts showing the performance impact of this.  I found in some simple testing that the performance benefits of using the .NET Framework instead of the built-in PowerShell commandlets,<em> i.e.</em> Get-Content, Set-Content &amp; Add-Content, were tremendous.  .NET adds complexity but it was totally worth it in this scenario.]]></content:encoded>
    </item>
    <item>
      <title>Configuration of HBase on Azure HDInsight as a Drill Data Source</title>
      <link>https://docs.microsoft.com/archive/blogs/data_otaku/configuration-of-hbase-on-azure-hdinsight-as-a-drill-data-source</link>
      <pubDate>Mon, 20 Jun 2016 20:34:36 GMT</pubDate>
      <dc:creator><![CDATA[Bryan C Smith]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/data_otaku/?p=1015</guid>
      <description><![CDATA[NOTE This post is part of a series on a deployment of Apache Drill on the Azure cloud.

In a...]]></description>
      <content:encoded><![CDATA[NOTE This post is part of a series on a <a title="Series" href="https://blogs.msdn.microsoft.com/data_otaku/2016/05/27/deploying-apache-drill-on-azure/"><span style="color: #337ab7">deployment of Apache Drill on the Azure cloud</span></a>.

In a <a href="https://blogs.msdn.microsoft.com/data_otaku/2016/06/13/configuration-of-hive-on-azure-hdinsight-as-a-drill-data-source/">previous post</a>, I showed how to connect my Azure-deployed Drill cluster to an Azure HDInsight (Hadoop) cluster via Hive.  Azure HDInsight also supports HBase.  In this post, I want to tackle how to get the Drill cluster to use Azure HDInsight HBase as its data source.

The <a href="https://drill.apache.org/docs/hbase-storage-plugin/">Drill documentation on this topic </a>is pretty straightforward. The two big wrinkles I need to work through are (1) that the ZooKeeper nodes and Thrift interface they direct Drill to are not accessible outside the Virtual Network that houses my Hadoop cluster and (2) Azure HDInsight (like the Hortonworks Data Platform its based on) does not use the default znode for HBase.  With that in mind, I'll connect my Drill cluster to HBase through the following steps:
<ol>
	<li>Deploy Azure HDInsight cluster to a Virtual Network accessible by Drill</li>
	<li>Configure HBase access within Drill</li>
	<li>Confirm HBase access within Drill</li>
</ol>
<strong>Deploy Azure HDInsight Cluster to an Accessible Virtual Network</strong>

My first action is to deploy my Azure HDInsight cluster to a virtual network that's accessible by Drill. This is important because the Thrift service used to access the Hive metastore is not accessible from outside the ring-fence created by the virtual network that surrounds Azure HDInsight.

If I'm deploying a new cluster, I might deploy it into the same virtual network used by Drill.  (In this scenario, I might deploy the cluster to a separate subnet within that Virtual Network just for manageability purposes.)  If I have an existing Azure HDInsight cluster in a virtual network and have deployed Drill to a separate virtual network,  I might configure the two virtual networks to leverage a vnet-to-vnet VPN tunnel per<a href="https://azure.microsoft.com/en-us/documentation/articles/vpn-gateway-vnet-vnet-rm-ps/"> these instructions</a>.

<strong>Configure HBase Access within Drill</strong>

Before I can configure the HBase cluster, I need to identify the ZooKeeper nodes that it uses, not the ZooKeeper nodes Drill is using.  To do this, I will use the Ambari interface for my Azure HDInsight cluster:
<blockquote>
<ol>
	<li>Open a browser to https://<em>myclustername</em>.azurehdinsight.net with appropriate substitution for <em>myclustername</em></li>
	<li>At the prompt, login using the HTTP user name and password established when you configured the HDInsight cluster</li>
	<li>Within the Ambari interface, go to Services | HBase | Configs | Advanced</li>
	<li>Under the Advanced hbase-site subheading, copy the value for hbase.zookeeper.quorum and note the value for zookeeper.znode.parent, which should be /hbase-unsecure.</li>
</ol>
</blockquote>
Now I can login to my Drill Web Console, select the Storage page, and click Update on my hbase plugin.

In the resulting window, I can paste the value of my hbase.zookeeper.quorum property into the appropriate position.  I also need to add a value for zookeeper.znode.parent under the config property.  These changes are highlighted in red in this example configuration:
<blockquote><span style="color: #000000;font-family: Courier">{
"type": "hbase",
"config": {
"hbase.zookeeper.quorum": "<span style="color: red">zk1-drillh.h3yoplooa.dx.internal.cloudapp.net,zk3-drillh.h3yoplooa.dx.internal.cloudapp.net,zk2-drillh.h3yoplooa.dx.internal.cloudapp.net</span>",
"hbase.zookeeper.property.clientPort": "2181"<span style="color: red">,</span>
<span style="color: red">"zookeeper.znode.parent": "/hbase-unsecure"</span>
},
"size.calculator.enabled": false,
"enabled": true
}</span></blockquote>
I click Update and then Enable to save this configuration.

<strong>Confirm HBase Access within Drill</strong>

Unlike the Hive storage plugin setup, I found that I needed to restart my Drill cluster before being able to use the HBase plugin.  Your mileage may vary but it may be a good idea for you to stop and restart the nodes in your Drill cluster before proceeding.

With the Drill cluster restarted and the plugin enabled, I should now be able to query HBase.  If I go to the Drill Web Console and click on the Query page, I can query the built-in ambarismoketest table as follows:
<blockquote><span style="color: #000000;font-family: Courier">SELECT * FROM hbase.`ambarismoketest`;</span></blockquote>
This should return to me a single row.]]></content:encoded>
    </item>
    <item>
      <title>Configuration of Hive on Azure HDInsight as a Drill Data Source</title>
      <link>https://docs.microsoft.com/archive/blogs/data_otaku/configuration-of-hive-on-azure-hdinsight-as-a-drill-data-source</link>
      <pubDate>Mon, 13 Jun 2016 21:20:21 GMT</pubDate>
      <dc:creator><![CDATA[Bryan C Smith]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/data_otaku/?p=1006</guid>
      <description><![CDATA[NOTE This post is part of a series on a deployment of Apache Drill on the Azure cloud.

With my...]]></description>
      <content:encoded><![CDATA[NOTE This post is part of a series on a <a title="Series" href="https://blogs.msdn.microsoft.com/data_otaku/2016/05/27/deploying-apache-drill-on-azure/">deployment of Apache Drill on the Azure cloud</a>.

With my Drill cluster deployed to the Azure cloud, another potential source of data is Azure HDInsight, Microsoft's managed Hadoop offering.  HDInsight makes use of WASB storage so that if the structure of my data was pretty straightforward, I might by-pass HDInsight and just <a href="https://blogs.msdn.microsoft.com/data_otaku/2016/05/30/configuration-of-azure-blob-storage-aka-wasb-as-a-drill-data-source/">go directly to the files in WASB </a>to obtain access.  But I may prefer to surface the data through Hive if wished to make use of file processing/interpretation instructions embedded in Hive table definitions.

NOTE Drill officially supports Hadoop connectivity to either Hive or HBase.  I address HBase connectivity <a href="https://blogs.msdn.microsoft.com/data_otaku/2016/06/20/configuration-of-hbase-on-azure-hdinsight-as-a-drill-data-source/">here</a>.

<a href="https://drill.apache.org/docs/hive-storage-plugin/">There are two official ways to access Hive data through Drill </a>and only one of these - what the Drill documentation refers to as the Remote Metastore Configuration - is recommended for production systems.  I'll focus on this configuration in this post.

That said, I would like to point out that Drill supports JDBC connections and Hive comes with a JDBC driver.  I have been able to configure a JDBC connection from Drill to Hive but find that Drill is generating invalid Hive QL SELECT statements.  I've opened <a href="https://issues.apache.org/jira/browse/DRILL-4717">a JIRA ticket on this issue </a>and hope this becomes yet another option down the road.

Returning to the Remote Metastore Configuration, it helps to understand that what Drill is really doing is reading the table definitions from the Hive metastore and then engaging the data files associated with these tables directly.  There are a few implications to this.

First, Drill must be able to speak to the Thrift service which provides access to the metastore content.  Second, Drill must be able to access the files under the Hive tables.  And finally, Drill must have access to the SerDe's used by Hive in order to process the data.  With all this in mind, I can now establish connectivity to Hive in Azure HDInsight with the following steps:
<ol>
	<li>Deploy Azure HDInsight cluster to a Virtual Network accessible by Drill</li>
	<li>Configure WASB access within Drill</li>
	<li>Deploy required SerDes to Drill servers</li>
	<li>Configure Hive access within Drill</li>
	<li>Confirm Hive access within Drill</li>
</ol>
<strong>Deploy Azure HDInsight Cluster to an Accessible Virtual Network</strong>

My first action is to deploy my Azure HDInsight cluster to a virtual network that's accessible by Drill. This is important because the Thrift service used to access the Hive metastore is not accessible from outside the ring-fence created by the virtual network that surrounds Azure HDInsight.

If I'm deploying a new cluster, I might deploy it into the same virtual network used by Drill.  (In this scenario, I might deploy the cluster to a separate subnet within that Virtual Network just for manageability purposes.)  If I have an existing Azure HDInsight cluster in a virtual network and have deployed Drill to a separate virtual network,  I might configure the two virtual networks to leverage a vnet-to-vnet VPN tunnel per<a href="https://azure.microsoft.com/en-us/documentation/articles/vpn-gateway-vnet-vnet-rm-ps/"> these instructions</a>.

<strong>Configure WASB Access within Drill &amp; Deploy Required SerDes to Drill Servers</strong>

I'll combine these steps as these actions must be performed on each Drill server. Following the instructions provided <a href="https://blogs.msdn.microsoft.com/data_otaku/2016/05/30/configuration-of-azure-blob-storage-aka-wasb-as-a-drill-data-source/">here</a>, I need to configure access to all WASB accounts used by Azure HDInsight.  In addition, I would need to copy each SerDe explicitly leveraged by my Hive tables from the /usr/hdp/current/hive-client/ directory on one of my Azure HDInsight head nodes to the /drill/current/jars/3rdparty/ directory on my Drill server.  Assuming I want to leverage the built-in RegEx SerDe, I could copy it to a Drill server with the following command:
<blockquote><span style="color: #000000;font-family: Courier">cd /drill/current/jars/3rdparty/
sudo scp <em>sshusername</em>@<em>myclustername</em>-ssh.azurehdinsight.net:/usr/hdp/current/hive-client/regepx-1.3.jar .</span></blockquote>
In the scp command above, be sure to provide the name of the SSH user account created with the cluster and substitute the name of your cluster for <em>myclustername</em>.  (Also, don't forget the space+period at the end of the command; I always seem to forget to include this with scp.)

NOTE I have the sense that copying the SerDes will be the trickiest part of this whole configuration. I suggest you come back to this once you have established basic connectivity.  The test at the bottom of this post leverages a built-in sample Hive table for which no SerDes need to be transferred between the environments.

<strong>Configure Hive Access within Drill</strong>

With WASB access and required SerDe's loaded to the Drill servers, I can now setup the Hive Storage Plugin. Before doing this, I need to grab a few values from the config files of my HDInsight cluster.  The easiest way to do this is to use Amabari:
<blockquote>
<ol>
	<li>Open a browser to https://<em>myclustername</em>.azurehdinsight.net with appropriate substitution for <em>myclustername</em></li>
	<li>At the prompt, login using the HTTP user name and password established when you configured the cluster</li>
	<li>Within the Ambari interface, go to Services | Hive  | Configs | Advanced</li>
	<li>Under the General subheading, locate and record the values for hive.metastore.uris and hive.metastore.warehouse.dir</li>
	<li>Navigate to Services | HDFS | Configs | Advanced</li>
	<li>Under the Advanced core-site subheading, locate and record the value for fs.defaultFS</li>
</ol>
</blockquote>
With these values recorded, you can now setup the storage plugin.  In your Drill Web Console, navigate to Storage and Update the pre-existing hive plugin.  Set the value to the following, pasting the values copied earlier into the appropriate locations (as highlighted in red in this sample):
<blockquote><span style="color: #000000;font-family: Courier">{
"type": "hive",
"enabled": true,
"configProps": {
"hive.metastore.uris": "<em><span style="color: red">thrift://hn0-myclust.h3yob50453plbaooa.dx.internal.cloudapp.net:9083,thrift://hn1-myclust.h3yob50453plbaooa.dx.internal.cloudapp.net:9083</span></em>",
"hive.metastore.warehouse.dir": "<em><span style="color: red">/hive/warehouse</span></em>",
"fs.default.name": "<em><span style="color: red">wasb://mycontainer@mydatafiles.blob.core.windows.net/</span></em>",
"hive.metastore.sasl.enabled": "false"
}
}</span></blockquote>
Please note, the storage plugin uses fs.default.name as the key for the property you retrieved under the name of fs.defaultFS.

I then update and enable the storage plugin to save these settings.

<strong>Confirm Hive Access within Drill</strong>

With the plugin enabled, I should now be able to query Hive.  If I simply flip over to the Query page in the Drill Web Console, I should be able to query the built-in sample table deployed with Azure HDInsight:
<blockquote><span style="color: #000000;font-family: Courier">SELECT * FROM hive.`hivesampletable` LIMIT 10;</span></blockquote>
&nbsp;]]></content:encoded>
    </item>
  </channel>
</rss>