Ramblings of a DBA

Tara Kizer
posts - 166, comments - 835, trackbacks - 75

My Links

Advertisement

News

Subscribe
Search this Blog

Archives

Post Categories

Work

Multiple “clustered” indexes on a SQL Server table

You can only have one clustered index on each SQL Server table, however there are two ways to create pseudo clustered indexes on a table:

  1. Create clustered indexed on a view (indexed view) that covers the table
  2. Create covering index on the entire table

Let's look at the following table:

CREATE TABLE t1 
(
    c1 int, 
    c2 varchar(5), 
    c3 bigint, 
    c4 datetime, 
    CONSTRAINT PK_t1 PRIMARY KEY CLUSTERED (c1, c2, c3, c4)
)
GO

For option 1 above, here's the indexed view and clustered index:

SET NUMERIC_ROUNDABORT OFF;
SET ANSI_PADDING, ANSI_WARNINGS, CONCAT_NULL_YIELDS_NULL, ARITHABORT,
    QUOTED_IDENTIFIER, ANSI_NULLS ON;
GO
CREATE VIEW v1 WITH SCHEMABINDING
AS 
SELECT c1, c2, c3, c4 FROM dbo.t1
GO
CREATE UNIQUE CLUSTERED INDEX cidx_v1 ON dbo.v1(c2, c3, c4, c1)
GO

For option 2 above, here’s the covering index:

CREATE INDEX idx_PseudoClust ON t1(c3, c4) INCLUDE (c2, c1)
GO

So why would you want to do this?  I'm sure that there are some very good reasons, but I've never had the need to do it.  I recently had the discussion with a Microsoft engineer and thought it was interesting enough to share.

Print | posted on Monday, November 09, 2009 11:23 AM |

Feedback

Gravatar

# re: Multiple “clustered” indexes on a SQL Server table

We use indexed views with clustered indexes on them all of the time. We typically don't cover the entire table though when doing it. Here is one reason that we use this technique:

You have a table like this:

MyTable
Id uniqueidentifier PK (Clustered)
ForeignId uniqueidentifier FK
ForeignId2 uniqueidentifier FK
OtherField nvarchar(100).....

MyTable_ByForeignId (this is an indexed view)
ForeignId
Id

PK (Clustered) will be ForeignId, Id

MyTable_ByForeignId2 (this is an indexed view)
ForeignId2
Id

PK (Clustered) will be ForeignId2, Id

This table has the primary key on the Id, because you want to be able to read, write and delete very fast for records in this table.

But you need to be able to lookup data in this table by either ForeignId or ForeignId2. What you would do is create an indexed view (schema bound) (or 2 one for each) and setup the cluster so that ForeignId is the first element and Id is the second element. Then you can write a query to join the data.

SELECT
MyTable.Id,
MyTable.ForeignId,
MyTable.ForeignId2,
MyTable.OtherField

FROM
MyTable

JOIN
MyTable_ByForeignId WITH(NOEXPAND)
ON MyTable_ByForeignId.Id = MyTable.Id
AND MyTable_ByForeignId.ForeignId = @SomeValue

JOIN
MyTable_ByForeignId2 WITH(NOEXPAND)
ON MyTable_ByForeignId2.Id = MyTable.Id
AND MyTable_ByForeignId2.ForeignId2 = @SomeValue2




When you check the performance you'll note that the query will use Clustered Index Seeks instead of scans to find the records matching the query. This can yield extremely fast performance for reads. There isn't much of a performance hit for updates since the foreign keys rarely (if ever) change....but make sure you don't update them unless they are changed, meaning don't just update them to themselves if you can avoid it so that the indexed views don't need to be updated when you update the table.

We've found this technique to by extremely helpful for our application.
11/10/2009 9:40 AM | Steve
Gravatar

# re: Multiple “clustered” indexes on a SQL Server table

I used option 2 once in a data warehouse application for an inventory balance table. The composite clustered primary key was product number and location number. This worked fine for queries that were interested mainly in particular product numbers.

Queries that were interested mainly in totals by location caused tables scans because products for a particular location were distributed fairly evenly throughout the table. I created a covering index that reversed the order of the first two columns to location number and product number.
11/10/2009 12:16 PM | Michael Valentine Jones
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET