بررسی عملگر hash join
ابتدا در management studio از منوی Query، گزینهی Include actual execution plan را انتخاب میکنیم. سپس کوئریهای زیر را اجرا میکنیم:
USE [WideWorldImporters]; GO SET STATISTICS IO ON; GO /* Query with a hash join */ SELECT [ol].[OrderID], [ol].[OrderLineID], [ol].[StockItemID], [ol].[PickedQuantity], [si].[StockItemName], [si].[UnitPrice] FROM [Warehouse].[StockItems] [si] JOIN [Sales].[OrderLines] [ol] ON [si].[StockItemID] = [ol].[StockItemID]; GO
دیتاست بالایی که ضخامت پیکان خارج شدهی از آن کمتر است، تعداد ردیفهای کمتری را نسبت به دیتاست درونی دارد (227 ردیف، در مقابل بیش از 231 هزار ردیف).
با حرکت اشارهگر ماوس بر روی هر کدام از ایندکسها، میتوان با دقت کردن به Output List آنها، دقیقا دریافت که هرکدام، چه ستونهایی از کوئری نهایی را تامین میکنند:
دیتاست بالایی که از PK_Warehouse_StockItems تامین میشود:
ALTER TABLE [Warehouse].[StockItems] ADD CONSTRAINT [PK_Warehouse_StockItems] PRIMARY KEY CLUSTERED ( [StockItemID] ASC )
دیتاست درونی که از NCCX_Sales_OrderLines تامین میشود و یک COLUMNSTORE INDEX است:
CREATE NONCLUSTERED COLUMNSTORE INDEX [NCCX_Sales_OrderLines] ON [Sales].[OrderLines] ( [OrderID], [StockItemID], [Description], [Quantity], [UnitPrice], [PickedQuantity] )
بهبود کارآیی hash join با فشرده سازی ایندکسهای آن
ایندکس NCCX_Sales_OrderLines که در کوئری فوق مورد استفاده قرار گرفته، همانطور که در قسمتی از تعریف آن نیز مشخص است، تعداد ستونهای بیشتری را از آنچه ما نیاز داریم، در بر دارد. در این حالت آیا اگر ایندکس مناسبتری را با تعداد ستون کمتری ایجاد کنیم، از آن استفاده میکند؟
CREATE NONCLUSTERED INDEX [IX_OrderLines_StockItemID] ON [Sales].[OrderLines]( [StockItemID] ASC, [PickedQuantity] ASC, [OrderID]) ON [PRIMARY]; GO
در این حالت اگر کوئری زیر را اجرا کنیم:
SELECT [ol].[OrderID], [ol].[OrderLineID], [ol].[StockItemID], [ol].[PickedQuantity], [si].[StockItemName], [si].[UnitPrice] FROM [Sales].[OrderLines] [ol] JOIN [Warehouse].[StockItems] [si] ON [ol].[StockItemID] = [si].[StockItemID] OPTION (RECOMPILE); GO
یک نکته: در این کوئری علت استفادهی از RECOMPILE، وادار کردن SQL server به محاسبهی مجدد کوئری پلن جاری است.
اکنون اگر نگارش فشرده شدهی ایندکسی را که ایجاد کردیم، با ذکر گزینهی DATA_COMPRESSION = PAGE تعریف کنیم، چه اتفاقی رخ میدهد؟
CREATE NONCLUSTERED INDEX [IX_OrderLines_StockItemID_Compressed] ON [Sales].[OrderLines]( [StockItemID] ASC, [PickedQuantity] ASC, [OrderID]) WITH (DATA_COMPRESSION = PAGE) ON [PRIMARY]; GO
یک نکته: اگر علاقمند بودید تا هزینهی این کوئریها را نسبت به یکدیگر محاسبه و مقایسه کنید، چون یک کوئری معمولی، همواره از آخرین پلن محاسبه شده استفاده میکند، اینکار میسر نیست. اما میتوان با ذکر صریح ایندکس مدنظر توسط راهنمای WITH INDEX، بهینه ساز کوئریها را وارد کرد تا از ایندکسی که ذکر میشود، بجای ایندکسی که فکر میکند بهتر است، استفاده کند. بنابراین اجرای هر 4 کوئری زیر با هم، 4 کوئری پلن متفاوت را بر اساس ایندکسهای متفاوتی، محاسبه کرده و نمایش میدهد:
SELECT [ol].[OrderID], [ol].[OrderLineID], [ol].[StockItemID], [ol].[PickedQuantity], [si].[StockItemName], [si].[UnitPrice] FROM [Sales].[OrderLines] [ol] JOIN [Warehouse].[StockItems] [si] ON [ol].[StockItemID] = [si].[StockItemID] OPTION (RECOMPILE); GO SELECT [ol].[OrderID], [ol].[OrderLineID], [ol].[StockItemID], [ol].[PickedQuantity], [si].[StockItemName], [si].[UnitPrice] FROM [Sales].[OrderLines] [ol] WITH (INDEX (IX_Sales_OrderLines_Perf_20160301_02)) JOIN [Warehouse].[StockItems] [si] ON [ol].[StockItemID] = [si].[StockItemID]; GO SELECT [ol].[OrderID], [ol].[OrderLineID], [ol].[StockItemID], [ol].[PickedQuantity], [si].[StockItemName], [si].[UnitPrice] FROM [Sales].[OrderLines] [ol] WITH (INDEX (IX_OrderLines_StockItemID)) JOIN [Warehouse].[StockItems] [si] ON [ol].[StockItemID] = [si].[StockItemID]; GO SELECT [ol].[OrderID], [ol].[OrderLineID], [ol].[StockItemID], [ol].[PickedQuantity], [si].[StockItemName], [si].[UnitPrice] FROM [Sales].[OrderLines] [ol] WITH (INDEX (IX_OrderLines_StockItemID_Compressed)) JOIN [Warehouse].[StockItems] [si] ON [ol].[StockItemID] = [si].[StockItemID]; GO
بررسی عملگر compute scalar
کار عملگر compute scalar، ارزیابی و محاسبهی یک عبارت است و خروجی آن نیز یک مقدار scalar است؛ مانند functions در SQL Server. مشکلی که با این عملگر وجود دارد این است که هزینهی انجام آن عموما در کوئری پلن ظاهر نمیشود (و یا با تخمین نادرستی ظاهر میشود) که میتواند گمراه کننده باشد. همچنین پلن حاصل، اشیایی را که توسط یک function مورد استفاده قرار میگیرند، لحاظ نمیکند.
برای نمونه اگر پلن دو کوئری زیر را با هم مقایسه کنیم:
SELECT COUNT(*) FROM [Sales].[Orders]; SELECT COUNT_BIG (*) FROM [Sales].[Orders];
از این جهت که (*)COUNT در SQL server به (*)COUNT_BIG تفسیر شده و اجرا میشود. به همین جهت آنچنان تفاوتی در اینجا قابل مشاهده نیست.
اما اگر function زیر را تعریف کنیم:
CREATE FUNCTION dbo.CountProductsSold ( @SalesPersonID INT ) RETURNS INT AS BEGIN DECLARE @SoldCount INT; SELECT @SoldCount = COUNT(DISTINCT [ol].[StockItemID]) FROM [Sales].[Orders] [o] JOIN [Sales].[OrderLines] [ol] ON [o].[OrderID] = [ol].[OrderID] WHERE [o].[SalespersonPersonID] = @SalesPersonID RETURN (@SoldCount); END
SELECT [FullName] AS [SalesPerson], [dbo].[CountProductsSold]([PersonID]) AS [NumberOfProductsSold] FROM [Application].[People] WHERE [IsSalesperson] = 1;
یک روش محاسبهی هزینهی فراخوانی این تابع، استفاده از extended events است. روش دیگر آن استفاده از اشیاء DMO's میباشد:
SELECT [fs].[last_execution_time], [fs].[execution_count], [fs].[total_logical_reads]/[fs].[execution_count] [AvgLogicalReads], [fs].[max_logical_reads], [t].[text], [p].[query_plan] FROM sys.dm_exec_function_stats [fs] CROSS APPLY sys.dm_exec_sql_text([fs].sql_handle) [t] CROSS APPLY sys.dm_exec_query_plan([fs].[plan_handle]) [p];
بنابراین compute scalar صورت گرفته دارای هزینهای است که در actual execution plan ظاهر نمیشود.
اکنون اگر از منوی Query، گزینهی Include actual execution plan را انتخاب نکنیم و بجای آن گزینهی Display estimated execution plan را انتخاب کنیم، به تصویر زیر خواهیم رسید:
در نیمهی پایینی آن، جزئیات دسترسیهای تابع فراخوانی شده نیز ذکر میشوند. بنابراین استفادهی از estimated execution planها در حین کار با توابع، بسیار مفید است.
C# 8.0 - Async Streams
‘AsyncEnumerableReader’ reached the configured maximum size of the buffer when enumerating a value of type ‘<type>’. This limit is in place to prevent infinite streams of ‘IAsyncEnumerable<>’ from continuing indefinitely. If this is not a programming mistake, consider ways to reduce the collection size, or consider manually converting ‘<type>’ into a list rather than increasing the limit.
برای تنظیم یا تغییر آن میتوان از خاصیت MvcOptions.MaxIAsyncEnumerableBufferLimit در برنامههای ASP.NET Core استفاده کرد.
Stack Overflow Developer Desktop Build - 2017
One of the things we're big on at Stack Exchange is hardware - we love it. More importantly, we love not waiting on it. With that in mind, we upgrade our developer machines every 2 years. In case it helps anyone else, I'm posting our current parts list here. This isn't set in stone, we review and update it to the latest tech every time we build a machine. We also customize the build for each developer if needed - for example those needing extra space or specific display connections, etc. I'll try and keep this page updated as we make changes
نکته: راههای اشاره شده برای مقابله با شنود پارامترها برای تمام شرایط قابل استفاده نیستند.
راه حل اول: استفاده از دستور With Recompile
مشکل شنود پارامتر این است که در اولین اجرای پروسیجر، پلن اجرایی را بر اساس پارامترهای ارسالی اولیه ایجاد میکند. راه حل غلبه بر این مشکل، کامپایل مجدد پروسیجر، بعد از هر اجرای آن است. بهمین جهت از دستور WITH RECOMPILE هنگامیکه قصد ایجاد پروسیجر را دارید استفاده نمایید. مانند کد زیر:
CREATE PROC [dbo].[DisplayBillingInfo] @BeginDate DATETIME, @EndDate DATETIME WITH RECOMPILE AS SELECT BillingDate, BillingAmt FROM BillingInfo WHERE BillingDate between @BeginDate AND @EndDate;
DBCC FREEPROCCACHE; EXEC dbo.DisplayBillingInfo @BeginDate = '2005-01-01', @EndDate = '2005-01-03'; EXEC dbo.DisplayBillingInfo @BeginDate = '1999-01-01', @EndDate = '1999-12-31';
راه حل دوم: غیر فعال نمودن شنود پارامتر
روش دیگر برطرف کردن مشکلات مرتبط با شنود پارامتر، غیر فعال کردن آن است. البته منظور از غیر فعال کردن، غیر فعال نمودن گزینهای در بانک اطلاعاتی نیست؛ بلکه با تغییر متن و نحوهی اجرا، میتوان شنود را غیر فعال نمود. در کد زیر با تغییر نحوه اجرای پروسیجر، قابلیت شنود پارامتر غیر فعال شده است:
CREATE PROC [dbo].[DisplayBillingInfo] @BeginDate DATETIME, @EndDate DATETIME WITH RECOMPILE AS DECLARE @StartDate DATETIME; DECLARE @StopDate DATETIME; SET @StartDate = @BeginDate; SET @StopDate = @EndDate; SELECT BillingDate, BillingAmt FROM BillingInfo WHERE BillingDate between @StartDate AND @StopDate;
راه حل سوم: ایجاد چند نوع پروسیجر
راه دیگر، ایجاد پروسیجرهای متفاوت برای پارامترهایی با کاردینالیتی متفاوت است. بهعبارت دیگر، دسته بندی پارامترهای ارسالی و ایجاد پروسیجرهایی خاص همان دسته. در مثالهای این سری از مطالب، دو دسته پارامتر 1) بازه زمانی کوتاه، مثلا چند روز و 2) بازه زمانی بلند، مثلا ماهیانه وجود داشت که میتوانید 2 دسته پروسیجر را یکی برای بازههای روزانه و دیگری برای بازههای زمانی ماهیانه ایجاد نمایید.
CREATE PROC [dbo].[DisplayBillingInfoNarrow] @BeginDate DATETIME, @EndDate DATETIME AS SELECT BillingDate, BillingAmt FROM BillingInfo WHERE BillingDate between @BeginDate AND @EndDate; GO CREATE PROC [dbo].[DisplayBillingInfoWide] @BeginDate DATETIME, @EndDate DATETIME AS SELECT BillingDate, BillingAmt FROM BillingInfo WHERE BillingDate between @BeginDate AND @EndDate; GO DROP PROCEDURE [dbo].[DisplayBillingInfo]; GO CREATE PROC [dbo].[DisplayBillingInfo] @BeginDate DATETIME, @EndDate DATETIME AS IF DATEDIFF(DD,@BeginDate, @EndDate) < 4 EXECUTE DisplayBillingInfoNarrow @BeginDate, @EndDate ELSE EXECUTE DisplayBillingInfoWide @BeginDate, @EndDate GO
درک بهتر this در JavaScript
شتابدهی JavaScript با GPU
select * from weather.forecast where woeid in (select woeid from geo.placefinder where text="CityName")