Reviewing The New DACPAC Code Analysis Rules For T-SQL

Reviewing The New DACPAC Code Analysis Rules For T-SQL



You can read the announcement post here.

Video Summary

In this video, I dive into a detailed review of the rules in the SQL query stress tool maintained by Erik EJ, a dear friend and colleague. While I’m not as familiar with non-SQL code, I managed to navigate through some of the C#-related rules and offer my insights. We cover a wide range of topics, from ensuring proper primary keys and natural keys to avoiding unnecessary `SELECT *` queries and using explicit column lists in `INSERT` statements. Along the way, we discuss best practices like wrapping transactions, using `TRY-CATCH` blocks for error handling, and avoiding cursors unless absolutely necessary. I also touch on some less common rules such as the use of `NOLOCK`, which can be risky in a production environment, and the avoidance of certain functions in the `WHERE` clause that can hinder query optimization. By exploring these rules, we aim to improve our coding practices and ensure more efficient and reliable SQL queries.

Full Transcript

Erik Darling here with Darling Data. You have to forgive me. I’m very excited about this because it’s not often that I get to do these kind of reviews, so you’ll have to pardon my giddiness at this sort of thing. Anyway, he’s not a dear friend. There goes my hand. But I do hope someday I can call Erik EJ my dear friend. You’ll probably hear me talk about him on this channel in the context of the SQL query stress tool that, you know, Adam Mechanic originally made and that Erik now, not this Erik, this Erik up there, the smart-looking one up there in the corner, now maintains. But you posed a really interesting thing earlier, and it’s a project where when you build a DAC pack, there’s this code analysis that will run on there, and I was checking out some of the rules for it. If you follow this link, you’ll eventually get over to this link. And there’s some very interesting stuff in there.

I’m actually going to shrink myself for this video a little bit to make sure it stays, make sure that we can see as much of the screen as possible. So there I go, down into the corner. I’m going to be much smaller than usual in this one, which for some of you just might be a relief. Might be a welcome relief to have a much smaller Erik in this video. I wish I had a balloon. I would have taken a little hit of helium when I shrunk myself. But we’re going to talk through some of the rules. I’m going to talk through some of my thoughts on some of the rules.

And hopefully we’ll all end up in a better place. So let’s start off at the top there. Missing natural key. So I should probably say at the beginning of this that I don’t read non-SQL code too well.

But if you’re the type of person who does, and you can read, I believe it’s all C-sharp stuff, since it ends in .cs, you can read through what a lot of these rules do. I’m going to sort of stumble blindly through just some of the descriptions. And if I’m wrong about anything, you can just ignore me. I promise. I’m okay with being wrong when it comes to this stuff.

Because, you know, you’ve got to be wrong sometime in your life. So first one, missing natural key. I’m not really sure how you could add a natural key to a table. You may have intentionally denormalized a table. You may be using entity attribute value tables for things.

I’m not really sure how the detection works on that. So we’re going to move on. The next one up is table missing primary key. You know, most OLTP systems, you want that. If it’s a data warehouse, who cares?

If you’re the type of person who has primary keys and foreign keys and unique constraints and stuff in a data warehouse, may we never meet would be unfriendly. Avoid wide primary keys. Yeah, I’m cool. I’m generally cool with that.

Primary keys should avoid using GUIDs or wide VARCAR columns. Sure. Yeah, yeah. It’s not a very good choice, admittedly. The one thing I’ll say in favor of GUIDs, though, is it’s really hard to run out of them.

It’s very easy to run out of integers. I’ve seen that a lot of times. These days, I think, if you’re just not sure, make your primary key. Make that identity column a big int. You’ll have a hard time going wrong there.

If I hear anyone from the peanut gallery talk about storage size and how big ints are 8 bytes and ints are 4 bytes, you can bite me. It’s a wrong thing to concentrate on. Maybe if you were on crappy old spinning disks on a 32-bit system, then…

