worksheet.Column(1).Style.Font.Name = "B Nazanin"; worksheet.Column(1).Style.Font.Size = 15;
worksheet.Column(1).Style.Font.Name = "B Nazanin"; worksheet.Column(1).Style.Font.Size = 15;
میخواهیم از یک لیست در گزارش خود استفاده کنیم؛ بطور مثال وقتی در LINQ از دستور ToList استفاده میکنیم و میخواهیم آنرا بصورت مستقیم به Stimul بفرستیم. فرض بر این است که شما DLLهای Stimul را به پروژه اضافه کرده اید و آماده گزارشگیری هستید.
مثلا مدلی در Entity FrameWork با نام base_CenterType
public class base_CenterType { public int ID { get; set; } public string Title { get; set; } public string Dsc { get; set; } }
و متدی بصورت ذیل:
public IList<base_CenterType> GetAll() { return _base_CenterType.ToList(); }
طراحی گزارش برای این لیست به این صورت است:
1- اضافه کردن StiWebReport به فرم به نام StiWebReport1
2- با کلیک بر روی فلش سمت راست و بالای StiWebReport1 و انتخاب Design Report، وارد قسمت طراحی میشویم:
3- با راست کلیک بر روی Business Object و انتخاب New Business Object پنجره مربوطه باز میشود:
4- بعد از زدن OK پنجره زیر باز خواهد شد که باید در کادر Name نام Business Object را انتخاب کنیم که برای خوانایی بهتر است همان نام کلاس را برای آن انتخاب کنیم. چون Category نداریم پس باید کادر آن خالی بماند.
در قسمت Columns باید ستونهای هم نام و هم نوع با خواص کلاس base_CenterType را ایجاد کنیم.
و نهایتا Business Objectی به نام base_CenterType با سه ستون ایجاد خواهد شد.
حال میتوانید ستونهای مورد نظر را در گزارش بکار ببرید.
با فرض اینکه گزارش را طراحی کرده و آنرا در ریشه درایو C ذخیره کردهاید، از قطعه کد زیر برای ارسال لیست به گزارش و نمایش آن استفاده میکنیم.
StiReport mainreport = new StiReport(); mainreport.RegBusinessObject("base_CenterType", base_CenterTypeService.GetAll()); mainreport.Load("C:\\StiWebReport2.mrt"); mainreport.Show();
UI
در نهایت نوبت به طراحی و کدنویسی UI میرسد تا بتوانیم محصولات را به کاربر نمایش دهیم. اما قبل از شروع باید موضوعی را یادآوری کنم. اگر به یاد داشته باشید، در کلاس ProductService موجود در لایهی Domain، از طریق یکی از روشهای الگوی Dependency Injection به نام Constructor Injection، فیلدی از نوع IProductRepository را مقداردهی نمودیم. حال زمانی که بخواهیم نمونه ای را از ProductService ایجاد نماییم، باید به عنوان پارامتر ورودی سازنده، شی ایجاد شده از جنس کلاس ProductRepository موجود در لایه Repository را به آن ارسال نماییم. اما از آنجایی که میخواهیم تفکیک پذیری لایهها از بین نرود و UI بسته به نیاز خود، نمونه مورد نیاز را ایجاد نموده و به این کلاس ارسال کند، از ابزارهایی برای این منظور استفاده میکنیم. یکی از این ابزارها StructureMap میباشد که یک Inversion of Control Container یا به اختصار IoC Container نامیده میشود. با Inversion of Control در مباحث بعدی بیشتر آشنا خواهید شد. StructureMap ابزاری است که در زمان اجرا، پارامترهای ورودی سازندهی کلاسهایی را که از الگوی Dependency Injection استفاده نموده اند و قطعا پارامتر ورودی آنها از جنس یک Interface میباشد را، با ایجاد شی مناسب مقداردهی مینماید.
به منظور استفاده از StructureMap در Visual Studio 2012 باید بر روی پروژه UI خود کلیک راست نموده و گزینهی Manage NuGet Packages را انتخاب نمایید. در پنجره ظاهر شده و از سمت چپ گزینهی Online و سپس در کادر جستجوی سمت راست و بالای پنجره واژهی structuremap را جستجو کنید. توجه داشته باشید که باید به اینترنت متصل باشید تا بتوانید Package مورد نظر را نصب نمایید. پس از پایان عمل جستجو، در کادر میانی structuremap ظاهر میشود که میتوانید با انتخاب آن و فشردن کلید Install آن را بر روی پروژه نصب نمایید.
جهت آشنایی بیشتر با NuGet و نصب آن در سایر نسخههای Visual Studio میتوانید به لینکهای زیر رجوع کنید:
کلاسی با نام BootStrapper را با کد زیر به پروژه UI خود اضافه کنید:
using StructureMap; using StructureMap.Configuration.DSL; using SoCPatterns.Layered.Repository; using SoCPatterns.Layered.Model; namespace SoCPatterns.Layered.WebUI { public class BootStrapper { public static void ConfigureStructureMap() { ObjectFactory.Initialize(x => x.AddRegistry<ProductRegistry>()); } } public class ProductRegistry : Registry { public ProductRegistry() { For<IProductRepository>().Use<ProductRepository>(); } } }
ممکن است یک WinUI ایجاد کرده باشید که در این صورت به جای فضای نام SoCPatterns.Layered.WebUI از SoCPatterns.Layered.WinUI استفاده نمایید.
هدف کلاس BootStrapper این است که تمامی وابستگیها را توسط StructureMap در سیستم Register نماید. زمانی که کدهای کلاینت میخواهند به یک کلاس از طریق StructureMap دسترسی داشته باشند، Structuremap وابستگیهای آن کلاس را تشخیص داده و بصورت خودکار پیاده سازی واقعی (Concrete Implementation) آن کلاس را، براساس همان چیزی که ما برایش تعیین کردیم، به کلاس تزریق مینماید. متد ConfigureStructureMap باید در همان لحظه ای که Application آغاز به کار میکند فراخوانی و اجرا شود. با توجه به نوع UI خود یکی از روالهای زیر را انجام دهید:
در WebUI:
فایل Global.asax را به پروژه اضافه کنید و کد آن را بصورت زیر تغییر دهید:
namespace SoCPatterns.Layered.WebUI { public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { BootStrapper.ConfigureStructureMap(); } } }
در WinUI:
در فایل Program.cs کد زیر را اضافه کنید:
namespace SoCPatterns.Layered.WinUI { static class Program { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); BootStrapper.ConfigureStructureMap(); Application.Run(new Form1()); } } }
سپس برای طراحی رابط کاربری، با توجه به نوع UI خود یکی از روالهای زیر را انجام دهید:
در WebUI:
صفحه Default.aspx را باز نموده و کد زیر را به آن اضافه کنید:
<asp:DropDownList AutoPostBack="true" ID="ddlCustomerType" runat="server"> <asp:ListItem Value="0">Standard</asp:ListItem> <asp:ListItem Value="1">Trade</asp:ListItem> </asp:DropDownList> <asp:Label ID="lblErrorMessage" runat="server" ></asp:Label> <asp:Repeater ID="rptProducts" runat="server" > <HeaderTemplate> <table> <tr> <td>Name</td> <td>RRP</td> <td>Selling Price</td> <td>Discount</td> <td>Savings</td> </tr> <tr> <td colspan="5"><hr /></td> </tr> </HeaderTemplate> <ItemTemplate> <tr> <td><%# Eval("Name") %></td> <td><%# Eval("RRP")%></td> <td><%# Eval("SellingPrice") %></td> <td><%# Eval("Discount") %></td> <td><%# Eval("Savings") %></td> </tr> </ItemTemplate> <FooterTemplate> </table> </FooterTemplate> </asp:Repeater>
در WinUI:
فایل Form1.Designer.cs را باز نموده و کد آن را بصورت زیر تغییر دهید:
#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() { this.cmbCustomerType = new System.Windows.Forms.ComboBox(); this.dgvProducts = new System.Windows.Forms.DataGridView(); this.colName = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.colRrp = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.colSellingPrice = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.colDiscount = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.colSavings = new System.Windows.Forms.DataGridViewTextBoxColumn(); ((System.ComponentModel.ISupportInitialize)(this.dgvProducts)).BeginInit(); this.SuspendLayout(); // // cmbCustomerType // this.cmbCustomerType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbCustomerType.FormattingEnabled = true; this.cmbCustomerType.Items.AddRange(new object[] { "Standard", "Trade"}); this.cmbCustomerType.Location = new System.Drawing.Point(12, 90); this.cmbCustomerType.Name = "cmbCustomerType"; this.cmbCustomerType.Size = new System.Drawing.Size(121, 21); this.cmbCustomerType.TabIndex = 3; // // dgvProducts // this.dgvProducts.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.dgvProducts.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { this.colName, this.colRrp, this.colSellingPrice, this.colDiscount, this.colSavings}); this.dgvProducts.Location = new System.Drawing.Point(12, 117); this.dgvProducts.Name = "dgvProducts"; this.dgvProducts.Size = new System.Drawing.Size(561, 206); this.dgvProducts.TabIndex = 2; // // colName // this.colName.DataPropertyName = "Name"; this.colName.HeaderText = "Product Name"; this.colName.Name = "colName"; this.colName.ReadOnly = true; // // colRrp // this.colRrp.DataPropertyName = "Rrp"; this.colRrp.HeaderText = "RRP"; this.colRrp.Name = "colRrp"; this.colRrp.ReadOnly = true; // // colSellingPrice // this.colSellingPrice.DataPropertyName = "SellingPrice"; this.colSellingPrice.HeaderText = "Selling Price"; this.colSellingPrice.Name = "colSellingPrice"; this.colSellingPrice.ReadOnly = true; // // colDiscount // this.colDiscount.DataPropertyName = "Discount"; this.colDiscount.HeaderText = "Discount"; this.colDiscount.Name = "colDiscount"; // // colSavings // this.colSavings.DataPropertyName = "Savings"; this.colSavings.HeaderText = "Savings"; this.colSavings.Name = "colSavings"; this.colSavings.ReadOnly = true; // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(589, 338); this.Controls.Add(this.cmbCustomerType); this.Controls.Add(this.dgvProducts); this.Name = "Form1"; this.Text = "Form1"; ((System.ComponentModel.ISupportInitialize)(this.dgvProducts)).EndInit(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.ComboBox cmbCustomerType; private System.Windows.Forms.DataGridView dgvProducts; private System.Windows.Forms.DataGridViewTextBoxColumn colName; private System.Windows.Forms.DataGridViewTextBoxColumn colRrp; private System.Windows.Forms.DataGridViewTextBoxColumn colSellingPrice; private System.Windows.Forms.DataGridViewTextBoxColumn colDiscount; private System.Windows.Forms.DataGridViewTextBoxColumn colSavings;
سپس در Code Behind، با توجه به نوع UI خود یکی از روالهای زیر را انجام دهید:
در WebUI:
وارد کد نویسی صفحه Default.aspx شده و کد آن را بصورت زیر تغییر دهید:
using System; using System.Collections.Generic; using SoCPatterns.Layered.Model; using SoCPatterns.Layered.Presentation; using SoCPatterns.Layered.Service; using StructureMap; namespace SoCPatterns.Layered.WebUI { public partial class Default : System.Web.UI.Page, IProductListView { private ProductListPresenter _productListPresenter; protected void Page_Init(object sender, EventArgs e) { _productListPresenter = new ProductListPresenter(this,ObjectFactory.GetInstance<Service.ProductService>()); this.ddlCustomerType.SelectedIndexChanged += delegate { _productListPresenter.Display(); }; } protected void Page_Load(object sender, EventArgs e) { if(!Page.IsPostBack) _productListPresenter.Display(); } public void Display(IList<ProductViewModel> products) { rptProducts.DataSource = products; rptProducts.DataBind(); } public CustomerType CustomerType { get { return (CustomerType) int.Parse(ddlCustomerType.SelectedValue); } } public string ErrorMessage { set { lblErrorMessage.Text = String.Format("<p><strong>Error:</strong><br/>{0}</p>", value); } } } }
در WinUI:
وارد کدنویسی Form1 شوید و کد آن را بصورت زیر تغییر دهید:
using System; using System.Collections.Generic; using System.Windows.Forms; using SoCPatterns.Layered.Model; using SoCPatterns.Layered.Presentation; using SoCPatterns.Layered.Service; using StructureMap; namespace SoCPatterns.Layered.WinUI { public partial class Form1 : Form, IProductListView { private ProductListPresenter _productListPresenter; public Form1() { InitializeComponent(); _productListPresenter = new ProductListPresenter(this, ObjectFactory.GetInstance<Service.ProductService>()); this.cmbCustomerType.SelectedIndexChanged += delegate { _productListPresenter.Display(); }; dgvProducts.AutoGenerateColumns = false; cmbCustomerType.SelectedIndex = 0; } public void Display(IList<ProductViewModel> products) { dgvProducts.DataSource = products; } public CustomerType CustomerType { get { return (CustomerType)cmbCustomerType.SelectedIndex; } } public string ErrorMessage { set { MessageBox.Show( String.Format("Error:{0}{1}", Environment.NewLine, value)); } } } }
با توجه به کد فوق، نمونه ای را از کلاس ProductListPresenter، در لحظهی نمونه سازی اولیهی کلاس UI، ایجاد نمودیم. با استفاده از متد ObjectFactory.GetInstance مربوط به StructureMap، نمونه ای از کلاس ProductService ایجاد شده است و به سازندهی کلاس ProductListPresenter ارسال گردیده است. در مورد Structuremap در مباحث بعدی با جزئیات بیشتری صحبت خواهم کرد. پیاده سازی معماری لایه بندی در اینجا به پایان رسید.
اما اصلا نگران نباشید، شما فقط پرواز کوتاه و مختصری را بر فراز کدهای معماری لایه بندی داشته اید که این فقط یک دید کلی را به شما در مورد این معماری داده است. این معماری هنوز جای زیادی برای کار دارد، اما در حال حاضر شما یک Applicaion با پیوند ضعیف (Loosely Coupled) بین لایهها دارید که دارای قابلیت تست پذیری قوی، نگهداری و پشتیبانی آسان و تفکیک پذیری قدرتمند بین اجزای آن میباشد. شکل زیر تعامل بین لایهها و وظایف هر یک از آنها را نمایش میدهد.
var facilities = context.Facilities.ToList();
var facilities = context.Facilities.Select(x => new { x.Name, x.MemberCost }).ToList();
var facilities = context.Facilities.Where(x => x.MemberCost > 0).ToList();
var facilities = context.Facilities.Where(x => x.MemberCost > 0 && x.MemberCost < (x.MonthlyMaintenance / 50)) .Select(x => new { x.FacId, x.Name, x.MemberCost, x.MonthlyMaintenance }).ToList();
var facilities = context.Facilities.Where(x => x.Name.Contains("Tennis")).ToList();
int[] ids = { 1, 5 }; var facilities = context.Facilities.Where(x => ids.Contains(x.FacId)).ToList();
var facilities = context.Facilities .Select(x => new { x.Name, Cost = x.MonthlyMaintenance > 100 ? "expensive" : "cheap" }).ToList();
var date = new DateTime(2012, 09, 01); var members = context.Members.Where(x => x.JoinDate >= date) .Select(x => new { x.MemId, x.Surname, x.FirstName, x.JoinDate }).ToList();
var members = context.Members.OrderBy(x => x.Surname) .Select(x => new { x.Surname }) .Distinct() .Take(10) .ToList();
var names = context.Members.Select(m => m.Surname).ToList() .Union(context.Facilities.Select(f => f.Name).ToList()) // For now we have to use `.ToList()` here .ToList();
SELECT surname FROM members UNION SELECT name FROM facilities;
var latest = context.Members.Max(x => x.JoinDate);
var latest2 = context.Members.Select(m => m.JoinDate).DefaultIfEmpty().Max();
SELECT MAX([m].[JoinDate]) FROM (SELECT NULL AS [empty]) AS [empty] LEFT OUTER JOIN [Members] AS [m] ON 1 = 1;
var latest3 = context.Members.Max(m => (DateTime?)m.JoinDate) ?? DateTime.Now;
var latest4 = context.Members.OrderByDescending(m => m.JoinDate).Select(m => m.JoinDate).FirstOrDefault();
var lastMember = context.Members.OrderByDescending(m => m.JoinDate) .Select(x => new { x.FirstName, x.Surname, x.JoinDate }) .FirstOrDefault();
var members = context.Members.Select(x => new { x.FirstName, x.Surname, x.JoinDate }) .Where(x => x.JoinDate == context.Members.Max(x => x.JoinDate)) .ToList();
همانطور که مشاهده میشود، همواره نیاز به یک Filegroup اضافهتری از آنچه مورد نظرتان در تعریف تابع است، میباشد. بنابراین اگر Function دارای n مقدار باشد، به n+1 مقدار برای Filegroup نیاز است.
همچنین هیچ محدودیتی برای اولین و آخرین بازه در نظر گرفته نمیشود. بنابراین جهت محدود کردن مقادیری که در این بازهها قرار میگیرند، میتوان از Check Constraint استفاده نمود.
یک سوال متداول اینکه از کدام مورد استفاده شود؟ در پاسخ باید گفت، به چگونگی تعریف پارتیشن هایتان وابسته است. مطابق شکل، تنها تفاوت این دو، در نقاط مرزی هر یک از پارتیشنها میباشد. در بیشتر اوقات هنگام کار با دادههای عددی میتوان از Left استفاده نمود و بطور مشابه هنگامیکه نوع دادهها از جنس زمان است، میتوان از Right استفاده کرد.
گام بعدی پس از ایجاد Partition Function، تعریف Partition Schema است، که به منظور قرار گرفتن هر یک از پارتیشنهای تعریف شده توسط Function در Filegroupهای مناسب آن استفاده میشود.
گام پایانی ایجاد یک جدول، استفاده از Partition Schema است، که دادهها را با توجه به رویه درون Partition Function مورد استفاده، ذخیره میکند. همانطور که میدانید هنگام ایجاد یک جدول، میتوان مکان ذخیره شدن آنرا مشخص نمود.
Create Table <name> (…) ON …
در هنگام ایجاد یک جدول، معمولاً جدول در Filegroup پیش فرض که PRIMARY است، قرار میگیرد. میتوان با نوشتن نام Partition Schema و همچنین Partition Key که پیشتر ذکر آن رفت، بعد از بخش ON، برای جدول مشخص نمائیم که دادههای آن به چه ترتیبی ذخیره شوند. ارتباط این سه به شرح زیر است:
توجه شود زمانیکه یک Primary Key Constraint به یک جدول اضافه میشود، یک Unique Clustered Index نیز همراه با آن ساخته میشود. چنانچه Primary Key شامل یک Clustered Index باشد، جدول با استفاده از این ستون (ستونهای) شاخص ذخیره خواهد شد، در حالیکه اگر Primary Key شامل یک Non Clustered Index باشد، یک ساختار ذخیره-سازی اضافی ایجاد خواهد شد که دادههای جدول در آن قرار خواهند گرفت.
به عنوان یک Best Practice هنگام ایجاد یک Partition Table به منظور پارتیشن بندی، از ساختار Aligned Index استفاده شود. بدین ترتیب که تعریف Index، شامل Partition Key (ستونی که معیاری برای پارتیشن بندی است) باشد. چنانچه این عمل انجام شود، دادههای ذخیره شده مرتبط با هر پارتیشن متناظر با همان شاخص، در فایل دادهای (NDF.) ذخیره خواهند شد. از این رو چنانچه کوئری درخواست شده از جدول روی یک محدوده باشد
Where [OrderDate] Between …
بدین ترتیب برای بهرمندی از این مزایا، استفاده از Aligned Index توصیه شده است.
از نیازمندیهای متداول در پارتیشنینگ میتوان به افزودن، حذف پارتیشنها و جابجایی محتوای یک پارتیشن که برای عملیات آرشیو استفاده میشود، اشاره کرد.
به منظور ایجاد یک محدوده جدید به پارتیشنها استفاده میشود. یک نکته مهم مادامی که عملیات انتقال دادهها به پارتیشن جدید انجام میگیرد، روی جدول یک قفل انحصاری قرار میگیرد و بدین ترتیب عملیات ممکن است زمانبر باشد.
به عنوان یک Best Practice همواره یک Partition خالی را Split نمائید و پس از آن اقدام به بارگذاری داده در آن نمائید.
به یاد داشته باشید پیش از انجام عملیات splitting روی Partition Function با تغییر در Partition Schema (و بکارگیری Next Used) مشخص نمائید چه محدودهای در این Filegroup جدید قرار خواهد گرفت.
به منظور ادغام پارتیشنها استفاده میشود، چنانچه پارتیشن خالی نیست، برای عملیات ادغام مسائل Performance به علت اینکه در طول عملیات از Lock (قفل انحصاری) استفاده میشود، در نظر گرفته شود.
چنانچه جدول و شاخصهای آن به صورت Aligned هستند، میتوانید از Switch in و Switch out استفاده نمائید. عملیات بدین ترتیب انجام میشود که بلافاصله محتوای یک پارتیشن یا جدول (Source) در یک پارتیشن خالی جدولی دیگر و یا یک جدول خالی (Target) قرار میگیرد. عملیات تنها روی Meta Data انجام میگیرد و هیچ داده ای منتقل نمیشود.
محدودیتهای بکارگیری به شرح زیر است:
- جدول یا پارتیشن Target باید حتماً خالی باشد.
- جداول Source و Target حتماً باید در یک Filegroup یکسان قرار داشته باشند.
- جدول Source باید حاوی Aligned Indexهای مورد نیاز Target و همچنین مطابقت در Filegroup را دارا باشد.
- چنانچه Target به عنوان یک پارتیشن است، اگر Source جدول است بایست دارای یک Check Constraint باشد در غیر این صورت چنانچه یک پارتیشن است باید محدوده آن در محدوده Target قرار گیرد.
در ابتدا یک بانک اطلاعاتی را به طریق زیر ایجاد میکنیم:
این بانک مطابق تصویر، شامل 3 عدد فایل گروپ (FG1، FG2 و FG3) و 3 عدد دیتا فایل (P1، P2 و P3) میباشد. Filegroup پیش فرض Primary است، که چنانچه در تعریف جداول به نام Partition Schema و Partition Key مرتبط اشاره نشود، به طور پیش فرض در Filegroup موسوم به Primary قرار میگیرد. چنانچه چک باکس Default انتخاب شود، همانطور که قابل حدس زدن است، آن Filegroup در صورت مشخص نکردن نام Filegroup در تعریف جدول، به عنوان مکان ذخیره سازی انتخاب میشود. چک باکس Read Only نیز همانطور که از نامش پیداست، چنانچه روی یک Filegroup تنظیم گردد، عملیات مربوط به Write روی دادههای آن قابل انجام نیست و برای Filegroup هایی که جنبه نگهداری آرشیو را دارند، قابل استفاده است.
چنانچه Filegroup ای را از حالت Read Only دوباره خارج کنیم، میتوان عملیات Write را دوباره برای آن انجام داد.
پس از ایجاد بانک اطلاعاتی، گام بعدی ایجاد یک Partition Function و پس از آن یک Partition Schema است. همانطور که مشاهده میکنید در Partition Function از سه مقدار استفاده شده، بنابراین در Partition Schema باید از چهار Filegroup استفاده شود، که در مثال ما از Filegroup پیش فرض که Primary است، استفاده شده است.
USE [PartitionDB] GO CREATE PARTITION FUNCTION pfOrderDateRange(DATETIME) AS RANGE LEFT FOR VALUES ('2010/12/31','2011/12/31','2012/12/31') GO CREATE PARTITION SCHEME psOrderDateRange AS PARTITION pfOrderDateRange TO (FG1,FG2,FG3,[PRIMARY]) GO
پس از طی گامهای قبل، به ایجاد یک جدول به صورت Aligned Index مبادرت ورزیده میشود.
CREATE TABLE Orders ( OrderID INT IDENTITY(1,1) NOT NULL, OrderDate DATETIME NOT NULL, OrderFreight MONEY NULL, ProductID INT NULL, CONSTRAINT PK_Orders PRIMARY KEY CLUSTERED (OrderID ASC, OrderDate ASC) ON psOrderDateRange (OrderDate) ) ON psOrderDateRange (OrderDate) GO
در ادامه برای بررسی درج اطلاعات در پارتیشن با توجه به محدوده آنها اقدام به افزودن رکوردهایی در جدول ساخته شده مینمائیم.
SET NOCOUNT ON DECLARE @OrderDate DATETIME DECLARE @X INT SET @OrderDate = '2010/01/01' SET @X = 0 WHILE @X < 300 BEGIN INSERT dbo.Orders ( OrderDate, OrderFreight, ProductID) VALUES( @OrderDate + @X, @X + 10, @X) SET @X = @X + 1 END GO SET NOCOUNT ON DECLARE @OrderDate DATETIME DECLARE @X INT SET @OrderDate = '2011/01/01' SET @X = 0 WHILE @X < 300 BEGIN INSERT dbo.Orders ( OrderDate, OrderFreight, ProductID) VALUES( @OrderDate + @X, @X + 10, @X) SET @X = @X + 1 END GO SET NOCOUNT ON DECLARE @OrderDate DATETIME DECLARE @X INT SET @OrderDate = '2012/01/01' SET @X = 0 WHILE @X < 300 BEGIN INSERT dbo.Orders ( OrderDate, OrderFreight, ProductID) VALUES( @OrderDate + @X, @X + 10, @X) SET @X = @X + 1 END GO
از طریق دستور Select زیر میتوان نحوه توزیع دادهها را در جدول مشاهده کرد.
USE [PartitionDB] GO SELECT OBJECT_NAME(i.object_id) AS OBJECT_NAME, p.partition_number, fg.NAME AS FILEGROUP_NAME, ROWS, au.total_pages, CASE boundary_value_on_right WHEN 1 THEN 'Less than' ELSE 'Less or equal than' END AS 'Comparition',VALUE FROM sys.partitions p JOIN sys.indexes i ON p.object_id = i.object_id AND p.index_id = i.index_id JOIN sys.partition_schemes ps ON ps.data_space_id = i.data_space_id JOIN sys.partition_functions f ON f.function_id = ps.function_id LEFT JOIN sys.partition_range_values rv ON f.function_id = rv.function_id AND p.partition_number = rv.boundary_id JOIN sys.destination_data_spaces dds ON dds.partition_scheme_id = ps.data_space_id AND dds.destination_id = p.partition_number JOIN sys.filegroups fg ON dds.data_space_id = fg.data_space_id JOIN (SELECT container_id, SUM(total_pages) AS total_pages FROM sys.allocation_units GROUP BY container_id) AS au ON au.container_id = p.partition_id WHERE i.index_id < 2
خروجی دستور فوق به شرح زیر است:
در ادامه به ایجاد یک Filegroup جدید میپردازیم.
/* Query 2-3- Split a partition*/ -- Add FG4: ALTER DATABASE PartitionDB ADD FILEGROUP FG4 Go ALTER PARTITION SCHEME [psOrderDateRange] NEXT USED FG4 GO ALTER PARTITION FUNCTION [pfOrderDateRange]() SPLIT RANGE('2013/12/31') GO -- Add Partition 4 (P4) to FG4: GO ALTER DATABASE PartitionDB ADD FILE ( NAME = P4, FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLEXPRESS\MSSQL\DATA\P4.NDF' , SIZE = 1024KB , MAXSIZE = UNLIMITED, FILEGROWTH = 10%) TO FILEGROUP [FG4] -- GO
و در ادامه به درج اطلاعاتی برای بررسی نحوه توزیع دادهها در Filegroup هایمان میپردازیم.
SET NOCOUNT ON DECLARE @OrderDate DATETIME DECLARE @X INT SET @OrderDate = '2013/01/01' SET @X = 0 WHILE @X < 300 BEGIN INSERT dbo.Orders ( OrderDate, OrderFreight, ProductID) VALUES( @OrderDate + @X, @X + 10, @X) SET @X = @X + 1 END GO SET NOCOUNT ON DECLARE @OrderDate DATETIME DECLARE @X INT SET @OrderDate = '2012/01/01' SET @X = 0 WHILE @X < 300 BEGIN INSERT dbo.Orders ( OrderDate, OrderFreight, ProductID) VALUES( @OrderDate + @X, @X + 10, @X) SET @X = @X + 1 END GO
جهت ادغام پارتیشنها به طریق زیر عمل میشود:
/* Query 2-4- Merge Partitions */ ALTER PARTITION FUNCTION [pfOrderDateRange]() MERGE RANGE('2010/12/31') Go
به منظور آرشیو نمودن اطلاعات به طریق زیر از Switch استفاده میکنیم. ابتدا یک جدول موقتی برای ذخیره رکوردهایی که قصد آرشیو آنها را داریم، ایجاد میکنیم. همانگونه که در تعریف جدول مشاهده میکنید، نام Filegroup ای که برای ساخت این جدول استفاده میشود، با Filegroup ای که قصد آرشیو اطلاعات آنرا داریم، یکسان است.
در ادامه میتوان مثلاً با ایجاد یک Temporary Table به انتقال این اطلاعات بدون توجه به Filegroup آنها پرداخت.
/* Query 2-5- Switch Partitions */ USE [PartitionDB] GO CREATE TABLE [dbo].[Orders_Temp]( [OrderID] [int] IDENTITY(1,1) NOT NULL, [OrderDate] [datetime] NOT NULL, [OrderFreight] [money] NULL, [ProductID] [int] NULL, CONSTRAINT [PK_OrdersTemp] PRIMARY KEY CLUSTERED ([OrderID] ASC,[OrderDate] ASC)ON FG2 ) ON FG2 GO USE [tempdb] GO CREATE TABLE [dbo].[Orders_Hist]( [OrderID] [int] NOT NULL, [OrderDate] [datetime] NOT NULL, [OrderFreight] [money] NULL, [ProductID] [int] NULL, CONSTRAINT [PK_OrdersTemp] PRIMARY KEY CLUSTERED ([OrderID] ASC,[OrderDate] ASC) ) GO USE [PartitionDB] GO ALTER TABLE [dbo].[Orders] SWITCH PARTITION 1 TO [dbo].[Orders_Temp] GO INSERT INTO [tempdb].[dbo].[Orders_Hist] SELECT * FROM [dbo].[Orders_Temp] GO DROP TABLE [dbo].[Orders_Temp] GO SELECT * FROM [tempdb].[dbo].[Orders_Hist]