Archive for the ‘SQL Server’ Category
SQL Server: clustered index and ordering
From Stack Overflow:
My table (SQL Server 2008) has more than 1 million records.
When I try to order records by
datetime, it takes 1 second, but when I order byid(integer), it only takes about 0.1 second.Is there any way to improve the efficiency?
The obvious answer would be create an index on
datetime
However, everything’s not so simple.
Let’s create a sample table and see why:
Read the rest of this entry »
Inverting date ranges
From Stack Overflow:
In an SQL table I keep bookings for various resouces, with a
StartDate/EndDatecolumn:
ResourceID StartDate EndDate 1 2009-01-01 2009-01-05 1 2009-01-07 2009-01-10 2 2009-01-03 2009-01-18 A record means that the given resource is busy.
I need to produce a list of all resources that are available for at least X consecutive days in a given time interval, e. g. from 2009-01-01 to 2009-01-20 and 5 consecutive days.
This would be quite an easy task if the table records reflected available resources. We could just check that a resource exists using EXISTS and that it has given length using DATEDIFF.
However, the table reflects the busy resources. This means we should invert the date ranges: given a list of consecutive non-overlapping date ranges, we should build a list of their complements, or the gaps between them.
Here’s how we do it.
First, we should build an ordered set of the date ranges. Since the ranges are not overlapping, the set can be ordered by any column (StartDate or EndDate), with the result being the same:
Generating row placeholders: SQL Server
Answering questions asked on the site.
Vladimir asks:
I have two tables: one contains document headers, the other one contains document items.
The document header defines how many items should be in this document (usually 10 or 15, but this can vary).
I need to generate a list of documents and a list of document items to feed them to a third party control. Normally, this could be easily achieved by doing a
SELECT *from both tables, but there is a problem. The control requires that the number of items for each document is equal to the value of the columns in the document header.That is, if document header says 15 items, there should be exactly 15 records for this document in the items rowset, even if there are less items in the actual table. It’s OK if the extra items have a value of NULL.
I tried to use
LEFT JOINandFULL JOINbut cannot figure out how to use them.
This is quite a common problem in SQL world.
SQL as such has no means to generate sets, only to transform them. In classical SQL, if we have a relation that contains 2 records on input and need to have a relation that contains 16 records on output, then we will need to do at least 4 cross joins — just to have enough rows to begin with.
There are two solutions to that problem.
First is having a dummy rowset handy: create a table of 1,000,000 records or use some kind of a system table whose only purpose is to provide enough rows (the actual values of them can be completely ignored).
Second solution is to generate rows on the fly using system-provided means. This is impossible in MySQL, however, all other major systems can do it using generate_series (PostgreSQL, the only system that has a native tool specially for this purpose); CONNECT BY query (Oracle); or, in your case, a recursive CTE (SQL Server).
Recursive queries are somewhat less efficient than stored tables, since they require looping and therefore having much logical reads, while reading from a dummy table requires but a few table scans.
However, the recursive queries also have several advantages:
- They are always available, even if you querying a database that lacks a dummy table and you don’t have a write access to the database
- They always have enough records
- They have consistent plans. If the dummy table contains lots of records and the optimizer chooses a fullscan, this can kill performance
That’s why I’ll base my solution on the recursive CTE.
The main idea to generate a list of document items is simple:
- Each document header we should replicate exactly as many times as the header says (using a recursive CTE), with an ordinal number
- We also should assign an ordinal number to each document item, using
ROW_NUMBER() - Finally, we should just left join the two resultsets
The query will look like this:
Read the rest of this entry »
Constraints and the optimizer in SQL Server: FOREIGN KEY
Continuing on from the yesterday’s article.
I was wondering: do the constraints declared on tables in SQL Server affect the decisions made by the optimizer?
SQL Server allows to define the following constraints on the columns:
PRIMARY KEYUNIQUEFOREIGN KEYCHECKDEFAULT
Today we will see how FOREIGN KEY affect the plans.
A FOREIGN KEY guarantees that every value of the column constrained with it is contained in a PRIMARY KEY or UNIQUE column of another table.
This fact can be used to simpify joins on these columns and using IN clause.
Let’s create the sample tables:
Read the rest of this entry »
Constraints and the optimizer in SQL Server: PRIMARY KEY and UNIQUE
Answering questions asked on the site.
Mitch asks:
I was wondering: do the constraints declared on tables in SQL Server affect the decisions made by the optimizer?
SQL Server allows to define the following constraints on the columns:
PRIMARY KEYUNIQUEFOREIGN KEYCHECKDEFAULT
Since these constraints imply some restrictions on data, it gives a theoretical possibility to use these restrictions while optimizing the queries so that some extra checks (which had already been made by the constraints) can be skipped.
SQL Server does use these possibilities and optimizes its plans taking the constraints into account.
In this article we will see how SQL Server uses PRIMARY KEY and UNIQUE to optimize the plans.
PRIMARY KEY and UNIQUE
Let’s create a pair of sample tables:
Read the rest of this entry »
Running root mean square: SQL Server
From Stack Overflow:
Lets say I have the following in a SQL Server table:
Value 1 3 2 6 3 I need to get the differences of each of these numbers (in order), then square them, sum them, divide by number of values and take the square root.
This value is called root mean square. It is widely used in statistics.
However, we should calculate it for the differences between the values, not for the values themselves. In Oracle, the differences between rows could be calculated easilty using LAG, an analytical function that returns a previous row. Unfortunately, SQL Server lacks this function.
It could be emulated using a subquery or CROSS APPLY along with ORDER BY and TOP, but this makes the query less elegant and less efficient for large tables (since using TOP in subqueries and CROSS APPLY forces NESTED LOOPS).
It is better to assign the row number to each row (using ROW_NUMBER) and then just make an equijoin between two consecutive rows. This looks much better and the optimizer is free to choose any join method, including Hash Join which is most efficient when large percent of the table rows participates in a join, or a Merge Join which will be also efficient, given that ROW_NUMBER() tends to output the ordered resultsets and SQL Server can utilize this fact.
Here’s the query:
WITH nums AS
(
SELECT value, ROW_NUMBER() OVER (ORDER BY orderer) AS rn
FROM source
)
SELECT SQRT(AVG(POWER(np.value - nn.value, 2)))
FROM nums np
JOIN nums nn
ON nn.rn = np.rn + 1
And let’s try it on the sample data:
WITH source (orderer, value) AS
(
SELECT 10, 1
UNION ALL
SELECT 20, 3
UNION ALL
SELECT 30, 2
UNION ALL
SELECT 40, 6
UNION ALL
SELECT 50, 3
),
nums AS
(
SELECT value, ROW_NUMBER() OVER (ORDER BY orderer) AS rn
FROM source
)
SELECT SQRT(AVG(POWER(np.value - nn.value, 2)))
FROM nums np
JOIN nums nn
ON nn.rn = np.rn + 1
| 2.6457513110645907 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1 row fetched in 0.0001s (0.0007s) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. SQL Server Execution Times: CPU time = 0 ms, elapsed time = 1 ms.
|--Compute Scalar(DEFINE:([Expr1027]=sqrt(CONVERT_IMPLICIT(float(53),[Expr1026],0))))
|--Compute Scalar(DEFINE:([Expr1026]=CASE WHEN [Expr1038]=(0) THEN NULL ELSE [Expr1039]/CONVERT_IMPLICIT(int,[Expr1038],0) END))
|--Stream Aggregate(DEFINE:([Expr1038]=COUNT_BIG(power([Union1011]-[Union1024],(2.000000000000000e+000))), [Expr1039]=SUM(power([Union1011]-[Union1024],(2.000000000000000e+000)))))
|--Merge Join(Inner Join, MANY-TO-MANY MERGE:([Expr1025])=([Expr1033]), RESIDUAL:([Expr1025]=([Expr1012]+(1))))
|--Sequence Project(DEFINE:([Expr1025]=row_number))
| |--Compute Scalar(DEFINE:([Expr1035]=(1)))
| |--Segment
| |--Sort(ORDER BY:([Union1023] ASC))
| |--Constant Scan(VALUES:(((10),(1)),((20),(3)),((30),(2)),((40),(6)),((50),(3))))
|--Compute Scalar(DEFINE:([Expr1033]=[Expr1012]+(1)))
|--Sequence Project(DEFINE:([Expr1012]=row_number))
|--Compute Scalar(DEFINE:([Expr1037]=(1)))
|--Segment
|--Sort(ORDER BY:([Union1010] ASC))
|--Constant Scan(VALUES:(((10),(1)),((20),(3)),((30),(2)),((40),(6)),((50),(3))))
The query works, uses the Merge Join and gives a correct result.
Minimum and maximum on bit fields: SQL Server
From Stack Overflow:
I have this query in SQL Server 2005:
SELECT * FROM tblJobs AS j -- joining other tables here WHERE Closed = 0 AND Invoiced = 0 AND Active = 1 AND DepartmentId <> 2 AND dbo.fncIsAllPointsDelivered(JobID) = 1This query is taking too long to run, and I know the problem is the UDF:
dbo.fncIsAllPointsDelivered(J.JobID) = 1The SQL for the UDF is here:
DECLARE @DetailCount int DECLARE @TrackingCount int SELECT @DetailCount = COUNT(*) FROM tblLoadDetails WHERE JobId = @JobId SELECT @TrackingCount = COUNT(*) FROM tblLoadDetails WHERE JobId = @JobId AND Delivered = 1 IF (@DetailCount = @TrackingCount AND @DetailCount > 0) RETURN 1 RETURN 0All of this runs blazingly fast unless the job has a large number of load details in it.
I am trying to think of a way to either make the UDF faster or get rid of the need for the UDF, but I am at a loss.
UDF is definitely a problem here, since the conditions based on the UDF‘s are not sargable, that is an index cannot be used to search for them.
Let’s look closed into this UDF.
Given a @JobID, the function searches the load details for this job. For the function to succeed, all load details should have the state of Delivered, and there must be at least one load detail for this job.
The UDF can be rewritten using IN and NOT IN conditions. The first condition makes sure that there is at least one load detail for the given job, the second one ensures that all load details have state Delivered.
Let’s create a sample table and see how it works:
Read the rest of this entry »
Date range with possible NULL values
From Stack Overflow:
Using SQL Server 2008.
I have a stored proc which has start and end date as input parameters for date range.
Looking for a single SQL query which has a
BETWEENpredicate on start and end date in theWHEREclause which can handle both cases where the dates are either bothNULLor both have values.
This can be done by writing an IS NULL check in the expression, like this:
WHERE (@startDate IS NULL OR date >= @startDate)
AND (@endDate IS NULL OR date >= @endDate)
or by using a COALESCE on the value of the date itself (so that the range always matches):
WHERE date BETWEEN ISNULL(@startDate, date) AND ISNULL(@endDate, date)
However, none of these conditions uses an index on date efficiently.
Let’s create a sample table ans see:
Read the rest of this entry »
Adjacency list vs. nested sets: SQL Server
Continuing the series:
What is better to store hierarchical data: nested sets model or adjacency list (parent-child) model?
For detailed explanations of the terms, see the first article in the series:
Now, let’s see what’s better for SQL Server.
We will create a single table that holds both adjacency list data and nested sets data, with 8 levels of nesting, 5 children of each parent node and 2,441,405 records:
Read the rest of this entry »
Finding sets within a time frame
From Stack Overflow:
I’ve got a set of data that contains users, their awards and the time they are granted.
When a user earns an award, the award and user is logged along with a date.
What I’d like is a query that finds a set of 3 achievements that were earned within 5 minutes of each other by the same user.
Any thoughts on how to accomplish this?
I’ll go a step further and try to build a general solution to find sets of N values within a given time frame.
We should return all rows that are parts of all 5-minute time frames containing at least N values. These timeframes can overlap.
We should build such a timeframe for each record, make sure that there are at least N values in this timeframe, and if yes, return these values (applying DISTINCT to get rid of the duplicates).
Let’s create a sample table and build a query to do this:

Subscribe in a reader