Well, I mean, you wouldn’t have big ints on a 32-bit system, would you? Anyway, keep going. Index on foreign key columns on both sides of a foreign key should be indexed.

Hell yeah, they should. Avoid long car types. Yeah, absolutely. That’s a dumb thing to do. I’d say that most of the time, you should avoid car and car types anyway.

There’s nothing wrong with bar car and bar car anyway. Let’s see. Avoid using select star. So, there are a couple places where this won’t matter.

And I’m not sure how… Again, me not being able to read. Me no read code good.

I’m not sure how in-depth the rule checks are. But if you do select star, like, in an exists clause like this, or if you do select star in, like, a cross-apply or, like, a derived join or something, but then you name the columns outside of that, it’s not, like, using select star for real.

It’s, like, you know, it doesn’t matter for the exists, and for here you actually pick the columns at some point. So, this is one place if the rule doesn’t go deeper.

It might want to. I’m not… I’m terribly, terribly inexperienced writing these types of linters. I don’t know how hard that would be, but…

You know, something to consider anyway. Let’s see. Non-transactional body.

Wrap multiple action statements within a transaction. Sure, I would say… It would even go a step further. I think there should be a check to see if set exact abort is set to on in a stored procedure, because if you’re going to put multiple action statements into a transaction, you probably don’t want to leave any of those hanging if the transaction fails and not have all of them rolled back.

So, that’s probably something you want to think about there. Low identity seed value. Start identity columns used in a primary key with a seed of 1,000 or higher. I have no idea why that’s a thing.

To be very honest, you have no clue why that’s a check. Equality compare with null rule. Equality and inequality comparisons involving a null constant found.

Use is null or is not null. You’re going to have a tough time if you use an equality predicate on null. You’re going to have a bad day.

Let’s see what else we got here. Unused variable. Variable declared but never referenced or assigned. To be honest with you, I’m relieved every time I see a variable get declared and then never used. Usually when that happens, they get used in a where clause.

And I think that should actually be a check. If you’re using local variables in a where clause, that should be in the performance check there. Let’s see.

Unexpected or expected error handling. Wrap multiple action statements within a try catch. Hell yeah, I’m on board with that. Top without order by. Top without order by, you guarantee nothing. Unless you wrote your query very, very carefully to only guarantee one row can ever match for something.

Yeah, you’re going to have a bad time if you write top without an order by. I’ll even go a step further and say if you write a top with an order by, but that column in the order by is not unique, you could also have a hell of a time, especially if your query goes parallel. Let’s see.

What did we get here? Implicit column list. Unused. Always use a column list with insert statements. Hell yes, you should. Don’t be lazy. Unused input parameter. I don’t think I care too much about that, but you know, clean up after yourself, I guess. Avoid deletes without where rule.

And then we have an unbounded update. So kind of two ways of saying the same thing. A delete statement without role limiting conditions and update statement without role limiting conditions. So again, I can’t read the code.

I’m not that smart. But there are a couple ways that you can write delete and update queries that do bound things that might look unbounded if you just focus on what’s underneath the delete or what’s underneath the update. So these would certainly bound any update or delete happening, but it might just seem unbounded if it’s just looking specifically for a where clause.

Redgate SQL prompt does this too, where like if you don’t have an explicit where clause on a modification query, it’ll freak out at you. You might have a join. You might, you know, have whatever going on there.

With the Redgate SQL prompt, you can actually fool it because you can actually put where one equals one. And just because it sees the word where after an update or delete or something, it’ll be like, oh, cool. You got, you got it.

Good job. You can just put some condition that always evaluates the true. And it’s like, oh, well, smart. Did it. You’re never going to delete everything in the table. You’re never going to update everything in the table because where one equals one, good enough for me.

Anyway, let’s get back to the list. Let’s get back to dark mode. SSMS is very bright, especially when I’m this little in the corner. So let’s see.

