متغیرهای استاتیک و برنامههای ASP.NET
Accord.NET #3
اگر بخواهیم نوع چهار میوه (سیب، گلابی، موز، پرتغال) را که از خط سورتینگ عبور میکنند، تشخیص دهیم و یا اینکه بخواهیم تشخیص اعداد دست نویس را داشته باشیم و یا اینکه حتی مطالب این وب سایت را که شامل چندین برچسب هستند، طبقه بندی کنیم، آیا در این تشخیصها SVM به ما کمک میکند؟ پاسخ مثبت است.
در فضای نام یادگیری ماشین Accord.NET دو تابع خوب MulticlassSupportVectorLearning و MultilabelSupportVectorMachine برای این گونه از مسائل تعبیه شده است. زمانیکه مسئلهی ما شامل مجموعه دادههایی بود که در چندین کلاس دسته بندی میشوند (مانند دسته بندی میوه، اعداد و ...) روش Multiclass و زمانیکه عناصر مجموعه داده ما به طور جداگانه شامل چندین برچسب باشند (مانند دسته بندی مطالب با داشتن چندین تگ، ...) روش Multilabel ابزار مفیدی خواهند بود. (+)
با توجه به دودویی بودن ماشین بردار پشتیبان، دو استراتژی برای به کارگیری این الگوریتم برای دسته بندیهای چند کلاسه وجود دارد:
- روش یک در مقابل همه - One-against-all : در این روش عملا همان روش دودویی SVM را برای هر یک از کلاسها به صورت جداگانه بررسی میکنیم. مثلا برای تشخیص میوه، یک بار دو کلاس سیب و غیر سیب (مابقی) بررسی میشوند و به همین ترتیب برای سایر کلاسها و در مجموع صفحات ابرصفحه جدا کننده بین هر کلاس در مقابل سایر کلاسها ایجاد میشود.
- روش یک در مقابل یک - One-against-one (*) : در این روش هر کلاس، با هر یک از کلاسهای دیگر به صورت تک تک بررسی میشود و صفحات ابرصفحه جدا کننده مابین هر جفت کلاس متفاوت ایجاد میشود. (بیشتر در +)
*روش "یک در مقابل یک" یا One-against-one اساس کار دسته بندی MulticlassSupportVectorMachine در فضای نام Accord.MachineLearning است.
یک مثال کاربردی : هدف در این مثال دسته بندی اعداد فارسی به کمک MulticlassSupportVectorMachine است.
به معرفی ابزار کار مورد نیاز میپردازیم.
1.مجموعه ارقام دستنویس هدی: مجموعه ارقام دستنویس هدی که اولین مجموعهی بزرگ ارقام دستنویس فارسی است، مشتمل بر ۱۰۲۳۵۳ نمونه دستنوشته سیاه سفید است. این مجموعه طی انجام یک پروژهی کارشناسی ارشد درباره بازشناسی فرمهای دستنویس تهیه شده است. دادههای این مجموعه از حدود ۱۲۰۰۰ فرم ثبت نام آزمون سراسری کارشناسی ارشد سال ۱۳۸۴ و آزمون کاردانی پیوستهی دانشگاه جامع علمی کاربردی سال ۱۳۸۳ استخراج شده است. (اطلاعات بیشتر درباره مجموعه ارقام دستنویس هدی) .
تعداد 1000 نمونه (از هر عدد 100 نمونه) از این مجموعه داده، با فرمت bmp در این پروژه مورد استفاده قرار گرفته که به همراه پروژه در انتهای این مطلب قابل دریافت است.
2.استخراج ویژگی (Feature extraction ) : در بازشناسی الگو و مفاهیم کلاس بندی، یکی از مهمترین گامها، استخراج ویژگی است. ما موظف هستیم تا اطلاعات مناسبی را به عنوان ورودی برای دسته بندیمان معرفی نماییم. روشهای مختلفی برای استخراج ویژگی وجود دارند. ویژگیها به دو دستهی کلی ویژگیهای ظاهری (Appearance) و ویژگیهای توصیف کننده ( Descriptive) قابل تقسیم هستند. در تشخیص حروف و اعداد، ویژگیهایی مانند شدت نور نقاط (Intensity)، تعداد حلقه بسته، تعداد خطوط راست، تعداد دندانه، تعداد نقطه (برای حروف) و ... در دستهی اول و ویژگیهایی مانند شیب خطوط، گرادیان، میزان افت یا شدت نور یک ناحیه، HOG و ... در دسته دوم قرار میگیرند. در این مطلب ما تنها از روش شدت نور نقاط برای استخراج ویژگیهایمان استفاده کردهایم.
کد زیر با دریافت یک فایل Bitmap، ابتدا ابعاد را به اندازه 32*32 تغییر میدهد و سپس آنرا به صورت یک بردار 1*1024 را بر میگرداند:
//تابع استخراج ویژگی private static double[] FeatureExtractor(Bitmap bitmap) { bitmap = BitmapResizer(bitmap, 32, 32); double[] features = new double[32 * 32]; for (int i = 0; i < 32; i++) for (int j = 0; j < 32; j++) features[i * 32 + j] = (bitmap.GetPixel(j, i).R == 255) ? 0 : 1; return features; } //تابع تغییر دهنده ابعاد عکس private static Bitmap BitmapResizer(Bitmap bitmap, int width, int height) { var newbitmap = new Bitmap(width, height); using (Graphics g = Graphics.FromImage((Image)newbitmap)) { g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; g.DrawImage(bitmap, 0, 0, width, height); } return newbitmap; }
ورودیها (inputs) = با توجه به اینکه تعداد نمونهها 50 مورد از هر عدد (مجموعا 500 نمونه) تعیین شده است و تعداد ویژگیهای هر نمونه یک بردار با طول 1024 است، ابعاد ماتریس ورودی مان [1024][500] میشود.
برچسبها (labels) = تعداد برچسب مسلما به تعداد نمونه هایمان یعنی 500 مورد میباشد و مقادیر آن قاعدتا عدد متناظر آن تصویر است.
برای این کار از قطعه کد زیر استفاده میکنیم :
var path = new DirectoryInfo(Directory.GetCurrentDirectory()).Parent.Parent.FullName + @"\dataset\"; // ایجاد ورودی و برچسب int trainingCount = 50; double[][] inputs = new double[trainingCount * 10][]; int[] labels = new int[trainingCount * 10]; var index = 0; var filename = ""; Bitmap bitmap; double[] feature; for (int number = 0; number < 10; number++) { for (int j = 0; j < trainingCount; j++) { index = (number * trainingCount) + j; filename = string.Format(@"{0}\{0} ({1}).bmp", number, j + 1); bitmap = new Bitmap(path + filename); feature = FeatureExtractor(bitmap); inputs[index] = feature; labels[index] = number; Console.WriteLine(string.Format("{0}.Create input and label for number {1}", index, number)); } }
private static double MachineLearning(IKernel kernel, double[][] inputs, int[] labels) { machine_svm = new MulticlassSupportVectorMachine(1024, kernel, 10); // معرفی دسته بندمان به الگوریتم یادگیری SMO MulticlassSupportVectorLearning ml = new MulticlassSupportVectorLearning(machine_svm, inputs, labels) { Algorithm = (svm, classInputs, classOutputs, i, j) => new SequentialMinimalOptimization(svm, classInputs, classOutputs) }; var error = ml.Run(); return error; }
// بررسی یک دسته از ورودیها index = 51; for (int number = 0; number < 10; number++) { filename = string.Format(@"{0}\{0} ({1}).bmp", number, index); bitmap = new Bitmap(path + filename); feature = FeatureExtractor(bitmap); double[] responses; int recognizednumber = machine_svm.Compute(feature, out responses); Console.WriteLine ( String.Format ( "Recognized number for file {0} is : '{1}' [{2}]", filename, recognizednumber, (recognizednumber == number ? "OK" : "Error") ) ); if (!machine_svm.IsProbabilistic) { // Normalize responses double max = responses.Max(); double min = responses.Min(); responses = Accord.Math.Tools.Scale(min, max, 0, 1, responses); //int minIndex = Array.IndexOf(responses, 0); } }
مشاهده میشود که تنها بازشناسی تصاویر اعداد 4 و 6، به اشتباه انجام شده است که جای نگرانی نیست و میتوان با افزایش تعداد نمونههای آموزشی و یا تغییرات پارامترها از جمله نوع کرنل و یا الگوریتم آموزنده این خطاها را نیز بر طرف کرد.
همانطور که دیدیم SVM گزینهی بسیار مناسبی برای طبقه بندی خیلی از مسائل دو کلاسه و یا حتی چند کلاسه است. اما آکورد دات نت Classifierهای خوب دیگری (مانند Naive Bayes و Decision Trees یا درخت تصمیم و ... ) را نیز در چارچوب خود جای داده که در مطالب آینده معرفی خواهند شد.
دریافت پروژه
نصب ابتدایی آن بسیار ساده است و نکته خاصی ندارد.
پس از نصب، یکبار VS.NET را بسته و سپس باز کنید. این قالب جدید، ذیل قسمت پروژههای ویندوز مرتبط با ویژوال سی شارپ، ظاهر خواهد شد:
در این حال اگر یک پروژه جدید را آغاز کنید، این قالب تدارک دیده شده، لایهها و قسمتهای مختلف را به صورت خودکار اضافه خواهد کرد:
نکته مهم! برنامه کامپایل نمیشود!
به عمد، جهت کاهش حجم قالب دریافتی فوق، فایلهای باینری آن پیوست نشدهاند. وگرنه باید بالای 30 مگابایت را دریافت میکردید که واقعا نیازی نیست.
اما اگر به هر پروژه داخل Solution دقت کنید، فایل متنی packages.config آن نیز پیوست شده است. به کمک این فایلها به سادگی میتوان تمام وابستگیها را از طریق NuGet بازیابی کرد.
برای این منظور ابتدا به اینترنت متصل شده (مهم) و سپس بر روی Solution کلیک راست کرده و گزینه فعال سازی Restore کلیه بستههای نیوگت را انتخاب کنید.
پس از اینکار، آخرین نگارش NuGet.exe از اینترنت دریافت و به پروژه اضافه میشود:
اکنون اگر Solution را Build کنید، اولین کاری که صورت خواهد گرفت، دریافت کلیه وابستگیها از سایت NuGet است. اندکی تامل کنید تا اینکار تمام شود.
پس از پایان کار دریافت، پوشه packages در کنار فایل sln پروژه ایجاد شده، تشکیل میشود.
یکبار وجود آنرا بررسی کنید.
اکنون اگر برنامه را Build کنید احتمالا پیغام میدهد که Fody را نمیتواند پیدا کند با اینکه دریافت شده و در پوشه packages موجود است. هر زمان، هر پیغام خطایی در مورد Fody مشاهده کردید، فقط یکبار VS.NET را بسته و باز کنید. مشکل حل میشود!
تنها کاری که پس از بازسازی پوشه packages بهتر است صورت گیرد (اختیاری است البته)، صدور دستور ذیل در خط فرمان پاور شل است:
PM> Update-Package
پس از اینکار، نیاز است یکبار VS.NET را بسته و مجددا باز کنید (مهم) تا تمام وابستگیها به درستی بارگذاری شوند. خصوصا بسته Fody که کار AOP را انجام میدهد. در غیر اینصورت موفق به Build پروژه نخواهید شد.
بنابراین به صورت خلاصه:
الف) یکبار گزینه فعال سازی Restore کلیه بستههای نیوگت را انتخاب کنید.
ب) پروژه را Build کنید تا وابستگیها را از سایت NuGet دریافت کند.
ج) دستور Update-Package را اجرا نمائید (اختیاری).
ج) VS.NET را پس از سه مرحله فوق، یکبار بسته و باز کنید.
در کل بستههای مورد استفاده به این شرح هستند:
PM> Install-Package MahApps.Metro -Pre PM> Install-Package MahApps.Metro.Resources PM> Install-Package EntityFramework PM> Install-Package structuremap PM> Install-Package PropertyChanged.Fody PM> Install-Package MvvmLight PM> Install-Package Microsoft.SqlServer.Compact
یک نکته جانبی
فید NuGet در VS.NET به Https تنظیم شده است. اگر دسترسی به Https برای شما به کندی صورت میگیرد فقط کافی است مسیر فید آنرا در منوی Tools، گزینهی Options، ذیل قسمت Package manager یافته و به http://nuget.org/api/v2 تغییر دهید؛ یعنی به Http خالی، بجای Https؛ تا سرعت دریافت بستههای NuGet مورد نظر افزایش یابند.
اجرای پروژه و برنامه
پس از این مراحل و Build موفقیت آمیز پروژه، برنامه را اجرا کنید. در اولین بار اجرای برنامه به صورت خودکار بانک اطلاعاتی به همراه ساختار جداول تشکیل میشوند. اما ... با خطای زیر مواجه خواهید شد:
The path is not valid. Check the directory for the database. [ Path = D:\...\bin\Debug\Db\db.sdf ]
<connectionStrings> <clear/> <add name="MyWpfFrameworkContext" connectionString="Data Source=|DataDirectory|\Db\db.sdf;Max Buffer Size=30720;File Mode=Read Write;" providerName="System.Data.SqlServerCE.4.0" /> </connectionStrings>
برای ورود به برنامه از نام کاربری Admin و کلمه عبور 123456 استفاده نمائید.
این کاربر پیش فرض در کلاس MyWpfFrameworkMigrations لایه دادههای برنامه، توسط متد addRolesAndAdmin اضافه شده است.
خوب! تا اینجا با نحوه نصب و راه اندازی این قالب جدید آشنا شدیم. در قسمتهای بعد، به جزئیات ارتباطات و نحوه استفاده از آن به عنوان پایه یک کار و پروژه جدید، خواهیم پرداخت.
به روز رسانی 1.1
جهت سازگاری با EF 6 ، StructureMap 3 و همچنین VS 2013، پروژه به روز شد: WpfFrmwork_1.1.zip
به روز رسانی نهایی
به روز شدهی این پروژه را بر اساس آخرین وابستگیهای آن از اینجا دریافت کنید.
در ادامه نحوه سازگار سازی این مجموعه را با ASP.NET MVC مرور خواهیم کرد:
الف) سورسهای اصلی Flash کنترل ارسال فایلها
اگر علاقمند به تغییر اطلاعاتی در فایل فلش نهایی هستید به پوشه OriginalFlashSource پروژه پیوست شده مراجعه کنید. در اینجا برای مثال یک سری از برچسبهای آن فارسی شدهاند و کامپایل مجدد.
ب) مزیت استفاده از Flash uploader
با استفاده از Flash uploader امکان انتخاب چندین فایل با هم وجود دارد. همچنین در صفحه دیالوگ انتخاب فایلها دقیقا میتوان پسوند فایلهای مورد نظر را نیز تعیین کرد. این دو مورد در حالت ارسال معمولی فایلها به سرور و استفاده از امکانات معمولی HTML وجود ندارند. به علاوه امکان نمایش درصد پیشرفت آپلود فایلها و همچنین حذف کلی لیست و حذف یک آیتم از لیست را هم درنظر بگیرید.
ج) معادل کنترل Web forms را در ASP.NET MVC به شکل زیر میتوان تهیه کرد:
@helper AddFlashUploader( string uploadUrl, string queryParameters, string flashUrl, int totalUploadSizeLimit = 0, int uploadFileSizeLimit = 0, string fileTypes = "", string fileTypeDescription = "", string onUploadComplete = "") { onUploadComplete = string.IsNullOrEmpty(onUploadComplete) ? "" : "completeFunction=" + onUploadComplete; queryParameters = Server.UrlEncode(queryParameters); fileTypes = string.IsNullOrEmpty(fileTypes) ? "" : "&fileTypes=" + Server.UrlEncode(fileTypes); fileTypeDescription = string.IsNullOrEmpty(fileTypeDescription) ? "" : "&fileTypeDescription=" + Server.UrlEncode(fileTypeDescription); var totalUploadSizeLimitData = totalUploadSizeLimit > 0 ? "&totalUploadSize=" + totalUploadSizeLimit : ""; var uploadFileSizeLimitData = uploadFileSizeLimit > 0 ? "&fileSizeLimit=" + uploadFileSizeLimit : ""; var flashVars = onUploadComplete + fileTypes + fileTypeDescription + totalUploadSizeLimitData + uploadFileSizeLimitData + "&uploadPage=" + uploadUrl + "?" + queryParameters; <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="575" height="375" id="fileUpload" align="middle"> <param name="allowScriptAccess" value="sameDomain" /> <param name="movie" value="@flashUrl" /> <param name="quality" value="high" /> <param name="wmode" value="transparent"> <param name=FlashVars value="@flashVars"> <embed src="@flashUrl" FlashVars="@flashVars" quality="high" wmode="transparent" width="575" height="375" name="fileUpload" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" /> </object> }
د) نحوه استفاده از HTML helper فوق:
@{ ViewBag.Title = "Index"; var uploadUrl = Url.Action("Uploader", "Home"); var flashUrl = Url.Content("~/Content/FlashUpload/FlashFileUpload.swf"); } <h2> Flash Uploader</h2> <div style="background: #E0EBEF;"> @FlashUploadHelper.AddFlashUploader( uploadUrl: uploadUrl, queryParameters: "User=Vahid&Id=تست", flashUrl: flashUrl, fileTypeDescription: "Images", fileTypes: "*.gif; *.png; *.jpg; *.jpeg", uploadFileSizeLimit: 0, totalUploadSizeLimit: 0, onUploadComplete: "alert('انجام شد');") </div>
using System.Collections.Generic; using System.IO; using System.Web; using System.Web.Mvc; namespace MvcFlashUpload.Controllers { public class HomeController : Controller { public ActionResult Index() { return View(); } public ActionResult Uploader(string User, string Id, IEnumerable<HttpPostedFileBase> FileData) { var queryParameter1 = User; var queryParameter2 = Id; // ... foreach (var file in FileData) { if (file.ContentLength > 0) { var fileName = Path.GetFileName(file.FileName); var path = Path.Combine(Server.MapPath("~/App_Data/Uploads"), fileName); file.SaveAs(path); } } return Content(" "); } } }
توضیحات:
در اینجا uploadUrl، مسیر اکشن متدی است که قرار است اطلاعات فایلها را دریافت کند. queryParameters اختیاری است. اگر تعریف شود تعدادی کوئری استرینگ دلخواه را میتواند به متد Uploader ارسال کند. برای نمونه در اینجا User و Id ارسال شدهاند یا هر نوع کوئری استرینگ دیگری که مدنظر است.
flashUrl مسیر فایل SWF را مشخص میکند. در اینجا فایل FlashFileUpload.swfدر پوشه Content/FlashUpload قرار گرفته است.
fileTypeDescription برچسبی است که نوع فایلهای قابل انتخاب را به کاربر نمایش میدهد و fileTypes نوعهای مجاز قابل ارسال را دقیقا مشخص میکند.
پارامترهای uploadFileSizeLimit و totalUploadSizeLimit در صورتیکه مساوی صفر وارد شوند، به معنای عدم محدودیت اندازه در فایلها و جمع حجم ارسالی در هر بار است.
استفاده از پارامتر onUploadComplete اختیاری است. در اینجا میتوان پس از پایان عملیات از طریق جاوا اسکریپت عملیاتی را انجام داد. برای مثال اگر خواستید کاربر را به صفحه خاصی هدایت کنید، window.locationرا مقدار دهی نمائید.
در متد Uploader کنترلر فوق، پارامترهای User و id اختیاری بوده و بر اساس queryParameters متد FlashUploadHelper.AddFlashUploader مشخص میشوند. اما نام FileData نباید تغییری کند؛ از این لحاظ که دقیقا همین نام در فایل فلش، مورد استفاده قرار گرفته است.
در اکشن متد دریافت فایلها، لیستی از فایلهای ارسالی به سرور دریافت شده و سپس بر این اساس میتوان آنها را در مکانی مشخص ذخیره نمود.
دریافت پروژه
MvcFlashUploader.zip
مسیریابی در Angular - قسمت اول - معرفی
بررسی مشکلات AngularJS 1.x
- کلید خارجی ترکیبی (composite foreign key)
- خود ارجاعی (self referencing)
- اعمال تغییرات به صورت آبشاری (cascade)
- چندین مسیر برای اعمال (multiple cascading path)
- جدول اتصال (junction table)- ارتباط یک به یک
توسط دستور create table به دو شکل میتوانیم بر روی ستونها قید (کلید اولیه، check، کلید خارجی، کلید یونیک...) تعریف نمود:
- قید ستونی
- قید جدولی
syntax مربوط به قید کلید خارجی در مدل ستونی به صورت زیر است:
<column_constraint> ::= [ CONSTRAINT constraint_name ] { ... | [ FOREIGN KEY ] REFERENCES [ schema_name . ] referenced_table_name [ ( ref_column ) ] [ ON DELETE { NO ACTION | CASCADE | SET NULL | SET DEFAULT } ] [ ON UPDATE { NO ACTION | CASCADE | SET NULL | SET DEFAULT } ] [ NOT FOR REPLICATION ] ... }
کلید خارجی ترکیبی
زمانی که در جدول والد (parent) کلید اولیه ترکیبی باشد، هر جدولی که بخواهد به کلید جدول والد ارجاعی داشته باشد باید از ترکیب دو ستون برای ساخت کلید خارجی استفاده کند.
فرض کنید جدول parent به این صورت است (ترکیب دو ستون col1 و col2 کلید اولیه است)
create table parent ( col1 int not null, col2 int not null, col3 char(1) null, -- Composite Primary Key primary key(col1, col2) );
و جدول child که دارای قید کلید خارجی ترکیبی به نام fk_comp است و به جدول parent ارجاع داده است:
create table child ( col0 int primary key, col1 int null, col2 int null, -- Composite Foreing Key Constraint constraint fk_comp foreign key (col1, col2) references parent(col1, col2) );
در این DDL هم از قید جدولی برای تعریف کلید خارجی ترکیبی استفاده شده است.
نمودار این دو جدول:
پس به عنوان نتیجه گیری، هرگاه جدول اصلی دارای کلید ترکیبی بود در جداول child نیز باید از کلید خارجی ترکیبی برای ایجاد relationship استفاده نمود.
اما این دو جدول را به یک شیوه دیگر نیز میتوان طراحی نمود. در جدول parent ترکیب دو ستون col1 و col2 را منحصربفرد (unique) گرفته و ستونی دیگر (مثلا از نوع identity) را به عنوان کلید اولیه در نظر گرفت (یا یک ستون از نوع محاسباتی تعریف کرده و آن را کلید قرار داد)
create table parent ( col0 int not null primary key identity, col1 int not null, col2 int not null, col3 char(1) null, -- Composite Unique Key unique(col1, col2) ); create table child ( col0 int primary key, col1 int null references parent );
فرض کنید بخشهای مختلف یک سازمان که بصورت چارت است را توسط جدول پیاده سازی کردیم. ستونهای جدول به این شرح هستند:
- کد بخش
- نام بخش
- کد بخش بالایی
create table chart ( chart_nbr int not null primary key, parent_nbr int null references chart, chart_name varchar(5) null );
حالا فرض کنید میخواهیم اطلاعات نامه هایی که بین بخشها رد و بدل میشود را در یک جدول ذخیره کنیم. جدول دارای ستونهای زیر خواهد بود:
- شماره نامه
- کد بخش فرستنده
- کد بخش گیرنده
create table letters ( letter_nbr int primary key, sec_sender int not null references chart, sec_reciver int not null references chart );
نمودار جدول نامهها و چارت:
نکته ای که در اینجا وجود دارد این است که اگر کلید جدول chart بروز شود آنگاه SQL Server از دو راه میتواند جدول letters را بروز رسانی کند، به این علت پیغام خطایی با عنوان multiple cascading paths صادر میشود. برای رفع این مشکل باید از trigger کمک گرفت.
جدول اتصال (junction table)
برای پیاده سازی رابطه N-N از جدول واسط کمک گرفته میشود. برای این منظور رابطه N-N را باید به دو رابطه 1-N تجزیه کرد.
فرض کنید یک جدول مربوط به خلبانان و جدول دیگر مربوط به مسیرهای پروازی (مثل مسیر ایران-ترکیه، ایران-عربستان...) است. یک خلبان ممکن است در چند مسیر پروازی هواپیما را هدایت کرده باشد و یا بالعکس یک مسیر پروازی ممکن است توسط N خلبان طی شده باشد.
برای پیاده سازی اینگونه سیستم هایی باید یک جدول ایجاد نمود که دارای دو کلید خارجی باشد یکی آنها به جدول خلبانان و دیگری به مسیرهای پروازی مرتبط است.
میتوان ترکیب دو کلید خارجی جدول واسط را کلید اولیه در نظر گرفت.
پس خواهیم داشت:
create table pilot ( pilot_code int primary key, pilot_name varchar(20) ); create table paths ( path_code int primary key, path_name varchar(20) ); create table junction ( pilot_code int references pilot, path_code int references paths, primary key (pilot_code, path_code) );
و نمودار آن:
رابطه یک به یک
زمانی که نمونههای محدودی از یک موجودیت دارای مقدار برای یکسری خصیصه هستند بهتر است جدول به دو جدول تجزیه شود تا فضای اضافی صرف جدول نشود. مثلا در مدرسه تنها 10 درصد دانش آموزان جزء تیم فوتبال هستند حال اگر بخواهیم اطلاعات مربوط به تیم فوتبال مثل تعداد گل زده، تعداد بازی ... در جدول اصلی ذخیره کنیم برای 90 درصد دانش آموزان مقداری نخواهیم داشت. برای حل این مساله ارتباط یک به یک پیشنهاد میشود.
create table student ( std_code int primary key, std_name varchar(25) not null ); create table football ( std_code int primary key constraint one_to_one_fk references student, std_cnt_goal int not null default (0) );
توجه داشته باشید که ستون std_code هم کلید اولیه هست و هم کلید خارجی که به جدول student ارجاع داده شده است.
نتیجه گیری
یک ستون همزمان میتواند کلید اولیه باشد و هم کلید خارجی (مثلا در ارتباط یک به یک)
همانطور که کلید اولیه ترکیبی داریم به همان شکل هم کلید خارجی ترکیبی داریم.
یک جدول میتواند به خودش ارجاع دهد که به آن اصطلاحا self-referencing میگویند
relationship چیزی جز کلید خارجی نیست و کلید خارجی نیز چیزی جز یک قید برای جامعیت دادهها نیست
جامعیت داده ارجاعی را میتوان توسط trigger پیاده سازی کرد
اگر SQL Server بیش از یک مسیر برای تغییر جدول child داشته باشد با مشکل مواجه خواهید شد
Build Events
C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\editbin.exe
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\editbin.exe" /STACK:1000000 "$(TargetPath)"
You can start this tool only from the Visual Studio command prompt. You cannot start it from a system command prompt or from Windows Explorer.
call "$(DevEnvDir)..\Tools\vsvars32.bat"
C:\Program Files\Microsoft Visual Studio 10.0\Common7\Tools\vsvars32.bat
call "$(DevEnvDir)..\Tools\vsvars32.bat" "C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\editbin.exe" /STACK:1000000 "$(TargetPath)"
call "$(DevEnvDir)..\Tools\vsvars32.bat" "editbin.exe" /STACK:1000000 "$(TargetPath)"
<Router AppAssembly="@typeof(Program).Assembly" AdditionalAssemblies="new[] { typeof(Component1).Assembly }"> @* ... Router component elements ... *@ </Router>