{"id":89706,"date":"2021-01-28T17:19:26","date_gmt":"2021-01-28T17:19:26","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=89706"},"modified":"2022-04-24T20:58:09","modified_gmt":"2022-04-24T20:58:09","slug":"dba-training-sql-server-hood","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/learn\/dba-training-sql-server-hood\/","title":{"rendered":"DBA in training: SQL Server under the hood"},"content":{"rendered":"<p><strong>The series so far:<\/strong><\/p>\n<ol>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/opinion\/career\/so-you-want-to-be-a-dba\/\">DBA in training: So, you want to be a DBA...<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/opinion\/career\/dba-in-training-preparing-for-interviews\/\">DBA in training: Preparing for interviews<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/opinion\/career\/dba-in-training-know-your-environments\/\">DBA in training: Know your environment(s)<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/database-administration\/dba-in-training-security\/\">DBA in training: Security<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/database-administration\/dba-in-training-backups-sla-and-restore-strategies\/\">DBA in training: Backups, SLAs, and restore strategies<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/database-administration\/dba-in-training-know-your-servers-limits\/\">DBA in training: Know your server\u2019s limits<\/a>\u00a0<\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/sql-training\/dba-training-sql-server-hood\/\">DBA in training: SQL Server under the hood<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/learn-sql-server\/dba-in-training-sql-server-high-availability-options\/\">DBA in training: SQL Server high availability options<\/a><\/li>\n<\/ol>\n\n<p>You are now more familiar with your environment, how to secure it, and how to keep it from breaking. It\u2019s time to begin delving into the weeds and learn more about SQL Server under the hood. (Well, sort of. You can spend a lifetime learning internals). This article will deal with basic internals, indexing, tuning, and sins against SQL Server, which should be avoided.<\/p>\n<h2>SQL Server \u2013 the house that Microsoft built<\/h2>\n<p>To get an idea of how SQL Server works, let\u2019s start at the bottom and work our way up. Here is a 10,000-mile view to help us get started:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-89707\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/01\/word-image-99.png\" alt=\"SQL Server under the hood architecture\" width=\"757\" height=\"509\" \/><\/p>\n<h2>The basic anatomy of a SQL Server instance<\/h2>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"786\" height=\"478\" class=\"wp-image-89708\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/01\/word-image-100.png\" \/><\/p>\n<p>The more rows of data that can fit on a page, the better performance will be for everything up the tree. This is the reason that you need to look at what kind of data types are making up your tables \u2013 even a small difference might not end up scaling. We\u2019ll go more into that point when we start talking about tables.<\/p>\n<h2>So, a query walks into the parser\u2026.<\/h2>\n<p>\u2026.and SQL Server will see if it parses or not. If it does parse, it is sent off to the Query Optimizer. The Query Optimizer sees if it already has a plan that will work for the query. If it does, it uses it. If not, it will look through the options until it finds a good enough plan (not necessarily the <em>best<\/em> one!). To do that, it looks through table structures and indexes; it combs through statistics to figure out what (if any) indexes can be used, and if they can, will it be a quick seek or a row-by-agonizing-row proposition. It coordinates with the Query Execution layer. Meanwhile, waaaaay down the stack, the Engine is reading pages into memory (if they aren\u2019t already there), locking tables or rows of data (if needed), and generally carrying out the nuts and bolts that make the query happen. The protocol layer takes the results and translates them back into a language that the end-user can understand.<\/p>\n<p>This, of course, is the 10,000 mile-high view of it, but you get the idea.<\/p>\n<h2>The DMVs and system objects<\/h2>\n<p>There are many built-in ways to find out what\u2019s going on under-the-hood: DMVs (dynamic management views) and other system objects.<\/p>\n<h3>Dynamic Management Objects<\/h3>\n<p>The Dynamic Management Objects are a set of views and functions supplied by Microsoft to help you look inside SQL Server. They need a bit of manipulation to present the data in an immediately useable way. Glenn Berry has done the work for you. Unless you just love a challenge, don\u2019t reinvent the wheel. You can find his diagnostic queries <a href=\"https:\/\/glennsqlperformance.com\/2020\/02\/04\/sql-server-diagnostic-information-queries-for-february-2020\/\">here<\/a>, or if you like PowerShell, <a href=\"https:\/\/dbatools.io\/\">DBATools<\/a> will run his queries and even export them to Excel for you. They are the source of invaluable information on your servers, and I have used them countless times when trying to troubleshoot issues or to baseline metrics.<\/p>\n<h3>INFORMATION_SCHEMA views<\/h3>\n<p>Microsoft also gives you something called the <a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/system-information-schema-views\/system-information-schema-views-transact-sql?view=sql-server-ver15\">INFORMATION_SCHEMA<\/a> views. These are a fast and easy way to get the answer when you have questions such as what table has what column, or how to find <a href=\"https:\/\/thenonclutteredindex.com\/2019\/04\/11\/find-occurrences-of-a-string-anywhere-on-an-instance\/\">occurrences of a string<\/a> anywhere on a server instance. It can also give you information on column-level permissions, constraint column usage, and more.<\/p>\n<h3>System functions<\/h3>\n<p>SQL Server comes equipped with a set of <a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/t-sql\/functions\/system-functions-transact-sql?view=sql-server-ver15\">useful functions<\/a> that are good to know. Some examples include the invaluable <code>GETDATE()<\/code>, which returns the current date as a datetime:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"559\" height=\"337\" class=\"wp-image-89709\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/01\/word-image-101.png\" \/><\/p>\n<p>They may also include <code>@@ROWCOUNT<\/code>, which does what you would expect: it returns the number of rows affected by your last code statement. In an admittedly contrived example, say you work for AdventureWorks and want to liquidate some inventory. You\u2019re considering the possibility of having a sale in a particular area, but it won\u2019t make sense to do so for a population under 200 people. You could use <code>@@ROWCOUNT<\/code> in your decision logic:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"406\" height=\"493\" class=\"wp-image-89710\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/01\/word-image-102.png\" \/><\/p>\n<p>If you think your statement will return a huge number of rows (over two billion), <code>ROWCOUNT_BIG()<\/code> will be the function to use instead. I have used all of these system function examples more often than I can count. You likely will as well, or you will find others more useful to you.<\/p>\n<p><em>Note: You may have noticed the system functions are not consistently written: some of them are prefaced with \u201c@@\u201d, and some are appended with \u201c()\u201d, so be careful to check which is which when you are using them.<\/em><\/p>\n<h3>System stored procedures<\/h3>\n<p>Like the system functions, the <a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/system-stored-procedures\/system-stored-procedures-transact-sql?view=sql-server-ver15\">system stored procedures<\/a> are Microsoft\u2019s internal stored procedures. There are a metric ton of them \u2013 so many that Microsoft lists them by category rather than listing them separately. In SSMS, you can find them by going to the Programmability tab, then expanding the Stored Procedures tab, then expanding the System Stored Procedures tab. Most are for internal processes and shouldn\u2019t be used unless you know what you are doing. Some examples that you can use include <code>sp_helpdb<\/code>, which will give you basic information about all your databases:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1055\" height=\"513\" class=\"wp-image-89711\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/01\/image-png.png\" alt=\"image.png\" \/><\/p>\n<p>Or, just more detailed information on one database in particular:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1066\" height=\"524\" class=\"wp-image-89712\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/01\/image-png-1.png\" alt=\"image.png\" \/><\/p>\n<p>Another example would be <code>sp_helpserver<\/code>, which will give you info on your server or linked servers on your instance. If you are a new DBA, stick to the <code>sp_help<\/code> procedures for now.<\/p>\n<h2>Databases in SQL Server<\/h2>\n<p>So, we\u2019ve taken the server-level, 10,000 mile-high view of SQL Server. Let\u2019s begin talking about databases.<\/p>\n<p>There are two types of databases in SQL Server: system databases and user databases.<\/p>\n<p>There are five system databases that you will typically see in a SQL Server instance:<\/p>\n<ol>\n<li>master \u2013 this is where you\u2019ll find all the basic information of your SQL Server instance, including the logins, databases, settings \u2013 everything. SQL Server doesn\u2019t start without this database, so it is paramount that this (as well as all of the other system databases except resource and tempdb) are regularly backed up.<\/li>\n<li>model \u2013 this is, yes, the \u201cmodel\u201d for what all your other databases will look like. Whatever you do here happens to any new database made afterward.<\/li>\n<li>msdb \u2013 this is where all the SQL Agent job information is located. It\u2019s also the home of database mail and service broker and the backup history. Jeremiah Peschka suggests <a href=\"https:\/\/facility9.com\/2009\/10\/an-introduction-to-sql-server-system-databases\/\">limiting the amount of backup history you keep<\/a>, but be very careful with this. Know what the business expectations are for backup history retention, and never assume that is your decision to make \u2013 it isn\u2019t.<\/li>\n<li>tempdb \u2013 this is SQL Server\u2019s scratchpad, where temp tables are made and discarded, where versioning is used for some of the isolation levels, and where query processing goes to do its work (and to spill data from badly performing queries). It is remade every time SQL Server reboots. You want to give tempdb plenty of room to work, and of course, it is the only database (along with the Resource database) you do not back up.<\/li>\n<li>Resource \u2013 this database will be invisible to you unless you know some special queries to look at it. This is where the system objects are kept. It is best to let it do its job and leave it alone.<\/li>\n<li>Distribution \u2013 you will see this special system database on the distribution server instance if you use replication.<\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n<p>User databases are just what they sound like \u2013 databases that were created by SQL Server users.<\/p>\n<h2>Filegroups<\/h2>\n<p>Data files are grouped into filegroups on databases. This allows for better organization and efficiency. There is always one filegroup \u2013 <code>PRIMARY<\/code> \u2013 that houses the main data file, or .mdf. But you can have as many as are needed. Jess Borland does a great job of discussing filegroups <a href=\"http:\/\/blogs.lessthandot.com\/index.php\/datamgmt\/dbadmin\/sql-server-filegroups-the-what\/\">here<\/a>.<\/p>\n<p>Why would you have more than one filegroup? Aside from the obvious organizational benefits, if you have multiple filegroups, you can leverage piecemeal or partial restores in an emergency. Paul Randal goes into this in his \u201cAdvanced Corruption Recovery Techniques\u201d course on Pluralsight. It\u2019s highly recommended that you listen to his course \u201cSQL Server: Detecting and Correcting Database Corruption\u201d first.<\/p>\n<h2>Tables, data types, and referential integrity<\/h2>\n<p>Just like there are system and user databases, there are system and user tables.<\/p>\n<ul>\n<li><strong>System tables \u2013<\/strong> here\u2019s an <a href=\"https:\/\/thomaslarock.com\/sql-server-system-tables\/\">article from Tom LaRock<\/a> explaining them.<\/li>\n<li><strong>User tables \u2013<\/strong> just what they sound like! These are tables created by users.<\/li>\n<\/ul>\n<p>When you query the content of a table in SSMS, the output looks a great deal like an Excel spreadsheet. One of the many advantages that SQL Server has over Excel lies in its ability to allow its tables to \u201ctalk\u201d to each other. Millions of rows and huge numbers of columns can be joined together from tables to give the end-user the data they need (although again, this is not a goal!).<\/p>\n<p>Why is this not a goal? Because as a DBA, one of your goals is to get the right data to the right user at the right time, <em>as quickly as possible<\/em>. To accomplish the \u201cas quickly as possible\u201d piece, it\u2019s important to use the right architecture from the beginning.<\/p>\n<p>Kimberly Tripp of SQL Skills illustrates the point of using the smallest data type that can reasonably be used to accomplish the objective <a href=\"https:\/\/app.pluralsight.com\/library\/courses\/sqlserver-why-physical-db-design-matters\/table-of-contents\">here<\/a>. A small difference in bytes saved can equal GB saved at scale. Moreover, there can be huge savings in IO, CPU, and other metrics. I highly recommend this course, which can save a great deal of pain and suffering later.<\/p>\n<p>So now that you realize how important they are, a review of <a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/t-sql\/data-types\/data-types-transact-sql\">data types<\/a> is in order. When new tables come through development, it\u2019s an opportunity for you to help ensure efficiency from the start, by looking at the data types and offering suggestions for more efficient ones that should accomplish the same business purpose.<\/p>\n<p>With very few exceptions, your tables should have a <em>primary key<\/em> \u2013 one or more fields that guarantees a record (row) is unique. Any field that you choose for the primary key must have data in it (in other words, it cannot be NULL). You are only allowed one primary key per table. Whatever primary key or clustering key you choose, it should be <a href=\"https:\/\/www.sqlskills.com\/blogs\/kimberly\/ever-increasing-clustering-key-the-clustered-index-debate-again\/\">unique, narrow, static, and ever-increasing<\/a>. This ensures that new rows have the best chance of being inserted at the end of the data page, rather than at different points in it. This is important (and we\u2019ll discuss why soon).<\/p>\n<p>When you create the primary key, you create a primary key constraint by default. A constraint is nothing more than a rule in SQL Server. This constraint ensures that every row is unique \u2013 preventing duplicate data. The primary key in one table can be used to link to another table. When that happens, it\u2019s called a <em>foreign key<\/em>.<\/p>\n<p>SQL Server will try to ensure that the data in related tables is correct (with your help). For instance, let\u2019s say you have a database to track bicycle orders (if this sounds suspiciously like AdventureWorks, well, it\u2019s not paranoid if it\u2019s true). In that database, you have tables for your <code>CustomerID<\/code>, <code>CustomerName<\/code>, <code>CustomerAddress<\/code>, and <code>OrderIDs<\/code>. If you have an order for a bicycle, you can make the relationship between the customer number, name and shipping information, and order number. So now, for every order number, there must be a customer number, a name, and shipping information. That relationship is called <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/aa292166(v=vs.71).aspx\"><em>referential integrity<\/em><\/a><em>.<\/em> It is usually enforced by constraints but can also use things like triggers or defaults (although constraints are preferred). Triggers can have performance overhead and can be tricky to maintain. For those reasons (among others), I don\u2019t recommend using them as a first course of action.<\/p>\n<h2>Indexing<\/h2>\n<p>So far, we\u2019ve taken SQL Server down to the bare bones and started building back up again. We have tables with the right data types to fit the most data possible on to our 8k data pages. The right relationships between the tables are established. That ensures that when the tables are queried, we have fewer pages to spin up into memory for processing, and performance will be great, right?<\/p>\n<p>Not yet. For one thing, we haven\u2019t indexed our tables.<\/p>\n<p>What are indexes in SQL Server?<\/p>\n<p>Indexes are just what you think they would be \u2013 sort of. SQL Server has two basic types of indexes: the <em>clustered index<\/em> and <em>nonclustered indexes<\/em>.<\/p>\n<p>The clustered index <em>is<\/em> the data: it\u2019s the order the data is physically sorted in. It is roughly analogous to the table of contents in a book. <em>It is not, however, necessarily the same thing as the primary key<\/em>. When you create a primary key in SQL Server, you create a unique clustered index by default. However, you <em>could<\/em> opt to create a unique nonclustered index to enforce the primary key constraint (not that I would recommend it in most cases).<\/p>\n<p>So, what is a nonclustered index, and how is it different? A nonclustered index is a copy of <em>some<\/em> of the table columns (with the clustering key tacked on under the covers). It doesn\u2019t have to be unique &#8211; and often is not. Why would you want that? Because frequently, when you query SQL Server, you don\u2019t want to look through every row of every column of the table in the clustered index \u2013 or even most of them &#8211; if you could find it faster by looking through a few rows in a few columns.<\/p>\n<p>Because nonclustered indexes are, in effect, copies of the tables, they need to be maintained, and they\u2019ll grow as the tables grow. So, you don\u2019t want too many of these per table. It\u2019s important to create the nonclustered indexes that you know will be used by most of your queries (which is the art, versus the science, of indexing).<\/p>\n<p>Underneath clustered and nonclustered indexes there are other possibilities that you will probably see as your career progresses:<\/p>\n<ol>\n<li>Unique clustered index (the default clustered index)<\/li>\n<li>Non-unique clustered index<\/li>\n<li>Unique nonclustered index<\/li>\n<li>Non-unique nonclustered index (the default nonclustered index)<\/li>\n<li>Unique filtered indexes<\/li>\n<li>Non-unique filtered indexes<\/li>\n<\/ol>\n<p>Finally, there is a thing called a HEAP. Heaps are tables without clustered indexes. When you are inserting records into a heap, it will be blazing fast. This is because SQL Server won\u2019t try to put the records in any sort of order. First come, first served! That seems great until you try querying a table sitting on a heap. Then, prepare to sit and watch the grass grow while SQL Server frantically tries to retrieve the records you asked for. Unless you\u2019re doing ETL work, you want your table to have a clustered index.<\/p>\n<h2>Index maintenance<\/h2>\n<p>Since you\u2019re a good DBA and your tables are well-indexed, do you know how SQL Server figures out which indexes to use? That is where statistics come in! Statistics are just units of measure that keep track of things such as data distribution in tables and how often indexes are accessed. In this way, SQL Server can look at a table named dbo.Employee and know things such as there are 11 rows of data for employees whose last name falls between Ba-Bl, and that there is a nonclustered index on that table that is used 15% of the time, but there is another index that could be there that would constitute a 93% improvement. SQL Server uses these numbers to determine its execution plans, to make indexing recommendations, etc. These recommendations are not perfect, but they can be a starting place to point you in the right direction.<\/p>\n<p>On tables where data is being inserted, updated, or deleted, your indexes and statistics are going to go through some wear and tear. Index wear and tear is called <em>fragmentation<\/em>, and there are two types:<\/p>\n<ul>\n<li><strong>Logical fragmentation \u2013<\/strong> the physical data page no longer matches the index key order, due to the data page splits that happen during INSERT or UPDATE queries.<\/li>\n<li><strong>Physical fragmentation \u2013<\/strong> there is more free space on the data page than there should be, due to page splits (usually through DELETE queries, but also during INSERT or UPDATE operations).<\/li>\n<\/ul>\n<p>As your indexes grow (or shrink) and change, statistics can get out of date as well. All of these things can cause performance problems unless you have a process to keep it all current. There are several options out there to help, but the best way to keep your indexes and statistics tuned is to use <a href=\"https:\/\/ola.hallengren.com\/\">Ola Hallengren\u2019s Maintenance Solution<\/a>. His scripts will set up all the SQL Server Agent Jobs you need to maintain your indexes, statistics (and will even manage your backups and database integrity checks, if you like). They are free, well-vetted, and safe to use in production.<\/p>\n<h2>Database Objects<\/h2>\n<p>We have discussed a couple of types of database object already \u2013 tables and indexes. But there is much more to SQL Server than just those two things. Let\u2019s touch on some of the database objects you can expect to see and work with as a DBA.<\/p>\n<h3>Views<\/h3>\n<p>Say that you want to see two columns from Table A, three columns from Table B, a column from Table C, and four columns from Table D joined together, but you don\u2019t want to retype the code to make that happen every single time you need it. You don\u2019t need to! You can make a <a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/views\/views\"><em>view<\/em><\/a>. There are system views and user-made views.<\/p>\n<p>A view is just that \u2013 a virtualized table. They can be materialized, though, in special circumstances. Those kinds of views are called <a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/learn-sql-server\/sql-server-indexed-views-the-basics\/\"><em>indexed views<\/em><\/a><em>. <\/em>Indexed views do have some <a href=\"https:\/\/www.brentozar.com\/archive\/2013\/11\/what-you-can-and-cant-do-with-indexed-views\/\">limitations<\/a>, but are a valuable tool to have in your toolbelt. They are particularly useful for aggregations.<\/p>\n<h3>Synonyms<\/h3>\n<p>A synonym is nothing more than a nickname. Rather than saying, <code>SELECT TOP 10 * FROM SERVERINSTANCE.DatabaseName.dbo.ObjectName<\/code> over and over in your code, you could just use a synonym: <code>SELECT TOP 10 * FROM ObjectName<\/code>. When you create the code for the synonym, you \u201cpoint\u201d it, like this:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">CREATE SYNONYM dbo.ObjectName FOR SERVERINSTANCE.DatabaseName.dbo.ObjectName<\/pre>\n<p>Aside from saving you the tedium of typing a fully qualified name over and over, synonyms offer a significant advantage: if the location of the object ever changes, just repoint the synonym, and all the code still works. If you don\u2019t use synonyms, and a referring object location ever changes, better hope you found every occurrence in every piece of code ever, or something will break, and it will probably be a long time before you find it.<\/p>\n<h3>Stored procedures<\/h3>\n<p>A stored procedure (also commonly called a \u201csproc\u201d) is a script that is stored on SQL Server. As with the other database objects we have discussed, there are system stored procedures and user-made sprocs. Aside from saving you the time and labor of retyping code every time, stored procedures have other upsides:<\/p>\n<ul>\n<li>They are usually flexible because they use parameters and variables.<\/li>\n<li>They are compiled in SQL Server, so the execution plans can be reused (this <em>should<\/em> be a good thing, but sometimes is not).<\/li>\n<li>They have been tested, and it is generally recommended that end-users use a stored procedure over ad-hoc code for this reason as well as performance reasons (precompiled code, prevention of cache bloat, etc.).<\/li>\n<\/ul>\n<h3>Functions<\/h3>\n<p>Functions are routines that will take a parameter, perform some sort of action on it (usually a calculation), and return the result of that action. Other code will see the output, but not the code of the function itself, making it somewhat like a miniature program in and of itself. Like stored procedures, they are compiled at runtime, and SQL Server can remember that the execution plan is there (if you query the cached_plans DMV, you\u2019ll see an object type of \u201cProc\u201d, though, which can be a little misleading).<\/p>\n<p>Aside from the system functions and aggregate functions, you\u2019ll be primarily dealing with two types: table-valued functions and scalar-valued functions. A table-valued function returns a result set in the form of a table. It is executed once per session (usually making it more performant than a scalar-valued function). A scalar-valued function will execute once per row of data returned, making it costly to use. We will go into this in more detail in the Query Tuning Basics section.<\/p>\n<h3>Triggers<\/h3>\n<p><a href=\"II.http:\/\/www.sqlteam.com\/article\/an-introduction-to-triggers-part-i\">Triggers<\/a> are stored procedures that automatically fire when an event \u201ctriggers\u201d them. They are attached to tables and are not visible unless you go looking for them in SSMS. When triggers break or act in unanticipated ways, their very nature (the fact that they go off automatically when triggered and are largely invisible) makes them somewhat difficult to troubleshoot. They are typically used for auditing or to keep corresponding tables current, but they should be used sparingly and with caution.<\/p>\n<h3>Assemblies<\/h3>\n<p><a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/clr-integration\/assemblies-database-engine\">Assemblies<\/a> are objects that are compiled together and then deployed as a single unit in the form of a .dll file. They can be made of up stored procedures, functions, triggers, types, and\/or aggregates. They are created in .NET instead of using T-SQL. They can present a security risk, so it\u2019s important to know how the business feels about them before using them and how to do it safely.<\/p>\n<h3>Types<\/h3>\n<p><a href=\"https:\/\/www.mssqltips.com\/sqlservertip\/1628\/sql-server-user-defined-data-types-rules-and-defaults\/\">Types<\/a> in SQL Server can be system data types, or user-defined. Sometimes, users need to define custom data types or table types, and here is where SQL Server allows that to happen.<\/p>\n<h3>Rules<\/h3>\n<p>Rules are just what you\u2019d think they are. They are a type of constraint that forces data to comply with its conditions. They are a deprecated feature as of 2012.<\/p>\n<h3>Sequences<\/h3>\n<p>A <a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/learn-sql-server\/sql-server-sequence-basics\/\">sequence<\/a> is a schema-bound object that creates a list of numbers, beginning with a specified value, then increments and returns them, according to user specifications.<\/p>\n<h2>Agent Jobs: Getting the right data to the right people at the <em>right time<\/em><\/h2>\n<p>Database jobs take multiple actions that SQL Server performs automatically for you at a specific time that you schedule. They are run by the SQL Server Agent. Job steps can be run as T-SQL, from the command prompt, using SSIS, PowerShell, or many other options. I will cover some examples of jobs and practice creating some as well in a future article.<\/p>\n<h2>Query tuning basics: Getting the right data to the right user at the right time, a<em>s quickly as possible<\/em><\/h2>\n<p>By this point, you have a basic understanding of SQL Server and how it works (I know you are thinking, <em>basic!!??<\/em> The good news is that you\u2019ve chosen a field that will ensure that you are a lifelong learner. At least you will never be bored!) We\u2019ve learned how to build tables with the right data types, and how to use primary and foreign keys to effectively define, link, index, and protect our data. We have a basic idea of the types of indexes and why they are used, and how SQL Server uses statistics to keep track of the data and optimize query plans. We understand database objects and how they are used.<\/p>\n<p>You would think we\u2019d be all ready to go. But we\u2019ve only laid the foundation.<\/p>\n<p>Because now, we\u2019re letting people into our database to query it. And people write evil, awful queries sometimes.<\/p>\n<p>It\u2019s usually not their fault. SQL works on a different paradigm than many other programming and scripting languages. SQL works best when it is used for set-based operations \u2013 getting batches of data at once. That means that some of the basics developers learn like cursors, which go through every row of a table &#8211; or even a group of tables- to return data can bring a SQL Server pain and suffering. As a DBA, you are a resource to help developers get the right data to the right people <em>as quickly as possible<\/em>.<\/p>\n<p>It\u2019s not uncommon for a horribly performing query to come to your attention that is bringing the server to its knees. The developer swears that it ran fine in test. There could be many reasons this is happening, but we\u2019ll focus on six of the most common ones. These are known as <em>antipatterns<\/em>. Future articles will address more specific query tuning techniques.<\/p>\n<h3>The query is bringing it ALL back<\/h3>\n<p>The first example that comes to mind is the dreaded <code>SELECT * FROM<\/code>\u2026. query, but it can be any query that is bringing back more columns than it needs. <code>SELECT *<\/code> in particular is discouraged because it can be a performance hit. This query can also bring back unexpected results if the underlying table structure has changed as well.<\/p>\n<p>The problem with a query that \u201cbrings it all back\u201d is that it causes unnecessarily increased IO and memory. I worked on a case some time ago where a query was timing out. SQL Server on that instance had been restarted a couple of times due to the stress on the server, with no help in sight. When my colleague and I first went to work on the issue, the problem query was generating over 217 million logical reads per execution. Refining the query to bring back only the columns needed resulted in 344,277 reads. CPU went from 129,031 to 1.470. If the vendor had allowed us to index the table needed, we could have gotten the reads down to 71.<\/p>\n<p>Moral of the story: Make sure the query brings back only what it needs.<\/p>\n<h3>Nested Views<\/h3>\n<p>This is the case where you find views calling views calling whatever else until &#8211; finally -the tables are called. SQL Server will try to help figure out what you need, but there is a performance hit just on that effort, and the execution plan will be needlessly complex. It can (and probably will) also cause prolonged execution times, returning more data than you needed in the first place. Finally, they are a nightmare to troubleshoot, and when one of the underlying views break, they can return unexpected results.<\/p>\n<p>Some time ago, I had a developer come for help with an application that was using a linked server to query our stage environment when he was expecting it to hit dev. He couldn\u2019t figure out why it was happening. It turned out that the code was calling a nested view. Every synonym in every view (and there were a lot of them) had to be scripted to find the culprit. It also meant that his result set could have been inaccurate. I\u2019ve also seen nested views that went seven views deep, bringing back huge amounts of data, running the same risks. That is one example. They can be worse.<\/p>\n<p>Moral of the story: Let your views be viewed as views only. Don\u2019t use them as reference objects.<\/p>\n<h3>Use the right temporary structures<\/h3>\n<p>Usually, this means using a temp table, but not always. Temp tables have the advantage of honoring statistics, having the ability to be indexed, and are <em>generally<\/em> more performant. However, if you\u2019re working with SSIS, if you need your table to survive a transaction rollback, or if you\u2019re working with functions, table variables can be the only way to go. They have a major drawback \u2013 the optimizer will always assume that a table variable will return just one row (which may be great if that\u2019s all you expect, or horrible if it returns hundreds of rows). SQL Server 2019 does help with table variables; you will get the correct estimate on row return on the first execution. However, it will use that estimate for the next one, so you come up against a parameter sniffing issue. Its query optimizer is getting smart enough to \u201clearn\u201d, so over time, performance will improve some.<\/p>\n<p>However, in some cases, using temp tables or table variables can hurt rather than help. For instance, if you are only going to query the data you are compiling once, you don\u2019t need the cost of loading a temp table or a table variable. This is where a CTE can be your best friend, or a subquery with a <code>CROSS APPLY<\/code> might work better There are a lot of options at your disposal. Test and see what works best for your query.<\/p>\n<h3>Death by a Thousand Cuts \u2013 Non-SARGable Queries<\/h3>\n<p>Non-SARGable queries are those queries that require row-by-agonizing-row (RBAR) processing. I\u2019ll focus on scalar-valued functions here, but any functions in the WHERE clause (that are on the column side of an operator) will do this as well.<\/p>\n<p>Scalar valued functions are query killers because they execute row-by-row. Where at all possible, either rewrite these as table-valued functions or inline the logic. You might not easily see the damage to the query by looking at an execution plan, but a Profiler trace (make sure reads are included) will show you just how evil they can be.<\/p>\n<h3>Implicit Conversions \u2013 the Silent Killer<\/h3>\n<p>Implicit conversions happen when a query is comparing two values with different data types. This forces SQL Server to figure out what you\u2019re talking about. The conversion is based on <a href=\"https:\/\/technet.microsoft.com\/en-us\/library\/ms190309(v=sql.110).aspx\">data type precedence<\/a> and causes increased CPU, but the real performance hit comes from the forced index scans. In rare cases, implicit conversions can even give inaccurate or misleading query results. Worse yet, they aren\u2019t always obvious (even in the execution plans). The fix is to <code>CAST<\/code> or <code>CONVERT<\/code> one of the data types to match the other.<\/p>\n<h3>Parameter Sniffing<\/h3>\n<p>The first time SQL Server gets a stored procedure, it optimizes for the parameter it receives from the end-user. Maybe it\u2019s looking for the <code>EmployeeID<\/code> of anyone with the last name of Smith (not a real selective query). But the next time the sproc is used, it\u2019s now looking for the <code>EmployeeID<\/code> of anyone with the last name of Meshungahatti. But the optimizer sees it has a plan it has already stored for Smith, and it uses that. The problem is, now SQL Server is estimating that it will return thousands of rows instead of one, wasting resources, memory, and IO. This can also work in reverse: SQL Server can assume that only a few rows will be returned when maybe millions actually will be. There are a few options to fix parameter sniffing \u2013 click <a href=\"https:\/\/www.youtube.com\/watch?v=FuRPCjeuwec\">here<\/a> and <a href=\"https:\/\/www.brentozar.com\/archive\/2013\/06\/the-elephant-and-the-mouse-or-parameter-sniffing-in-sql-server\/\">here<\/a> for more info. Also, be aware of these:<\/p>\n<ol>\n<li>In SQL Server 2016, there is an option to turn parameter sniffing off per database (<code>ALTER DATABASE SCOPE to set PARAMETER_SNIFFING =OFF<\/code>) but it works a lot like <code>OPTIMIZE FOR UNKNOWN<\/code> under the covers. It can impact the performance of other queries, so be careful with this one.<\/li>\n<li>You can enable trace flag 4136 (SQL Server 2008 R2 CU2 and up) to disable parameter sniffing at the instance level \u2013 again, be careful with this.<\/li>\n<\/ol>\n<h2>SQL Server under the hood<\/h2>\n<p>SQL Server is a big product, and there is a lot to learn. This article covered how SQL Server is built and some of the anti-patterns to look for when query tuning.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this article of the series, Pamela Mooney explains the architecture of SQL Server under the hood, including some query anti-patterns to avoid.&hellip;<\/p>\n","protected":false},"author":316278,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143525],"tags":[5842],"coauthors":[97572],"class_list":["post-89706","post","type-post","status-publish","format-standard","hentry","category-learn","tag-sql-monitor"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/89706","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/316278"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=89706"}],"version-history":[{"count":5,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/89706\/revisions"}],"predecessor-version":[{"id":89717,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/89706\/revisions\/89717"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=89706"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=89706"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=89706"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=89706"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}