What else we got here? Avoid joining tables with views. I mean, I agree you should avoid it. Sometimes it is unavoidable. Sometimes it is inevitable. But, yeah, views are often quite deceptive in how bad and how deep.

How awful they are. Now, it’s not that, like, the view itself, right? Like, views as a construct, you can put totally fast queries in a view.

The problem is that people often hide really bad queries in views. So the worse the stuff you put in the view, the worse going into that view gets. And views often end up turning into sort of like a query results junk drawer where you just keep adding left joins and columns and more craziness to it.

And eventually life just spirals out of control. So, you know, avoid joining tables with views. I’m not going to say that’s wrong.

I’m just going to say that, you know, it’s really what lies beneath in the view that is bad rather than just the concept of a view itself. It also, it could be an indexed view, right?

Indexed views are usually pretty spiffy when you join to those. Let’s see. Incomplete or missing join predicate. So I guess you could figure this out if you were looking at, like, foreign keys and they’re like a multi-column foreign key and you’re only joining on one column from it or something.

But I’m really not sure how you would figure out that part. Consider using exists instead of in rule. So consider using exists instead of in when used with the subquery.

All on board with that. In and not in, I’m fine using that. If you have a list of, like, literal values, like, you know, like strings or numbers or something. But as soon as you have to go look at values in another table, absolutely.

Use exists and not exists instead. Let’s see. Avoid exec or execute. Use parameterize sp execute SQL instead. Hell yes, I am all on board with that.

Avoid order by with numbers. Yeah, that’s really annoying. Bush league laziness. You don’t, like, if you have an expression in your query that’s, like, long and complicated, the nice thing is, because of the laws of logical query processing, you can just put whatever you name that expression in the order by, it works.

You don’t have to repeat all that stuff. You don’t have to, like, copy and paste some big long math. Down in your order by, you just say, like, math equals big long math thing and then order by math. And then you’re fine.

Let’s see. Unspecified type length. Do not use string types without specifying a length. Hell yes, you shouldn’t do that. It’s dumb. Unspecified precision or scale.

Do not use decimal or numeric data types without specifying. Yeah. If you don’t do that, you might as well just use float. Lazy son of a gun. Consider column prefix rule.

Consider prefixing column names with table. Yes. Yes. Make your query readable. Make sure people know where things are coming from. The least you can do is basic decency. Avoid use of hints.

I’m big on hints, so I’m not going to touch that one. Sometimes you know better than the optimizer. Avoid using car index. Avoid using car index function in the where clause.

So this one’s a little weird because I know from reading further down that there’s a general check for functions in the where clause, which I assume would cover stuff like is null, substring, replace, left, right, car index, pad index, any of that stuff.

So it’s a little weird that there’s like a solo rule for that up here because car index is not the only thing that’s crappy in a where clause. Also, when we’re looking at stuff like this, it’s not just the where clause.

If you use functions in a join clause or if you use functions in an order by, you can also be in for a really bad time because you screw up SQL Server’s ability to use indexes in a nice orderly fashion.

It’s not like SQL Server can’t use indexes. It just can’t use them as well as it can when you write things cleanly in a way that doesn’t, you know, is null, replace, replace all the letter M with the number 52 and order by that or something. Ridiculous.

What else do we have here? All right. Avoid use of or in a where clause. So if you use or on a single column, like in the context of the Stack Overflow database, if you were to say where post type ID equals one or post type ID equals two, that’s not going to hurt you too much.

I mean, you’re going to be way better off if that is indexed in a way that makes finding ones and twos helpful. Where things get really screwy is when you spread that across columns, especially across columns and different tables.

That can certainly get messy, especially when you start involving things like joins, because if you have a left join and you’re like where like inner table column equals this or outer table column equals something else, you end up with these late filters in your query plan that get really awful.

I would say a better check would be to avoid the use of or in a join clause because if you do like join, like I’ve talked about this a million times on my blog and videos, but if you do like join, like users to post on like, you know, this equals this or this equals that, joins with or clauses really screw things up like 99% of the time or something.

