{"id":15927,"date":"2017-07-18T13:05:00","date_gmt":"2017-07-18T20:05:00","guid":{"rendered":"https:\/\/devwww.3cloudsolutions.com\/post\/now-presenting-fun-with-functions-string_split-2\/"},"modified":"2023-09-20T09:36:02","modified_gmt":"2023-09-20T16:36:02","slug":"now-presenting-fun-with-functions-string-split","status":"publish","type":"post","link":"https:\/\/3cloudsolutions.com\/resources\/now-presenting-fun-with-functions-string-split\/","title":{"rendered":"Now Presenting: Fun with Functions (String_Split)"},"content":{"rendered":"<p>In my 17 years of database development, a few projects stand out from requirements so elaborate that I really had to stretch my imagination. One such assignment required the importation of comma separated value (CSV) files where the fields may not be in the same place and new fields may appear without warning. As you can imagine, this wouldn\u2019t\u00a0have been\u00a0difficult if the source was a semi-structured format like JSON, but in this case, it was\u00a0good old CSV.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" style=\"margin-right: auto; margin-left: auto; display: block;\" src=\"https:\/\/3cloudsolutions.com\/wp-content\/uploads\/2022\/11\/String_Split-Function-1.png\" alt=\"String_Split Function.png\" width=\"805\" height=\"509\" \/><\/p>\n<p>Thanks to the new functions within SQL Server 2016, this once-daunting task becomes easily doable\u00a0when you combine dynamic SQL statements and String_Split. String_Split is a unique function where you can take a list of strings and parse them into rows. See the example from <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/mt684588.aspx\">MSDN<\/a> below:<\/p>\n<div class=\"step_num\" aria-label=\"Step 3\">\n<div class=\"codebox\">\n<div class=\"mw-geshi mw-code mw-content-ltr\" dir=\"ltr\">\n<div class=\"html5 source-html5\">\n<p class=\"r\"><code>SELECT value<br \/>\n<\/code><span style=\"font-weight: inherit;\">FROM STRING_SPLIT(&#8216;Lorem ipsum dolor sit amet.&#8217;, &#8216; &#8216;);<\/span><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<table style=\"margin-right: auto; margin-left: auto;\">\n<tbody>\n<tr>\n<td style=\"border: 1px solid #d6d6d6; text-align: center; background-color: #d6d6d6;\"><strong>value<\/strong><\/td>\n<\/tr>\n<tr>\n<td style=\"border: 1px solid #d6d6d6; text-align: center; background-color: #ffffff;\">Lorem<\/td>\n<\/tr>\n<tr>\n<td style=\"border: 1px solid #d6d6d6; text-align: center; background-color: #ffffff;\">ipsum<\/td>\n<\/tr>\n<tr>\n<td style=\"border: 1px solid #d6d6d6; text-align: center; background-color: #ffffff;\">dolor<\/td>\n<\/tr>\n<tr>\n<td style=\"border: 1px solid #d6d6d6; text-align: center; background-color: #ffffff;\">sit<\/td>\n<\/tr>\n<tr>\n<td style=\"border: 1px solid #d6d6d6; text-align: center; background-color: #ffffff;\">amet.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<p><span style=\"background-color: transparent;\">If you are already thinking that this will provide you with a separate row for each field name of the CSV, you are headed in the right direction. String_Split can provide a glimpse into the format of any CSV file, and with the help of the Pivot operator and dynamic SQL statements, you can build a process to handle changing file structures or the addition of new fields without having to rewrite your code.<\/span><\/p>\n<h2>Technical Requirements<\/h2>\n<p>In this instance, sensor data is coming from a 3<sup>rd<\/sup> party vendor, and after it is processed by a proprietary tool, a CSV file is cut for the client to consume. Per the specifications, only the 1<sup>st<\/sup> field is consistent and will contain a date, but all other fields may be in a different order or include never-before-seen fields. Another important piece of information to make note of is that all fields, aside from the date, will be floats (large decimals).<\/p>\n<table style=\"height: 71px; margin-right: auto; margin-left: auto;\" width=\"815\">\n<tbody>\n<tr>\n<td style=\"border: 1px solid #d6d6d6; width: 220px; text-align: center; background-color: #d6d6d6;\"><strong>date<\/strong><\/td>\n<td style=\"border: 1px solid #d6d6d6; width: 142px; text-align: center; background-color: #d6d6d6;\"><strong>sensor1<\/strong><\/td>\n<td style=\"border: 1px solid #d6d6d6; width: 142px; text-align: center; background-color: #d6d6d6;\"><strong>sensor2<\/strong><\/td>\n<td style=\"border: 1px solid #d6d6d6; width: 142px; text-align: center; background-color: #d6d6d6;\"><strong>sensor3<\/strong><\/td>\n<td style=\"border: 1px solid #d6d6d6; width: 142px; text-align: center; background-color: #d6d6d6;\"><strong>sensor4<\/strong><\/td>\n<\/tr>\n<tr>\n<td style=\"border: 1px solid #d6d6d6; width: 220px; text-align: center;\">19-06-2017\u00a012:00:00.500<\/td>\n<td style=\"border: 1px solid #d6d6d6; width: 142px; text-align: center;\">0.1<\/td>\n<td style=\"border: 1px solid #d6d6d6; width: 142px; text-align: center;\">5.0<\/td>\n<td style=\"border: 1px solid #d6d6d6; width: 142px; text-align: center;\">600.0<\/td>\n<td style=\"border: 1px solid #d6d6d6; width: 142px; text-align: center;\">1700.0<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p style=\"text-align: center;\"><strong><em>File Example 1<\/em><\/strong><\/p>\n<table style=\"height: 71px; margin-right: auto; margin-left: auto;\" width=\"815\">\n<tbody>\n<tr>\n<td style=\"border: 1px solid #d6d6d6; width: 220px; text-align: center; background-color: #d6d6d6;\"><strong>date<\/strong><\/td>\n<td style=\"border: 1px solid #d6d6d6; width: 142px; text-align: center; background-color: #d6d6d6;\"><strong>sensor5<\/strong><\/td>\n<td style=\"border: 1px solid #d6d6d6; width: 142px; text-align: center; background-color: #d6d6d6;\"><strong>sensor1<\/strong><\/td>\n<td style=\"border: 1px solid #d6d6d6; width: 142px; text-align: center; background-color: #d6d6d6;\"><strong>sensor3<\/strong><\/td>\n<td style=\"border: 1px solid #d6d6d6; width: 142px; text-align: center; background-color: #d6d6d6;\"><strong>sensor17<\/strong><\/td>\n<\/tr>\n<tr>\n<td style=\"border: 1px solid #d6d6d6; width: 220px; text-align: center;\">20-06-2017\u00a012:00:00.500<\/td>\n<td style=\"border: 1px solid #d6d6d6; width: 142px; text-align: center;\">6.5<\/td>\n<td style=\"border: 1px solid #d6d6d6; width: 142px; text-align: center;\">21.0<\/td>\n<td style=\"border: 1px solid #d6d6d6; width: 142px; text-align: center;\">168.5<\/td>\n<td style=\"border: 1px solid #d6d6d6; width: 142px; text-align: center;\">2.0<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p style=\"text-align: center;\"><strong><em>File Example 2<\/em><\/strong><\/p>\n<h2>High-Level Approach<\/h2>\n<p>Let\u2019s walk through each step of the process, using the file examples above as a guide. Below is a summary of the steps we will take with more details to follow further down.<\/p>\n<p style=\"padding-left: 30px;\"><strong>Step 1:<\/strong> Import CSV into a single-column staging table<br \/>\n<strong>Step 2:<\/strong> Parse 1<sup>st<\/sup> row into a staging metadata table (field\u00a0names)<br \/>\n<strong>Step 3:<\/strong> Compare staging metadata to historic metadata and add new fields if necessary<br \/>\n<strong>Step 4:<\/strong> Assemble dynamic pivot query to parse and insert values into staging table<br \/>\n<strong>Step 5:<\/strong> Assemble dynamic query to insert into production table<span style=\"background-color: transparent;\">\u00a0<\/span><\/p>\n<p><span style=\"background-color: transparent;\"><img decoding=\"async\" style=\"width: 670px; margin-right: auto; margin-left: auto; display: block;\" src=\"https:\/\/3cloudsolutions.com\/wp-content\/uploads\/2022\/11\/String_Split.png\" alt=\"String_Split.png\" width=\"670\" \/><\/span><\/p>\n<h3>Step 1:<\/h3>\n<p>Import the CSV into a staging table so that all the data is in 1 column. This can be done by using an obscure delimiter that doesn\u2019t appear in the file like \u2018|\u2019 or \u2018~\u2019. The point is to keep all the data together so that String_Split can be used on the entire row of data.<\/p>\n<h3>Step 2:<\/h3>\n<p>Capture the first row of data into a variable and use the String_Split function to capture the Fields into a staging metadata table (example code below). The staging metadata table is very important when creating the dynamic <code><span style=\"color: #0000ff; background-color: #d6d6d6;\">Insert<\/span><\/code>\u00a0\/\u00a0<code><span style=\"color: #0000ff; background-color: #d6d6d6;\">Select<\/span><\/code>\u00a0statements to persist the correct order of the fields since they can vary.<\/p>\n<div class=\"step_num\" aria-label=\"Step 3\">\n<div class=\"codebox\">\n<div class=\"mw-geshi mw-code mw-content-ltr\" dir=\"ltr\">\n<div class=\"html5 source-html5\">\n<p><span style=\"color: #0000ff;\">DECLARE<\/span> @Header <span style=\"color: #0000ff;\">VARCHAR<\/span><span style=\"color: #999999;\">(<\/span>5000<span style=\"color: #999999;\">)<\/span>, @FieldName <span style=\"color: #0000ff;\">VARCHAR<\/span><span style=\"color: #808080;\"><span style=\"color: #999999;\">(<\/span><span style=\"color: #515050;\">5000<\/span><span style=\"color: #999999;\">);<\/span><\/span><br \/>\n<span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0<span style=\"color: #0000ff;\">SELECT<\/span> @Header <span style=\"color: #999999;\">= <\/span>DataTXT<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0<span style=\"color: #0000ff;\">FROM<\/span> stage<span style=\"color: #999999;\">.<\/span>sensor<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0<span style=\"color: #0000ff;\">WHERE<\/span> RowID <span style=\"color: #999999;\">=<\/span> 1<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span style=\"color: #999999;\">AND <\/span>FileID <span style=\"color: #999999;\">=<\/span> @FileID<span style=\"color: #999999;\">;<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0<span style=\"color: #0000ff;\">WHILE<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0<span style=\"color: #999999;\">(<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span style=\"color: #0000ff;\">SELECT<\/span> <span style=\"color: #ff00ff;\">CHARINDEX<\/span><span style=\"color: #999999;\">(<\/span><span style=\"color: #ff0000;\">&#8216;,&#8217;<\/span><span style=\"color: #999999;\">,<\/span> @Header<span style=\"color: #999999;\">,<\/span> 1<span style=\"color: #999999;\">)<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0<span style=\"color: #999999;\">) &gt;<\/span> 1<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span style=\"color: #0000ff;\">BEGIN<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span style=\"color: #0000ff;\">SELECT<\/span> @FieldName <span style=\"color: #999999;\">= LEFT(<\/span>@Header<span style=\"color: #999999;\">,<\/span> <span style=\"color: #ff00ff;\">CHARINDEX<\/span><span style=\"color: #999999;\">(<\/span><span style=\"color: #ff0000;\">&#8216;,&#8217;<\/span><span style=\"color: #999999;\">,<\/span> @Header<span style=\"color: #999999;\">,<\/span> 1<span style=\"color: #999999;\">)-<\/span>1<span style=\"color: #999999;\">);<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span style=\"color: #0000ff;\">INSERT INTO<\/span> stage<span style=\"color: #999999;\">.<\/span>metadata<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span style=\"color: #999999;\">(<\/span>FileID<span style=\"color: #999999;\">,<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 FieldName<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span style=\"color: #999999;\">\u00a0)<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span style=\"color: #0000ff;\">VALUES<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span style=\"color: #999999;\">(<\/span>@FileID<span style=\"color: #999999;\">,<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 @FieldName<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span style=\"color: #999999;\">);<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span style=\"color: #0000ff;\">SET<\/span> @Header <span style=\"color: #999999;\">=<\/span> <span style=\"color: #ff00ff;\">SUBSTRING<\/span><span style=\"color: #999999;\">(<\/span>@Header<span style=\"color: #999999;\">,<\/span> CHARINDEX<span style=\"color: #999999;\">(<\/span><span style=\"color: #ff0000;\">&#8216;,&#8217;<\/span><span style=\"color: #999999;\">,<\/span> @Header<span style=\"color: #999999;\">,<\/span> 1<span style=\"color: #999999;\">)+<\/span>1<span style=\"color: #999999;\">,<\/span> 5000<span style=\"color: #999999;\">);<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span style=\"color: #0000ff;\">END<\/span><span style=\"color: #999999;\">;<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0<span style=\"color: #0000ff;\">INSERT INTO<\/span> stage<span style=\"color: #999999;\">.<\/span>metadata<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0<span style=\"color: #999999;\">(<\/span>FileID<span style=\"color: #999999;\">,<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 FieldName<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 <span style=\"color: #999999;\">\u00a0)<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0<span style=\"color: #0000ff;\">VALUES<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0<span style=\"color: #999999;\">(<\/span>@FileID<span style=\"color: #999999;\">,<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 @Header<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0<span style=\"color: #999999;\"> \u00a0);<\/span><\/span><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<h3>Step 3:<\/h3>\n<p>After capturing the fields from the incoming file, you should compare them to the existing metadata fields expected in the resulting table that the data will be inserted into. The following code creates dynamic SQL statements that can be executed against the production table to add additional fields. Since it\u2019s a given that the fields are all floats except \u2018date\u2019, then you don\u2019t have to worry about conversion issues. However, we worked out a solution that added an additional field to the metadata table indicating the data type for future flexibility.<\/p>\n<div class=\"step_num\" aria-label=\"Step 3\">\n<div class=\"codebox\">\n<div class=\"mw-geshi mw-code mw-content-ltr\" dir=\"ltr\">\n<div class=\"html5 source-html5\">\n<p>DECLARE cur_fields CURSOR FORWARD_ONLY<br \/>\n<span style=\"font-weight: inherit;\">FOR SELECT<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0s<span style=\"color: #999999;\">.<\/span>FieldName<span style=\"color: #999999;\">,<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 <span style=\"color: #ff0000;\">\u00a0&#8216;Alter Table dbo.sensor ADD [&#8216;<\/span><span style=\"color: #999999;\">+<\/span>s<span style=\"color: #999999;\">.<\/span>FieldName<span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8216;] FLOAT&#8217;<\/span> AS Remediation<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 FROM stage<span style=\"color: #999999;\">.<\/span>metadata AS s<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 WHERE <span style=\"color: #999999;\">NOT EXISTS<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\"><span style=\"color: #999999;\">\u00a0 \u00a0 (<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 SELECT<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span style=\"color: #999999;\">*<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 FROM dbo<span style=\"color: #999999;\">.<\/span>metadata AS m<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 WHERE s<span style=\"color: #999999;\">.<\/span>FieldName <span style=\"color: #999999;\">=<\/span> m<span style=\"color: #999999;\">.<\/span>FieldName<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 <span style=\"color: #999999;\">\u00a0 )<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span style=\"color: #999999;\">AND<\/span> s<span style=\"color: #999999;\">.<\/span>FileID <span style=\"color: #999999;\">=<\/span> @FileID<br \/>\n<\/span><span style=\"font-weight: inherit;\"><br \/>\nDECLARE @FieldName VARCHAR<span style=\"color: #999999;\">(<\/span>500<span style=\"color: #999999;\">),<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 @sqlAlter\u00a0 NVARCHAR<span style=\"color: #999999;\">(<\/span>1000<span style=\"color: #999999;\">);<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\"><br \/>\n<\/span><span style=\"font-weight: inherit;\">OPEN cur_fields<span style=\"color: #999999;\">;<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\"><br \/>\n<\/span><span style=\"font-weight: inherit;\">FETCH NEXT FROM cur_fields INTO @FieldName<span style=\"color: #999999;\">,<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 @sqlAlter<span style=\"color: #999999;\">;<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\"><br \/>\n<\/span><span style=\"font-weight: inherit;\">WHILE <span style=\"color: #ff00ff;\">@@FETCH_STATUS<\/span> <span style=\"color: #999999;\">=<\/span> 0<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 BEGIN<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 INSERT INTO dbo<span style=\"color: #999999;\">.<\/span>metadata_history<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 <span style=\"color: #999999;\">(<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0FileID<span style=\"color: #999999;\">,<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0FieldName<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 <span style=\"color: #999999;\">)<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 VALUES<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 <span style=\"color: #999999;\">(<\/span>@FileID<span style=\"color: #999999;\">,<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0@FieldName<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 <span style=\"color: #999999;\">);<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 INSERT INTO dbo<span style=\"color: #999999;\">.<\/span>metadata<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 <span style=\"color: #999999;\">(<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0FieldName<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 <span style=\"color: #999999;\">)<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 VALUES<span style=\"color: #999999;\">(<\/span>@FieldName<span style=\"color: #999999;\">);<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 EXEC <span style=\"color: #800000;\">sp_executesql<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0@sqlAlter<span style=\"color: #999999;\">;<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 FETCH NEXT FROM cur_fields INTO @FieldName<span style=\"color: #999999;\">,<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 @sqlAlter<span style=\"color: #999999;\">;<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 END<span style=\"color: #999999;\">;<\/span><\/span><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<h3>Steps 4 &amp; 5:<\/h3>\n<p>The last 2 steps are accomplished using 1 procedure as shown in the code below. The first part will use the metadata tables to pull out the fields necessary for the <span style=\"color: #0000ff; background-color: #d6d6d6;\"><code>Select<\/code><\/span>\u00a0and the <span style=\"color: #0000ff; background-color: #d6d6d6;\"><code>Insert<\/code><\/span>. A dynamic SQL statement is then created to parse the data within the CSV, skipping the header row. The final dynamic SQL statement creates a Merge based on some other client requirements, but a simple insert would also work depending on your situation.<\/p>\n<div class=\"step_num\" aria-label=\"Step 3\">\n<div class=\"codebox\">\n<div class=\"mw-geshi mw-code mw-content-ltr\" dir=\"ltr\">\n<div class=\"html5 source-html5\">\n<p><span style=\"color: #0000ff;\">DECLARE<\/span> @sqlCMD\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <span style=\"color: #0000ff;\">NVARCHAR<\/span><span style=\"color: #999999;\">(<\/span><span style=\"color: #ff00ff;\">MAX<\/span><span style=\"color: #999999;\">),<\/span><br \/>\n<span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 @sqlHeader\u00a0\u00a0\u00a0 <span style=\"color: #0000ff;\">NVARCHAR<\/span><span style=\"color: #999999;\">(<\/span><span style=\"color: #ff00ff;\">MAX<\/span><span style=\"color: #999999;\">),<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 @sqlStage\u00a0\u00a0\u00a0\u00a0 <span style=\"color: #0000ff;\">NVARCHAR<\/span><span style=\"color: #999999;\">(<\/span><span style=\"color: #ff00ff;\">MAX<\/span><span style=\"color: #999999;\">),<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 @Fields\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <span style=\"color: #0000ff;\">NVARCHAR<\/span><span style=\"color: #999999;\">(<\/span><span style=\"color: #ff00ff;\">MAX<\/span><span style=\"color: #999999;\">),<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 @SourceFields <span style=\"color: #0000ff;\">NVARCHAR<\/span><span style=\"color: #999999;\">(<\/span><span style=\"color: #ff00ff;\">MAX<\/span><span style=\"color: #999999;\">),<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 \u00a0 @SetFields\u00a0\u00a0\u00a0 <span style=\"color: #0000ff;\">NVARCHAR<\/span><span style=\"color: #999999;\">(<\/span><span style=\"color: #ff00ff;\">MAX<\/span><span style=\"color: #999999;\">);<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\"><br \/>\n<\/span><span style=\"font-weight: inherit;\"><span style=\"color: #0000ff;\">SET<\/span> @Fields <span style=\"color: #999999;\">=<\/span> <span style=\"color: #ff0000;\">&#8221;<\/span><span style=\"color: #999999;\">;<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\"><span style=\"color: #0000ff;\">SET<\/span> @SourceFields <span style=\"color: #999999;\">=<\/span> <span style=\"color: #ff0000;\">&#8221;<\/span><span style=\"color: #999999;\">;<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\"><span style=\"color: #0000ff;\">SET<\/span> @SetFields <span style=\"color: #999999;\">=<\/span> <span style=\"color: #ff0000;\">&#8221;<\/span><span style=\"color: #999999;\">;<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\"><br \/>\n<\/span><span style=\"font-weight: inherit;\"><span style=\"color: #0000ff;\">SELECT<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0@Fields <span style=\"color: #999999;\">=<\/span> @Fields<span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8216;[&#8216;<\/span><span style=\"color: #999999;\">+<\/span>FieldName<span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8216;], &#8216;<\/span><span style=\"color: #999999;\">,<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0@SourceFields <span style=\"color: #999999;\">=<\/span> @SourceFields<span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8216;source.[&#8216;<\/span><span style=\"color: #999999;\">+<\/span>FieldName<span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8216;], &#8216;<\/span><span style=\"color: #999999;\">,<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0@SetFields <span style=\"color: #999999;\">=<\/span> @SetFields<span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8216;target.[&#8216;<\/span><span style=\"color: #999999;\">+<\/span>FieldName<span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8216;] = source.[&#8216;<\/span><span style=\"color: #999999;\">+<\/span>FieldName<span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8216;], &#8216;<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\"><span style=\"color: #0000ff;\">FROM<\/span> stage<span style=\"color: #999999;\">.<\/span>metadata<br \/>\n<\/span><span style=\"font-weight: inherit;\"><span style=\"color: #0000ff;\">WHERE<\/span> FileID <span style=\"color: #999999;\">=<\/span> @FileID<br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0 \u00a0 <span style=\"color: #999999;\">AND<\/span> FieldID <span style=\"color: #999999;\">&gt;<\/span> 0<span style=\"color: #999999;\">;<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\"><br \/>\n<\/span><span style=\"font-weight: inherit;\"><span style=\"color: #0000ff;\">SET<\/span> @Fields <span style=\"color: #999999;\">= LEFT(<\/span>@Fields<span style=\"color: #999999;\">,<\/span> <span style=\"color: #ff00ff;\">LEN<\/span><span style=\"color: #999999;\">(<\/span>@Fields<span style=\"color: #999999;\">) &#8211;<\/span> 1<span style=\"color: #999999;\">);<\/span><\/p>\n<p><\/span><span style=\"font-weight: inherit;\"><span style=\"color: #0000ff;\">SET<\/span> @SourceFields <span style=\"color: #999999;\">= LEFT(<\/span>@SourceFields<span style=\"color: #999999;\">,<\/span> <span style=\"color: #ff00ff;\">LEN<\/span><span style=\"color: #999999;\">(<\/span>@SourceFields<span style=\"color: #999999;\">) &#8211;<\/span> 1<span style=\"color: #999999;\">);<\/span><\/p>\n<p><\/span><span style=\"font-weight: inherit;\"><span style=\"color: #0000ff;\">SET<\/span> @SetFields <span style=\"color: #999999;\">= LEFT(<\/span>@SetFields, <span style=\"color: #ff00ff;\">LEN<\/span><span style=\"color: #999999;\">(<\/span>@SetFields<span style=\"color: #999999;\">) &#8211;<\/span> 1<span style=\"color: #999999;\">);<\/span><\/p>\n<p><\/span><span style=\"font-weight: inherit;\"><span style=\"color: #0000ff;\">TRUNCATE TABLE<\/span> stage<span style=\"color: #999999;\">.<\/span>sensor_parsed<span style=\"color: #999999;\">;<\/span><\/p>\n<p><\/span><span style=\"font-weight: inherit;\"><span style=\"color: #0000ff;\">SET<\/span> @sqlHeader <span style=\"color: #999999;\">=<\/span> <span style=\"color: #ff0000;\">&#8216;WITH RawData(RowID, FieldID, Date, SensorValue) AS (SELECT RowID, ROW_NUMBER() OVER(PARTITION BY RowID ORDER BY RowID) AS FieldID, LEFT(DataTXT, CHARINDEX(&#8216;<\/span><span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8221;&#8221;<\/span><span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8216;,&#8217;<\/span><span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8221;&#8221;<\/span><span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8216;, DataTXT, 1)-1) AS Date, value FROM stage.sensor s CROSS APPLY string_split (SUBSTRING(DataTXT, CHARINDEX(&#8216;<\/span><span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8221;&#8221;<\/span><span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8216;,&#8217;<\/span><span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8221;&#8221;<\/span><span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8216;, DataTXT, 1)+1, 5000), &#8216;<\/span><span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8221;&#8221;<\/span><span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8216;,&#8217;<\/span><span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8221;&#8221;<\/span><span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8216;) WHERE FileID = &#8216;<\/span><span style=\"color: #999999;\">+<\/span><span style=\"color: #ff00ff;\">CONVERT<\/span><span style=\"color: #999999;\">(<\/span><span style=\"color: #0000ff;\">VARCHAR<\/span><span style=\"color: #999999;\">(<\/span>10<span style=\"color: #999999;\">),<\/span> @FileID<span style=\"color: #999999;\">)+<\/span><span style=\"color: #ff0000;\">&#8216; AND RowID &gt;= 1 and isnumeric(value) = 1), Result(RowID, Date, FieldID, FieldName, SensorValue) AS (SELECT r.RowID, Date, m.FieldID, m.FieldName, r.SensorValue FROM stage.metadata AS m JOIN RawData AS r ON m.FieldID = r.FieldID where m.FileID = &#8216;<\/span><span style=\"color: #999999;\">+<\/span><span style=\"color: #ff00ff;\">CONVERT<\/span><span style=\"color: #999999;\">(<\/span><span style=\"color: #0000ff;\">VARCHAR<\/span><span style=\"color: #999999;\">(<\/span>10<span style=\"color: #999999;\">),<\/span> @FileID<span style=\"color: #999999;\">)+<\/span><span style=\"color: #ff0000;\">&#8216;) &#8216;<\/span><span style=\"color: #999999;\">;<\/span><\/p>\n<p><\/span><span style=\"font-weight: inherit;\"><span style=\"color: #0000ff;\">SET<\/span> @sqlStage <span style=\"color: #999999;\">=<\/span> <span style=\"color: #ff0000;\">&#8216;insert into stage.sensor_parsed (RowID, Date, EquipmentID, FieldID, FieldName, SensorValue) select RowID, Date, FieldID, FieldName, SensorValue from Result where SensorValue is not NULL&#8217;<\/span><span style=\"color: #999999;\">;<\/span><\/p>\n<p><\/span><span style=\"font-weight: inherit;\"><span style=\"color: #0000ff;\">SET<\/span> @sqlCMD <span style=\"color: #999999;\">=<\/span> @sqlHeader <span style=\"color: #999999;\">+<\/span> @sqlStage<span style=\"color: #999999;\">;<\/span><\/p>\n<p><\/span><span style=\"font-weight: inherit;\"><span style=\"color: #0000ff;\">EXEC<\/span> <span style=\"color: #800000;\">sp_executesql<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0@sqlCMD<span style=\"color: #999999;\">;<\/span><\/p>\n<p><\/span><span style=\"font-weight: inherit;\"><span style=\"color: #0000ff;\">SET<\/span> @sqlCMD <span style=\"color: #999999;\">=<\/span> <span style=\"color: #ff0000;\">&#8221;<\/span><span style=\"color: #999999;\">;<\/span><\/p>\n<p><\/span><span style=\"font-weight: inherit;\"><span style=\"color: #0000ff;\">SELECT<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0@sqlCMD <span style=\"color: #999999;\">=<\/span> <span style=\"color: #ff0000;\">&#8216;merge dbo.sensor as target using (SELECT &#8216;<\/span><span style=\"color: #999999;\">+<\/span><span style=\"color: #ff00ff;\">CONVERT<\/span><span style=\"color: #999999;\">(<\/span> <span style=\"color: #0000ff;\">VARCHAR<\/span><span style=\"color: #999999;\">(<\/span>10<span style=\"color: #999999;\">),<\/span> @FileID<span style=\"color: #999999;\">)+<\/span><span style=\"color: #ff0000;\">&#8216;, [Date], &#8216;<\/span><span style=\"color: #999999;\">+<\/span>@Fields<span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8216;,\u00a0 getdate() as [CreateDT] from (select [RowID], [Date], [FieldName], [SensorValue] from stage.sensor_parsed ) as s Pivot(Max([SensorValue]) for [FieldName] in (&#8216;<\/span><span style=\"color: #999999;\">+<\/span>@Fields<span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8216;)) as p) as source ([FileID], [Date], &#8216;<\/span><span style=\"color: #999999;\">+<\/span>@Fields<span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8216;, [CreateDT]) on (target.[Date] = source.[Date] and target.[FileID] = source.[FileID]) when matched then update set &#8216;<\/span><span style=\"color: #999999;\">+<\/span>@SetFields<span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8216; when not matched then insert([FileID], [Date], &#8216;<\/span><span style=\"color: #999999;\">+<\/span>@Fields<span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8216;, [CreateDT]) values (source.[FileID], source.[Date], &#8216;<\/span><span style=\"color: #999999;\">+<\/span>@SourceFields<span style=\"color: #999999;\">+<\/span><span style=\"color: #ff0000;\">&#8216;, source.[CreateDT]);&#8217;<\/span><span style=\"color: #999999;\">;<\/span><\/p>\n<p><\/span><span style=\"font-weight: inherit;\"><span style=\"color: #0000ff;\">EXEC<\/span> <span style=\"color: #800000;\">sp_executesql<\/span><br \/>\n<\/span><span style=\"font-weight: inherit;\">\u00a0 \u00a0@sqlCMD<span style=\"color: #999999;\">;<\/span><\/span><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<h4>Summary<\/h4>\n<p>These project requirements really drove the direction of this process, resulting in a SQL table that can expand horizontally as new fields come in through the CSV. However, there are endless scenarios making for an even simpler process, assuming requirements differ from the above-mentioned project, and you have the freedom to consider other storage methods, such as JSON.<\/p>\n<p>String_Split is a fantastic new tool for dealing with edge cases and in this engagement, it provided a fairly elegant solution which would have been next to impossible in prior versions of SQL Server.<\/p>\n<p>Have questions about SQL? Our team of experts would love to <a href=\"\/get-started\/\">chat with you<\/a>!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>New functions within SQL Server 2016 allow you to combine dynamic SQL statements &amp; String_Split, allowing you to take a list of strings &amp; parse them into rows.<\/p>\n","protected":false},"author":21,"featured_media":14631,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"content-type":"","footnotes":""},"categories":[395,260],"tags":[319],"class_list":["post-15927","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-data-science-ai","category-data-ai","tag-machine-learning-ai","topics-blog"],"acf":[],"_links":{"self":[{"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/posts\/15927","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=15927"}],"version-history":[{"count":0,"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/posts\/15927\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/media\/14631"}],"wp:attachment":[{"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/media?parent=15927"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/categories?post=15927"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/3cloudsolutions.com\/wp-json\/wp\/v2\/tags?post=15927"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}