SqlBulkCopy-Column Mapping

Column Mapping داینامیک در SSIS با استفاده از کلاس SqlBulkCopy دربرابر Data Flow Task

 (Data Flow Task) یک جزء ضروری در SQL Server (SSIS) است، زیرا به توسعه‌دهندگان SSIS ETL این امکان را می‌دهد که به راحتی داده‌ها را از منابع داده مختلف استخراج کنند؛ تبدیل‌های داده‌ای اولیه و فازی تا تبدیلهای پیشرفته را انجام دهند؛ و داده‌ها را به انبار داده منتقل کنند. با این حال، با تمام محبوبیت و راحتی آن، مواردی وجود دارد که DFT به اندازه کافی خوب نیست. برای نشان دادن برخی از محدودیت‌های DFT، لیستی تصادفی از گلزنان برتر لیگ برتر برای فصل 2019-2020 در یک فایل داریم.

Pos

Player

Nationality

Club

Goals

Jersey Number

1

Jamie Vardy

England

Leicester City

17

9

2

Pierre-Emerick Aubameyang

Gabon

Arsenal

13

14

2

Danny Ings

England

Southampton

13

9

4

Marcus Rashford

England

Manchester United

12

10

4

Tammy Abraham

England

Chelsea

12

9

اسکریپت زیر تعریفی از یک جدول SQL Server را نشان می‌دهد که برای ذخیره داده‌های وارد شده از رکوردهای فایل بالا استفاده خواهد شد.

CREATE TABLE [dbo].[PLGoalScorers](
  [Pos] [tinyint] NULL,
  [Player] [varchar](50) NULL,
  [Nationality] [varchar](50) NULL,
  [Club] [varchar](50) NULL,
  [Goals] [tinyint] NULL,
  [Jersey Number] [tinyint] NULL
) ON [PRIMARY]
GO
با تعریف سورس (فایل ) و مقصد (جدول SQL Server در اسکریپت )،  رکوردها را با موفقیت با استفاده از یک Data Flow Task در SSIS، همانطور که در شکل ۱ نشان داده شده است، وارد می‌کنیم.
Successful Flat File to SQL Server table Data Migration using Data Flow Task in SSIS
 
محدودیت اول Data Flow Task: عدم امکان به‌روزرسانی خودکار نگاشت‌های ستونی
حال، فرض کنید مجموعه داده‌های منبع ما بعداً به‌روزرسانی شود تا ستون Age بازیکن را نیز شامل شود، همانطور که در شکل زیر نشان داده شده است.
Premier League’s leading goal scorer's dataset with a newly added column at the end
برای وارد کردن این ستون جدید، تعریف جدول مقصد SQL Server خود را طوری تغییر می‌دهیم که ستون “سن” را نیز شامل شود. با این حال، وقتی پکیج SSIS Data Flow Task خود را اجرا می‌کنیم – بدون به‌روزرسانی متادیتای flat file connection آن، در شکل زیر متوجه خواهید شد که تمام داده‌های زیر ستون “Age” به ستون موجود “Jersey Number” اضافه شده‌اند. دلیل این امر آن است که نگاشت‌های ستون در هر دو flat file connection و Data Flow Task به صورت پویا به‌روزرسانی نشده‌اند تا آخرین تغییرات ساختاری را در مجموعه داده‌های منبع و مقصد شناسایی کنند.
Preview in SQL Server table of mapping mismatch between Jersey Number and Age column
 
