এই ভ্রান্ত ধারণার কারণ সম্ভবত এটি বিশ্বাস করে যে এটি সমস্ত কলামটি পড়ে শেষ করবে। এটি সহজেই দেখা যায় যে এটি এমন নয়।
CREATE TABLE T
(
X INT PRIMARY KEY,
Y INT,
Z CHAR(8000)
)
CREATE NONCLUSTERED INDEX NarrowIndex ON T(Y)
IF EXISTS (SELECT * FROM T)
PRINT 'Y'
পরিকল্পনা দেয়
এটি দেখায় যে এসকিউএল সার্ভার সূচকটি সমস্ত কলামকে অন্তর্ভুক্ত করে না সত্ত্বেও ফলাফল যাচাই করার জন্য উপলব্ধ সংকীর্ণ সূচকটি ব্যবহার করতে সক্ষম হয়েছিল। সূচক অ্যাক্সেসটি একটি অর্ধ যোগদানের অপারেটরের নিচে রয়েছে যার অর্থ এটি প্রথম সারিতে ফিরে আসার সাথে সাথে স্ক্যান করা বন্ধ করতে পারে।
সুতরাং এটি পরিষ্কার যে উপরোক্ত বিশ্বাসটি ভুল।
তবে ক্যোরি অপটিমাইজার টিমের কনর কানিংহ্যাম এখানে ব্যাখ্যা করেছেন যে তিনি সাধারণত SELECT 1
এই ক্ষেত্রে ব্যবহার করেন কারণ এটি ক্যোয়ারীর সংকলনে সামান্য পারফরম্যান্সের পার্থক্য করতে পারে ।
কিউপি *
পাইপলাইনের প্রথম দিকে সমস্তগুলি গ্রহণ এবং প্রসারিত করবে এবং তাদেরকে বস্তুর সাথে আবদ্ধ করবে (এই ক্ষেত্রে, কলামগুলির তালিকা)। এটি কোয়েরির প্রকৃতির কারণে বিনা শর্তযুক্ত কলামগুলি সরিয়ে ফেলবে।
সুতরাং EXISTS
এই মত একটি সহজ subquery জন্য :
SELECT col1 FROM MyTable WHERE EXISTS
(SELECT * FROM Table2 WHERE
MyTable.col1=Table2.col2)
এর *
সম্ভাব্য কয়েকটি বড় কলামের তালিকায় প্রসারিত হবে এবং তারপরে এটি নির্ধারিত হবে যে এর শব্দার্থতত্ত্বগুলিতে col
EXISTS
কলামগুলির কোনওটির প্রয়োজন নেই, সুতরাং মূলত সেগুলি সমস্তই সরানো যেতে পারে।
" SELECT 1
" কোয়েরি সংকলনের সময় সেই টেবিলের জন্য কোনও বিনাবিহীন মেটাডেটা পরীক্ষা করা এড়ানো হবে।
তবে রানটাইমের সময় ক্যোয়ারির দুটি রূপ একরকম হবে এবং অভিন্ন রানটাইম থাকবে।
আমি খালি টেবিলে বিভিন্ন সংখ্যক কলাম সহ এই কোয়েরিটি প্রকাশ করার সম্ভাব্য চারটি উপায় পরীক্ষা করেছি। SELECT 1
বনাম SELECT *
বনাম SELECT Primary_Key
বনাম SELECT Other_Not_Null_Column
।
আমি একটি লুপের সাহায্যে ক্যুরিগুলি চালিয়েছি OPTION (RECOMPILE)
এবং প্রতি সেকেন্ডে মৃত্যুদন্ড কার্যকর করার সংখ্যাটি পরিমাপ করেছি। নীচে ফলাফল
+
| Num of Cols | * | 1 | PK | Not Null col |
+
| 2 | 2043.5 | 2043.25 | 2073.5 | 2067.5 |
| 4 | 2038.75 | 2041.25 | 2067.5 | 2067.5 |
| 8 | 2015.75 | 2017 | 2059.75 | 2059 |
| 16 | 2005.75 | 2005.25 | 2025.25 | 2035.75 |
| 32 | 1963.25 | 1967.25 | 2001.25 | 1992.75 |
| 64 | 1903 | 1904 | 1936.25 | 1939.75 |
| 128 | 1778.75 | 1779.75 | 1799 | 1806.75 |
| 256 | 1530.75 | 1526.5 | 1542.75 | 1541.25 |
| 512 | 1195 | 1189.75 | 1203.75 | 1198.5 |
| 1024 | 694.75 | 697 | 699 | 699.25 |
+
| Total | 17169.25 | 17171 | 17408 | 17408 |
+
যেমন দেখা যায় যে SELECT 1
এবং SELECT *
উভয় পদ্ধতির মধ্যে পার্থক্য নগণ্য। যদিও SELECT Not Null col
এবং SELECT PK
কিছুটা দ্রুত উপস্থিত হয়।
সারণীর কলামগুলির সংখ্যা বাড়ার সাথে সাথে চারটি প্রশ্নেরই কর্মক্ষমতা হ্রাস পায়।
টেবিলটি খালি হওয়ায় এই সম্পর্কটি কলাম মেটাডেটার পরিমাণের দ্বারা কেবল বর্ণনামূলক বলে মনে হয়। জন্য COUNT(1)
এটি দেখতে যে এই পুনর্লিখিত পরার সহজ COUNT(*)
নীচের থেকে প্রক্রিয়ায় কিছু সময়ে।
SET SHOWPLAN_TEXT ON;
GO
SELECT COUNT(1)
FROM master..spt_values
যা নিম্নলিখিত পরিকল্পনা দেয়
|
|
|
এসকিউএল সার্ভার প্রক্রিয়াতে একটি ডিবাগার সংযুক্ত করা এবং নীচে সম্পাদন করার সময় এলোমেলোভাবে বিরতি
DECLARE @V int
WHILE (1=1)
SELECT @V=1 WHERE EXISTS (SELECT 1 FROM
আমি দেখেছি যে বেশিরভাগ ক্ষেত্রে টেবিলটিতে 1,024 কলাম রয়েছে কল স্ট্যাকটি নীচের মতো কিছু দেখায় যে এটি SELECT 1
ব্যবহার করার সময়ও কলাম মেটাডেটা লোড করার সময়টি সত্যই ব্যয় করে চলেছে (ক্ষেত্রে যেখানে টেবিলটিতে 1 টি কলাম এলোমেলোভাবে ভেঙে 10 টি চেষ্টায় কল স্ট্যাকের এই বিটটিকে আঘাত করে না)
sqlservr.exe!CMEDAccess::GetProxyBaseIntnl() - 0x1e2c79 bytes
sqlservr.exe!CMEDProxyRelation::GetColumn() + 0x57 bytes
sqlservr.exe!CAlgTableMetadata::LoadColumns() + 0x256 bytes
sqlservr.exe!CAlgTableMetadata::Bind() + 0x15c bytes
sqlservr.exe!CRelOp_Get::BindTree() + 0x98 bytes
sqlservr.exe!COptExpr::BindTree() + 0x58 bytes
sqlservr.exe!CRelOp_FromList::BindTree() + 0x5c bytes
sqlservr.exe!COptExpr::BindTree() + 0x58 bytes
sqlservr.exe!CRelOp_QuerySpec::BindTree() + 0xbe bytes
sqlservr.exe!COptExpr::BindTree() + 0x58 bytes
sqlservr.exe!CScaOp_Exists::BindScalarTree() + 0x72 bytes
... Lines omitted ...
msvcr80.dll!_threadstartex(void * ptd=0x0031d888) Line 326 + 0x5 bytes C
kernel32.dll!_BaseThreadStart@8() + 0x37 bytes
এই ম্যানুয়াল প্রোফাইলিংয়ের প্রয়াসটিকে ভিএস 2012 কোড প্রোফাইলার ব্যাক আপ করেছে যা দুটি ক্ষেত্রে সংকলনের সময় গ্রহণকারী ফাংশনগুলির একটি খুব আলাদা নির্বাচন দেখায় ( শীর্ষ 15 ফাংশন 1024 কলাম বনাম শীর্ষ 15 ফাংশন 1 কলাম )।
সংস্করণ SELECT 1
এবং SELECT *
সংস্করণ উভয়ই কলামের অনুমতিগুলি পরীক্ষা করতে সক্ষম হয় এবং যদি ব্যবহারকারীকে টেবিলের সমস্ত কলামে অ্যাক্সেস না দেওয়া হয় তবে ব্যর্থ হন।
আমি গাদা একটি কথোপকথন থেকে আঁকিয়ে একটি উদাহরণ
CREATE USER blat WITHOUT LOGIN;
GO
CREATE TABLE dbo.T
(
X INT PRIMARY KEY,
Y INT,
Z CHAR(8000)
)
GO
GRANT SELECT ON dbo.T TO blat;
DENY SELECT ON dbo.T(Z) TO blat;
GO
EXECUTE AS USER = 'blat';
GO
SELECT 1
WHERE EXISTS (SELECT 1
FROM T);
GO
REVERT;
DROP USER blat
DROP TABLE T
সুতরাং কেউ অনুমান করতে পারে যে ব্যবহার করার সময় সামান্য আপাত পার্থক্যটি SELECT some_not_null_col
হ'ল এটি নির্দিষ্ট কলামে কেবলমাত্র অনুমতিগুলি পরীক্ষা করে (যদিও এখনও সকলের জন্য মেটাডেটা লোড করে)। তবে অন্তর্নিহিত সারণীতে কলামগুলির সংখ্যা বাড়ার সাথে যদি কিছু ছোট হয় তবে দুটি পদ্ধতির মধ্যে শতাংশের পার্থক্য হিসাবে এটি সত্যের সাথে খাপ খায় না বলে মনে হয়।
কোনও ইভেন্টে আমি তাড়াহুড়া করব না এবং আমার ফর্মটিতে আমার সমস্ত প্রশ্নের পরিবর্তন করব না কারণ পার্থক্যটি খুব সামান্য এবং কোয়েরি সংকলনের সময় কেবল স্পষ্ট। অপসারণ OPTION (RECOMPILE)
যাতে পরবর্তী মৃত্যুদন্ড কার্যকর করা ক্যাশেড পরিকল্পনাটি ব্যবহার করতে পারে নীচে দেওয়া।
+
| Num of Cols | * | 1 | PK | Not Null col |
+
| 2 | 144933.25 | 145292 | 146029.25 | 143973.5 |
| 4 | 146084 | 146633.5 | 146018.75 | 146581.25 |
| 8 | 143145.25 | 144393.25 | 145723.5 | 144790.25 |
| 16 | 145191.75 | 145174 | 144755.5 | 146666.75 |
| 32 | 144624 | 145483.75 | 143531 | 145366.25 |
| 64 | 145459.25 | 146175.75 | 147174.25 | 146622.5 |
| 128 | 145625.75 | 143823.25 | 144132 | 144739.25 |
| 256 | 145380.75 | 147224 | 146203.25 | 147078.75 |
| 512 | 146045 | 145609.25 | 145149.25 | 144335.5 |
| 1024 | 148280 | 148076 | 145593.25 | 146534.75 |
+
| Total | 1454769 | 1457884.75 | 1454310 | 1456688.75 |
+
আমার ব্যবহৃত পরীক্ষার স্ক্রিপ্টটি এখানে পাওয়া যাবে