polybase and alwaysencrypted added

This commit is contained in:
Jos de Bruijn
2016-05-26 16:04:52 -07:00
parent 766908b429
commit 9ed969cfdd
22 changed files with 1235 additions and 9 deletions
@@ -0,0 +1,19 @@
-- Always Encrypted Demo - Window 2
-- note this demo is continued from the first demo window
-- 4b. Right-click in this window and choose Connection, then Change Connection.
-- 4c. In the connection dialog, click Options.
-- 4d. Type WideWorldImporters for the database name.
-- 4e. Click on Additional Connection Parameters and enter: Column Encryption Setting=enabled
-- 4f. Click Connect
-- Note that when acting as a client with access to the certificate, we
-- can see the data. Remember that this can only work because
-- the client happens to be the same machine as the server in our
-- case.
SELECT * FROM Purchasing.Supplier_PrivateDetails ORDER BY SupplierID;
GO
-- Continue on the first window.
@@ -0,0 +1,132 @@
-- Always Encrypted Demo
USE WideWorldImporters;
GO
-- WWI have decided to store some national ID and credit card details for suppliers
-- but these details need to always be encrypted
-- Remove any existing column keys and/or table
DROP TABLE IF EXISTS Purchasing.Supplier_PrivateDetails;
IF EXISTS (SELECT 1 FROM sys.column_encryption_keys WHERE name = N'WWI_ColumnEncryptionKey')
BEGIN
DROP COLUMN ENCRYPTION KEY WWI_ColumnEncryptionKey;
END;
IF EXISTS (SELECT 1 FROM sys.column_master_keys WHERE name = N'WWI_ColumnMasterKey')
BEGIN
DROP COLUMN MASTER KEY WWI_ColumnMasterKey;
END;
GO
-- We need a column master key. This key is used to encrypt the column encryption keys.
-- The column master key isn't really stored in the database. It's created and stored on the
-- client system. SQL Server only holds a link to it so that SQL Server can tell the
-- client application where to locate the master key. The client system will encrypt a column
-- encryption key with this master key.
-- The wizard will create a certificate, install it in the certificate store, then
-- register it with SQL Server via CREATE COLUMN MASTER KEY
-- 1a. In Object Explorer, expand the security node in WideWorldImporters, then expand
-- the Always Encrypted Keys node and note the contents.
-- 1b. Right-click the Column Master Keys node and click New Column Master Key.
-- 1c. For the name, enter WWI_ColumnMasterKey.
-- 1d. Note the available entries in the Key store dropdown list. Choose Windows Certificate Store - Current User.
-- This will only be a temporary location for the certificate.
-- 1e. Click Generate Certificate to create the new certificate. Note that an Always Encrypted certificate
-- has been created. Ensure that it is selected, then click OK.
-- We have used the MSSQL_CERTIFICATE_STORE which uses the Windows store
-- but we can use any store that implements the SqlColumnEncryptionKeyStoreProvider
-- class. (And is registered by calling the SqlConnection.RegisterColumnEncryptionKeyStoreProviders()
-- method). This requires .NET framework 4.6.1 or later on the client.
-- The certificate could also have been created via the makecert utility and just loaded on the client.
-- We can see the newly created master key. Note the key_path. This path is relative to the client.
SELECT * FROM sys.column_master_keys;
-- The next key that we need is used for performing column encryption. It's held encrypted on the
-- database server and is decrypted (and cached) on the client application before use.
-- On the client system, it is protected by the column master key.
-- 2a. In Object Explorer, right-click the Column Encryption Keys node and click New Column Encryption Key.
-- 2b. In the Name textbox, enter WWI_ColumnEncryptionKey and from the Column master key dropdown list,
-- select WWI_ColumnMasterKey to be used to encrypt this new key. Then click OK.
-- We can see the newly created encryption key.
SELECT * FROM sys.column_encryption_keys;
-- Now let's create the table that will use always encrypted.
-- We'll have one deterministic encryption column and two random
-- encryption (salted) columns.
CREATE TABLE Purchasing.Supplier_PrivateDetails
(
SupplierID int
CONSTRAINT PKFK_Purchasing_Supplier_PrivateDetails PRIMARY KEY
CONSTRAINT FK_Purchasing_Supplier_PrivateDetails_Suppliers
FOREIGN KEY REFERENCES Purchasing.Suppliers (SupplierID),
NationalID nvarchar(30) COLLATE Latin1_General_BIN2
ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = WWI_ColumnEncryptionKey,
ENCRYPTION_TYPE = DETERMINISTIC,
ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL,
CreditCardNumber nvarchar(30) COLLATE Latin1_General_BIN2
ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = WWI_ColumnEncryptionKey,
ENCRYPTION_TYPE = RANDOMIZED,
ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL,
ExpiryDate nvarchar(5) COLLATE Latin1_General_BIN2
ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = WWI_ColumnEncryptionKey,
ENCRYPTION_TYPE = RANDOMIZED,
ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL
);
GO
-- Note that we can't directly insert unencrypted data
-- Note the error returned. The data in the columns is only
-- understood by the client system.
INSERT Purchasing.Supplier_PrivateDetails
(SupplierID, NationalID, CreditCardNumber, ExpiryDate)
VALUES
(1, N'93748567', N'7382-5849-2903-2838', N'11/19');
GO
-- Let's ensure the table is empty, then we'll use a client application
-- to populate the data. Note that we can still perform standard
-- table operations like truncation.
TRUNCATE TABLE Purchasing.Supplier_PrivateDetails;
GO
-- 3a. Now execute the .NET app to populate the data
-- Note that it has been inserted but is not visible within the database
SELECT * FROM Purchasing.Supplier_PrivateDetails ORDER BY SupplierID;
GO
-- To emulate a client application that has access to the keys, we
-- can use SSMS to connect. Note that this can only work because
-- the client happens to be the same machine as the server in our
-- case.
-- 4a. Open the second query window for this demonstration and follow the instructions there.
-- 5a. Clean up afterwards.
-- Remove any existing column keys and/or table
DROP TABLE IF EXISTS Purchasing.Supplier_PrivateDetails;
IF EXISTS (SELECT 1 FROM sys.column_encryption_keys WHERE name = N'WWI_ColumnEncryptionKey')
BEGIN
DROP COLUMN ENCRYPTION KEY WWI_ColumnEncryptionKey;
END;
IF EXISTS (SELECT 1 FROM sys.column_master_keys WHERE name = N'WWI_ColumnMasterKey')
BEGIN
DROP COLUMN MASTER KEY WWI_ColumnMasterKey;
END;
GO
@@ -0,0 +1,22 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25123.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PopulateAlwaysEncryptedData", "PopulateAlwaysEncryptedData\PopulateAlwaysEncryptedData.csproj", "{83DD3CB9-58BA-46F4-8E7C-3F749A659C53}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{83DD3CB9-58BA-46F4-8E7C-3F749A659C53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{83DD3CB9-58BA-46F4-8E7C-3F749A659C53}.Debug|Any CPU.Build.0 = Debug|Any CPU
{83DD3CB9-58BA-46F4-8E7C-3F749A659C53}.Release|Any CPU.ActiveCfg = Release|Any CPU
{83DD3CB9-58BA-46F4-8E7C-3F749A659C53}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="PopulateAlwaysEncryptedData.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
<userSettings>
<PopulateAlwaysEncryptedData.Properties.Settings>
<setting name="WWI_ConnectionString" serializeAs="String">
<value />
</setting>
</PopulateAlwaysEncryptedData.Properties.Settings>
</userSettings>
</configuration>
@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{83DD3CB9-58BA-46F4-8E7C-3F749A659C53}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>PopulateAlwaysEncryptedData</RootNamespace>
<AssemblyName>PopulateAlwaysEncryptedData</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="PopulateAlwaysEncryptedDataMain.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="PopulateAlwaysEncryptedDataMain.Designer.cs">
<DependentUpon>PopulateAlwaysEncryptedDataMain.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="PopulateAlwaysEncryptedDataMain.resx">
<DependentUpon>PopulateAlwaysEncryptedDataMain.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
@@ -0,0 +1,106 @@
namespace PopulateAlwaysEncryptedData
{
partial class PopulateAlwaysEncryptedDataMain
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(PopulateAlwaysEncryptedDataMain));
this.DescriptionTextBox = new System.Windows.Forms.TextBox();
this.ConnectionStringLabel = new System.Windows.Forms.Label();
this.ConnectionStringTextBox = new System.Windows.Forms.TextBox();
this.PopulateButton = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// DescriptionTextBox
//
this.DescriptionTextBox.BackColor = System.Drawing.SystemColors.Info;
this.DescriptionTextBox.Location = new System.Drawing.Point(13, 13);
this.DescriptionTextBox.Multiline = true;
this.DescriptionTextBox.Name = "DescriptionTextBox";
this.DescriptionTextBox.Size = new System.Drawing.Size(866, 49);
this.DescriptionTextBox.TabIndex = 0;
this.DescriptionTextBox.TabStop = false;
this.DescriptionTextBox.Text = resources.GetString("DescriptionTextBox.Text");
//
// ConnectionStringLabel
//
this.ConnectionStringLabel.AutoSize = true;
this.ConnectionStringLabel.Location = new System.Drawing.Point(10, 82);
this.ConnectionStringLabel.Name = "ConnectionStringLabel";
this.ConnectionStringLabel.Size = new System.Drawing.Size(141, 17);
this.ConnectionStringLabel.TabIndex = 1;
this.ConnectionStringLabel.Text = "Connection String:";
//
// ConnectionStringTextBox
//
this.ConnectionStringTextBox.Location = new System.Drawing.Point(13, 115);
this.ConnectionStringTextBox.Name = "ConnectionStringTextBox";
this.ConnectionStringTextBox.Size = new System.Drawing.Size(863, 24);
this.ConnectionStringTextBox.TabIndex = 1;
//
// PopulateButton
//
this.PopulateButton.Location = new System.Drawing.Point(368, 167);
this.PopulateButton.Name = "PopulateButton";
this.PopulateButton.Size = new System.Drawing.Size(115, 39);
this.PopulateButton.TabIndex = 0;
this.PopulateButton.Text = "&Populate";
this.PopulateButton.UseVisualStyleBackColor = true;
this.PopulateButton.Click += new System.EventHandler(this.PopulateButton_Click);
//
// PopulateAlwaysEncryptedDataMain
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(891, 228);
this.Controls.Add(this.PopulateButton);
this.Controls.Add(this.ConnectionStringTextBox);
this.Controls.Add(this.ConnectionStringLabel);
this.Controls.Add(this.DescriptionTextBox);
this.Font = new System.Drawing.Font("Verdana", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.MaximizeBox = false;
this.Name = "PopulateAlwaysEncryptedDataMain";
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
this.Text = "Populate Always Encrypted Data";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.PopulateAlwaysEncryptedDataMain_FormClosing);
this.Load += new System.EventHandler(this.PopulateAlwaysEncryptedDataMain_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox DescriptionTextBox;
private System.Windows.Forms.Label ConnectionStringLabel;
private System.Windows.Forms.TextBox ConnectionStringTextBox;
private System.Windows.Forms.Button PopulateButton;
}
}
@@ -0,0 +1,144 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace PopulateAlwaysEncryptedData
{
public partial class PopulateAlwaysEncryptedDataMain : Form
{
public PopulateAlwaysEncryptedDataMain()
{
InitializeComponent();
}
private void PopulateAlwaysEncryptedDataMain_Load(object sender, EventArgs e)
{
ConnectionStringTextBox.Text = Properties.Settings.Default.WWI_ConnectionString;
if (ConnectionStringTextBox.Text.Length == 0)
{
ConnectionStringTextBox.Text = "Server=.;Database=WideWorldImporters;Integrated Security=true;Column Encryption Setting=enabled";
}
}
private void PopulateAlwaysEncryptedDataMain_FormClosing(object sender, FormClosingEventArgs e)
{
Properties.Settings.Default.WWI_ConnectionString = ConnectionStringTextBox.Text;
Properties.Settings.Default.Save();
}
private void PopulateButton_Click(object sender, EventArgs e)
{
int supplierID;
try
{
using (SqlConnection con = new SqlConnection(ConnectionStringTextBox.Text))
{
DataSet ds = new DataSet();
con.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = con;
cmd.CommandText = "SELECT SupplierID FROM Purchasing.Suppliers ORDER BY SupplierID;";
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(ds, "Suppliers");
}
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = con;
cmd.CommandText = "TRUNCATE TABLE Purchasing.Supplier_PrivateDetails;";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT Purchasing.Supplier_PrivateDetails "
+ "(SupplierID, NationalID, CreditCardNumber, ExpiryDate) "
+ "VALUES (@SupplierID, @NationalID, @CreditCardNumber, @ExpiryDate);";
cmd.Parameters.Add(new SqlParameter("@SupplierID", SqlDbType.Int));
cmd.Parameters.Add(new SqlParameter("@NationalID", SqlDbType.NVarChar, 30));
cmd.Parameters.Add(new SqlParameter("@CreditCardNumber", SqlDbType.NVarChar, 30));
cmd.Parameters.Add(new SqlParameter("@ExpiryDate", SqlDbType.NVarChar, 5));
DataTable suppliers = ds.Tables["Suppliers"];
for (int counter = 0;counter < suppliers.Rows.Count;counter++)
{
supplierID = (int) suppliers.Rows[counter]["SupplierID"];
cmd.Parameters["@SupplierID"].SqlValue = supplierID;
cmd.Parameters["@NationalID"].SqlValue = CreateNationalID();
cmd.Parameters["@CreditCardNumber"].SqlValue = CreateCreditCardNumber();
cmd.Parameters["@ExpiryDate"].SqlValue = CreateExpiryDate();
cmd.ExecuteNonQuery();
}
}
con.Close();
MessageBox.Show("Inserted " + ds.Tables["Suppliers"].Rows.Count.ToString() + " rows");
}
}
catch (Exception ex)
{
MessageBox.Show("Unable to populate the data. The returned error was:\n" + ex.ToString());
}
}
private string CreateNationalID()
{
string nationalID = "";
Random rnd = new Random();
for (int counter = 0;counter < 8;counter++)
{
int digit = rnd.Next(0, 9);
nationalID += digit.ToString();
}
return nationalID;
}
private string CreateCreditCardNumber()
{
string creditCardNumber = "";
Random rnd = new Random();
for (int counter = 0; counter < 16; counter++)
{
int digit = rnd.Next(0, 9);
creditCardNumber += digit.ToString();
if (counter == 3 || counter == 7 || counter == 11)
{
creditCardNumber += "-";
}
}
return creditCardNumber;
}
private string CreateExpiryDate()
{
string expiryDate = "";
Random rnd = new Random();
int month = rnd.Next(1, 12);
string monthString = month.ToString();
if (monthString.Length == 1) monthString = "0" + monthString;
int currentYear = DateTime.Now.Year - 2000;
int year = rnd.Next(currentYear, currentYear + 4);
string yearString = year.ToString();
if (yearString.Length == 1) yearString = "0" + yearString;
expiryDate = monthString + "/" + yearString;
return expiryDate;
}
}
}
@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="DescriptionTextBox.Text" xml:space="preserve">
<value>This application is used to populate the Purchasing.Supplier_PrivateDetails table which must have already been configured for Always Encrypted. Ensure that the connection string details are valid, then click the Populate button.</value>
</data>
</root>
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace PopulateAlwaysEncryptedData
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new PopulateAlwaysEncryptedDataMain());
}
}
}
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("PopulateAlwaysEncryptedData")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PopulateAlwaysEncryptedData")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("83dd3cb9-58ba-46f4-8e7c-3f749a659c53")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
@@ -0,0 +1,71 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace PopulateAlwaysEncryptedData.Properties
{
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources
{
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources()
{
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager
{
get
{
if ((resourceMan == null))
{
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PopulateAlwaysEncryptedData.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture
{
get
{
return resourceCulture;
}
set
{
resourceCulture = value;
}
}
}
}
@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
@@ -0,0 +1,38 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace PopulateAlwaysEncryptedData.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string WWI_ConnectionString {
get {
return ((string)(this["WWI_ConnectionString"]));
}
set {
this["WWI_ConnectionString"] = value;
}
}
}
}
@@ -0,0 +1,9 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="PopulateAlwaysEncryptedData.Properties" GeneratedClassName="Settings">
<Profiles />
<Settings>
<Setting Name="WWI_ConnectionString" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
</Settings>
</SettingsFile>
@@ -36,7 +36,7 @@ To run this sample, you need the following prerequisites.
<!-- Examples -->
1. SQL Server 2016 (or higher) or Azure SQL Database.
2. The WideWorldImporters database.
2. The WideWorldImporters database (Full version).
<a name=run-this-sample></a>
@@ -0,0 +1,66 @@
# Sample performance with Operational Analytics in WideWorldImporters
This script shows the performance of analytics queries in the operational database. It relies on the [Order Insert](../../workload-drivers/vehicle-location-insert/) workload driver.
The main purpose is to show the performance benefits of using nonclustered columnstore indexes for analytics queries on operational systems, and how they limit the impact on the operational workload.
### Contents
[About this sample](#about-this-sample)<br/>
[Before you begin](#before-you-begin)<br/>
[Running the sample](#run-this-sample)<br/>
[Sample details](#sample-details)<br/>
[Disclaimers](#disclaimers)<br/>
[Related links](#related-links)<br/>
<a name=about-this-sample></a>
## About this sample
<!-- Delete the ones that don't apply -->
1. **Applies to:** SQL Server 2016 (or higher), Azure SQL Database
1. **Key features:** nonclustered columnstore index
1. **Workload:** Operational Analytics
1. **Programming Language:** T-SQL
1. **Authors:** Greg Low, Jos de Bruijn
1. **Update history:** 26 May 2016 - initial revision
<a name=before-you-begin></a>
## Before you begin
To run this sample, you need the following prerequisites.
**Software prerequisites:**
<!-- Examples -->
1. SQL Server 2016 (or higher) or Azure SQL Database.
2. SQL Server Management Studio
3. The WideWorldImporters database (Full version).
<a name=run-this-sample></a>
## Running the sample
1. Run the [Order Insert](../../workload-drivers/vehicle-location-insert/) workload for a while. When starting from the vanilla WideWorldImporters database the recommendation is to run at least 20 minutes, to ensure that enough data is generated and that compression kicks in (the sample has a COMPRESSION_DELAY of 10 minutes for the columnstore indexes).
2. Execute the sample script.
3. Open the Messages pane in SSMS and observe the time taken to run the query with and without columnstore index.
## Sample details
The [Order Insert](../../workload-drivers/vehicle-location-insert/) workload driver is used to simulate an order processing workload. The queries in the sample script are reporting queries run with and without columnstore index.
<a name=disclaimers></a>
## Disclaimers
The code included in this sample is not intended to be used for production purposes.
<a name=related-links></a>
## Related Links
<!-- Links to more articles. Remember to delete "en-us" from the link path. -->
TBD
@@ -0,0 +1,139 @@
-- Demonstrate WorldWideImporters Polybase connections
-- Requires PolyBase to be installed.
USE WideWorldImportersDW;
GO
-- WideWorldImporters have customers in a variety of cities but feel they are likely missing
-- other important cities. They have decided to try to find other cities have a growth rate of more
-- than 20% over the last 3 years, and where they do not have existing customers.
-- They have obtained census data (a CSV file) and have loaded it into an Azure storage account.
-- They want to combine that data with other data in their main OLTP database to work out where
-- they should try to find new customers.
-- First, let's apply Polybase connectivity and set up an external table to point to the data
-- in the Azure storage account.
EXEC [Application].Configuration_ApplyPolybase;
GO
-- In Object Explorer, refresh the WideWorldImporters database, then expand the Tables node.
-- Note that SQL Server 2016 added a new entry here for External Tables. Expand that node.
-- Expand the dbo.CityPopulationStatistics table, expand the list of columns and note the
-- values that are contained. Let's look at the data:
SELECT * FROM dbo.CityPopulationStatistics;
GO
-- How did that work? First the procedure created an external data source like this:
/*
CREATE EXTERNAL DATA SOURCE AzureStorage
WITH
(
TYPE=HADOOP, LOCATION = 'wasbs://data@sqldwdatasets.blob.core.windows.net'
);
*/
-- This shows how to connect to AzureStorage. Next the procedure created an
-- external file format to describe the layout of the CSV file:
/*
CREATE EXTERNAL FILE FORMAT CommaDelimitedTextFileFormat
WITH
(
FORMAT_TYPE = DELIMITEDTEXT,
FORMAT_OPTIONS
(
FIELD_TERMINATOR = ','
)
);
*/
-- Finally the external table was defined like this:
/*
CREATE EXTERNAL TABLE dbo.CityPopulationStatistics
(
CityID int NOT NULL,
StateProvinceCode nvarchar(5) NOT NULL,
CityName nvarchar(50) NOT NULL,
YearNumber int NOT NULL,
LatestRecordedPopulation bigint NULL
)
WITH
(
LOCATION = '/',
DATA_SOURCE = AzureStorage,
FILE_FORMAT = CommaDelimitedTextFileFormat,
REJECT_TYPE = VALUE,
REJECT_VALUE = 4 -- skipping 1 header row per file
);
*/
-- From that point onwards, the external table can be used like a local table. Let's run that
-- query that they wanted to use to find out which cities they should be finding new customers
-- in. We'll start building the query by grouping the cities from the external table
-- and finding those with more than a 20% growth rate for the period:
WITH PotentialCities
AS
(
SELECT cps.CityName,
cps.StateProvinceCode,
MAX(cps.LatestRecordedPopulation) AS PopulationIn2016,
(MAX(cps.LatestRecordedPopulation) - MIN(cps.LatestRecordedPopulation)) * 100.0
/ MIN(cps.LatestRecordedPopulation) AS GrowthRate
FROM dbo.CityPopulationStatistics AS cps
WHERE cps.LatestRecordedPopulation IS NOT NULL
AND cps.LatestRecordedPopulation <> 0
GROUP BY cps.CityName, cps.StateProvinceCode
)
SELECT *
FROM PotentialCities
WHERE GrowthRate > 2.0;
GO
-- Now let's combine that with our local city and sales data to exclude those where we already
-- have customers. We'll find the 100 most interesting cities based upon population.
WITH PotentialCities
AS
(
SELECT cps.CityName,
cps.StateProvinceCode,
MAX(cps.LatestRecordedPopulation) AS PopulationIn2016,
(MAX(cps.LatestRecordedPopulation) - MIN(cps.LatestRecordedPopulation)) * 100.0
/ MIN(cps.LatestRecordedPopulation) AS GrowthRate
FROM dbo.CityPopulationStatistics AS cps
WHERE cps.LatestRecordedPopulation IS NOT NULL
AND cps.LatestRecordedPopulation <> 0
GROUP BY cps.CityName, cps.StateProvinceCode
),
InterestingCities
AS
(
SELECT DISTINCT pc.CityName,
pc.StateProvinceCode,
pc.PopulationIn2016,
FLOOR(pc.GrowthRate) AS GrowthRate
FROM PotentialCities AS pc
INNER JOIN Dimension.City AS c
ON pc.CityName = c.City
WHERE GrowthRate > 2.0
AND NOT EXISTS (SELECT 1 FROM Fact.Sale AS s WHERE s.[City Key] = c.[City Key])
)
SELECT TOP(100) *
FROM InterestingCities
ORDER BY PopulationIn2016 DESC;
GO
-- Clean up if required
/*
DROP EXTERNAL TABLE dbo.CityPopulationStatistics;
GO
DROP EXTERNAL FILE FORMAT CommaDelimitedTextFileFormat;
GO
DROP EXTERNAL DATA SOURCE AzureStorage;
GO
*/
@@ -0,0 +1,74 @@
# Sample Querying of External Data Source in WideWorldImportersDW
This script demonstrates the use of PolyBase to query an external data source.
Demographics data is available in Azure blob storage. This data is joined with sales data recorded in the local database to determine which would be good candidates for future expansion of the business.
### Contents
[About this sample](#about-this-sample)<br/>
[Before you begin](#before-you-begin)<br/>
[Running the sample](#run-this-sample)<br/>
[Sample details](#sample-details)<br/>
[Disclaimers](#disclaimers)<br/>
[Related links](#related-links)<br/>
<a name=about-this-sample></a>
## About this sample
<!-- Delete the ones that don't apply -->
1. **Applies to:** SQL Server 2016 (or higher), Azure SQL Database
1. **Key features:** PolyBase
1. **Workload:** Analytics
1. **Programming Language:** T-SQL
1. **Authors:** Greg Low, Jos de Bruijn
1. **Update history:** 26 May 2016 - initial revision
<a name=before-you-begin></a>
## Before you begin
To run this sample, you need the following prerequisites.
**Software prerequisites:**
<!-- Examples -->
1. SQL Server 2016 (or higher) with PolyBase, connected to the internet.
2. SQL Server Management Studio
3. The WideWorldImportersDW database (Full version).
<a name=run-this-sample></a>
## Running the sample
1. Execute the sample script.
2. Inspect external tables in the database.
3. Review query results.
## Sample details
The sample script performs a configuration and runs three queries:
1. An external table `dbo.CitePopulationStatistics` is created in the database, pointing to a data set in Azure blob storage.
2. The data in Azure storage is queried through Transact-SQL, showing all the data in the data source.
3. Cities with a significant growth rate (>= 20%) are identified.
4. Top cities for potential expansion are identified based on external data as well as sales data in the local database.
<a name=disclaimers></a>
## Disclaimers
The code included in this sample is not intended to be used for production purposes.
<a name=related-links></a>
## Related Links
<!-- Links to more articles. Remember to delete "en-us" from the link path. -->
TBD
@@ -58,7 +58,7 @@
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(13, 91);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(141, 17);
this.label1.Size = new System.Drawing.Size(172, 20);
this.label1.TabIndex = 1;
this.label1.Text = "Connection String:";
//
@@ -66,7 +66,7 @@
//
this.ConnectionStringTextBox.Location = new System.Drawing.Point(13, 123);
this.ConnectionStringTextBox.Name = "ConnectionStringTextBox";
this.ConnectionStringTextBox.Size = new System.Drawing.Size(1065, 24);
this.ConnectionStringTextBox.Size = new System.Drawing.Size(1065, 28);
this.ConnectionStringTextBox.TabIndex = 0;
//
// InsertButton
@@ -84,7 +84,7 @@
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(13, 178);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(150, 17);
this.label2.Size = new System.Drawing.Size(182, 20);
this.label2.TabIndex = 3;
this.label2.Text = "Number of Threads:";
//
@@ -102,7 +102,7 @@
0,
0});
this.NumberOfThreadsNumericUpDown.Name = "NumberOfThreadsNumericUpDown";
this.NumberOfThreadsNumericUpDown.Size = new System.Drawing.Size(120, 24);
this.NumberOfThreadsNumericUpDown.Size = new System.Drawing.Size(120, 28);
this.NumberOfThreadsNumericUpDown.TabIndex = 1;
this.NumberOfThreadsNumericUpDown.Value = new decimal(new int[] {
10,
@@ -115,7 +115,7 @@
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(715, 185);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(320, 17);
this.label5.Size = new System.Drawing.Size(398, 20);
this.label5.TabIndex = 9;
this.label5.Text = "Average Order Insertion Time (Milliseconds):";
//
@@ -125,7 +125,7 @@
this.AverageOrderInsertionTimeTextBox.Font = new System.Drawing.Font("Verdana", 24F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.AverageOrderInsertionTimeTextBox.Location = new System.Drawing.Point(782, 221);
this.AverageOrderInsertionTimeTextBox.Name = "AverageOrderInsertionTimeTextBox";
this.AverageOrderInsertionTimeTextBox.Size = new System.Drawing.Size(220, 46);
this.AverageOrderInsertionTimeTextBox.Size = new System.Drawing.Size(220, 56);
this.AverageOrderInsertionTimeTextBox.TabIndex = 10;
this.AverageOrderInsertionTimeTextBox.TabStop = false;
//
@@ -136,7 +136,7 @@
//
// MultithreadedOrderInsertMain
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleDimensions = new System.Drawing.SizeF(11F, 20F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1107, 345);
this.Controls.Add(this.AverageOrderInsertionTimeTextBox);
@@ -37,7 +37,7 @@ To run this sample, you need the following prerequisites.
<!-- Examples -->
1. SQL Server 2016 (or higher) or Azure SQL Database.
2. Visual Studio 2015.
3. The WideWorldImporters database.
3. The WideWorldImporters database (Full version).
<a name=run-this-sample></a>