PDA

View Full Version : Bulk-Copy به صورت یکی در میان



Farhad.B.S
دوشنبه 07 خرداد 1386, 15:29 عصر
با سلام،

فرض کنید موجودیت های زیر در برنامه ای استفاده میشوند:
1.فروشگاه
2.محصول
3.قسمت های تشکیل دهنده محصول

هر فروشگاه شامل تعدادی محصول است و هر محصول هم دارای تعدادی Part (قسمت های تشکیل دهنده) است.

حال نیاز بر این است تا محصولات یک فروشگاه به طور کامل در فروشگاه دیگری کپی شوند بنابراین نیاز خواهد بود تا محصولات فروشگاه و همچنین Part های هر محصول برای فروشگاه جدید کپی شوند. (کپی به صورت فیزیکال مورد نیاز است چرا که ممکن است صاحب فروشگاه مقصد بخواهد پس از کپی مشخصات یک محصول یا Part های آن را تغییر دهد)

از آنجایی که ساختار جداول به گونه ایست که PK هر یک از جداول، فیلدی IDENTITY درنظر گرفته شده و امکان disable کردن آن وجود ندارد چگونه میتوان چنین عملیاتی را بدون استفاده از Cursor و حلقه ها انجام داد ؟
در واقع نیاز به روشی است تا INSERT در دو جدول مختلف را به طور همزمان (یکی در میان) انجام دهد.

ساختار جداول :
http://i12.tinypic.com/4v5qfbb.gif

AminSobati
سه شنبه 08 خرداد 1386, 01:57 صبح
اگر لازم داشتین، میتونین بوسیله SET IDENTITY INSERT قابلیت Identityرو غیر فعال کنین تا هر عددی که لازم داشتین خودتون وارد کنین:


create table products(
pid int identity(1,1),
pname varchar(10))

insert products(pname) select 'a'
insert products(pname) select 'b'
insert products(pname) select 'c'
insert products(pname) select 'd'

set identity_insert products on
insert products(pid,pname) select pid+10,pname from products
set identity_insert products off

select * from products

Farhad.B.S
سه شنبه 08 خرداد 1386, 12:59 عصر
با تشکر،
دو مسئله وجود دارد.
یکی اینکه آیا SET IDENTITY INSERT فقط Identity Insert را بر روی جدول مورد نظر در سشن فعلی غیر فعال میکند یا در تمامی سشن ها ؟
و اگر جواب در تمامی سشن هاست، اگر insert دیگری در زمان کپی اطلاعات توسط کاربر دیگری در یکی از جداول انجام شود آیا باعث ایجاد مشکل نمیشود ؟

و دوم اینکه کپی قرار است به صورت دسته جمعی انجام شود یعنی چیزی مثل :


INSERT INTO Products(StoreId, ProductInfo)
SELECT @NewStoreId, ProductInfo FROM Products WHERE StoreId=@SourceStoreId

و در همین زمان لازم است تا Part های هر محصول نیز کپی شوند. درواقع پس از insert هر رکورد در جدول محصولات باید part های آن نیز کپی شوند ، بنابراین آیا نیاز به جدولی برای مپ کردن ProductId های جدید و ProductId های مبدا خواهد بود ؟

Farhad.B.S
سه شنبه 08 خرداد 1386, 18:53 عصر
فرض کنید که بخواهیم با استفاده از مپ کردن Id محصولات قدیم و جدید این کار را انجام دهیم.
من از این استفاده کردم :



CREATE TABLE #MapProducts (SourceProductId bigint, DestProductId bigint)

INSERT INTO #MapProducts (SourceProductId, DestProductId)
SELECT ProductId, CASE WHEN
((SELECT COUNT(*) FROM #MapProducts) > 0) THEN (SELECT MAX(DestProductId) + 1 FROM #MapProducts)
ELSE (SELECT MAX(ProductId) + 1 FROM Products) END
FROM Products WHERE StoreId=@SourceStoreId



اما مشکل اینجاست که در دستور :


CASE WHEN ((SELECT COUNT(*) FROM #MapProducts) > 0) THEN (SELECT MAX(DestProductId) + 1 FROM #MapProducts)


SqlServer قبل از عملیات insert حقیقی مقدار COUNT را محاسبه میکند و بنابراین این خط هیچگاه اجرا نخواهد شد.
آیا نمیتوان دستور Insert را وادار کرد تا پس از درج "هر رکورد" مقدار COUNT زرا محاسبه کند ؟

AminSobati
سه شنبه 08 خرداد 1386, 21:13 عصر
Identity Insert فقط برای همون Connection (یا به قول شما Session) فعال میشه.
برای کپی کردن Part ها هم فکر میکنم بشه از روشی که مثال زدم استفاده کنین ولی بعدش یک Update هم نیاز داره تا Partهای جدید رو به محصولات جدید مرتبط کنه

Farhad.B.S
چهارشنبه 09 خرداد 1386, 12:13 عصر
با تشکر،
برای بخش دوم چطور ؟

SqlServer قبل از عملیات insert حقیقی مقدار COUNT را محاسبه میکند و بنابراین این خط هیچگاه اجرا نخواهد شد.
آیا نمیتوان دستور Insert را وادار کرد تا پس از درج "هر رکورد" مقدار COUNT زرا محاسبه کند ؟

AminSobati
چهارشنبه 09 خرداد 1386, 12:38 عصر
با تشکر،
برای بخش دوم چطور ؟

SqlServer قبل از عملیات insert حقیقی مقدار COUNT را محاسبه میکند و بنابراین این خط هیچگاه اجرا نخواهد شد.
آیا نمیتوان دستور Insert را وادار کرد تا پس از درج "هر رکورد" مقدار COUNT زرا محاسبه کند ؟


دقیقا متوجه نشدم اینجا چه عملیاتی داره انجام میشه

Farhad.B.S
چهارشنبه 09 خرداد 1386, 13:10 عصر
هدف اینه که قبل از انجام عملیات کپی محصولات، یک جدول موقتی ایجاد شه و داخل آن Id هر یک از محصولات که قراره کپی بشند و Id جدیدی که باید برای محصول کپی شده درج شود ،قرار بگیرد.
مشکل در ساخت این Id های جدید هستش، در واقع این Id های جدید باید از روی جدول Products ساخته شوند.
یعنی باید ابتدا از آخرین ProductId + 1 و بعد از درج اولین رکورد در جدول موقت از آخرین DestId + 1 استفاده شود.

Farhad.B.S
پنج شنبه 10 خرداد 1386, 17:15 عصر
فکر میکنم بااین حل میشه :



CREATE TABLE #MapProducts (SourceProductId bigint, DestProductId bigint)

SELECT @maxprodid = MAX(ProductId) FROM Products

INSERT INTO #MapProducts (SourceProductId, DestProductId)
SELECT ProductId, @maxprodid + ROW_NUMBER() OVER (ORDER BY ProductId) FROM Products
FROM Products WHERE StoreId=@SourceStoreId