Advanced SQL
Last updated
Last updated
Best practices / tips:
ALWAYS use explicit join syntax, no join conditions in the WHERE clause
Try to only use inner or left joins if possible, reorder joins if necessary to achieve this
User proper indentation in your selects to maintain readability and add comments
Start with the most limiting table in terms of “relevant for final result” (often the optimizer takes care of this)
Know your tables, check for usable indexes and be aware of their ORDER
When you use DBISQL, you can check out if indexes were used, by looking into the PLAN VIEWER in the extras menu (SHIFT+F5). To get real query times, use the 3rd statistics option
If you need all columns from a specific table in your multi table select, you can use Tablename.* to achieve this, that way you do not query all tables from your select
Use table aliases for all tables with long names
When referencing columns, always prefix with the table name / alias. If in the future someone adds a new column to one of the referenced tables, you might avoid a same column name conflict, which would otherwise break your statement.
The following keywords allow you to combine results of multiple selects as long as they match in column types and count. If necessary you can fill up with NULLs.
UNION: Concatenates the results of both selects INTERSECT: Shows rows, which were present in both selects EXCEPT: Shows rows, which were present exclusively in one of the selects
By default the mode DISTINCT is used, which is normally a tad slower because the database has to eliminate duplicates from the final result. If you append the keyword ALL this behavior is suppressed.
Derived tables are cached results from a select, which can be used in the FROM clause. These will be executed only once, starting from inner layer executing one by one until reaching the outer select.
This technique allows you to shrink down your dataset very early, by nesting your most restricting WHERE attributes in the deepest layer of a complex multilevel select, so that it will be executed first.
Common table expressions allow you to execute helper selects before your main select fires. They can often be used for divide and conquer approaches to break down a complex multilevel select into an easy to read sequential structure.
CTEs are introduced by the WITH keyword and share a scope throughout the complete select. CTEs are also able to be executed recursively, to use this feature you need to use WITH RECURSIVE (…).
Since CTEs are executed prior your main select, you can also abuse them to define variables or manage your retrieval arguments, so that your select can be easily copied to DBISQL and filled with dummy data (replace static data with your retrieval arguments later):
As another example I will transform the previous DT example into an equivalent CTE:
As mentioned before, CTEs can also be executed recursively.
Sometimes it might be necessary to raise the recursion limit option (SET OPTION max_recursive_iterations = n; // default = 100) of the database to accomplish a specific task.
The following example demonstrates how you could realize the “Belegexplorer” purely in SQL:
As you can see in row 1 there is our seed row. Then position 10 was split into two resulting in rows 2 and 5. Rows 3 , 4, 6 and 7 are the following 1:1 takeover operations that have been applied to 2 and 5.
Generally you can use every aggregate functions which you know from group by also with a window function, but without the limiting factor, that all other rows have to be either aggregated or part of the group by clause.
In addition to the general aggregate functions you get functions like row_number and first_ / last value on top.
Window functions can generally be recognized by the OVER keyword, which describes over which part of the query you want to iterate per row. Their scope behaves like a “sliding Window” that moves over your rows per “partition”. The parameters for these windows can either be declared directly in each OVER clause or as a group using the WINDOW keyword, which can then be referenced in the OVER clause.
CROSS APPLY and OUTER APPLY work like INNER JOIN and LEFT OUTER JOIN against derived tables with some additional features. CROSS/OUTER APPLY also works against expressions and procedures and is evaluated per row while sharing the same scope as the main select.
The resulting columns of the expression or select within the CROSS/OUTER APPLY are appended to your select and do not require an ON clause like explicit JOINs because of the shared scope.
This feature works exactly like a LATERAL join which is part of the SQL standard, while CROSS/OUTER APPLY is a Microsoft vendor extension, so it is recommended to use the LATERAL syntax instead.
Imagine we had the following helper function for date time calculations:
Then we could easily append all those fields to any row that contains a DATE or DATETIME column:
Both selects return the same results and execute the same plan. Just be aware that even when the CROSS APPLY syntax seems to be a bit more intuitive, it is less portable.
This section is still work in progress. It just contains rudimentary information at the moment.
WITH Keyword allows to determine if a table should not be locked or ignored if locked.
UPDATE (same applies for INSERT) statement with joins but not locking all referred columns:
Set isolation level per statement: