{"id":15966,"date":"2016-12-15T14:09:38","date_gmt":"2016-12-15T22:09:38","guid":{"rendered":"https:\/\/devwww.3cloudsolutions.com\/post\/microsoft-r-services-integrating-with-api-through-t-sql-2\/"},"modified":"2024-04-08T14:11:12","modified_gmt":"2024-04-08T21:11:12","slug":"microsoft-r-services-integrating-with-api-through-t-sql","status":"publish","type":"post","link":"https:\/\/3cloudsolutions.com\/resources\/microsoft-r-services-integrating-with-api-through-t-sql\/","title":{"rendered":"Microsoft R Services: Integrating with API Through T-SQL"},"content":{"rendered":"<p>It\u2019s hard to talk about machine learning without someone bringing up <a href=\"https:\/\/www.microsoft.com\/en-us\/sql-server\/sql-server-r-services?wt.mc_id=dx_875446&amp;gclid=CjwKEAiAg5_CBRDo4o6e4o3NtG0SJAB-IatYheYR3XniaXbmTvGK1XgKpHSAA-Ljd--i8Ky7nfSUshoClYDw_wcB\">Microsoft R<\/a> or <a href=\"http:\/\/www.r-studio.com\/\">R-Studio<\/a>. Although it has been around for over two decades, R has recently gained immense popularity by entering the big data landscape. With Microsoft\u2019s release of <a href=\"https:\/\/www.microsoft.com\/en-us\/sql-server\/sql-server-2016\">SQL Server 2016<\/a>, we now have an enterprise data warehouse directly integrated with one of the most diverse and open source analytical engines available. However, R is far more than just an analytical engine.<\/p>\n<p><!--more--><\/p>\n<h2><img loading=\"lazy\" decoding=\"async\" style=\"display: block; margin-left: auto; margin-right: auto;\" src=\"https:\/\/3cloudsolutions.com\/wp-content\/uploads\/2022\/11\/iStock-514570298edited.png\" alt=\"iStock-514570298edited.png\" width=\"805\" height=\"509\" \/><br \/>\nCapabilities<\/h2>\n<p>The demographic for R is truly based on Data Scientists or quantitative people, but it\u2019s not limited to just algorithms thanks to its ability to use custom packages developed by a large community of users. Packages can be as simple as a single function or as involved as complex modules able to communicate over different protocols and deliver highly refined visualizations.<\/p>\n<h2>Business Intelligence Tool?<\/h2>\n<p>When I was first exposed to R (about 10 years ago) I honestly didn\u2019t know where to start outside of the usual tutorials on ingesting and manipulating data. After trying different packages, I concluded that I may be able to leverage this tool to do just about anything. Testing that hypothesis, I built a data visualization tool for email marketing campaigns, but this wasn&#8217;t just for graphs. I found a package that could pull maps from <a href=\"https:\/\/www.google.com\/maps\/\">Google Maps<\/a> based on coordinates and then overlay markers representing customers treated with a campaign. Additionally, different colored markers were added for those who converted in weekly time frames. The final piece was to use an animated GIF program to combine the images for viewing on the web. Again, R is very versatile.<\/p>\n<h2>API Integration<\/h2>\n<p>Once R was made accessible from T-SQL, it was time to solve a long-standing difficulty I\u2019ve had with data integration. Application Program Interface (API) is everywhere and it\u2019s very common that those who have built one will also engage with it; however, to a database developer, it\u2019s not common at all. If you wanted to get data from an API into the database, you would have to reach out to an application developer using .NET \/ Java \/ Python etc. to build a custom interface and deliver the data into the database. So, as fun as that sounds, building a stored procedure to handle it for you is much easier and faster.<\/p>\n<h2>Example API XML Integration<\/h2>\n<p>The following example shows a simplified approach for building a data integration with an API and parsing it into a table structure. A common architecture would include a web tier, data tier, and multiple people to develop and deploy a solution. However, I\u2019m going to show you a single query that will deliver the current weather report for Grand Blanc, MI into a table.<\/p>\n<p><strong>*NOTE<\/strong>: Before you attempt to use this query, take a moment to review <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/mt590540.aspx\">Known Issues for SQL Server R Services<\/a>. My experience did not go very well until I read the section on \u201c<strong>Remote compute contexts blocked by firewall in SQL Server instances running on Azure virtual machines<\/strong>\u201d which also applies to installs on local machines.<\/p>\n<p><strong>*REQUIREMENTS<\/strong>: SQL Server 2016 and access to the machine to install addition R packages. To use the code below, you will need to install the R packages \u201cxml2\u201d and \u201cXML\u201d. Instructions on the procedure can be found <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/mt591989.aspx\">here<\/a>. The API used is free with registration which is required to get an API key to access the data and can be found <a href=\"https:\/\/openweathermap.org\/api\">here<\/a>.<\/p>\n<p>The code is broken up into 3 parts, first declaring variables, like so:<\/p>\n<div class=\"step_num\" aria-label=\"Step 3\">\n<div class=\"codebox\" style=\"display: inline-block; background-color: #f6f5f4; padding-top: 0.5em; padding-bottom: 0.5em; overflow: auto; margin-top: 1em; margin-bottom: 0.5em; width: 100%;\">\n<div class=\"mw-geshi mw-code mw-content-ltr\" dir=\"ltr\">\n<div class=\"html5 source-html5\">\n<p class=\"r\"><code>DECLARE @Rscript NVARCHAR(MAX),<\/code><br \/>\n<code>\u00a0 \u00a0 \u00a0@API_URL NVARCHAR(MAX)<\/code><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Next, we need to assign our API URL to a variable and the R code necessary to call and parse the data. Your code might look something like this:<\/p>\n<div class=\"step_num\" aria-label=\"Step 3\">\n<div class=\"codebox\" style=\"display: inline-block; background-color: #f6f5f4; padding-top: 0.5em; padding-bottom: 0.5em; overflow: auto; margin-top: 1em; margin-bottom: 0.5em; width: 100%;\">\n<div class=\"mw-geshi mw-code mw-content-ltr\" dir=\"ltr\">\n<div class=\"html5 source-html5\">\n<p class=\"r\"><code>SET @API_URL = 'http:\/\/api.openweathermap.org\/data\/2.5\/weather?zip=48439,us&amp;APPID=&amp;mode=xml'<\/code><\/p>\n<p><code>SET @Rscript = N'library(xml2);library(XML);<\/code><br \/>\n<code>weather.doc &lt;- read_xml(\"'+@API_URL+'\");<\/code><br \/>\n<code>weather.xml &lt;- xmlRoot(xmlParse(weather.doc));<\/code><br \/>\n<code>city.id &lt;- xpathSApply(weather.xml, \"\/\/*\/city\", xmlGetAttr, \"id\", NA);<\/code><br \/>\n<code>city.name &lt;- xpathSApply(weather.xml, \"\/\/*\/city\", xmlGetAttr, \"name\", NA);<\/code><br \/>\n<code>city.lon &lt;- xpathSApply(weather.xml, \"\/\/*\/city\/coord\", xmlGetAttr, \"lon\", NA);<\/code><br \/>\n<code>city.lat &lt;- xpathSApply(weather.xml, \"\/\/*\/city\/coord\", xmlGetAttr, \"lat\", NA);<\/code><br \/>\n<code>city.country &lt;- xmlToDataFrame(getNodeSet(weather.xml, \"\/\/*\/city\/country\"));<\/code><br \/>\n<code>city.sun.rise &lt;- xpathSApply(weather.xml, \"\/\/*\/city\/sun\", xmlGetAttr, \"rise\", NA);<\/code><br \/>\n<code>city.sun.set &lt;- xpathSApply(weather.xml, \"\/\/*\/city\/sun\", xmlGetAttr, \"set\", NA);<\/code><br \/>\n<code>temp &lt;- xpathSApply(weather.xml, \"\/\/*\/temperature\", xmlGetAttr, \"value\", NA);<\/code><br \/>\n<code>temp.min &lt;- xpathSApply(weather.xml, \"\/\/*\/temperature\", xmlGetAttr, \"min\", NA);<\/code><br \/>\n<code>temp.max &lt;- xpathSApply(weather.xml, \"\/\/*\/temperature\", xmlGetAttr, \"max\", NA);<\/code><br \/>\n<code>temp.unit &lt;- xpathSApply(weather.xml, \"\/\/*\/temperature\", xmlGetAttr, \"unit\", NA);<\/code><br \/>\n<code>humidity &lt;- xpathSApply(weather.xml, \"\/\/*\/humidity\", xmlGetAttr, \"value\", NA);<\/code><br \/>\n<code>humidity.unit &lt;- xpathSApply(weather.xml, \"\/\/*\/humidity\", xmlGetAttr, \"unit\", NA);<\/code><br \/>\n<code>pressure &lt;- xpathSApply(weather.xml, \"\/\/*\/pressure\", xmlGetAttr, \"value\", NA);<\/code><br \/>\n<code>pressure.unit &lt;- xpathSApply(weather.xml, \"\/\/*\/pressure\", xmlGetAttr, \"unit\", NA);<\/code><br \/>\n<code>wind.speed &lt;- xpathSApply(weather.xml, \"\/\/*\/wind\/speed\", xmlGetAttr, \"value\", NA);<\/code><br \/>\n<code>wind.speedname &lt;- xpathSApply(weather.xml, \"\/\/*\/wind\/speed\", xmlGetAttr, \"name\", NA);<\/code><br \/>\n<code>wind.gusts &lt;- xpathSApply(weather.xml, \"\/\/*\/wind\/gusts\", xmlGetAttr, \"value\", NA);<\/code><br \/>\n<code>wind.direction &lt;- xpathSApply(weather.xml, \"\/\/*\/wind\/direction\", xmlGetAttr, \"value\", NA);<\/code><br \/>\n<code>wind.directioncode &lt;- xpathSApply(weather.xml, \"\/\/*\/wind\/direction\", xmlGetAttr, \"code\", NA);<\/code><br \/>\n<code>wind.directionname &lt;- xpathSApply(weather.xml, \"\/\/*\/wind\/direction\", xmlGetAttr, \"name\", NA);<\/code><br \/>\n<code>clouds &lt;- xpathSApply(weather.xml, \"\/\/*\/clouds\", xmlGetAttr, \"value\", NA);<\/code><br \/>\n<code>clouds.name &lt;- xpathSApply(weather.xml, \"\/\/*\/clouds\", xmlGetAttr, \"name\", NA);<\/code><br \/>\n<code>visibility &lt;- xpathSApply(weather.xml, \"\/\/*\/visibility\", xmlGetAttr, \"value\", NA);<\/code><br \/>\n<code>precipitation &lt;- xpathSApply(weather.xml, \"\/\/*\/precipitation\", xmlGetAttr, \"mode\", NA);<\/code><br \/>\n<code>weather.number &lt;- xpathSApply(weather.xml, \"\/\/*\/weather\", xmlGetAttr, \"number\", NA);<\/code><br \/>\n<code>weather.value &lt;- xpathSApply(weather.xml, \"\/\/*\/weather\", xmlGetAttr, \"value\", NA);<\/code><br \/>\n<code>weather.icon &lt;- xpathSApply(weather.xml, \"\/\/*\/weather\", xmlGetAttr, \"icon\", NA);<\/code><br \/>\n<code>lastupdate &lt;- xpathSApply(weather.xml, \"\/\/*\/lastupdate\", xmlGetAttr, \"value\", NA);<\/code><br \/>\n<code>OutputDataSet &lt;- data.frame(city.id, city.name, city.lon, city.lat, city.country, city.sun.rise, <\/code><br \/>\n<code>city.sun.set, temp, temp.min, temp.max, temp.unit, humidity, humidity.unit, pressure, <\/code><br \/>\n<code>pressure.unit, wind.speed, wind.speedname, wind.gusts, wind.direction, wind.directioncode, <\/code><br \/>\n<code>wind.directionname, clouds, clouds.name, visibility, precipitation, weather.number, <\/code><br \/>\n<code>weather.value, weather.icon, lastupdate)';<\/code><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>The last step is to execute the R script and define our Result Sets, like so:<\/p>\n<div class=\"step_num\" aria-label=\"Step 3\">\n<div class=\"codebox\" style=\"display: inline-block; background-color: #f6f5f4; padding-top: 0.5em; padding-bottom: 0.5em; overflow: auto; margin-top: 1em; margin-bottom: 0.5em; width: 100%;\">\n<div class=\"mw-geshi mw-code mw-content-ltr\" dir=\"ltr\">\n<div class=\"html5 source-html5\">\n<p class=\"r\"><code>EXEC sp_execute_external_script<\/code><br \/>\n<code>\u00a0 \u00a0 \u00a0@language = N'R',<\/code><br \/>\n<code>\u00a0 \u00a0 \u00a0@script = @Rscript<\/code><br \/>\n<code>\u00a0 \u00a0 \u00a0WITH RESULT SETS(([city.id] VARCHAR(100), [city.name] VARCHAR(100), [city.lon] VARCHAR(100),<\/code><br \/>\n<code>[city.lat] VARCHAR(100), [city.country] VARCHAR(100), [city.sun.rise] VARCHAR(100), <\/code><br \/>\n<code>[city.sun.set] VARCHAR(100), [temp] VARCHAR(100), [temp.min] VARCHAR(100), <\/code><br \/>\n<code>[temp.max] VARCHAR(100), [temp.unit] VARCHAR(100), [humidity] VARCHAR(100), <\/code><br \/>\n<code>[humidity.unit] VARCHAR(100), [pressure] VARCHAR(100), [pressure.unit] VARCHAR(100),<\/code><br \/>\n<code>[wind.speed] VARCHAR(100), [wind.speedname] VARCHAR(100), [wind.gusts] VARCHAR(100),<\/code><br \/>\n<code>[wind.direction] VARCHAR(100), [wind.directioncode] VARCHAR(100), <\/code><br \/>\n<code>[wind.directionname] VARCHAR(100), [clouds] VARCHAR(100), [clouds.name] VARCHAR(100), <\/code><br \/>\n<code>[visibility] VARCHAR(100), [precipitation] VARCHAR(100), [weather.number] VARCHAR(100), <\/code><br \/>\n<code>[weather.value] VARCHAR(100), [weather.icon] VARCHAR(100), [lastupdate] VARCHAR(100)))<\/code><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>The R code consists of 5 parts, starting with loading the libraries needed to handle the XML as well as the API call. Curl is the package enabling the communication and will not have to be loaded directly as it gets loaded with the other packages. Here is step 1:<\/p>\n<div class=\"step_num\" aria-label=\"Step 3\">\n<div class=\"codebox\" style=\"display: inline-block; background-color: #f6f5f4; padding-top: 0.5em; padding-bottom: 0.5em; overflow: auto; margin-top: 1em; margin-bottom: 0.5em; width: 100%;\">\n<div class=\"mw-geshi mw-code mw-content-ltr\" dir=\"ltr\">\n<div class=\"html5 source-html5\">\n<p class=\"r\"><code>library(xml2);library(XML);<\/code><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Step 2 makes the API call and reads the XML into a variable:<\/p>\n<div class=\"step_num\" aria-label=\"Step 3\">\n<div class=\"codebox\" style=\"display: inline-block; background-color: #f6f5f4; padding-top: 0.5em; padding-bottom: 0.5em; overflow: auto; margin-top: 1em; margin-bottom: 0.5em; width: 100%;\">\n<div class=\"mw-geshi mw-code mw-content-ltr\" dir=\"ltr\">\n<div class=\"html5 source-html5\">\n<p class=\"r\"><code>weather.doc &lt;- read_xml(\"'+@API_URL+'\");<\/code><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Step 3 parses the XML wrapper and leaves you with the root element and remaining body:<\/p>\n<div class=\"step_num\" aria-label=\"Step 3\">\n<div class=\"codebox\" style=\"display: inline-block; background-color: #f6f5f4; padding-top: 0.5em; padding-bottom: 0.5em; overflow: auto; margin-top: 1em; margin-bottom: 0.5em; width: 100%;\">\n<div class=\"mw-geshi mw-code mw-content-ltr\" dir=\"ltr\">\n<div class=\"html5 source-html5\">\n<p class=\"r\"><code>weather.xml &lt;- xmlRoot(xmlParse(weather.doc));<\/code><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>This is the data returned and contained in \u201cweather.xml\u201d:<\/p>\n<div class=\"step_num\" aria-label=\"Step 3\">\n<div class=\"codebox\" style=\"display: inline-block; background-color: #f6f5f4; padding-top: 0.5em; padding-bottom: 0.5em; overflow: auto; margin-top: 1em; margin-bottom: 0.5em; width: 100%;\">\n<div class=\"mw-geshi mw-code mw-content-ltr\" dir=\"ltr\">\n<div class=\"html5 source-html5\">\n<p class=\"r\"><code><\/code><br \/>\n<code>- <\/code><br \/>\n<code> <\/code><br \/>\n<code>US <\/code><br \/>\n<code> <\/code><br \/>\n<code><\/code><br \/>\n<code> <\/code><br \/>\n<code> <\/code><br \/>\n<code> <\/code><br \/>\n<code>- <\/code><br \/>\n<code> <\/code><br \/>\n<code> <\/code><br \/>\n<code> <\/code><br \/>\n<code><\/code><br \/>\n<code> <\/code><br \/>\n<code> <\/code><br \/>\n<code> <\/code><br \/>\n<code> <\/code><br \/>\n<code> <\/code><br \/>\n<code><\/code><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Step 4 parses each element or attribute of the XML into individual variables:<\/p>\n<div class=\"step_num\" aria-label=\"Step 3\">\n<div class=\"codebox\" style=\"display: inline-block; background-color: #f6f5f4; padding-top: 0.5em; padding-bottom: 0.5em; overflow: auto; margin-top: 1em; margin-bottom: 0.5em; width: 100%;\">\n<div class=\"mw-geshi mw-code mw-content-ltr\" dir=\"ltr\">\n<div class=\"html5 source-html5\">\n<p class=\"r\"><code>city.id &lt;- xpathSApply(weather.xml, \"\/\/*\/city\", xmlGetAttr, \"id\", NA);<\/code><br \/>\n<code>city.name &lt;- xpathSApply(weather.xml, \"\/\/*\/city\", xmlGetAttr, \"name\", NA);<\/code><br \/>\n<code>city.lon &lt;- xpathSApply(weather.xml, \"\/\/*\/city\/coord\", xmlGetAttr, \"lon\", NA);<\/code><br \/>\n<code>city.lat &lt;- xpathSApply(weather.xml, \"\/\/*\/city\/coord\", xmlGetAttr, \"lat\", NA);<\/code><br \/>\n<code>city.country &lt;- xmlToDataFrame(getNodeSet(weather.xml, \"\/\/*\/city\/country\"));<\/code><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Step 5 combines all of the individual data frames into a single data frame using the expected SQL <strong>OutDataSet<\/strong> variable, as seen below.<\/p>\n<div class=\"step_num\" aria-label=\"Step 3\">\n<div class=\"codebox\" style=\"display: inline-block; background-color: #f6f5f4; padding-top: 0.5em; padding-bottom: 0.5em; overflow: auto; margin-top: 1em; margin-bottom: 0.5em; width: 100%;\">\n<div class=\"mw-geshi mw-code mw-content-ltr\" dir=\"ltr\">\n<div class=\"html5 source-html5\">\n<p class=\"r\"><code>OutputDataSet &lt;- data.frame(city.id, city.name, city.lon, city.lat, city.country, city.sun.rise, city.sun.set, temp, temp.min, temp.max, temp.unit, humidity, humidity.unit, pressure, pressure.unit, wind.speed, wind.speedname, wind.gusts, wind.direction, wind.directioncode, wind.directionname, clouds, clouds.name, visibility, precipitation, weather.number, weather.value, weather.icon, lastupdate)<\/code><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>The below example shows the expected output once executed.<img loading=\"lazy\" decoding=\"async\" style=\"margin-right: auto; margin-left: auto; display: block;\" src=\"https:\/\/3cloudsolutions.com\/wp-content\/uploads\/2022\/11\/Rservices.png\" alt=\"Rservices.png\" width=\"563\" height=\"552\" \/><\/p>\n<h2>Summary<\/h2>\n<p>R will continue to gain popularity in the Data Science field for its ability to model and forecast trends, but I hope to see more database developers start to explore its capabilities. This example was intended to give database developers a bridge between the power of a relational database management system and the exorbitant amount of data available through APIs.<\/p>\n<p>If you\u2019re still looking for more information or need help with Microsoft R, <a href=\"https:\/\/www.blue-granite.com\/contact-us\">contact BlueGranite<\/a> today!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Microsoft R has gained popularity by entering the big data landscape. Thanks to SQL Server 2016, we now have an EDW directly integrated with analytical engines.<\/p>\n","protected":false},"author":21,"featured_media":14739,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"content-type":"","footnotes":""},"categories":[297],"tags":[304,341],"class_list":["post-15966","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-data-platform","tag-modern-data-platform","tag-r","topics-blog"],"acf":[],"_links":{"self":[{"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/posts\/15966","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/users\/21"}],"replies":[{"embeddable":true,"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/comments?post=15966"}],"version-history":[{"count":0,"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/posts\/15966\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/media\/14739"}],"wp:attachment":[{"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/media?parent=15966"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/categories?post=15966"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/tags?post=15966"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}