WHERE IN ব্যবহার করে মুছুন অপারেশনের সময় অপ্রত্যাশিত স্ক্যান


40

আমি নীচের মত একটি কোয়েরি পেয়েছি:

DELETE FROM tblFEStatsBrowsers WHERE BrowserID NOT IN (
    SELECT DISTINCT BrowserID FROM tblFEStatsPaperHits WITH (NOLOCK) WHERE BrowserID IS NOT NULL
)

tblFEStats ব্রাউজারগুলি 553 সারি পেয়েছে।
tblFEStatsPaperHits 47,974.301 সারি পেয়েছে।

tblFEStatsBrowsers:

CREATE TABLE [dbo].[tblFEStatsBrowsers](
    [BrowserID] [smallint] IDENTITY(1,1) NOT NULL,
    [Browser] [varchar](50) NOT NULL,
    [Name] [varchar](40) NOT NULL,
    [Version] [varchar](10) NOT NULL,
    CONSTRAINT [PK_tblFEStatsBrowsers] PRIMARY KEY CLUSTERED ([BrowserID] ASC)
)

tblFEStatsPaperHits:

CREATE TABLE [dbo].[tblFEStatsPaperHits](
    [PaperID] [int] NOT NULL,
    [Created] [smalldatetime] NOT NULL,
    [IP] [binary](4) NULL,
    [PlatformID] [tinyint] NULL,
    [BrowserID] [smallint] NULL,
    [ReferrerID] [int] NULL,
    [UserLanguage] [char](2) NULL
)

TblFEStatsPaperHits- তে একটি ক্লাস্টার ইনডেক্স রয়েছে যা ব্রাউজারআইডি অন্তর্ভুক্ত করে না। অভ্যন্তরীণ কোয়েরি সম্পাদন করার জন্য tblFEStatsPaperHits- এর সম্পূর্ণ টেবিল স্ক্যানের প্রয়োজন হবে - যা সম্পূর্ণ ঠিক।

বর্তমানে, টিবিএলএফইএসটিস ব্রাউজারগুলিতে প্রতিটি সারির জন্য একটি সম্পূর্ণ স্ক্যান কার্যকর করা হয়, যার অর্থ আমি 553 টিবিএলএফএটিএসটিস পেপারহিটগুলির পুরো টেবিল স্ক্যান পেয়েছি।

কেবলমাত্র যেখানে উপস্থিত রয়েছে সেখানে পুনর্লিখন পরিকল্পনাটি পরিবর্তন করে না:

DELETE FROM tblFEStatsBrowsers WHERE NOT EXISTS (
    SELECT * FROM tblFEStatsPaperHits WITH (NOLOCK) WHERE BrowserID = tblFEStatsBrowsers.BrowserID
)

যাইহোক, অ্যাডাম মাচানিকের পরামর্শ অনুসারে, একটি হ্যাশ জয়িন বিকল্প যুক্ত করার ফলে সর্বোত্তম বাস্তবায়ন পরিকল্পনার ফলস্বরূপ (কেবলমাত্র একক স্ক্যান tblFEStatsPaperHits):

DELETE FROM tblFEStatsBrowsers WHERE NOT EXISTS (
    SELECT * FROM tblFEStatsPaperHits WITH (NOLOCK) WHERE BrowserID = tblFEStatsBrowsers.BrowserID
) OPTION (HASH JOIN)

এখন এটি কীভাবে এটি ঠিক করতে হবে তা নিয়ে তেমন প্রশ্নই আসে না - আমি হয় অপশন (হ্যাশ যোগ) ব্যবহার করতে পারি বা ম্যানুয়ালি একটি টেম্প টেবিল তৈরি করতে পারি। আমি আরও ভাবছি কেন ক্যোয়ারী অপ্টিমাইজার বর্তমানে এটির পরিকল্পনাটি কেন ব্যবহার করবে।