I made that number up, but it’s close enough for me. Let’s see. Avoid cursors.

Well, sometimes you need a cursor, right? Sometimes, sometimes you need a cursor, especially like if you have a store procedure that like dumps a bunch of like stuff into a temp table and then cursors over that temp table to call another store procedure.

There’s not like a better way to do that, right? If you place that with a while loop, it’s not like you buy yourself anything. So, um, I like it as general advice, but you know, sometimes, sometimes you gotta use that cursor. Alright, uh, use of nolok.

Do not use the nolok clause. Hell yes. Don’t do it. It’s bad for you. Uh, especially because if, if you’re examining some code that’s going to end up in a DAC pack, it’s production code.

If you’re using nolokins in production code, you’re not writing production code. You’re not writing production quality code. Uh, let’s see here. Force delay. Do not use wait for delay statement and store procedures functions and triggers. Hmm.

If you have a store procedure with, I mean, the, first off, the good news is you can’t use wait for in a function because that’s a side affecting thing. It’s like you can’t use like, like new ID or new sequential ID or some of the other, uh, built-in stuff for that. Um, so that’s, that’s at least one thing.

You can probably remove the check for functions. But, um, if I have a, I mean, triggers, it’d be kind of stupid to use it, but, you know. But, uh, store procedure, sometimes you put a loop in a store procedure. You should have a wait for in there so you’re not just zooming through things.

Uh, let’s see. What else do we have here? Um, do not use set row count yet. It’s deprecated. It’s kind of silly. Alias tables rule.

Consider alias and tables. Hell yes. Make your queries readable and understandable. Uh, object schema not qualified. Yeah. If you’re not qualifying object schema, you’re a bad person. Uh, avoid select into of temp table or table variables.

Well, I disagree with this one. Um, A, you can, well, I mean, A, you can’t select into a table variable. That’s, this is not allowed. You can’t do it.

Um, but for temp tables, I think it’s a very useful trick sometimes. Um, especially because, uh, with select into like 2016 plus you get nice fully parallel insert plan. It’s usually a good thing.

Um, I think I understand where this check is coming from though, because like if, you know, uh, the way that the query engine might infer some of the data types with select into might not be exactly what you would expect. So like it’s not always, like sometimes that happens, but for the most part, select into is pretty safe. Uh, possible side effects of implicit CAS.

The arguments of the function zero are not of the same data type. I, I don’t, I don’t understand that one. To be honest with you. Uh, raise error.

Uh, I, I don’t think I care much about that. Uh, excessive indexes on a table. Well, for me, indexes are all about quality over quantity. You might have what looks like excessive indexes, but all of those indexes are really useful to a lot of queries.

So, uh, I try not to, I try not to call indexes excessive unless I know a lot about how those indexes are used. Um, let’s see. Use of approximate data type.

Do not use rail or float. Yeah, pretty much. Uh, ambiguous column name. Avoid using columns that match other columns by name, but are different data type or size. That’s a really good one.

Uh, I remember a long time ago, I think I wrote a script or two to find those sort of things in a database. So I’m glad that this is a rule here because especially if you need to compare those columns to each other, then like, you know, you have two string columns.

One’s a varchar, one’s an nvarchar. Bad performance time. Uh, let’s see. Um, uh, expression reducible to constant. Uh, I don’t get it. So we’re going to skip that one.

Uh, do not use deprecated types rule. Yeah, you shouldn’t use text and text or image types. That goes for Microsoft too. Uh, duplicate overlapping indexes. I actually want to read the code for that one at some point because I’m, I would, I would like to see how they do it.

I would like to see if that’s easier in C sharp than it is in T SQL because it’s a real pain in the butt with that. Uh, explicit collation. Eh, whatever.

Uh, object level option override. I don’t know what that one is. Unsafe identity retrieval. Yeah, yeah. Don’t use added identity. Especially if you have multiple rows going into something. Uh, do not mix DML and DDL.

