<?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>Blog of Wolfgang Werner</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/wwerner1/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/wwerner1/feed.xml</link>
    <description />
    <lastBuildDate>Tue, 16 Apr 2019 05:37:07 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Install of SQL Server 2012 on Windows Server 2012 R2</title>
      <link>https://docs.microsoft.com/archive/blogs/wwerner1/install-of-sql-server-2012-on-windows-server-2012-r2</link>
      <pubDate>Fri, 13 Dec 2013 05:24:00 GMT</pubDate>
      <dc:creator><![CDATA[Wolfgang Werner MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/wwerner1/2013/12/13/install-of-sql-server-2012-on-windows-server-2012-r2/</guid>
      <description><![CDATA[I recently had a customer engagement where the customer wanted to install a SQL 2012&nbsp;two node...]]></description>
      <content:encoded><![CDATA[<p>I recently had a customer engagement where the customer wanted to install a SQL 2012&nbsp;two node Failover Cluster with one instance. OK, easy, did this a couple of times.</p>
<p>But then the customer came up with the Information, that the OS will be Windows Server 2012 R2. OK, older SQL on newer OS is very often a case where we need a service pack as minimum requirement. And yes, there is an article in MSDN.</p>
<p><a href="https://support.microsoft.com/kb/2681562/en-us">https://support.microsoft.com/kb/2681562/en-us</a></p>
<p>SP1 is needed for the Support of SQL 2012 on Windows Server 2012 R2. In the past there was often something like this, just remember SQL 2008 on Windows Server 2008 R2. It was needed to just install SQL 2008 RTM and apply the SQL SP afterwards.</p>
<p>There are already media out there for SQL 2012 including SP1, but my customer only had the SQL 2012 RTM Version. So we tried it with the RTM Version, and wanted to apply the SP1 afterwards, because there was no recommendation until this time if SQL 2012 RTM with SP1 install is needed, or only SQL 2012 RTM and apply the SP after that.</p>
<p>So we started the Installation from the SQL 2012 RTM media, it all went well, the Progress bar with the insallation started.... and it was just nearly in the middle, when a message box came up, that the SQL Browser Service could not start. Hmm... went to services console, start.... no, error, could not start. So a look into the Windows Eventlog was needed. There was a nice little message...<span style="font-family: 'Segoe UI','sans-serif'; font-size: 9pt; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-ansi-language: EN-US; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">The SQL configuration for SQL is inaccessible or invalid. Hmm...</span></p>
<p><span style="font-family: 'Segoe UI','sans-serif'; font-size: 9pt; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-ansi-language: EN-US; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">OK, we cancelled the setup and observed that was installed in 'Programs and features'. Setup Support files, SQL Browser, SQL Server (64 Bit), nearly everything was listed what is known for a successful installation. So we decided to de-install everything. It worked, only the 'SQL Server (64 Bit)' did not find any instance. OK, this really comes at the end of a SQL cluster installation, we stopped earlier.</span></p>
<p><span style="font-family: 'Segoe UI','sans-serif'; font-size: 9pt; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-ansi-language: EN-US; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">I wanted to cleanup all the stuff in 'Program Files' and so on, but it was locked somehow.... OK, we did a reboot on both servers. Still no cleanup on Node 1 of the already installed bits in Program files etc. possible. So we left them there.</span></p>
<p><span style="font-family: 'Segoe UI','sans-serif'; font-size: 9pt; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-ansi-language: EN-US; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">We now decided to go a different way. There is a great thing in SQL 2012 setup, called SMART SETUP. If the server is in the internet it will check there for updates. Server, in the internet..... huuuuhhhhh. No, never ever. But there is another option, you can point SQL Server Setup to a directory with the updates. So, we already had SQL 2012 SP1 setup there (This is around some 900 MB). We decided to go also for a cumulative update (CU), 6 was the actual one, so we downloaded this also. We created a directory on drive C of the server (C:\SQLUpdates, name it as you want, it is only an example here...) and copied both updates into this directory. Be careful: The CU update is in a self extracting zip, so unzip is first to get the exe and copy this to the directory.</span></p>
<p><span style="font-family: 'Segoe UI','sans-serif'; font-size: 9pt; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-ansi-language: EN-US; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">So, how does SQL Setup now finds the updates?</span></p>
<p>&nbsp;Easy.... but not with a nomal double click on Setup.exe. Open a command line, and type 'SETUP.EXE /Action=installfailovercluster /updatesource=C:\SQLupdates'</p>
<p>When you install a normal SQL instance, the Action is 'install', when you add a node to a SQL Cluster the Action is 'addnode'.</p>
<p>We started the Setup from the SQL 2012 RTM media this way, on one of the pages at the beginning SQL Server tells you what it found (Service Pack 1 and another update on top of this, right) and... the Setup ran until the end, and our Failovercluster instance was there (It was fine with all the stuff alredy in Program Files etc.... very great Setup). We did the same on node 2, with Action as 'addnode' (Copy the updates locally to node 2, or share the Directory and call in /updatesource= from an UNC path. ) and it was fine. The Cluster was up and running.</p>
<p>In the meantime, the article above says that there might be Setup issues and SQL 2012 SP1 with Minimum CU3 should be used. Ah, really, I can prove this, you better go with a CU on top of SP1 to be on the safe side. Check the MSDN article frequently, it is updated from time to time with new Information.</p>]]></content:encoded>
    </item>
    <item>
      <title>Partitioning… some tricks and hints to create the switch in and out table</title>
      <link>https://docs.microsoft.com/archive/blogs/wwerner1/partitioning-some-tricks-and-hints-to-create-the-switch-in-and-out-table</link>
      <pubDate>Tue, 16 Apr 2013 02:45:00 GMT</pubDate>
      <dc:creator><![CDATA[Wolfgang Werner MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/wwerner1/2013/04/16/partitioning-some-tricks-and-hints-to-create-the-switch-in-and-out-table/</guid>
      <description><![CDATA[Partitioning of tables in SQL Server is commonly used since the transparent partitioning was...]]></description>
      <content:encoded><![CDATA[<p>Partitioning of tables in SQL Server is commonly used since the transparent partitioning was introduced in SQL 2005. No longer need for union all views and stuff like that, just insert, update, delete on a table without the need to remember how the table was partitioned over hundreds of single tables.</p>
<p>I did a lot of partitioning, in DWH scenarios (to load data in and out, to get rid of old data, effective queries with partition elimination) and in OLTP (old data out without blocking of a delete query, partition elimination).</p>
<p>With column store indexes in SQL 2012 you need to do partitioning to get data into the table if the columnstore index is already there. No insert, update, delete possible with activated column store index.</p>
<p>In my last project, an OLTP like DWH, I partitioned 25 tables, just for the purpose to get rid of old data, and to use partition elimination when querying the data and processing the OLAP cubes on top of the data.</p>
<p>When you want to snap a Partition&nbsp;out (and also&nbsp;into a&nbsp;table) you need to have a table with the exactly same structure as the partitioned table, and it must be in the same filegroup as the partition you want to work with. Most people use the &lsquo;select top 0 * from dbo.foo into dbo.foo_copy&rsquo;. This works, but try this with data compression or when your table just has calculated columns. It will fail.</p>
<p>So, OK, other idea (and what I used mostly in the past): I create a table dbo.PartitionConfig, which has a row for each partitioned table, and columns like &lsquo;tablename&rsquo;, &lsquo;create_table_statement&rsquo;, &lsquo;PK_statement&rsquo;, 'Index_statement&rsquo;, &lsquo;drop_statement&rsquo; and so on. With this I can set the commands configurable and in my partitioning code I just get the data from the table, nothing hardcoded. Dynamic SQL with the commands, and go. Works fine.</p>
<p>But with this the next problem came up: imagine you have different systems (Dev, Test1, Test2, ProdTest and Production). There will be the time, when you need to do an &lsquo;alter table&rsquo; to a partitioned table with data. No problem, this works, but alter table adds the column at the right end of the table. If you support &lsquo;full builds&rsquo; and &lsquo;update builds&rsquo; in your building environment there will be a time, when the create table statement for the &lsquo;full build&rsquo; will have a different column order than the &lsquo;update build&rsquo; based on alter table commands. Believe me, in a project with 5 or more SQL Dev guys this will happen :-).</p>
<p>What is the problem with this? When switching out or in data in partitioning SQL Server does not care about the column names, so being in wrong order will be a problem. My config table was filled with a SQL script, and I was not willing to do a &lsquo;new build&rsquo; and an &lsquo;update build&rsquo; script just for the column order.</p>
<p>So this was the point to do something new. So I wrote some SP code to read the metadata from the partitioned table, do a &lsquo;create table&rsquo; statement with this, find out the primary key, the compression state of the partition (yes, true, you can compress a partition each for its own, with difference in page or row compression). And, find out about calculated columns!</p>
<p>For the ease of this example I just do not use a partitioned table here, my code also works without partitions. Why this works? Well, also a not partitioned table is a table which consists of one partition, the whole table :-).</p>
<p>Attached you will find the Procs and Scripts to setup the Scenario, they are commented inside the code. Here are only the calls and what happens.</p>
<p>This is my table example:</p>
<p><span style="font-family: courier new,courier;">CREATE</span><br /><span style="font-family: courier new,courier;">TABLE PartTest</span></p>
<p><span style="font-family: courier new,courier;">(</span><span style="font-family: courier new,courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ID INT NOT NULL,</span></p>
<p><span style="font-family: courier new,courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DayDate DATE NOT NULL,</span></p>
<p><span style="font-family: courier new,courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DayTime TIME(0) NOT NULL,</span></p>
<p><span style="font-family: courier new,courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AnyValue decimal(10,2) NOT NULL,</span></p>
<p><span style="font-family: courier new,courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AnyString </span><span style="font-family: courier new,courier;">VARCHAR(255) NOT NULL,</span></p>
<p><span style="font-family: courier new,courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CalcColumn </span><span style="font-family: courier new,courier;">AS 5 * ID,</span></p>
<p><span style="font-family: courier new,courier;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CONSTRAINT </span><span style="font-family: courier new,courier;">PK_PartTest PRIMARY </span><span style="font-family: courier new,courier;">KEY (ID, DayDate)</span></p>
<p><span style="font-family: courier new,courier;">) WITH (DATA_COMPRESSION = ROW)</span></p>
<p><span style="font-family: courier new,courier;">GO</span></p>
<p>First of all, the copy of the table already exists, so I have a procedure to drop it.</p>
<p><span style="font-family: courier new,courier;">EXEC [dbo].[usp_DropSwitchOutTable] 'dbo.PartTest_Out'</span></p>
<p>Then, just create the copy using the stored procedure. The first parameter is the source table, the second one is the name of the table I want to create as a copy, the third is the filegroup in which the copy table should be created (it must be the same as the source table when switch in and out of partitions should work, as already mentioned).</p>
<p><span style="font-family: courier new,courier;">EXEC [dbo].[usp_CreateSwitchOutTable] 'dbo.PartTest', 'dbo.PartTest_Out', 'PRIMARY'</span></p>
<p>Check out your database, it now has a second table, it should have the same structure as the source table and is in the filegroup you specified. How do I find out in which filegroup my<br />partition is, if I have a table with partitions spread over multiple groups? You find out in the metadata, just check my attached function.</p>
<p><span style="font-family: courier new,courier;">dbo.usf_GetFilegroupForPartition</span></p>
<p>Parameters are Name of the partition schema, and the Partition number. How to get this number, see later in this article. How to get the partition schema? Oh, just use my attached function</p>
<p><span style="font-family: courier new,courier;">dbo.usf_GetPartitionSchemaForTable</span></p>
<p>All you need is the table name in the form of schema.tablename.</p>
<p>Next step is to add the primary key to the copy table.</p>
<p><span style="font-family: courier new,courier;">EXEC [dbo].[usp_CreatePrimaryKeySwitchOutTable] 'dbo.PartTest', 'dbo.PartTest_Out'</span></p>
<p>Check it out, there will be a PK now on the copy.</p>
<p>Finally, find out the compression and apply it to the copy table.</p>
<p><span style="font-family: courier new,courier;">EXEC [dbo].[usp_SetSwitchOutTableCompression] 'dbo.PartTest', 'dbo.PartTest_Out', 1</span></p>
<p>Check out your copy table, it will now have row compression. The third parameter is&nbsp;the partition number, without partitions in the table this is 1, if you want to use a partition inside your table you must find out the number from the metadata. The easiest way to do so is the $partition metadata function, you just need to know the name of the partition function of your table, and by which criteria it is partitioned, so if it is by day, just throw in the date, and you get the Partition ID or number in which the data for that day is in. Check out <a href="https://msdn.microsoft.com/de-de/library/ms188071.aspx">https://msdn.microsoft.com/de-de/library/ms188071.aspx</a>&nbsp;for the details of the function.</p>
<p>If you do not know the name of the partition function of your table, use my attached function.</p>
<p><span style="font-family: courier new,courier;">dbo.usf_GetPartitionFunctionForTable</span></p>
<p>All you need is the table name in the form of schema.tablename.</p>
<p>So, this is all the magic.</p>
<p>Do a test on your own, just add a column to the table with alter table, and run the code again. The copy table should have the row added also. Or try to create the table in a filegroup other than PRIMARY. Or, much better, setup a partitioned table and use my code.</p>
<p>What to do with the created copy of the table? Well, just identity the partition you want to switch out of your partitioned table and use the alter table switch Partition statement to move the data out, drop the copy table, and the data is gone. Don&rsquo;t forget to so a merge on your partitioned table afterwards to get rid of the &lsquo;hole&rsquo; in your table.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Components.PostAttachments/00/10/41/13/70/Partitioning.zip" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-components-postattachments/00-10-41-13-70/Partitioning.zip">Partitioning.zip</a></p>]]></content:encoded>
    </item>
    <item>
      <title>HDInsight.... how to upload data</title>
      <link>https://docs.microsoft.com/archive/blogs/wwerner1/hdinsight-how-to-upload-data</link>
      <pubDate>Mon, 10 Dec 2012 03:06:00 GMT</pubDate>
      <dc:creator><![CDATA[Wolfgang Werner MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/wwerner1/2012/12/10/hdinsight-how-to-upload-data/</guid>
      <description><![CDATA[I did some tests with the HDInsight CTP implementation on Windows. I have used theone on my own...]]></description>
      <content:encoded><![CDATA[<p>I did some tests with the HDInsight CTP implementation on Windows. I have used the<br />one on my own server machine not the HDInsight for Azure.</p>
<p>I have installed it, and did the example that comes with the CTP. Ok, it worked, but I<br />wanted to upload some of my own data and create a hadoop table on my csv &lsquo;big&rsquo;<br />data.</p>
<p>But how?</p>
<p>I have inspected the scripts from the example and found out, that HDInsight is just a<br />structure of data files. So uploading the files is necessary.</p>
<p>The right thing to use for any action in HDInsight is the Hadoop console, the link is on<br />the desktop.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/45/35/7181.blog1.jpg" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-45-35/7181.blog1.jpg"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/45/35/7181.blog1.jpg" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x200/__key/communityserver-blogs-components-weblogfiles/00-00-01-45-35/7181.blog1.jpg" alt="" border="0" /></a></p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/45/35/2526.blog2.jpg" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-45-35/2526.blog2.jpg"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/45/35/2526.blog2.jpg" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x300/__key/communityserver-blogs-components-weblogfiles/00-00-01-45-35/2526.blog2.jpg" alt="" border="0" /></a></p>
<p>To upload data one should in the first step create a directory for the data. This<br />can simply be done by using the hadoop command line tool in the console.</p>
<p><span style="font-family: courier new,courier;">hadoop dfs -mkdir /test/data</span></p>
<p>easy&hellip;.</p>
<p>To upload data:</p>
<p><span style="font-family: courier new,courier;">hadoop dfs -copyFromLocal C:\temp\testdata.csv /test/data/testdata.csv</span></p>
<p>easy&hellip;.</p>
<p>Get rid of the data again?</p>
<p><span style="font-family: courier new,courier;">hadoop dfs -rmr -skipTrash /test</span></p>
<p>Be careful, -rmr means recursive, so it kills also all subdirectories and files in<br />there. And&hellip; -skipTrash means that is skips the trash, so it is gone immediately<br />with no way to get it back.</p>
<p>For better use of the commands I have changed some of the CTP examples powershell<br />scripts to be more generic. The powershell must be called in the hadoop command<br />window. The powershell syntax there in General</p>
<p><span style="font-family: courier new,courier;">powershell -ExecutionPolicy unrestricted &ndash;F &lt;SCRIPTNAME&gt;.ps1 &lt;ARGUMENTS&gt;&hellip;</span></p>
<p>I have created three powershell scripts, create_Directory.ps1, import_Data.ps1 and<br />remove_Directory.ps1.</p>
<p>create_Directory.ps1:</p>
<p><span style="font-family: courier new,courier;">powershell -ExecutionPolicy unrestricted &ndash;F create_Directory.ps1 &lt;DIRECTORY&gt;</span></p>
<p><span style="font-family: courier new,courier;">&lt;DIRECTORY&gt;</span> can be /test, /test/data, test/data/whatever&hellip; and so on.</p>
<p>import_Data.ps1:</p>
<p><span style="font-family: courier new,courier;">powershell -ExecutionPolicy unrestricted &ndash;F import_Data.ps1 &lt;SourceDirectory&gt; &lt;SourceFile&gt; &lt;targetDirectory&gt; &lt;targetFile&gt;</span></p>
<p>remove_Directory.ps1:</p>
<p><span style="font-family: courier new,courier;">powershell -ExecutionPolicy unrestricted &ndash;F remove_Directory.ps1 &lt;DIRECTORY&gt;</span></p>
<p>Be careful, the calls inside are recursive and with skip trash.</p>
<p>The powershell scripts are attached. Have fun :-)</p>
<p>Perhaps I do another blog post on how to create a simple table out of the imported data&hellip;.</p>
<p>&nbsp;</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Components.PostAttachments/00/10/37/60/20/Scripts.zip" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-components-postattachments/00-10-37-60-20/Scripts.zip">Scripts.zip</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Column encrypted in Database, but decrypted in clear text in Cube</title>
      <link>https://docs.microsoft.com/archive/blogs/wwerner1/column-encrypted-in-database-but-decrypted-in-clear-text-in-cube</link>
      <pubDate>Tue, 04 Dec 2012 08:31:00 GMT</pubDate>
      <dc:creator><![CDATA[Wolfgang Werner MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/wwerner1/2012/12/04/column-encrypted-in-database-but-decrypted-in-clear-text-in-cube/</guid>
      <description><![CDATA[In one of my last projects I have had the following challenge: The customer wanted meto encrypt a...]]></description>
      <content:encoded><![CDATA[<p><span style="font-size: small;">In one of my last projects I have had the following challenge: The customer wanted me</span><br /><span style="font-size: small;">to encrypt a sensitive column in the database, better say in a dimension table.</span><br /><span style="font-size: small;">But, the column must show up in the cube when browsing, and it has to be</span><br /><span style="font-size: small;">clearly readable there. So the customer wanted to prevent that someone can</span><br /><span style="font-size: small;">select the data in clear text from the table, or steal the backup of the</span><br /><span style="font-size: small;">database and is able to read the data. But someone who has the right to browse</span><br /><span style="font-size: small;">the Cube must read the column (the column is used as the name column in the</span><br /><span style="font-size: small;">dimension).</span></p>
<p>OK, tough goal. But why?</p>
<p><span style="font-size: small;">To encrypt a column in the database (be aware: this has nothing to do with</span><br /><span style="font-size: small;">transparent data encryption, TDE, this here is about encryption of a single</span><br /><span style="font-size: small;">column in a table) you create the column as varbinary(128). Then you need a</span><br /><span style="font-size: small;">key, symmetric or asymmetric, to encrypt and decrypt. Or a certificate.</span></p>
<p><span style="font-size: small;">So, what can one use here?</span></p>
<p><span style="font-size: small;">Encrypt by Key:</span></p>
<ul>
<li><span style="line-height: 107%; font-family: 'Calibri','sans-serif'; font-size: small; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">Symmetric or asymmetric</span></li>
<li><span style="line-height: 107%; font-family: 'Calibri','sans-serif'; font-size: small; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">Key will be generated in the database</span></li>
<li><span style="line-height: 107%; font-family: 'Calibri','sans-serif'; font-size: small; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">You need an additional master key in the database, which protects this key</span></li>
<li><span style="font-size: small;"><span style="line-height: 107%; font-family: 'Calibri','sans-serif'; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">You </span><span style="line-height: 107%; font-family: 'Calibri','sans-serif'; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">open the key, and encrypt</span></span></li>
<li><span style="line-height: 107%; font-family: 'Calibri','sans-serif'; font-size: small; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">You open the key, and decrypt</span></li>
<li><span style="line-height: 107%; font-family: 'Calibri','sans-serif'; font-size: small; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">There are different algorithms (DES, AES etc&hellip;)</span></li>
</ul>
<p><span style="font-size: small;">Encrypt by certificate:</span></p>
<ul>
<li><span style="line-height: 107%; font-family: 'Calibri','sans-serif'; font-size: small; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">Create a certificate</span></li>
<li><span style="line-height: 107%; font-family: 'Calibri','sans-serif'; font-size: small; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">Encryption using certificates is based on asymmetric algorithms</span></li>
</ul>
<p><span style="font-size: small;">So, OK, first stop here: the requirement was, that the dimension table can hold some</span><br /><span style="font-size: small;">100 000 rows. But asymmetric encryption is not recommended for lots of rows.</span><br /><span style="font-size: small;">So, encrypt by certificate only works with asymmetric algorithms, so this is</span><br /><span style="font-size: small;">not an option.</span></p>
<p><span style="font-size: small;">So we need to go for Encryption by key. But here our next problem comes up: We must</span><br /><span style="font-size: small;">open the key first to use it.</span></p>
<p><span style="font-size: small;">We should reflect on how the data from a SQL Server database is read from SSAS. It</span><br /><span style="font-size: small;">can be done directly from the table, or, the recommended way, using a view. But</span><br /><span style="font-size: small;">in both ways it is not possible to use an &lsquo;open key&rsquo; in the SQL code&hellip;.</span></p>
<p><span style="line-height: 107%; font-family: 'Calibri','sans-serif'; font-size: medium; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">One possibility is to use a view, and inside the view use a stored procedure with<br />open query. In the procedure you can run any code you want. I tried that, and<br />it worked&hellip;. But, should I really go for this?</span></p>
<p><span style="font-size: medium;"><span style="line-height: 107%; font-family: 'Calibri','sans-serif'; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US"><span style="line-height: 107%; font-family: 'Calibri','sans-serif'; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">Just think a little bit more about it. And, there is a solution. The key must open<br />itself when needed, and really, there is a possibility to do that. All you need<br />is a combination of symmetric and asymmetric keys, a SQL Server key and some<br />permissions </span></span><span style="line-height: 107%; font-family: 'Calibri','sans-serif'; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US"><span style="line-height: 107%; font-family: 'Calibri','sans-serif'; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">for the right account&hellip;.</span></span></span></p>
<p><span style="font-size: small;">Let&rsquo;s do it using demo code&hellip;</span></p>
<p><span style="line-height: 107%; font-family: 'Calibri','sans-serif'; font-size: medium; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">First we need a demo DB:</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">use master</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">go</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">CREATE DATABASE Crypt</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">go</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">use Crypt</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">GO</span></p>
<p><span style="font-size: medium;">Then we need a database master key:</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">-- Database Master Key.... we need one&hellip;.</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">if not exists (select</span><br /><span style="font-family: courier new,courier; font-size: small;">* from sys.symmetric_keys where symmetric_key_id = 101)</span></p>
<p><span style="line-height: 107%; font-family: courier new,courier; font-size: small; mso-ansi-language: EN-US; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-fareast-language: EN-US; mso-bidi-language: AR-SA; mso-bidi-font-size: 11.0pt;" lang="EN-US">create master key encryption by password =<br />'Password123'</span></p>
<p><span style="font-size: medium;">Do not forget the password that you set here!!! If you need to restore the database</span><br /><span style="font-size: medium;">later on a different instance of SQL Server, you need that, otherwise the</span><br /><span style="font-size: medium;">encrypted data stays encrypted forever&hellip;.</span></p>
<p><span style="font-size: medium;">To protect (encrypt) this key, we connect it to the SQL instance the DB is running</span><br /><span style="font-size: medium;">on. This prevents people from opening the master key when stealing a backup,</span><br /><span style="font-size: medium;">and we need it for the automatic opening later. The Service Master Key opens</span><br /><span style="font-size: medium;">automatically when the instance starts running and decrypts the database master</span><br /><span style="font-size: medium;">key. But: the database master key is encrypted by the service master key, so</span><br /><span style="font-size: medium;">the DB is bound to the SQL instance for that to work:</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">ALTER MASTER KEY ADD</span><br /><span style="font-family: courier new,courier; font-size: small;">ENCRYPTION BY SERVICE MASTER KEY</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">go</span></p>
<p><span style="font-size: medium;">Now we create an asymmetric key. This will not be used for the column encryption</span><br /><span style="font-size: medium;">(asymmetric is bad for a lot of data, you remember&hellip;.), it is used to protect a</span><br /><span style="font-size: medium;">symmetric key we create afterwards:</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">IF NOT EXISTS (select</span><br /><span style="font-family: courier new,courier; font-size: small;">* from sys.asymmetric_keys where name = 'STORE_CRYPT_ASYM')</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">BEGIN</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Create asymmetric key STORE_CRYPT_ASYM</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with algorithm = RSA_2048;</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">END</span></p>
<p><span style="font-size: medium;">Use any algorithm here which is available, I went for the RSA_2048.</span></p>
<p><span style="font-size: medium;">Now we create the symmetric key, which will be encrypted by the asymmetric key we just</span><br /><span style="font-size: medium;">created. With this trick, we do not need to encrypt the symmetric key with a</span><br /><span style="font-size: medium;">password, which must later be used for an &lsquo;open key&rsquo;&hellip;..</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">IF NOT EXISTS (select</span><br /><span style="font-family: courier new,courier; font-size: small;">* from sys.symmetric_keys where name = 'STORE_CRYPT')</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">BEGIN</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">create symmetric key STORE_CRYPT with</span><br /><span style="font-family: courier new,courier; font-size: small;">algorithm = DES</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; encryption by asymmetric key STORE_CRYPT_ASYM</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">END</span></p>
<p><span style="font-size: medium;">Here we use the DES on the symmetric keys, it is just my favor, use any that is</span><br /><span style="font-size: medium;">supported there, look at books online.</span></p>
<p><span style="font-size: medium;">Now we create a table:</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">CREATE TABLE Demo</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">(</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ID int identity(1,1) not null PRIMARY KEY,</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CryptColumn varbinary(128) not null,</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InsertDateTime datetime not null</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">)</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">GO</span></p>
<p><span style="font-size: medium;">For the inserts into the table, we need to open the symmetric key first&hellip;</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">open symmetric key</span><br /><span style="font-family: courier new,courier; font-size: small;">STORE_CRYPT&nbsp;decryption by Asymmetric key STORE_CRYPT_ASYM</span></p>
<p><span style="font-size: medium;">This works because we are sysadmin on the SQL instance. It also works as a dbo in</span><br /><span style="font-size: medium;">the database. For others to do that, you need the right permissions set&hellip;. More</span><br /><span style="font-size: medium;">later to come here on that topic.</span></p>
<p><span style="font-size: medium;">We do an insert into the Table&hellip;</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">insert into dbo.Demo</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">(CryptColumn, InsertDateTime)</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">select EncryptByKey(Key_GUID('STORE_CRYPT'), 'Dies ist ein Test'), GETDATE()</span></p>
<p><span style="font-size: medium;">Close the key afterwards</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">close symmetric key STORE_CRYPT</span></p>
<p><span style="font-size: medium;">We do a select on the table&hellip;</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/45/35/3731.blog1.jpg" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-45-35/3731.blog1.jpg"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/45/35/3731.blog1.jpg" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x200/__key/communityserver-blogs-components-weblogfiles/00-00-01-45-35/3731.blog1.jpg" alt="" border="0" /></a></p>
<p><span style="font-size: medium;">All fine, not readable with a normal select.</span></p>
<p><span style="font-size: medium;">Now we create a role in the database</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">if not exists (select * from sys.sysusers where name = 'DeCryptColumns' and issqlrole = 1)</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create role DeCryptColumns</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">go</span></p>
<p><span style="font-size: medium;">We give this role the two rights necessary to use the Keys&hellip;</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">grant view definition on symmetric key::STORE_CRYPT to [DeCryptColumns];</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">go</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">grant control on asymmetric key::STORE_CRYPT_ASYM to [DeCryptColumns]</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">go</span></p>
<p><span style="font-size: medium;">A select on the table with the decrypt column code inside&hellip; and, just remember&hellip;. We did</span><br /><span style="font-size: medium;">not open the key as we did for the insert!</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">s</span><span style="font-family: courier new,courier; font-size: small;">elect ID,</span></p>
<p><span style="font-family: courier new,courier; font-size: small;">CONVERT(VARCHAR,</span><br /><span style="font-family: courier new,courier; font-size: small;">DecryptByKeyAutoAsymKey(AsymKey_ID('STORE_CRYPT_ASYM'), NULL, CryptColumn)) AS CryptColumn,</span><br /><span style="font-family: courier new,courier; font-size: small;">InsertDateTime FROM dbo.Demo</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/45/35/1030.blog2.jpg" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-45-35/1030.blog2.jpg"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/45/35/1030.blog2.jpg" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x350/__key/communityserver-blogs-components-weblogfiles/00-00-01-45-35/1030.blog2.jpg" alt="" width="660" height="154" border="0" /></a></p>
<p><span style="font-size: medium;">This happens when you have the right to use the keys, they open by itself and</span><br /><span style="font-size: medium;">decrypt.</span></p>
<p><span style="font-size: medium;">If you don&rsquo;t have the rights, your just get NULL instead of the clear text in the</span><br /><span style="font-size: medium;">CryptColumn data column.</span></p>
<p><span style="font-size: medium;">So, all you need to do, to have this in clear text in the SSAS cube is&hellip;..</span></p>
<ul>
<li><span style="line-height: 107%; font-family: 'Calibri','sans-serif'; font-size: 11pt; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">Use a view to select the data for the cube and include the DecryptByKeyAutoAsymKey<br />for the column to be decrypted</span></li>
<li><span style="line-height: 107%; font-family: 'Calibri','sans-serif'; font-size: 11pt; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">Create the account used for processing the cube as login in the SQL Server instance<br />(you must do so in any case, otherwise you have no chance to read the data)</span></li>
<li><span style="line-height: 107%; font-family: 'Calibri','sans-serif'; font-size: 11pt; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">Map the login additionally to the DeCryptColumns role in the database. I suppose<br />you gave the account already the db_datareader role to be able to read data in<br />the database to fill the cube</span></li>
<li><span style="line-height: 107%; font-family: 'Calibri','sans-serif'; font-size: 11pt; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;" lang="EN-US">Process the cube, and the column is in clear text when browsing the cube</span></li>
</ul>
<p><span style="font-size: medium;">The data is readable in the cube, but encrypted in the table. So someone without rights</span><br /><span style="font-size: medium;">to the keys only sees NULL, and someone who steals the database backup also</span><br /><span style="font-size: medium;">sees encrypted data, or NULL.</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>]]></content:encoded>
    </item>
    <item>
      <title>SSIS - what to do if query is slow</title>
      <link>https://docs.microsoft.com/archive/blogs/wwerner1/ssis-what-to-do-if-query-is-slow</link>
      <pubDate>Wed, 28 Sep 2011 05:06:19 GMT</pubDate>
      <dc:creator><![CDATA[Wolfgang Werner MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/wwerner1/2011/09/28/ssis-what-to-do-if-query-is-slow/</guid>
      <description><![CDATA[I have had a problem at a customer where a source query in a data flow task was slow. I ran the...]]></description>
      <content:encoded><![CDATA[<p>I have had a problem at a customer where a source query in a data flow task was slow. I ran the query in Management Studio and it was fast. There was a filter in the query to limit the data for one day, realized with two SSIS Variables passed to the query with the usual SSIS method by putting them in the query as question mark (?). When I ran it in Management Studio I have of course set the filter in the query directly, so I replaced the ? with the real dates.<br />This was the only difference I found out between being slow in SSIS and fast in Management Studio (OK, to be precise, I also changed the query a little bit to have less IO than in the original one, but this did not change anything dramatically in the time the query took on SSIS).</p>
<p><br />So I added a script task in SSIS and build up the whole query, including the values from the variables, in VB.net in the script task, and gave it back as another variable. I have used the variable with the query inside than as datasource in the data flow task and... it was fast, also in SSIS.<br />It seems to me that sometimes passing variables with the placeholders ? in SSIS really generate bad execution plans in SQL Server. I changed some other things in the query, but the biggest effort was to not use the placeholders for the variables and build the query in a script task completely and just pass it to the data source.</p>
<p><br />By the way... the query (together with two other queries which I have changed accordingly) had a runtime of 3 hours for the SSIS package.... my changes brought it down to 1 min 40 seconds !</p>]]></content:encoded>
    </item>
    <item>
      <title>Be careful with string comparisons in SSIS</title>
      <link>https://docs.microsoft.com/archive/blogs/wwerner1/be-careful-with-string-comparisons-in-ssis</link>
      <pubDate>Fri, 09 Sep 2011 06:27:00 GMT</pubDate>
      <dc:creator><![CDATA[Wolfgang Werner MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/wwerner1/2011/09/09/be-careful-with-string-comparisons-in-ssis/</guid>
      <description><![CDATA[I have worked a lot with SSIS the last few months, and in every project with SSIS Icome across the...]]></description>
      <content:encoded><![CDATA[<p>I have worked a lot with SSIS the last few months, and in every project with SSIS I<br />come across the fact that SSIS treats strings with regards to upper and<br />lowercase.</p>
<p>Today SQL Server databases are in nearly 99% of all cases installed as<br />case-insensitive. So you get your data to import from somewhere, directly from<br />a database, by Excel Files, by csv-Files, and you just import to SQL Server.<br />During import to your data warehouse there are some lookups necessary, to look<br />for IDs in referential tables etc. All the import stuff on your DWH works fine,<br />for month, and then, suddenly, peng! The job failed last night. After hours of<br />investigation you found out that in one of the imports a string that you use<br />for a lookup task is written in different upper and lowercase than before, and<br />this crashes your lookup task, which is configured to fail the component if a<br />no lookup match happens.</p>
<p>So, my hint is: <strong>always compare strings in lowercase&nbsp;(or Uppercase, will also work)</strong></p>
<p>in SSIS if you cannot make sure that</p>
<p>the upper and lowercase on the important lookup columns always comes in the<br />right case. Just imagine, your data comes from UNIX, so upper and lower case is<br />really a difference there, but in your system, on SQL Server, case insensitive,<br />those strings are the same.</p>
<p>What I do in SSIS: I add all strings that I need for comparison in a derived column<br />task to lower(column) in addition to the original column. When doing comparisons,<br />i.e. in a lookup task, I do not use the reference table as data source, I<br />always write a query, and make the lookup strings lower(Column) in the<br />recordset. Then I compare the derived lower column with the lower column from<br />the lookup. And, voila, the lookup always works, regardless of what comes from<br />the outside world into your DWH :-)</p>]]></content:encoded>
    </item>
    <item>
      <title>Replication SQL Server to Sybase</title>
      <link>https://docs.microsoft.com/archive/blogs/wwerner1/replication-sql-server-to-sybase</link>
      <pubDate>Wed, 08 Jun 2011 10:29:00 GMT</pubDate>
      <dc:creator><![CDATA[Wolfgang Werner MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/wwerner1/2011/06/08/replication-sql-server-to-sybase/</guid>
      <description><![CDATA[I wanted to share some questions I came across during a migration from Sybase ASE to SQL Server...]]></description>
      <content:encoded><![CDATA[<p>I wanted to share some questions I came across during a migration from Sybase ASE to SQL Server 2008.</p>
<p>Is it possible to do a transactional replication from SQL Server to Sybase?</p>
<ul>
<li>Yes, it is. You can replicate via the Sybase OLE DB driver, if you set it up as a linked Server. You can only setup this replication using the SQL Server system SPs for replication.</li>
</ul>
<p>Is it supported?</p>
<ul>
<li>Hmm, it is not explicitly not supported :-). Transactional replication is supported for so called non-SQL Subscribers, but all the examples are only for Oracle as replication target.</li>
</ul>
<p>Which replication will work?</p>
<ul>
<li>Transactional, thats it. And only push is possible.</li>
</ul>
<p>What if I want to replicate from Sybase ASE to SQL?</p>
<ul>
<li>You can use the Sybase Replication Server, but this is not for free. It costs per processor core of the replication machine. Hint: use a virtual machine as replication server and give it one processor. Saves you money</li>
</ul>
<p>Can I replicate via stored procedures as in SQL Server?</p>
<ul>
<li>YES. You can create SPs in Sybase for Update, Insert and Delete and configure those to be called when the data comes in.</li>
</ul>
<p>Thats it. If you want to know more about, give me a mail. It is too much to write down here.</p>]]></content:encoded>
    </item>
    <item>
      <title>Some hints and tips for the MAPS tool</title>
      <link>https://docs.microsoft.com/archive/blogs/wwerner1/some-hints-and-tips-for-the-maps-tool</link>
      <pubDate>Wed, 18 May 2011 13:16:00 GMT</pubDate>
      <dc:creator><![CDATA[Wolfgang Werner MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/wwerner1/2011/05/18/some-hints-and-tips-for-the-maps-tool/</guid>
      <description><![CDATA[A few weeks ago I had a small SQL consolidation project. The target was to consolidate five...]]></description>
      <content:encoded><![CDATA[<p style="text-align: left;">A few weeks ago I had a small SQL consolidation project. The target was to consolidate five stand-alone SQL Servers (SQL 2000 and 2005) and one SQL 2005 cluster. When consolidating, first of all you must know about the workload on those servers. But how to find out the workload? Spending hours and days in front of the servers, looking at them? Perfmon Counters?</p>
<p style="text-align: left;">No, there is a tool from Microsoft, the Microsoft Assessment and Planning Toolkit (MAPS). I agreed with the customer that we will use it. When using it I found out some specials which I will talk about here.</p>
<p>1. Installation</p>
<p>Installation is easy, download the tool from the web, run setup, and&hellip;.. no, not done. First of all, the tools needs a SQL Server instance to store the data. And, the special thing is, the SQL Instance cannot be anywhere, it must be on the computer where you install the tool. During setup it gives you the chance to download SQL 2008 Express, which is really good for the job here, but you can also pre-install your own SQL Instance using Express, Standard, Enterprise or Developer Edition. But there is one thing you must be aware of: the installation must be a named instance and the name must be MAPS. Really true.</p>
<p>2. Installation 2nd</p>
<p>When installing there is the prerequisite that Office 2007 SP2 or Office 2010 must be on the computer. The MAPS tool can automatically create nice decision maker ready Excel sheets with results, but just for collecting data there is really no need to have Office on the computer. It would be more useful to not have this need, just install and collect to SQL. Then backup the SQL database, copy to your laptop, restore there rand make the rest of the work there, including creating those nice Excel sheets. This is the way I did it; I took the results with me and did the documentation in my home office. But, nevertheless, the installation needs it, so prepare your customer to have the installation media ready.</p>
<p>3. Collection of data</p>
<p>The collection runs automatically. First of all you must configure a database on the MAPS, instance, the tool asks you when starting if you want to create a new one, or use an existing one. You should create a new one when you want to do a new collection, choose an existing one when you want to use already collected data.</p>
<p>On a new collection: first of all you must run the &lsquo;Inventory and assessment wizard&rsquo;. This one will collect basic data about the servers, OS version, SQL version, services running etc. Which server to collect can be specified in many ways, by a text file in which the servers are just entered, each one manually in the tool etc. I will not explain every option here, use the tool and discover yourself. You can enter one user account for collecting the data, of enter a number of user accounts and the tool will try all of them one after another on each of the configured servers until it finds one which has access. This is useful if the servers are in different domains; this was the case at my customer.</p>
<p>After the successful run of the &lsquo;Inventory and assessment wizard&rsquo; you can collect the performance data. Make sure that you capture all times, busy times, month endings (creation of bills etc.). In my case we let the tool collect the data for 6 weeks! 30 minutes is the minimum time, by the way, otherwise the tool is doing nothing :-)</p>
<p>4. Collection, be careful!</p>
<p>At my customer the collection failed. But why? OK, it took me some time, but then I found out that the WMI service was disabled at the servers, and the tool does WMI calls. So enable and start the service, and it works&hellip;. But then it stopped&hellip;&nbsp; ok, a lot of customers have group policies which disable the WMI service because of security. So, my hint: Clarify with the customer before you start, because I have the experience at my customers that it takes some days to change the group policies (and in addition, there will be some discussions with the AD staff for what you need WMI :-)).</p>
<p>5. Collection, be very careful!!!</p>
<p>The program must be opened while collecting; it is not a windows service! So, you must lock the console and wait. In my case at the customer a different person started the collection and locked the console, so the person I worked with at the day we wanted to stop the collection just used its administrative account to log out the account (which is a hard program stop from process perspective).</p>
<p>OK, we did this, logged on to the computer, started the MAPS program, when asked we took the existing database we collected the data into, went to the section &lsquo;Server consolidation&rsquo;, and then to the option &lsquo;Performance metrics results&rsquo;&hellip;. And the MAPS tool told us &lsquo;sorry, I do not have any collected performance data&rsquo;. WHAT?</p>
<p>OK, there is the option of just opening the database in Management Studio, there are tables called dbo.Performance_disk, dbo.Performance_memory etc&hellip;. and all of them are full of data! But why has the tool the opinion there is no data?</p>
<p>There is a table called dbo.Performance_statistics&hellip; and this is empty. I checked on my database on my laptop with a successful collection, and there is a row in the database for each server I have collected. OK, so what happened? Easy&hellip;. The MAPS tool collects for days and weeks and months, and write the data to the SQL database, but&hellip; this table is written when the collection is ended, but we&hellip;. Stopped the program by a hard logoff of the user&hellip; and then it does not write the table, and&hellip; the tool says &lsquo;sorry&rsquo; :-(.</p>
<p>Solution? Easy&hellip; we just started a new collection, the tool ask if we want to overwrite the data (NO, NO, NO!!!), or if we want to keep the data (YES YES YES!!! :-)). We collect&hellip;&hellip; 31 minutes (as you remember mentioned before, 30 minutes is the minimum), after that we close the collection in the tool, and we are done. The table is filled now, and the MAPS program shows the results. YES! :-)</p>
<p>6. One last hint</p>
<p>The collected data is a nice playground for your documentation. The documentation written by the MAPS tool is ok, but only gives number of collections of data, minimum value, maximum value, average&hellip;. But perhaps you want to know how often the processor was above 90 % during the collection time for a special server, or stuff like that. All of that you find easily in the table in the database. T-SQL is your friend, the tables are named dbo.Performance_ and the rest tells you whats inside, disk, memory, processor&hellip;&hellip;. :-)</p>
<p>That&rsquo;s it for today. By the way, this info here is for MAPS 5.0. There is new version out, 5.5, but this was in beta when I did my job and not public available. So I decided to use the officially released 5.0 version. Are my hints here still valid for 5.5&hellip; no idea. Find out yourself :-)</p>]]></content:encoded>
    </item>
  </channel>
</rss>