ব্রাউজারআইডি কলামে কিউওর কোনও পরিসংখ্যান নেই বলে আমি অনুমান করছি যে এটি সবচেয়ে খারাপ - ৫০ মিলিয়ন স্বতন্ত্র মান হিসাবে ধরে নিচ্ছে, এইভাবে মেমরি / টেম্পডিবি ওয়ার্কটেবলের জন্য বেশ বড় প্রয়োজন। এর মতো, সবচেয়ে নিরাপদতম উপায় হল টিবিএলএফইএসটিএসব্রাউজারগুলিতে প্রতিটি সারির জন্য স্ক্যান করা। দুটি টেবিলের মধ্যে ব্রাউজারআইডি কলামগুলির মধ্যে কোনও বিদেশী কী সম্পর্ক নেই, তাই কিউও টিবিএলএফইএসটিস ব্রাউজারগুলি থেকে কোনও তথ্য বাদ দিতে পারে না।

এটি, এটি যতটা সহজ শোনায়, কারণ?

আপডেট 1 টি
কয়েকটি স্ট্যাটাস দেওয়ার জন্য: বিকল্প (হ্যাশ যোগ):
208.711 লজিকাল রিডস (12 স্ক্যান)

বিকল্প (লুপ
জয়েন , হ্যাশ গ্রুপ): 11.008.698 লজিকাল রিড (Brow ব্রাউজারআইডি স্ক্যান (339))

কোনও বিকল্প নেই:
11.008.775 লজিকাল রিডস (Brow ব্রাউজারআইডি স্ক্যান (339))

আপডেট 2 দুর্দান্ত
উত্তর, আপনারা সবাই - ধন্যবাদ! মাত্র একটি বাছাই করা শক্ত। যদিও মার্টিন প্রথম ছিল এবং রেমাস একটি দুর্দান্ত সমাধান সরবরাহ করেছে, বিশদটি সম্পর্কে মানসিকতার জন্য আমাকে কিউইকে দিতে হবে :)


5
আপনি কি এক সার্ভার থেকে অন্য সার্ভারের অনুলিপি হিসাবে পরিসংখ্যানগুলি স্ক্রিপ্ট আউট করতে পারেন যাতে আমরা প্রতিলিপি করতে পারি?
মার্ক স্টোরী-স্মিথ

2
@ মার্কস্টোরি-স্মিথ শিওর - পেস্টবিন. com/9 এইচএইচআরপিএফকে আপনি খালি ডাটাবেসে স্ক্রিপ্টটি চালাচ্ছেন বলে ধরে নেওয়া, এটি কার্যকর করার পরিকল্পনাটি দেখানো সহ সমস্যাযুক্ত প্রশ্নের পুনরুত্পাদন করতে সক্ষম করে। উভয় প্রশ্নের স্ক্রিপ্টের শেষে অন্তর্ভুক্ত করা হয়েছে।
মার্ক এস রাসমুসেন

উত্তর:


61

"আমি আরও ভাবছি কেন ক্যোয়ারী অপ্টিমাইজার বর্তমানে এটির পরিকল্পনাটি কেন ব্যবহার করবে" "

এটিকে অন্য উপায়ে বলতে গেলে, বিকল্পগুলি (যার মধ্যে অনেকগুলি রয়েছে ) এর সাথে তুলনা করে নীচের পরিকল্পনাটি কেন অপ্টিমাইজারের কাছে সস্তা বলে মনে হচ্ছে প্রশ্ন the

মূল পরিকল্পনা

যোগদানের অভ্যন্তরীণ দিকটি প্রতিটি সংযুক্ত মানের জন্য মূলত নিম্নলিখিত ফর্মটির ক্যোয়ারী চালাচ্ছে BrowserID:

DECLARE @BrowserID smallint;

SELECT 
    tfsph.BrowserID 
FROM dbo.tblFEStatsPaperHits AS tfsph 
WHERE 
    tfsph.BrowserID = @BrowserID 
OPTION (MAXDOP 1);

কাগজ হিট স্ক্যান

মনে রাখবেন যে, সারি আনুমানিক সংখ্যা 185,220 (না 289,013 ) সমতা তুলনা পরোক্ষভাবে বাদ যেহেতু NULL(যদি না ANSI_NULLSহয় OFF)। উপরোক্ত পরিকল্পনার আনুমানিক ব্যয় 206.8 ইউনিট।

এখন একটি TOP (1)ধারা যুক্ত করুন:

DECLARE @BrowserID smallint;

SELECT TOP (1)
    tfsph.BrowserID 
FROM dbo.tblFEStatsPaperHits AS tfsph 
WHERE 
    tfsph.BrowserID = @BrowserID 
OPTION (MAXDOP 1);

শীর্ষ সহ (1)

আনুমানিক ব্যয় এখন 0.00452 ইউনিট। শীর্ষ শারীরিক অপারেটরের সংযোজন শীর্ষ অপারেটরে 1 টি সারি সারি লক্ষ্য নির্ধারণ করে । প্রশ্নটি তখন ক্লাস্টারড ইনডেক্স স্ক্যানের জন্য কীভাবে 'সারি লক্ষ্য' অর্জন করবে; তা হল, এক সারিটির পূর্বেকারের সাথে মিল হওয়ার আগে স্ক্যানটির কতটি সারি প্রক্রিয়াকরণের আশা করা উচিত BrowserID?

প্রাপ্ত পরিসংখ্যান সম্পর্কিত তথ্য 166 স্বতন্ত্র BrowserIDমান দেখায় (1 / [সমস্ত ঘনত্ব] = 1 / 0.006024096 = 166)। ব্যয় বহন করে যে পৃথক মানগুলি দৈহিক সারিগুলির উপরে সমানভাবে বিতরণ করা হয়, সুতরাং ক্লাস্টারড ইনডেক্স স্ক্যানের সারি লক্ষ্যটি 166.302 এ সেট করা হয়েছে (নমুনাযুক্ত পরিসংখ্যান সংগ্রহের পর থেকে টেবিল কার্ডিনালিটির পরিবর্তনের জন্য অ্যাকাউন্টিং)।

প্রত্যাশিত 166 টি সারি স্ক্যান করার আনুমানিক ব্যয় খুব বড় নয় (এমনকি প্রতিটি পরিবর্তনের জন্য একবার 339 বার কার্যকর করা হয়েছিল BrowserID) - ক্লাস্টারড ইনডেক্স স্ক্যানটি সারি লক্ষ্যটির স্কেলিং প্রভাবটি দেখায়, আনুমানিক 1.3219 ইউনিট ব্যয় দেখায় । ইনপুট / আউটপুট এবং CPU- র জন্য unscaled অপারেটর খরচ হিসাবে দেখানো হয় 153,931 , এবং 52,8698 যথাক্রমে:

সারি লক্ষ্য পরিমাণের আনুমানিক ব্যয়

অনুশীলনে, এটি খুব সম্ভবত সম্ভাবনা নেই যে সূচি থেকে স্ক্যান করা প্রথম 166 টি সারি (যে কোনও ক্রমে তারা ফিরে আসবে তা) প্রতিটি সম্ভাব্য BrowserIDমানকে ধারণ করে । তবুও, DELETEপরিকল্পনাটি মোট 1.40921 ইউনিটে অর্জিত হয়েছে এবং সেই কারণে অপ্টিমাইজার দ্বারা নির্বাচিত হয়েছে। বার্ট ডানকান সাম্প্রতিক পোস্টে রো গোলস গোল রোগ শিরোনামে এই ধরণের আরেকটি উদাহরণ দেখায় ।

এটি লক্ষণীয়ও বটে যে এক্সিকিউশন প্ল্যানে শীর্ষস্থানীয় অপারেটর অ্যান্টি সেমি জয়েন্টের সাথে সম্পর্কিত নয় (বিশেষত 'শর্ট-সার্কিট' মার্টিন উল্লেখ করেছেন)। আমরা প্রথমে GbAggToConstScanOrTop নামক একটি অনুসন্ধানের নিয়ম অক্ষম করে শীর্ষস্থানটি কোথা থেকে আসে তা দেখতে শুরু করতে পারি :

DBCC RULEOFF ('GbAggToConstScanOrTop');
GO
DELETE FROM tblFEStatsBrowsers 
WHERE BrowserID NOT IN 
(
    SELECT DISTINCT BrowserID 
    FROM tblFEStatsPaperHits WITH (NOLOCK) 
    WHERE BrowserID IS NOT NULL
) OPTION (MAXDOP 1, LOOP JOIN, RECOMPILE);
GO
DBCC RULEON ('GbAggToConstScanOrTop');

GbAggToConstScanOrTop অক্ষম