That’s, that’s actually an interesting one. Um, it’s not like it’s going to cause you any problems, but I’m all on board with this because I like when things are nice and organized. I would even go a step further and say like, you know, you shouldn’t be declaring stuff like halfway or most of the way through a store procedure.

You should be declaring as much as you can. Well, you should declare everything up at the top where you set it. It’s up to you.

It was between you and your maker. Um, you know, I guess for stuff like cursors, that’s a little different because you don’t want to like, you know, start opening and declaring and opening cursors up here. If you’re actually never going to use them, you might actually never actually get to a point in the code where those happen. Uh, let’s see.

Um, ordinary. Ordinal parameters used. Uh, always use parameter names. Hmm. So that’s interesting because there’s actually, um, a known performance increase, uh, when you use ordinal, uh, or positional parameters with, with, um, uh, natively compiled code. So, you know, not that anyone uses that anyway.

Uh, permission change in store procedure. I don’t, I don’t, I don’t care about that. Uh, invalid database options. I don’t know what that means.

Past because SQL Server is going to compile an execution plan for all of that, regardless of if, uh, your code path is actually explored. So, um, you get like cardinality estimation and query plans based on like a code path that might never have happened. And so you get like zero row or like one row expected and you get like this dinky little query plan.

But then you execute the store procedure and you actually hit that part and it’s like a lot of roads start jumping around. And yeah, it’s a bad time. Uh, let’s see here.

Uh, consider caching get date to variable. Uh, so if you need, uh, so there are two things with this. One, if you’re like updating a bunch of tables to like have a column be equal to like get date or assist date time, it makes more sense to cache it in a variable because then when you do the update, they’re all going to have the same, like you set the column to the variable.

They’re all going to have the same matching time rather than like the time of when the update happened. But if you’re going to put that in a join, oh, I guess, I mean, I’m going to say join or where clause because it could be either one. But, uh, if you’re going to put that in a join or where clause, you’re going to screw yourself because you’re going to get the local dense, local.

So you can get the density vector estimate and your queries performance probably going to stink and you’re going to stick a recompile hint on it or I don’t know, whatever. Uh, but yeah, so there are times when you should do that and times when you shouldn’t do that. Uh, avoid not for replication. I hate replication, so I avoid replication.

I am not for replication, even though I have two kids. Uh, let’s see. Nested views. Again, you know, views are only as bad as what you do with them, but nesting views, you know, you end up with multiple junk drawers and that’s not fun at all. Uh, unacred string pattern. Uh, try to avoid patterns and start with percent.

And yeah, that’s generally pretty crappy. Sometimes unavoidable, but you know, uh, aggregate of unique set. Avoid using distinct keyword inside aggregate functions. Well, sometimes, again, unavoidable.

Uh, I think the, you know, where things are interesting with that is because, like, if you’re doing that on, like, a single column in your query, uh, it’s generally not, like, that bad. Like, especially, I think I talked about this in a blog post recently. If you’re, like, saying select from users, left join post, and you want to count stuff from the post table, you’re going to want to, well, actually, no, it’s not distinct anyway.

Never mind. Uh, but, uh, sometimes you do need to count distinct things. Where things get tricky with SQL Server for queries that aren’t using batch mode, um, multiple distinct aggregates really make a crappy query plan.

Performance really eats it on that. Like, one, you can, one is usually tolerable, but, like, multiple distinct aggregates, things go out the window. Um, noisy trigger.

Well, I suppose you should put a silencer on that. Uh, so yeah, avoid returning results and set no count on your triggers. Yeah, absolutely. Uh, use of inequality.

Well, that’s not, that’s not quite right. Those, those are perfectly sargable things. If you, if you, if you want to figure out if something is not equal to something, I don’t know what else you’re going to do in a way that would be sargable. This is more about indexing than anything else.

