Techniques for Microsoft SQL database design and optimization
apriorit.com
apriorit.com
Ignore all the advice about "rules". Create a realistic dataset and run performance tests before you release anything into production. Check your query-plans to find the pain-points. The database is frequently treated black box surrounded by superstition and cargo-cult "rules" that are either out of date or just educated guesses.
Wrap anything weird in a view so you can rebuild it as you see fit.
Avoid getting fancy as much as possible until the simple, normalized, naive implementation fails.
Unfortunately query plan builders can do odd things. Is IN faster than EXISTS? Sometimes yes, sometimes no. The only way to know is to test, and this is true across the board.
The gist of it is, the query planner for some database engines ( pg and mssql ) often takes cardinality into account when selecting plans. Therefore those plans change with the size of the dataset.
EDIT: It occurs to me to wonder if you're asking if the resultset changed. No, I've not seen that answer change.
The underlying reason is that as data changes the plan changes. For example, on a small table, it is faster/more efficient to do a table scan than go to the index and then to the table for data. At some threshold this changes. If table size can change the plan, then the query itself could also need changing as the table grows, indexes grow, and differing index cardinality emerges.
This is so true.
Depending on your write patterns and your queries, a realistic dataset can mean:
1. Same distribution of keys as production
2. Same rate of writes as production
3. Same order of writes as production
4. Same volume of data at rest as production
( edited for formatting )
The best query plan can look good even on a representative data set but your customers don't always have representative data sets and they don't really care if it works on the bench with 10 million records if their 10 million record data set does not work.
Here are some I've used:
Inside Microsoft SQL Server 2008 T-SQL Querying: T-SQL Querying https://books.google.com/books?id=FZlCAwAAQBAJ&printsec=fron...
Good resource for designing high performing queries and investigating performance problems.
PostgreSQL 9.0: High Performance https://books.google.com/books?id=OWOAu0GcsqoC&printsec=fron...
Greg lays out a great foundation for doing performance analysis that is largely applicable to any rdbms platform.
Optimizing Oracle Performance https://books.google.com/books?id=D7ElCwAAQBAJ&printsec=fron...
Another great book that explains the process of approaching and solving performance problems.
Joe Celko's SQL For Smarties https://books.google.com/books?id=i-19BAAAQBAJ&printsec=fron...
Everyone writing SQL should read Joe.
1. SET STATISTICS IO ON. This is much more useful than setting time on imo. This will give you physical / logical reads per table as well as how specifically they were fetched. Usually I don't care if the query took 11 seconds, I want to know what took those 11 seconds.
2. The % numbers that everyone relies on when skimming execution plans are total BS. No one seems to realize this, but the percentage values are based off of the estimated query plan even when you're running the actual execution plan. Use the plan to determine which operators were chosen, disregard the % values. To get the true amount of work done, look at stats IO above. If your underlying issues is missing or stale stats for example, that incorrect data will pass through to your execution plan and that plan will lie to you.
3. Try to determine why a plan is getting generated. Don't just keep trying wacky code changes until you can get the correct plan (once..), find out what the optimizer is seeing and why it's doing what it's doing. You may know what operation is best in the current moment ("this was much faster as a nested loop") but instead of using a join hint, force the optimizer's hand by correcting whatever underlying issue is making it think that the merge/hash is a better route. This will save future-you hours of head-bashing when, inevitably, that join hint that was added three years ago causes a big production issue.
One of the most frustrating things coming from a world of say python or C is a function that implements an algorithm of say O(n) it will run in O(n) all-day-everyday but if a table's stats are out of date or there's a missing index or anything related goes awry a typically fine running query could perform terribly. And that's infuriating but it's the nature of the beast I guess.
But that beautiful kernel of relational theory is wrapped up in a six-foot-ball of duck-tape, bubble-gum, and hot glue in the form of a zillion hacks and leaky abstractions.
At a certain point it really feels like databases are failing to live up to their promises. I expect things like sharding to be hard. I'm okay with that. But if I have one database server, I expect to be able to create tables, maybe indexes, and for it to Just Work given that information. That's what we're paying for.
That is do not:
select a.bla, b.blabla from foo a left join bar b on (a.id = b.id)
Do:
select a.bla, (select blabla from bar where id = a.id) as blabla from foo a
I have reduced a big etl query with about 20 left joins from 2 hours running time down to 7 minutes by this.
And before you ask, yes the tables have been properly indexed, it is just that left join performance in sql server is very bad in some situations.
Think an application/case with a table with credit scoring variables where each variable has a case id and a variable type id, so i had to join the same table multiple times with different criteria. But replacing all of the left joins here with subselects did reduce the query time by an insane amount.
However I have seen this behaviour with unique indexed left joins too just not so extreme.
And for comparison (i have the same schema in my analytics postgresql database), the exact same query here takes about 1.5 minutes with left joins.
Edit: nope, Chrome too. I literally can't fit all the text on a small screen, no matter how feverishly I pinch, rotate or pan!
* DON'T USE USER DEFINED FUNCTIONS IN ANY PREDICATE OF ANY TYPE EVER, THIS IS DEATH.
* If you want things to go fast, consider that your query needs to be able to be converted into a "search argument" to use any of those precious indexes you made, so:
LIKE 'hdfhafsdh%' -- This works!
LIKE '%fafads%' -- Scanning all the things, so sad.
In the same vein, any function you use on your values in the tables mean that there are often no indexes you can use to help your operation (with a few technical exceptions around specific optimizations SQL Server has around datetime <-> date calculations )
Download SQL Sentry Plan Explorer - it just became free and with it you can shred apart your execution plans with ease and keep histories of the work you have done on a query, or watch a plan change over time as your tune it with ease.
http://stackoverflow.com/questions/2554333/multi-statement-t...