এই পরিকল্পনার আনুমানিক ব্যয় হয়েছে 364.912 , এবং দেখায় যে শীর্ষগুলি একটি গ্রুপ দ্বারা সমষ্টি স্থান নিয়েছে (সম্পর্কযুক্ত কলাম দ্বারা গোষ্ঠীকরণBrowserID )। সমষ্টিটি ক্যোয়ারী পাঠ্যে অনর্থক কারণে নয়DISTINCT : এটি একটি অপ্টিমাইজেশন যা দুটি অনুসন্ধানের নিয়ম, LASJNtoLASJNonDist এবং LASJOnLclDist দ্বারা প্রবর্তন করা যেতে পারে । এই দু'টিকে অক্ষম করাও এই পরিকল্পনাটি তৈরি করে:

DBCC RULEOFF ('LASJNtoLASJNonDist');
DBCC RULEOFF ('LASJOnLclDist');
DBCC RULEOFF ('GbAggToConstScanOrTop');
GO
DELETE FROM tblFEStatsBrowsers 
WHERE BrowserID NOT IN 
(
    SELECT DISTINCT BrowserID 
    FROM tblFEStatsPaperHits WITH (NOLOCK) 
    WHERE BrowserID IS NOT NULL
) OPTION (MAXDOP 1, LOOP JOIN, RECOMPILE);
GO
DBCC RULEON ('LASJNtoLASJNonDist');
DBCC RULEON ('LASJOnLclDist');
DBCC RULEON ('GbAggToConstScanOrTop');

স্পুল প্ল্যান

এই পরিকল্পনার আনুমানিক ব্যয় 40729.3 ইউনিট।

গ্রুপ বাই টু টপ রূপান্তর ছাড়াই, অপ্টিমাইজার 'প্রাকৃতিকভাবে' BrowserIDঅ্যান্টি-সেমি যোগ দেওয়ার আগে একত্রিত হয়ে একটি হ্যাশ যোগদানের পরিকল্পনা বেছে নেয় :

DBCC RULEOFF ('GbAggToConstScanOrTop');
GO
DELETE FROM tblFEStatsBrowsers 
WHERE BrowserID NOT IN 
(
    SELECT DISTINCT BrowserID 
    FROM tblFEStatsPaperHits WITH (NOLOCK) 
    WHERE BrowserID IS NOT NULL
) OPTION (MAXDOP 1, RECOMPILE);
GO
DBCC RULEON ('GbAggToConstScanOrTop');

কোনও শীর্ষ ডিওপি 1 পরিকল্পনা নেই

এবং MAXDOP 1 সীমাবদ্ধতা ছাড়াই একটি সমান্তরাল পরিকল্পনা:

শীর্ষ কোনও সমান্তরাল পরিকল্পনা নেই

মূল ক্যোয়ারীটি 'ঠিক' করার আরেকটি উপায় BrowserIDহ'ল কার্যকরকরণ পরিকল্পনার প্রতিবেদনে যে অনুপস্থিত সূচী তৈরি করা হবে । অভ্যন্তরীণ দিকটি সূচী করা হলে নেস্টেড লুপগুলি সবচেয়ে ভাল কাজ করে। আধা যোগদানের জন্য কার্ডিনালিটির আনুমানিক মূল্যায়ন চূড়ান্ততম সময়ে। যথাযথ ইনডেক্সিং না থাকা (বড় টেবিলটিতে একটি অনন্য কীও নেই!) কিছুতেই সহায়তা করবে না।

আমি এটি সম্পর্কে সারি লক্ষ্যগুলিতে, পার্ট 4 এ আরও লিখেছি : দ্য অ্যান্টি জয়েন অ্যান্টি প্যাটার্ন


3
আমি তোমাকে প্রণাম জানাই, আপনি আমাকে এমন বেশ কয়েকটি নতুন ধারণার সাথে পরিচয় করিয়ে দিয়েছিলেন যা আমি এর আগে কখনও পাই নি। যখন আপনি অনুভব করেন যে আপনি কিছু জানেন, তখন কোনও লোক আপনাকে নীচে নামিয়ে দেবে - ভাল উপায়ে :) সূচি যোগ করা অবশ্যই সহায়তা করবে। তবে, এই এককালীন অপারেশন ছাড়াও ব্রাউজারআইডি কলাম দ্বারা ক্ষেত্রটি কখনও অ্যাক্সেস / একত্রিত হয় না এবং সারণীটি বেশ বড় হওয়ায় আমি এই বাইটগুলি সংরক্ষণ করব (এটি অনেকগুলি অভিন্ন ডাটাবেসের মধ্যে কেবল একটি)। টেবিলে কোনও অনন্য কী নেই কারণ এর কোনও প্রাকৃতিক স্বাতন্ত্র্য নেই। সমস্ত নির্বাচন পেপারআইডি এবং IDচ্ছিকভাবে একটি পিরিয়ড দ্বারা হয়।
মার্ক এস রাসমুসেন