So, um, make sure your indexes match your, match your where clause and things, things will go a lot better for you. Ah, this is probably my favorite one. Dangling cursor.

That’s what I’m going to be for Halloween this year. I’m going to be a dangling cursor. Uh, so yeah, close your cursors, deallocate your cursors. It’s generally, generally a good rule to follow.

Uh, filtering on a calculated value. Avoid wrapping columns within, so this is what I was talking about with car index. So this whole thing, avoid wrapping columns within a function in the where clause. Yes, avoid that.

Uh, let’s see. Function and data modification. Avoid the use of user-defined functions with update, insert, delete statements. So that one’s interesting because you can get around a lot of the Halloween protection issues with that if you use the schema binding hint with your, or the schema binding directive or option or whatever you want to call it. Uh, in your functions, you can get around, you get, you fix the Halloween protection stuff.

So, um, maybe the check looks for that. I don’t know. Uh, non-member test and predicate. Avoid using not in predicate in the where clause.

Well, it’s not exactly right either. Uh, not in is fine if you have a list of, like, literal values, like 1, 2, 3, 4, 5. But, uh, as soon as you go out and touch another table, you might come back with no’s.

It causes a lot of stuff. Uh, let’s see. Unindexed membership test. Consider indexing the column referenced by in predicates in order to avoid table scans. Oh, sure.

Index columns that are in your where clause. Index columns that are in your join. Seems like reasonable advice. I’m not sure why in gets special treatment.

Uh, let’s see here. Existence tested with join. Consider replacing outred. Yes. Absolutely. Do that. Table variable and join. Avoid the use of table variables and join clauses.

I’m on board with that, too. Uh, they tend to not be, not shine so bright when you do that stuff. You know, you know, we don’t want to get into the nitty-gritty of it, but I, on the, in general, I agree with that principle.

Avoid column calculations. Avoid the use of calculations on columns in the where clause. Absolutely.

That should also go for join clauses. Uh, quality test with mismatched types, data types. Yes. Make sure that your data types match. It’s a great thing. Uh, update of primary key.

Who in their right mind would do that? I mean, developers. So, you know, who knows what, who knows what they get up to when you’re not watching. So, I assume there’s a good reason for that check. Um, let’s see.

High join count. Well, yeah. I don’t know how many joins they consider high, but, uh, that, that, that, that, that can, that can cause issues. Uh, missing clustered index.

Hell yes. Yes. OLTP workloads generally should have clustered indexes. Manipulated parameter value. Yeah. Avoid modificational parameters prior to use. I mean, not just in select queries.

I mean, any, any query that you, you do that could be detrimental. Especially if, again, join. I mean, definitely where clause, though. Procedure level recompile option.

Consider using, yes, that’s actually good advice. Uh, query level recompile hints. Do a lot more than store procedure recompile hints. Enumerating for existence check.

When checking for existence uses, exists instead of count. Well, this one I disagree with a bit. See, when you use exists to check if something exists, uh, SQL Server internally sets a row goal. And when you set that row goal, you can end up with some really wacky query plans.

Or some really slow query plans. So I’m going to run three different queries here. Uh, the first one is the exist check, where you’re going to see row goals kick in and performance not be so great.

The second one is just one that I found amusing. And the third one is where, uh, select count big, uh, is actually, actually performs better than the exist check. So we’re going to look at these query plans now.

I didn’t set no count on. I should, I should be fined by the DACPAT committee. So, uh, when you do a select, so this is the, if, this is the, just the plain if exists. And we get this tiny little loopy little navigational plan here.

And, uh, we get this plan because SQL Server, uh, sets a row goal with these, um, these semi joins of one to just check if something is there or not. And depending on how the optimizer views its odds of finding something quickly, you can get these really, really sort of goofy query plans. And this part is particularly spectacular because we look through 37 million rows trying to find that one magical row, but we don’t actually end up finding anything.

So that drags on for a very long time. Uh, the thing with count is that if you just say we’re greater than zero, you end up with all the same execution plan, right? That’s no better, right?