محدودیت دوم Data Flow Task: تغییر مکان ستون‌ها، ETL را مختل می‌کند.
سناریوی دیگری که احتمالاً باعث بروز خطاهای Data Flow Task شما می‌شود، زمانی است که ستون‌های مجموعه داده منبع تغییر مکان می‌دهند. برای مثال، شکل زیر همان مجموعه داده فایل ما را نشان می‌دهد، اما این بار، ستون‌های «Player» و «Pos» جای خود را عوض کرده‌اند.
Preview of Premier League’s leading goal scorer's dataset with first two column swapping positions
اولین چیزی که هنگام تلاش برای وارد کردن داده‌های تغییر یافته متوجه خواهید شد این است که در حالی که به نظر می‌رسد محتوای ردیف‌ها جابجا شده است – و بنابراین با تغییرات در آخرین مجموعه داده ما (در شکل بالا) مطابقت دارد – عناوین ستون‌ها در خروجی flat file source در موقعیت‌های قدیمی خود باقی می‌مانند و در نتیجه، همانطور که در شکل زیر نشان داده شده است، بین مقادیر و عناوین عدم تطابق ایجاد می‌شود.
Preview of Flat File Source data via a Data Viewer within a Data Flow Task indicating a mismatch of values between the first two columns
جای تعجب نیست که وقتی در Data Viewer روی play  کلیک می کنیم تا این داده‌های ورودی ناهماهنگ به روند خود ادامه دهند، همانطور که در شکل زیر نشان داده شده است، اجرای پکیج به علت وجود خطا متوقف شده، با خطای Data conversion مواجه می‌شویم. زیرا ستون اول به جای یک رشته، انتظار یک مقدار عددی را داشته است.
Error at OLE DB Destination component during data migration using SSIS's Data Flow TaskData Flow Task error details relating to failure to convert value due to incorrect data type
برای رفع این خطای Data Flow Task، باید به صورت دستی متادیتای flat file source را به‌روزرسانی کنیم و سپس ستون‌های ورودی و خروجی Data Flow Task را مجدداً نگاشت کنیم. نگاشت دستی ستون‌های ورودی و خروجی در Data Flow Task می‌تواند در صورتی که با یک یا دو منبع با فیلدهای محدود سروکار دارید، قابل تحمل باشد، اما در صورت سروکار داشتن با چندین منبع داده با صدها ستون، می‌تواند به راحتی به یک کار بسیار دلهره‌آور و خسته‌کننده تبدیل شود.

نگاشت داینامیک ستون منبع با مقصد با استفاده از کلاس SqlBulkCopy

محدودیت Data Flow Task که در بالا نشان داده شد، به راحتی با استفاده از کلاس SqlBulkCopy قابل حل است. همانطور که احتمالاً حدس زده‌اید، کلاس SqlBulkCopy شامل جایگزینی Data Flow Task شما با یک اسکریپت .Net است که سپس می‌تواند از طریق یک Script Task در SSIS اجرا شود. 3 جزء اصلی برای وارد کردن موفقیت‌آمیز داده‌های شما با استفاده از کلاس SqlBulkCopy وجود دارد.

1.تعریف کانکشن ها

همانند پیکربندی یک connection manager در Data Flow Task، کلاس SqlBulkCopy مستلزم آن است که شما اتصالات منبع و مقصد را مشخص کنید.  دو متغیر محلی تعریف می کنیم که یک connection string به یک پایگاه داده SQL Server و همچنین مسیری به محل ذخیره فایل منبع CSV ما را مشخص می‌کنند.

var xyz = @”Data Source=(local)\SQL17DEV;Initial Catalog=DemoDB;Integrated Security=SSPI;”; var x = @”C:\Sifiso\pl_leading_goalscorers.csv”; GetDataTabletFromCSVFile(x, xyz);


2.شیء DataTable خود را آماده کنید.

در مرحله بعد، یک شیء DataTable مطابق کد زیر می‌سازیم که برای ذخیره داده‌ها در زمان اجرا استفاده خواهد شد. در این مثال، نقش DataTable مشابه Data Flow Task در SSIS است، با این حال، یک مزیت قابل توجه این واقعیت است که DataTable یک شیء حافظه است که در زمان اجرا ایجاد شده و در پایان اجرا حذف می‌شود – هرگز مانند Data Flow Task پایدار نمی‌ماند. این بدان معناست که نگاشت‌های ستون منبع به مقصد DataTable همیشه هر بار که شیء DataTable ایجاد می‌شود، به‌روزرسانی می‌شوند، که این یک مزیت ارزشمند هنگام کار با یک جدول SQL Server یا فلت فایل با ساختار ستونی است که دائماً در حال تغییر است.