22

আমি যখন আপনার স্ক্রিপ্টটি কেবল একটি পরিসংখ্যান এবং প্রশ্নের ক্যোয়ারী তৈরি করতে চালিত করি তখন আমি নীচের পরিকল্পনাটি পাই।

পরিকল্পনা

পরিকল্পনায় প্রদর্শিত টেবিল কার্ডিনালিটিগুলি হ'ল

  • tblFEStatsPaperHits: 48063400
  • tblFEStatsBrowsers : 339

সুতরাং এটি অনুমান করে যে এটির জন্য tblFEStatsPaperHits339 বার স্ক্যান করতে হবে । প্রতিটি স্ক্যানে পারস্পরিক সম্পর্কযুক্ত প্রাকটিকেট থাকে tblFEStatsBrowsers.BrowserID=tblFEStatsPaperHits.BrowserID AND tblFEStatsPaperHits.BrowserID IS NOT NULLযা স্ক্যান অপারেটরের নীচে ঠেলা যায়।

পরিকল্পনার অর্থ এই নয় যে 339 টি পূর্ণ স্ক্যান হবে। এটি যেমন কোনও এন্টি সেমি জয়েন্ট অপারেটরের অধীনে রয়েছে তত তাড়াতাড়ি প্রতিটি স্ক্যানের প্রথম ম্যাচের সারিটি পাওয়া গেলে এটি এর বাকী অংশটি শর্ট সার্কিট করতে পারে। এই নোডের জন্য আনুমানিক সাবট্রি ব্যয় 1.32603এবং পুরো পরিকল্পনাটি কস্ট করা হয় 1.41337

হ্যাশ জয়েনের জন্য এটি নীচের পরিকল্পনাটি দেয়

হ্যাশ যোগ দিন

সামগ্রিক পরিকল্পনাটি 418.415(নেস্টেড লুপস পরিকল্পনার তুলনায় প্রায় 300 গুণ বেশি ব্যয়বহুল) একা পুরোপুরি ক্লাস্টারড ইনডেক্স স্ক্যানের সাথে tblFEStatsPaperHitsকাস্ট করা 206.8হয়। 1.32603পূর্বে প্রদত্ত 339 আংশিক স্ক্যানগুলির জন্য অনুমানের সাথে এটি তুলনা করুন (গড় আংশিক স্ক্যান আনুমানিক ব্যয় = 0.003911592)।

সুতরাং এটি নির্দেশ করবে যে এটি প্রতিটি আংশিক স্ক্যানের জন্য সম্পূর্ণ স্ক্যানের চেয়ে 53,000 গুণ কম ব্যয়বহুল হিসাবে ব্যয় করছে। যদি ব্যয়গুলি সারি গণনার সাথে রৈখিকভাবে স্কেল করতে হয় তবে এর অর্থ হ'ল এটি ধরে নেওয়া হচ্ছে যে এটি মিলিয়ে যাওয়ার সারিটি খুঁজে পাওয়ার আগে এবং শর্ট সার্কিটের আগে গড়ে গড়ে প্রতিটি পুনরুক্তিতে 900 টি সারি প্রক্রিয়াকরণ করতে হবে।

আমি মনে করি না তবে ব্যয়গুলি সেই রৈখিক উপায়ে স্কেল করে। আমি মনে করি তারা স্থির স্টার্টআপ ব্যয়ের কিছু উপাদানও অন্তর্ভুক্ত করে। TOPনিম্নলিখিত প্রশ্নের মধ্যে বিভিন্ন মান চেষ্টা করে

SELECT TOP 147 BrowserID 
FROM [dbo].[tblFEStatsPaperHits] 

147নিকটস্থ আনুমানিক সাবট্রি খরচ দেয় 0.0039115920.0039113। যেভাবেই এটি স্পষ্ট যে এটি প্রতিটি ব্যয়কে লক্ষ লক্ষের পরিবর্তে কয়েকশো সারির ক্রমে কেবলমাত্র টেবিলের একটি ক্ষুদ্র অনুপাত প্রক্রিয়া করতে হবে এই ধারণাটি অনুসারে ব্যয় নির্ধারণ করছে is

