اشتراکها
مروری بر SQL Server 2016
آیا میدانید آخرین باری که از یک دیتابیس مفروض بکآپ گرفته شده، چه زمانی بوده است؟ (یا اینکه اصلا چه اهمیتی داره؟!)
USE master;
SELECT B.name AS Database_Name,
ISNULL(STR(ABS(DATEDIFF(day, GetDate(),
MAX(Backup_finish_date)))), 'NEVER') AS DaysSinceLastBackup,
ISNULL(CONVERT(char(10), MAX(backup_finish_date), 101), 'NEVER') AS
LastBackupDate
FROM master.dbo.sysdatabases B
LEFT OUTER JOIN msdb.dbo.backupset A
ON A.database_name = B.name
AND A.type = 'D'
GROUP BY
B.Name
ORDER BY
B.name
همانطور که مطلع هستید در SQL Server 2008 امکان فشرده سازی خودکار بک آپها هم فراهم شده است. با استفاده از اسکریپت زیر میتوان از درصد فشرده سازی بکآپها گزارش گرفت:
SELECT bs.server_name,
bs.database_name AS 'Database Name',
CONVERT (BIGINT, bs.backup_size / 1048576 ) AS
'Uncompressed Backup Size (MB)',
CONVERT (BIGINT, bs.compressed_backup_size / 1048576 ) AS
'Compressed Backup Size (MB)',
CONVERT (NUMERIC (20,2), (CONVERT (FLOAT, bs.backup_size) /
CONVERT (FLOAT, bs.compressed_backup_size))) AS 'Compression Ratio',
DATEDIFF (SECOND, bs.backup_start_date, bs.backup_finish_date) AS
'Backup Elapsed Time (sec)',
bs.backup_finish_date AS 'Backup Finish Date'
FROM msdb.dbo.backupset AS bs
WHERE DATEDIFF (SECOND, bs.backup_start_date, bs.backup_finish_date) > 0
AND bs.backup_size > 0
AND bs.type = 'D' -- Change to L if you want Log backups
ORDER BY
bs.backup_finish_date DESC
برای فعال سازی گزینه فشرده سازی بکآپها با استفاده از دستورات T-SQL میتوان به صورت زیر عمل کرد (در SQL server 2008):
USE master;
EXEC sp_configure 'backup compression default', '1';
RECONFIGURE WITH OVERRIDE;
ماخذ مورد استفاده:
http://www.sqlservercentral.com
Extended events ای که در قسمت قبل بررسی شدند، جهت جمع آوری اطلاعات آماری تک کوئریها مورد استفاده قرار میگیرند؛ اما Dynamic management objects یا به اختصار DMO's، تجمعی عمل میکنند (برای مثال جهت محاسبهی میانگین logical reads چند کوئری مانند هم). متن یک کوئری و پلن آن، توسط DMO's مختلفی قابل استخراج هستند. متن یک کوئری توسط sys.dm_exec_sql_text قابل استخراج است و برای دسترسی به کوئری پلنها از sys.dm_exec_query_plan، sys.dm_exec_cached_plans و sys.dm_exec_text_query_plan استفاده میشود. در این حالت برای دسترسی به اطلاعات آماری از sys.dm_exec_query_stats و sys.dm_exec_function_stats کمک گرفته خواهد شد.
استفاده از Dynamic management objects برای جمع آوری اطلاعات آماری کوئریها
در ادامه در طی چند مثال، روش استخراج اطلاعات آماری کوئریها را توسط DMO's بررسی میکنیم.
دریافت متن کوئریهای در حال اجرا
توسط کوئری زیر که توسط تابع sys.dm_exec_sql_text اجرا میشود، میتوان لیست کوئریهای در حال اجرای بر روی بانکهای اطلاعاتی جاری را بدست آورد:
در اینجا text، همان متن کوئری است و هربار که این کوئری اجرا میشود، نتیجهی متفاوتی را بر اساس کوئریهایی که در آن لحظه در حال اجرا هستند، دریافت خواهیم کرد.
تابع sys.dm_exec_sql_text برای اجرا نیاز به یک sql_handle دارد که آنرا از طریق sys.dm_exec_requests میتوان تامین کرد.
دریافت پلن کوئریهای در حال اجرا
توسط کوئری زیر که توسط تابع sys.dm_exec_query_plan اجرا میشود، میتوان لیست پلن کوئریهای در حال اجرای بر روی بانکهای اطلاعاتی جاری را بدست آورد:
تابع sys.dm_exec_query_plan برای اجرا نیاز به یک plan_handle دارد که آنرا از طریق sys.dm_exec_requests میتوان تامین کرد.
حاصل این کوئری، به همراه text یا اصل متن کوئریهای در حال اجرا و همچنین query_plan، یا همان اطلاعات XML ای پلن که در قسمت اول، نمونهای از آنرا بررسی کردیم، میباشد که با کلیک بر روی هر کدام در management studio، نمایش گرافیکی آنها ظاهر خواهد شد. البته این پلنها، تنها تخمینها را به همراه دارند؛ چون از کش خوانده میشوند.
دریافت لیست پلنهای کش شده
توسط Viewای به نام sys.dm_exec_cached_plans میتوان به لیست پلنهای کش شدهی در سیستم دسترسی یافت:
البته خروجی آن، آنچنان جالب نیست. چون یکی از ستونهای آن، فقط حاوی همان plan_handle ای است که در مثال قبل بررسی کردیم و به خودی خود، حاوی اطلاعات قابل مشاهدهای نیست. به همین جهت اگر بخواهیم آنرا با کوئریهایی که تاکنون نوشتیم، ترکیب کنیم به کوئری زیر خواهیم رسید:
مزیت این کوئری نسبت به موارد قبلی، وجود ستونهای جدید objtype و size_in_bytes است که بیانگر نوع کوئری، مانند AdHoc و اندازهی پلن در کش هستند.
دریافت متن پلنهای تو در تو و عمیق
با استفاده از تابع sys.dm_exec_text_query_plan میتوان به متن پلنهای عمیق دسترسی یافت. در این حالت خروجی کوئری در management studio به صورت یک لینک قابل کلیک ظاهر نمیشود و صرفا یک متن قابل کپی است که میتوان آنرا با پسوند sqlplan برای بررسیهای بعدی، ذخیره کرد:
در اینجا اعداد 0 و 1- به معنای ابتدا و انتهای batch هستند.
دریافت اطلاعات آماری کوئریهای درحال اجرا
توسط viewای به نام sys.dm_exec_query_stats میتوان به اطلاعات آماری کوئریهای در حال اجرا دسترسی یافت:
این کوئری تعداد ستونهای قابل توجهی را به همراه دارد مانند Physical reads، logical reads و .... به همین جهت نیاز است اطلاعات مفید آنرا فیلتر کرد:
این کوئری در حقیقت ترکیبی است از کوئریهایی که تاکنون نوشتیم و در آن text و query_plan از sys.dm_exec_sql_text و sys.dm_exec_query_plan تامین شدهاند، به همراه تعدادی ستون مفید sys.dm_exec_query_stats مانند last_execution_time و AvgLogicalReads. به علاوه در اینجا کوئریهایی که بیشتر از 25 بار اجرا شدهاند و یا total_logical_reads آنها بیش از 10 هزار بوده، در خروجی ظاهر خواهند شد (مفهوم تجمعی بودن DMO's).
از SQL Server 2016 به بعد، امکان دریافت اطلاعات آماری توابع نیز میسر شدهاست:
یک نکته: قابلیت جدیدی تحت عنوان Query Store از زمان SQL Server 2016 معرفی شدهاست و کار آن دریافت تمام اطلاعاتی است که تاکنون بررسی کردیم و تفاوت آن، در ذخیره شده بودن آن است. یعنی این اطلاعات را داخل بانک اطلاعاتی در حال بررسی ذخیره میکند که شامل متن و پلن کوئری و همچنین اطلاعات آماری آن است که توسط DMO's تهیه میشود.
استفاده از Dynamic management objects برای جمع آوری اطلاعات آماری کوئریها
در ادامه در طی چند مثال، روش استخراج اطلاعات آماری کوئریها را توسط DMO's بررسی میکنیم.
دریافت متن کوئریهای در حال اجرا
توسط کوئری زیر که توسط تابع sys.dm_exec_sql_text اجرا میشود، میتوان لیست کوئریهای در حال اجرای بر روی بانکهای اطلاعاتی جاری را بدست آورد:
SELECT [r].[session_id], DB_NAME([r].[database_id]) [DatabaseName], [t].[text] FROM sys.dm_exec_requests [r] CROSS APPLY sys.dm_exec_sql_text([r].sql_handle) [t]; GO
تابع sys.dm_exec_sql_text برای اجرا نیاز به یک sql_handle دارد که آنرا از طریق sys.dm_exec_requests میتوان تامین کرد.
دریافت پلن کوئریهای در حال اجرا
توسط کوئری زیر که توسط تابع sys.dm_exec_query_plan اجرا میشود، میتوان لیست پلن کوئریهای در حال اجرای بر روی بانکهای اطلاعاتی جاری را بدست آورد:
SELECT [r].[session_id], DB_NAME([r].[database_id]) [DatabaseName], [t].[text], [p].[query_plan] FROM sys.dm_exec_requests [r] CROSS APPLY sys.dm_exec_sql_text([r].sql_handle) [t] CROSS APPLY sys.dm_exec_query_plan([r].[plan_handle]) [p]; GO
حاصل این کوئری، به همراه text یا اصل متن کوئریهای در حال اجرا و همچنین query_plan، یا همان اطلاعات XML ای پلن که در قسمت اول، نمونهای از آنرا بررسی کردیم، میباشد که با کلیک بر روی هر کدام در management studio، نمایش گرافیکی آنها ظاهر خواهد شد. البته این پلنها، تنها تخمینها را به همراه دارند؛ چون از کش خوانده میشوند.
دریافت لیست پلنهای کش شده
توسط Viewای به نام sys.dm_exec_cached_plans میتوان به لیست پلنهای کش شدهی در سیستم دسترسی یافت:
SELECT * FROM sys.dm_exec_cached_plans;
SELECT [r].[session_id], DB_NAME([r].[database_id]) [DatabaseName], [cp].[objtype], [cp].[size_in_bytes], [t].[text], [p].[query_plan] FROM sys.dm_exec_requests [r] CROSS APPLY sys.dm_exec_sql_text([r].sql_handle) [t] CROSS APPLY sys.dm_exec_query_plan([r].[plan_handle]) [p] JOIN sys.dm_exec_cached_plans [cp] ON [r].[plan_handle] = [cp].[plan_handle]; GO
دریافت متن پلنهای تو در تو و عمیق
با استفاده از تابع sys.dm_exec_text_query_plan میتوان به متن پلنهای عمیق دسترسی یافت. در این حالت خروجی کوئری در management studio به صورت یک لینک قابل کلیک ظاهر نمیشود و صرفا یک متن قابل کپی است که میتوان آنرا با پسوند sqlplan برای بررسیهای بعدی، ذخیره کرد:
SELECT [r].[session_id], DB_NAME([r].[database_id]) [DatabaseName], [tq].[query_plan] FROM sys.dm_exec_requests [r] CROSS APPLY sys.dm_exec_text_query_plan([r].plan_handle, 0, -1) [tq]; GO
دریافت اطلاعات آماری کوئریهای درحال اجرا
توسط viewای به نام sys.dm_exec_query_stats میتوان به اطلاعات آماری کوئریهای در حال اجرا دسترسی یافت:
SELECT * FROM sys.dm_exec_query_stats; GO
SELECT [qs].[last_execution_time], [qs].[execution_count], [qs].[total_logical_reads]/[qs].[execution_count] [AvgLogicalReads], [qs].[max_logical_reads], [t].[text], [p].[query_plan] FROM sys.dm_exec_query_stats [qs] CROSS APPLY sys.dm_exec_sql_text([qs].sql_handle) [t] CROSS APPLY sys.dm_exec_query_plan([qs].[plan_handle]) [p] WHERE [qs].[execution_count] > 25 OR [qs].[total_logical_reads] > 10000 ORDER BY [qs].[total_logical_reads]/[qs].[execution_count] DESC; GO
از SQL Server 2016 به بعد، امکان دریافت اطلاعات آماری توابع نیز میسر شدهاست:
SELECT * FROM sys.dm_exec_function_stats; GO
یک نکته: قابلیت جدیدی تحت عنوان Query Store از زمان SQL Server 2016 معرفی شدهاست و کار آن دریافت تمام اطلاعاتی است که تاکنون بررسی کردیم و تفاوت آن، در ذخیره شده بودن آن است. یعنی این اطلاعات را داخل بانک اطلاعاتی در حال بررسی ذخیره میکند که شامل متن و پلن کوئری و همچنین اطلاعات آماری آن است که توسط DMO's تهیه میشود.
مطلب "منسوخ شدهها در نگارشهای جدید SQL server" را احتمالا به خاطر دارید. جهت تکمیل آن، کوئری زیر را هم میتوان ذکر کرد:
SELECT instance_name,
cntr_value
FROM sys.dm_os_performance_counters
WHERE OBJECT_NAME = 'SQLServer:Deprecated Features'
AND cntr_value > 0
ORDER BY
cntr_value DESC
توسط این کوئری گزارشی از منسوخ شدههای مورد استفاده در دیتابیسهای شما ارائه میشود. برای مثال چندبار از text و ntext استفاده کردهاید، آیا هنوز compatibility level دیتابیسهای خود را تغییر ندادهاید و مثالهایی از این دست.
برای مثال جهت یافتن سریع فیلدهای منسوخ شده text و image دیتابیس جاری از کوئری زیر میتوان کمک گرفت:
SELECT O.Name,
col.name AS ColName,
systypes.name
FROM syscolumns col
INNER JOIN sysobjects O
ON col.id = O.id
INNER JOIN systypes
ON col.xtype = systypes.xtype
WHERE O.Type = 'U'
AND OBJECTPROPERTY(o.ID, N'IsMSShipped') = 0
AND systypes.name IN ('text', 'ntext', 'image')
ORDER BY
O.Name,
Col.Name
نظرات مطالب
یافتن تداخلات Collations در SQL Server
تهیه اسکریپت تغییر Collation تمامی فیلدهای ناهمگون در دیتابیس جاری :
DECLARE @defaultCollation NVARCHAR(1000) SET @defaultCollation = CAST( DATABASEPROPERTYEX(DB_NAME(), 'Collation') AS NVARCHAR(1000) ) select 'ALTER TABLE ' + QUOTENAME(C.TABLE_SCHEMA) +'.'+ QUOTENAME(C.Table_Name) +' ALTER COLUMN ' +' [' +Column_Name+'] ' + DATA_TYPE+'('+cast(character_maximum_length as varchar(10))+')' +' COLLATE Persian_100_CI_AS ' +(case IS_NULLABLE when 'YES' then 'NULL' else 'NOT NULL' end )+';' FROM Information_Schema.Columns C INNER JOIN Information_Schema.Tables T ON C.Table_Name = T.Table_Name WHERE T.Table_Type = 'Base Table' AND RTRIM(LTRIM(Collation_Name)) <> RTRIM(LTRIM(@defaultCollation)) --AND DATA_TYPE='nvarchar' AND character_maximum_length>0 ORDER BY C.Table_Name, C.Column_Name
به همراه بسته Features pack اس کیوال سرور 2012، دو بسته SqlDom.msi نیز وجود دارند (نسخههای X86 و X64). این بسته حاوی اسمبلی Microsoft.SqlServer.TransactSql.ScriptDom.dll میباشد که نهایتا در آدرس Program Files\Microsoft SQL Server\110\SDK\Assemblies کپی خواهد شد.
به کمک آن میتوان عبارات پیچیده T-SQL را Parse و آنالیز کرد. البته باید در نظر داشت هرچند این بسته جهت SQL Server 2012 ارائه شده اما این اسمبلی با نگارشهای 2005 به بعد اس کیوال سرور کاملا سازگار است و اساسا نیازی هم به SQL Server ندارد. در ادامه مروری خواهیم داشت بر نحوه استفاده از آن.
یافتن کوئریهای * Select در بین انبوهی از اسکریپتها به کمک SQLDom
در مورد مضرات کوئریهای * select پیشتر مطلبی را در این سایت خواندهاید. در ادامه قصد داریم به کمک امکانات اسمبلی Microsoft.SqlServer.TransactSql.ScriptDom.dll، تعدادی عبارت T-SQL را آنالیز کرده و مشخص کنیم که آیا حاوی * select هستند یا خیر. کد کامل آنرا در ذیل مشاهده میکنید:
توضیحات:
پس از نصب SQLDom.msi، ارجاعی را به اسمبلی زیر اضافه نمائید تا بتوانید کد فوق را کامپایل کنید:
Program Files\Microsoft SQL Server\110\SDK\Assemblies\Microsoft.SqlServer.TransactSql.ScriptDom.dll
کار با ایجاد وهلهای از TSql110Parser شروع میشود. متد Parse آن، آرگومانی از نوع TextReader را قبول میکند. برای مثال با استفاده از StringReader میتوان محتوای یک متغیر رشتهای را به آن ارسال کرد و یا توسط StreamReader یک فایل sql را.
پس از فراخوانی متد Parse، بهتر است بررسی شود که آیا عبارت T-SQL دریافتی معتبر بوده است یا خیر. اینکار را توسط لیستی از ParseErrorهای دریافتی میتوان انجام داد.
خروجی متد Parse، حاوی یک سری Batch آنالیز شده است. هر عبارت Go در اینجا یک Batch را تشکیل میدهد. سپس در داخل هر batch به دنبال batch.Statements خواهیم گشت تا بتوان به عبارات T-SQL آنها دسترسی یافت.
در ادامه کار اصلی توسط متد processStatement صورت میگیرد. عبارات دریافتی، در حالت کلی از نوع TSqlStatement هستند اما در اصل میتوانند یکی از مشتقات آن نیز باشند. در اینجا فقط دو مورد CreateProcedureStatement و SelectStatement بررسی شدهاند (مطابق رشته tSql ابتدای مثال). هر دو عبارت، از کلاس TSqlStatement مشتق شدهاند.
در متد processStatement عبارات select معمولی و همچنین آنهایی که داخل رویههای ذخیره شده تعریف شدهاند، استخراج شده و در نهایت بررسی میشوند که آیا از نوع SelectStarExpression هستند یا خیر (همان * select صورت مساله).
خروجی مثال فوق به شرح زیر است:
به کمک آن میتوان عبارات پیچیده T-SQL را Parse و آنالیز کرد. البته باید در نظر داشت هرچند این بسته جهت SQL Server 2012 ارائه شده اما این اسمبلی با نگارشهای 2005 به بعد اس کیوال سرور کاملا سازگار است و اساسا نیازی هم به SQL Server ندارد. در ادامه مروری خواهیم داشت بر نحوه استفاده از آن.
یافتن کوئریهای * Select در بین انبوهی از اسکریپتها به کمک SQLDom
در مورد مضرات کوئریهای * select پیشتر مطلبی را در این سایت خواندهاید. در ادامه قصد داریم به کمک امکانات اسمبلی Microsoft.SqlServer.TransactSql.ScriptDom.dll، تعدادی عبارت T-SQL را آنالیز کرده و مشخص کنیم که آیا حاوی * select هستند یا خیر. کد کامل آنرا در ذیل مشاهده میکنید:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using Microsoft.SqlServer.TransactSql.ScriptDom; namespace DbCop { // Microsoft® SQL Server® 2012 Transact-SQL ScriptDom // SQL Server 2012 managed parser, Supports SQL Server 2005+ // SQLDom.msi (redist x86/x64) // http://www.microsoft.com/en-us/download/details.aspx?id=29065 // X86: http://go.microsoft.com/fwlink/?LinkID=239634&clcid=0x409 // X64: http://go.microsoft.com/fwlink/?LinkID=239635&clcid=0x409 // Program Files\Microsoft SQL Server\110\SDK\Assemblies\Microsoft.SqlServer.TransactSql.ScriptDom.dll class Program { static void Main() { const string tSql = @" -- select * in PROCEDURE CREATE PROCEDURE dbo.SelectStarTest AS SELECT * FROM dbo.tbl1 go -- select * in PROCEDURE with TableVar Create PRocedure SelectAll AS Declare @X table(Id integer) Select * from @x go -- select * in PROCEDURE with ctex CREATE PROCEDURE dbo.SelectAllCte AS WITH ctex AS ( SELECT * FROM sys.objects ) SELECT * FROM ctex go -- normal select * select * from tbl1; select * from dbo.tbl2; "; IList<ParseError> errors; TSqlScript sqlFragment; using (var reader = new StringReader(tSql)) { var parser = new TSql110Parser(initialQuotedIdentifiers: true); sqlFragment = (TSqlScript)parser.Parse(reader, out errors); } if (errors != null && errors.Any()) { var sb = new StringBuilder(); foreach (var error in errors) sb.AppendLine(error.Message); throw new InvalidOperationException(sb.ToString()); } var i = 0; foreach (var batch in sqlFragment.Batches) { Console.WriteLine("Batch: {0}, Statement(s): {1}", ++i, batch.Statements.Count); foreach (var statement in batch.Statements) { processStatement(statement); } Console.WriteLine(); } Console.WriteLine("\nPress a key..."); Console.Read(); } private static void processStatement(TSqlStatement statement) { var createProcedureStatement = statement as CreateProcedureStatement; if (createProcedureStatement != null) { var statementList = createProcedureStatement.StatementList; foreach (var procedureStatement in statementList.Statements) { processStatement(procedureStatement); } } var selectStatement = statement as SelectStatement; if (selectStatement != null) { var query = selectStatement.QueryExpression; var selectElements = ((QuerySpecification)query).SelectElements; foreach (var selectElement in selectElements) { var expression = selectElement as SelectStarExpression; if (expression == null) continue; Console.WriteLine( "`Select *` detected @StartOffset:{0}, Line:{1}, T-SQL: {2}", expression.StartOffset, expression.StartLine, statementToString(selectStatement)); } } } private static string statementToString(TSqlFragment selectStatement) { var text = new StringBuilder(); for (var i = selectStatement.FirstTokenIndex; i <= selectStatement.LastTokenIndex; i++) { text.Append(selectStatement.ScriptTokenStream[i].Text); } return text.ToString(); } } }
توضیحات:
پس از نصب SQLDom.msi، ارجاعی را به اسمبلی زیر اضافه نمائید تا بتوانید کد فوق را کامپایل کنید:
Program Files\Microsoft SQL Server\110\SDK\Assemblies\Microsoft.SqlServer.TransactSql.ScriptDom.dll
کار با ایجاد وهلهای از TSql110Parser شروع میشود. متد Parse آن، آرگومانی از نوع TextReader را قبول میکند. برای مثال با استفاده از StringReader میتوان محتوای یک متغیر رشتهای را به آن ارسال کرد و یا توسط StreamReader یک فایل sql را.
پس از فراخوانی متد Parse، بهتر است بررسی شود که آیا عبارت T-SQL دریافتی معتبر بوده است یا خیر. اینکار را توسط لیستی از ParseErrorهای دریافتی میتوان انجام داد.
خروجی متد Parse، حاوی یک سری Batch آنالیز شده است. هر عبارت Go در اینجا یک Batch را تشکیل میدهد. سپس در داخل هر batch به دنبال batch.Statements خواهیم گشت تا بتوان به عبارات T-SQL آنها دسترسی یافت.
در ادامه کار اصلی توسط متد processStatement صورت میگیرد. عبارات دریافتی، در حالت کلی از نوع TSqlStatement هستند اما در اصل میتوانند یکی از مشتقات آن نیز باشند. در اینجا فقط دو مورد CreateProcedureStatement و SelectStatement بررسی شدهاند (مطابق رشته tSql ابتدای مثال). هر دو عبارت، از کلاس TSqlStatement مشتق شدهاند.
در متد processStatement عبارات select معمولی و همچنین آنهایی که داخل رویههای ذخیره شده تعریف شدهاند، استخراج شده و در نهایت بررسی میشوند که آیا از نوع SelectStarExpression هستند یا خیر (همان * select صورت مساله).
خروجی مثال فوق به شرح زیر است:
Batch: 1, Statement(s): 1 `Select *` detected @StartOffset:140, Line:5, T-SQL: SELECT * FROM dbo.tbl1 Batch: 2, Statement(s): 1 `Select *` detected @StartOffset:368, Line:12, T-SQL: Select * from @x Batch: 3, Statement(s): 1 `Select *` detected @StartOffset:659, Line:22, T-SQL: WITH ctex AS ( SELECT * FROM sys.objects ) SELECT * FROM ctex Batch: 4, Statement(s): 2 `Select *` detected @StartOffset:753, Line:26, T-SQL: select * from tbl1; `Select *` detected @StartOffset:791, Line:27, T-SQL: select * from dbo.tbl2;
اشتراکها
دلایل ارتقاء به SQL Server 2017
Following is a chart that shows licensing cost comparison between standard and Enterprise Edition of SQL Server 2012, 2016 and 2017.
Version | Edition | License Cost | 2 Quad core Processors | 4 Quad core Processors |
---|---|---|---|---|
Per Core | 8 Cores | 16 Cores | ||
SQL Server 2012 | Standard | $1,793 | $14,344 | $28,688 |
SQL Server 2012 | Enterprise | $6,874 | $54,992 | $109,984 |
SQL Server 2016\ 2017 | Standard | $1,858 | $14,864 | $29,728 |
SQL Server 2016 \2017 | Enterprise | $7,128 | $57,024 | $114,048 |