static DataTable GetDataTabletFromCSVFile(string csv_file_path, string connname) { DataTable csvData = new DataTable(); try { using (TextReader csvReader = File.OpenText(csv_file_path)) { string line; while ((line = csvReader.ReadLine()) != null) { string[] items = line.Trim().Split(‘,’); if (csvData.Columns.Count == 0) { for (int i = 0; i < items.Length; i++) { csvData.Columns.Add(new DataColumn(items[i], typeof(string))); } } else { csvData.Rows.Add(items); } } }

3.نوشتن داده‌ها در جدول SQL Server

پس از اینکه با موفقیت محتوای مجموعه داده‌های منبع خود را در یک DataTable استخراج کردید، می‌توانید کلاس SqlBulkCopy را فراخوانی کرده و از آن برای کپی کردن انبوه DataTable در یک جدول SQL Server استفاده کنید. همانطور که در خط 86 در کد زیر نشان داده شده است، متد کلاس SqlBulkCopy که مسئول انتقال واقعی داده‌ها به SQL Server است، WriteToServer نام دارد و یک DataTable را به عنوان آرگومان دریافت می‌کند.

using (SqlConnection dbConnection = new SqlConnection(connname)) { dbConnection.Open(); using (SqlBulkCopy s = new SqlBulkCopy(dbConnection)) { s.DestinationTableName = “dbo.[PLGoalScorers]”; foreach (var column in csvData.Columns) { s.ColumnMappings.Add(column.ToString(), column.ToString()); } s.WriteToServer(csvData); }

متوجه شده‌اید که نگاشت ستون منبع به مقصد در کد بالا در خط 84 پیاده‌سازی شده است، که در آن هر دو ستون منبع (ورودی) و مقصد (خروجی) به نام‌های ستون مشابه نگاشت شده‌اند. نگاشت ستون‌های ورودی و خروجی به نام ستون یکسان، تنها یکی از چندین گزینه نگاشت ستون موجود در کلاس SqlBulkCopy است. در بخش بعدی، بقیه گزینه‌های نگاشت ستون موجود در کلاس SqlBulkCopy را بررسی خواهیم کرد.

گزینه های نگاشت ستون SqlBulkCopy Class:

گزینه شماره ۱: نگاشت ستون بر اساس نام

نگاشت یک ستون بر اساس نام، فرض می‌کند که نام ستون داده شده (مثلاً “Player”) هم در منبع و هم در مقصد وجود دارد. یکی از مزایای این نوع نگاشت این است که لزوماً نیازی به حفظ ترتیب ستون‌ها در هر دو مجموعه داده منبع و مقصد ندارید. شکل زیر نگاشت کلاس SqlBulkCopy از یک ستون بر اساس نام را نشان می‌دهد.

Using the column by name mapping option

شکل زیر نشان می‌دهد که چگونه نگاشت مشخص‌شده در شکل بالا منجر به اختصاص نام ستون یکسان «Player» به هر دو ستون مقصد و منبع می‌شود.

Runtime demonstration of column by name mapping between DestinationColumn and SourceColumn properties

یکی از بزرگترین معایب نگاشت ستون بر اساس نام این است که وقتی هیچ تطابق دقیقی روی نام ستون مشخص شده وجود نداشته باشد، با خطای “The ColumnMapping داده شده با هیچ ستونی در منبع یا مقصد مطابقت ندارد” مواجه خواهید شد. سایر مزایا و معایب استفاده از نگاشت ستون بر اساس نام در کلاس SqlBulkCopy به شرح زیر است:

مزایا:

نگاشت ستون بر اساس نام تحت تأثیر موقعیت ستون‌ها قرار نمی‌گیرد.

معایب:

نگاشت ستون بر اساس نام نیاز به تطابق دقیق روی نام ستون دارد – هرگونه فاصله، کاراکتر پنهان و غیره باعث عدم تطابق نگاشت می‌شود که منجر به خطای “The ColumnMapping داده شده با هیچ ستونی در منبع یا مقصد مطابقت ندارد” می‌شود.

گزینه شماره 2: نگاشت ستون بر اساس موقعیت یا شاخص ستون

یکی دیگر از گزینه‌های نگاشت ستون در کلاس SqlBulkCopy شامل نگاشت ستون‌های ورودی و خروجی بر اساس موقعیت یا شاخص ستون است. این رویکرد فرض می‌کند که ستون‌های منبع و مقصد به درستی قرار گرفته‌اند. در واقع اگر روش ColumnMappings.Add را پیاده‌سازی نکنید، این رفتار نگاشت پیش‌فرض است، همانطور که در کد زیر نشان داده شده است.

using (SqlConnection dbConnection = new SqlConnection(connname)) { dbConnection.Open(); using (SqlBulkCopy s = new SqlBulkCopy(dbConnection)) { s.DestinationTableName = “dbo.[PLGoalScorers]”; foreach (var column in csvData.Columns)

نگاشت موقعیت ستون به ستون در کلاس SqlBulkCopy همچنین این گزینه را برای توسعه‌دهندگان دارد که با مشخص کردن موقعیت ستون‌ها، نگاشت‌های ستون را به طور صریح پیاده‌سازی کنند. ساده‌ترین پیاده‌سازی این گزینه نگاشت در شکل زیر نشان داده شده است که در آن یک متغیر محلی اعلان شده و در انتهای هر ترکیب نگاشت ستون، یک واحد افزایش می‌یابد.

using ColumnMappings.Add method to implement column by column position mapping option.

برخلاف گزینه نگاشت ستون بر اساس نام، نگاشت موقعیت ستون بر اساس ستون تحت تأثیر عدم تطابق نام ستون‌های ورودی و خروجی قرار نمی‌گیرد، در عوض، داده‌ها با موفقیت منتقل می‌شوند، مشروط بر اینکه انواع داده‌های ستون منبع و هدف سازگار باشند. بنابراین، مسئولیت اطمینان از سازگاری موقعیت ستون و انواع داده‌های ستون‌های منبع و مقصد بر عهده توسعه‌دهنده است. در غیر این صورت، خطایی مشابه شکل زیر بازگردانده خواهد شد.

Column by position mapping error relating to a mismatch between source and target data types

نگاشت موقعیت ستون به ستون مزایا و معایب خاص خود را دارد، همانطور که در زیر نشان داده شده است:

مزایا:

نگاشت موقعیت ستون به ستون تحت تأثیر تغییرات نام ستون‌ها قرار نمی‌گیرد. نگاشت موقعیت ستون به ستون می‌تواند به طور خودکار ستون‌ها را بدون مشخص کردن متد ColumnMappings.Add نگاشت کند.

معایب:

ستون‌های منبع و مقصد باید به درستی موقعیت‌یابی شوند.
نیاز به سازگاری نوع داده بین ستون‌های منبع و مقصد دارد.

گزینه شماره ۳: ترکیبی از گزینه‌های Column by Name و Column by Position

یکی دیگر از گزینه‌های نگاشت ستون که در کلاس SqlBulkCopy در دسترس شماست، استفاده از بهترین‌های هر دو حالت است – همانطور که در شکل زیر نشان داده شده است که در آن ستون‌های منبع ورودی بر اساس موقعیتشان در برابر نام ستون‌ها در مقصد نگاشت می‌شوند. این بدان معناست که نام ستون‌ها در مقصد را می‌توان بدون نیاز به مرتب‌سازی مجدد موقعیت ستون‌ها در منبع، تغییر نام داد یا جابجا کرد. این گزینه نگاشت را هنگام کار با منبعی که header ندارد، بسیار مفید خواهید یافت.

Hybrid of Column by Name & Column by Position mapping options available.

در پایان، وقتی با منبع داده‌ای سروکار دارید که ساختار آن دائماً در حال تغییر است، استفاده از کلاس SqlBulkCopy را در داخل یک Script Task به جای Data Flow Task در نظر بگیرید. این کار تضمین می‌کند که می‌توانید نگاشت‌های ستونی را در زمان اجرا بین منبع و مقصد به صورت پویا ایجاد کنید، بدون اینکه مجبور باشید متادیتای ستون ورودی و خروجی را به صورت دستی به‌روزرسانی کنید، همانطور که اغلب در data flow task اتفاق می‌افتد. یکی دیگر از مزایای استفاده از کلاس SqlBulkCopy این است که سرعت و عملکرد مهاجرت داده بهتری را نسبت به data flow task ارائه می‌دهد. با این حال، کلاس SqlBulkCopy محدودیت‌های خاص خود را دارد:

  1. کلاس SqlBulkCopy مستلزم آن است که توسعه‌دهندگان ETL شما مهارت‌های برنامه‌نویسی .Net را کسب کنند.
  2. کلاس SqlBulkCopy فقط می‌تواند خروجی خود را در یک جدول SQL Server بنویسد.
  3. اغلب هنگام کار با کلاس SqlBulkCopy با خطای “The given ColumnMapping does not match up with any column in the source or destination.” مواجه می‌شوید که در واقع مبهم است.
برچسب ها: بدون برچسب

نظر بگذارید

آدرس ایمیل شما منتشر نخواهد شد. قسمتهای مورد نیاز علامت گذاری شده اند *