আমি নিশ্চিত নই যে এটি গণিতটি ঠিক কী গণিত করে এবং এই পরিকল্পনার বাকী অংশে এটি সত্যই সারি গণনা অনুমানের সাথে যোগ করে না (নেস্টেড লুপগুলি থেকে 236 আনুমানিক সারিগুলি আসবে এমনটি বোঝায় যে সেখানে 236 ছিল এমন ক্ষেত্রে যেখানে কোনও মিলের সারি পাওয়া যায় নি এবং একটি পূর্ণ স্ক্যানের প্রয়োজন ছিল)। আমি ধরে নিলাম এটি কেবলমাত্র এমন একটি ক্ষেত্রে যেখানে মডেলিং অনুমানগুলি কিছুটা নিচে পড়ে এবং নেস্টেড লুপগুলি পরিকল্পনার মধ্যে উল্লেখযোগ্যভাবে ফেলে দেয়।


20

আমার বইতে এমনকি 50M সারিগুলির একটি স্ক্যান গ্রহণযোগ্য নয় ... আমার স্বাভাবিক কৌশলটি স্বতন্ত্র মানগুলি বাস্তবায়িত করা এবং ইঞ্জিনটিকে আপ টু ডেট রাখার মাধ্যমে এটি অর্পণ করা:

create view [dbo].[vwFEStatsPaperHitsBrowserID]
with schemabinding
as
select BrowserID, COUNT_BIG(*) as big_count
from [dbo].[tblFEStatsPaperHits]
group by [BrowserID];
go

create unique clustered index [cdxVwFEStatsPaperHitsBrowserID] 
  on [vwFEStatsPaperHitsBrowserID]([BrowserID]);
go

50M সারি স্ক্যান করার প্রয়োজনীয়তা বাদ দিয়ে এটি আপনাকে ব্রাউজারআইডি প্রতি এক সারিতে একটি উপাদানযুক্ত সূচক দেয়। ইঞ্জিনটি এটি আপনার জন্য রক্ষণাবেক্ষণ করবে এবং আপনার পোস্ট করা বিবৃতিতে কিউও এটি 'যেমন রয়েছে' ব্যবহার করবে (ডাব্লু / ও কোনও ইঙ্গিত বা কোয়েরি পুনর্লিখন)।

অবক্ষয়টি অবশ্যই বিতর্ক। যে কোনও সন্নিবেশ বা মুছে ফেলা অপারেশন tblFEStatsPaperHits(এবং আমি অনুমান করি যে ভারী সন্নিবেশ সহ একটি লগিং টেবিল) কোনও প্রদত্ত ব্রাউজারআইডিতে অ্যাক্সেস সিরিয়ালাইজ করতে হবে। আপনি যদি এটিতে কিনতে ইচ্ছুক থাকেন তবে এমন উপায় রয়েছে যা এই কার্যক্ষম (বিলম্বিত আপডেট, ২ টি পর্যায়ক্রমে লগিং ইত্যাদি) তৈরি করে।


আমি আপনাকে শুনছি, যে কোনও স্ক্যান বড় তা অবশ্যই অগ্রহণযোগ্য। এক্ষেত্রে এটি কিছু এককালীন ডেটা ক্লিনআপ অপারেশনের জন্য তাই আমি অতিরিক্ত সূচী তৈরি না করা বেছে নিচ্ছি (এবং এটি সিস্টেমকে বাধাগ্রস্ত করায় অস্থায়ীভাবে করতে পারি না)। আমার কাছে EE নেই তবে প্রদত্ত যে এটি এককালীন, ইঙ্গিতগুলি ঠিক থাকবে। আমার মূল কৌতূহল ছিল কিউও কীভাবে পরিকল্পনার সাথে উঠল :) টেবিলটি লগিং টেবিল এবং সেখানে ভারী সন্নিবেশ রয়েছে। একটি পৃথক অ্যাসিনক্রোনাস লগিং টেবিল রয়েছে যদিও এরপরে tblFEStatsPaperHits এ সারিগুলি আপডেট করে যাতে প্রয়োজন হলে আমি নিজেই এটি পরিচালনা করতে পারি।
মার্ক এস রাসমুসেন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.