You end up doing the same thing in here. Where count can be better is when you give SQL Server some loftier goals. So I said where count is between one and the integer max. I should have done the big int max, but, uh, cause I’m doing count big, but pay no attention to the little man in the corner.

But this query, and despite having a very strange query plan, this runs for 12 seconds. This runs for nine seconds. This query down here runs for three and a half seconds.

But because SQL Server is unable to set that, uh, that row goal, uh, in the same way, we get a nice query, uh, well, I mean, come on, get up. Uh, you know what? Screw it.

We’re just going to move that a little bit. SQL Server does some weird stuff with this query plan. I admit this is a bizarre looking query plan, but the performance of this is better. So, uh, I wouldn’t necessarily always want to do exists over count. Um, correlated subquery, avoid the use of correlated subqueries.

So this is sort of like the view thing. Uh, the correlated subquery is only as bad as what you put in it and is only as slow as the indexes you have to support it. So I’m not sure I totally agree with that check.

Uh, the ones down here, uh, UDF with system prefix procedure with SP. Well, I’m guilty of all of that. Uh, use of default schema. Um, I’m not sure, I’m not sure what that is.

And then general naming rules. I’m not sure what those are. So anyway, um, some good, a lot of good in here. Some stuff, I don’t know if it needs to be fleshed out more. I don’t know if there’s maybe some, I don’t know if maybe there is actually some stuff to do the additional stuff that I’m talking about and test first things a little bit further.

But, uh, I do think that this is a very good start to making, uh, setting developers on the right track. This just might need a little bit more detail and a few more levels of figuring out what’s going on before, um, before you flag these things as issues. Now, one thing I do like about this is it has a column called ignorable.

And a lot of this stuff is flagged as ignorable at times. So you don’t necessarily have to, um, punish yourself with, with all of these checks. Uh, especially if you, um, especially if you know that you’re doing okay with some of these things.

So, anyway, uh, a big thank you to, uh, Eric EJ and, um, I’m sorry, I, uh, there was a, I don’t know, actually, is it in here? Let’s see. Um, yes.

Well, actually, that’s just MS SQL build. Uh, so a big thank you to Eric EJ and to Jay Mazak. I apologize if I pronounce that incorrectly, uh, for putting this together because I do think it is a net positive for developers. But, um, you know, some of the rules, a little weird to me, but, uh, I don’t know.

Perhaps, perhaps, perhaps they’re better than they seem on the face of things. But, anyway, uh, thank you for watching. Uh, hope you enjoyed yourselves.

Hope you learned something. You liked the video? Give it a thumbs up. This is a longer one than usual. This is a half hour. Jeez Louise. Windbag I am. Uh, and if you like this sort of SQL Server content, please do subscribe to my channel. Um, that’s all.

All right. Cool. Thank you. Goodbye. I hit the button. Stop recording.

Going Further


If this is the kind of SQL Server stuff you love learning about, you’ll love my training. Blog readers get 25% off the Everything Bundle — over 100 hours of performance tuning content. Need hands-on help? I offer consulting engagements from targeted investigations to ongoing retainers. Want a quick sanity check before committing to a full engagement? Schedule a call — no commitment required.



5 thoughts on “Reviewing The New DACPAC Code Analysis Rules For T-SQL

  1. I appreciate the round-up of these. I had seen the plug-in before but I don’t think the “SQL Smells” were part of the checks before. I also appreciate that some can be ignored if needed when you’ve chosen an option that works well for your codebase but isn’t a best practice. (like using a cursor for that one task that just … needs a cursor to work well)

    I’ve got to do some playing with this now to see how it plays out with one of the code bases I support. I think the code analysis might cry a little, though. 🙂

    1. Yeah, I get it, but like… it’s really easy to hit the integer max these days. The pain of converting to big ints is significant. All the posts that tell you about doing it with page compression don’t go into the many caveats.

Comments are closed.