SQL Server, query using right index but still slow - sql

I have a query as follows:
SELECT 1
FROM [Call]
INNER JOIN [Caller] ON [Call].callerId = [Caller].id
WHERE [Call].[phoneNumber] = #phoneNumber
AND
[Caller].callerType = #callerType
AND
[Call].[time] > #time
AND
[Call].[status] = #status
AND
[Call].[type] <> #type
There is a clustered primary key index on [Caller] id column. There is a non-clustered index on [Call] as follows:
CREATE INDEX IX_callerId_time_phonenumber_status_type
ON dbo.[Call]
(
[callerId] ASC,
[time] ASC,
[phoneNumber] ASC,
[status] ASC,
[type] ASC
)
I notice in the execution plan that 90% of the cost of my query is as follows:
Predicate:
[Call].[status] = 10 AND [Call].[type] <> 10
Object:
[Call].[IX_callerId_time_phonenumber_status_type]
So it's using the right index but I'm still getting bad performance. Any ideas?

Predicate [Call].[time] > #time is fairly unselective, but the structure of your index forces SQL Server to give it priority over some other criteria that are probably more selective. It likely chooses to scan a big chunk of the index for each callerId. Reordering the index like so would probably improve performance for this particular query:
CREATE INDEX IX_callerId_time_phonenumber_status_type
ON dbo.[Call]
(
[callerId] ASC,
[phoneNumber] ASC,
[status] ASC,
[time] ASC,
[type] ASC
)

Not knowing if "Time" is really a Time datatype or a varchar datatype, I would suggest the following index.
CREATE NONCLUSTERED INDEX IX_callerId_time_phonenumber_status_type
ON [dbo].[Call] ([PhoneNumber],[Status],[Time],[Type])

Have you ruled out parameter sniffing? Wondering if the optimizer is stuck on some not-good-for-the-values.
This is my fav article http://www.brentozar.com/archive/2013/06/the-elephant-and-the-mouse-or-parameter-sniffing-in-sql-server/

Related

How to improve the performance of this query? It is taking more than a min?

I have below query:
It is taking more than a minute...I need to improve the performance and bring it to less than 10 sec.
Appreciate your responses.
SELECT
DISTINCT
RP.RegionPerilID
,RP.RegionName + ' ' + RP.ShortName AS RegionPerilName
,LossLevelID
,LL.LossLvlName AS LossLevelName
FROM Axis_Accumulation.dbo.AIREventSet ES
JOIN Axis_Accumulation.dbo.vw_RegionPerils RP ON RP.RegionPerilId = ES.RegionPerilId
JOIN ART.LA.ELT_Blend ELT WITH (NOLOCK) ON ES.EventNum = ELT.EventNum AND ELT.Versionid = #versionId
JOIN dbo.LA_LossLevel LL ON ELT.LossLevelID = LL.LossLvlID AND LL.LosstypeId = 3 -- Line of Business
ORDER By RegionPerilName
You should try to create these indexes, if they not already exist :
CREATE NONCLUSTERED INDEX IX_AIREventSet ON Axis_Accumulation.dbo.AIREventSet
(
RegionPerilID
)
INCLUDE
(
EventNum
)
CREATE NONCLUSTERED INDEX IX_AIREventSet_EvenNum ON Axis_Accumulation.dbo.AIREventSet
(
EventNum
)
CREATE NONCLUSTERED INDEX IX_vw_RegionPerils ON Axis_Accumulation.dbo.vw_RegionPerils
(
RegionPerilID
)
INCLUDE
(
RegionName,
ShortName
)
CREATE NONCLUSTERED INDEX IX_ELT_Blend ON ART.LA.ELT_Blend
(
EventNum,
Versionid,
)
INCLUDE
(
LossLevelID
)
CREATE NONCLUSTERED INDEX IX_ELT_Blend_LossLevelID ON ART.LA.ELT_Blend
(
LossLevelID
)
CREATE NONCLUSTERED INDEX IX_LA_LossLevel ON dbo.LA_LossLevel
(
LossLvlID,
LosstypeId
)
INCLUDE
(
LossLvlName
)
If in the list below, the fields are unique, add the UNIQUE clause in the index definition.
The INCLUDE statement is important because it brings all the data by seeking one index.
Add the indexes one by one and check in the execution plan if they are used. If not you can delete it.
This last index should help for sorting the data !
CREATE CLUSTERED UNIQUE INDEX IX_AIREventSet_Clustered ON Axis_Accumulation.dbo.AIREventSet
(
RegionName,
ShortName,
RegionPerilID,
[YourTableId]
)

Need re-factoring / indexing advice for a very large table

Ok, so I have a table that's just become a monster. And querying on it has become insanely slow for some of our customers. Here's the table in question:
CREATE TABLE [EventTime](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[EventId] [bigint] NOT NULL,
[Time] [datetime] NOT NULL,
CONSTRAINT [PK_EventTime] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
)
CREATE NONCLUSTERED INDEX [IX_EventTime_Main] ON [EventTime]
(
[Time] ASC,
[EventId] ASC
)
It has a FK to the Events table. An event is action taken from a certian user, ip, service, and accountId. This EventTime table tells us what events happened at what time. An event can happen today at 3am and also 12pm last week. The idea is to not duplicate event rows.
Now this EventTime table has become massive for some customers; our biggest being 240mill rows and growing. And querying it has become insanely slow when looking at a time set > a few days. Here's the query we're executing today (Note: I'm running queries locally from a rip of the DB to minimize network latency or TO's caused by collectors hitting the DB):
SELECT
a.TrailId, a.[NameId], a.[ResourceId], a.[AccountId], a.[ServiceId]
FROM [EventTime] b WITH (NOLOCK) INNER JOIN [Event] a WITH (NOLOCK) ON a.Id = b.EventId
WHERE
a.TrailId IN (1, 2, 3, 4, 5) AND
a.NameId IN (6) AND
b.[Time] >= '2014-10-29 00:00:00.000' AND
b.[Time] <= '2014-11-12 23:59:59.000'
ORDER BY b.[Time] ASC
Note, trailId is a column in the Event table that tells us what customer to filter down to in the query. We have the list of TrailIds before we execute this query. Now this query very slow, about 45mins to execute. Here's some queries I've tried:
SELECT
a.EventId, a.[NameId], a.[ResourceId], a.[AccountId], a.[ServiceId]
FROM [EventTime] b WITH(NOLOCK)
JOIN [Event] a WITH(NOLOCK) on a.Id = b.EventId
WHERE
b.EventId IN (SELECT Id from [Event] where TrailId IN (1, 2, 3, 4, 5) AND NameId IN (6) ) AND
b.[Time] >= '2014-08-01 00:00:00.000' AND
b.[Time] <= '2014-11-12 23:59:59.000' AND
ORDER BY b.[Time] ASC
subquery worked well for small queries but for larger date ranges the performance suffered greatly. Next I tried
DECLARE #ListofIDs TABLE(Ids bigint)
INSERT INTO #ListofIDs (Ids)
SELECT Id from Event where TrailId IN (140, 629, 630, 631, 632) AND NameId IN (468)
SELECT
a.EventId, a.[NameId], a.[ResourceId], a.[AccountId], a.[ServiceId]
FROM [EventTime] b WITH(NOLOCK)
JOIN [Event] a WITH(NOLOCK) on a.Id = b.EventId
WHERE
b.EventId IN (SELECT Ids FROM #ListofIDs) AND
b.[Time] >= '2014-08-01 00:00:00.000' AND
b.[Time] <= '2014-11-12 23:59:59.000' AND
ORDER BY b.[Time] ASC
Casting my subquery into a table array for my main query to reference did help a bit. The query took about 33mins. But's it's still way way too slow =/
Next I tried playing with indexes. I figured I might have been putting too much into one index. So I dropped the existing and broke it out into two.
CREATE NONCLUSTERED INDEX [IX_EventTime_Main] ON [EventTime]
(
[Time] ASC,
)
GO
CREATE NONCLUSTERED INDEX [IX_EventTime_Event] ON [EventTime]
(
[EventId] ASC
)
This didn't seem to do anything. Same query times.
I think the core issue is, this table is just very unorganized. The Time column has very specific time values and none of them are in order. For example, customer 8's collector might be saving EventTimes for 2014-11-12 04:12:01.000 and customer 10 is saving 2015-03-15 13:59:21.000. So the query has to process and sort all these dates prior to filtering down. So indexing [Time] probably isn't effective at all.
Anyone have any ideas on how I can speed this up?
This is your query:
SELECT e.TrailId, e.[NameId], e.[ResourceId], e.[AccountId], e.[ServiceId]
FROM [EventTime] et WITH (NOLOCK) INNER JOIN
[Event] e WITH (NOLOCK)
ON e.Id = et.EventId
WHERE e.TrailId IN (1, 2, 3, 4, 5) AND
e.NameId = 6 AND
et.[Time] >= '2014-10-29 00:00:00.000' AND
et.[Time] <= '2014-11-12 23:59:59.000'
ORDER BY et.[Time] ASC
The best indexes for this query are probably: Event(NameId, TrailId), EventTime(EventId, Time). This assume that the result set is not humongous (tens of millions of rows), in which case an optimization to get rid of the order by would be desirable.
I would ditch the ID column and make the primary key a composite clustered one on EventId and Time:
CREATE TABLE [EventTime](
[EventId] [bigint] NOT NULL,
[Time] [datetime] NOT NULL,
CONSTRAINT [PK_EventTime] PRIMARY KEY CLUSTERED
(
[EventId] ASC
, [Time] ASC
)
)
CREATE NONCLUSTERED INDEX [IX_EventTime_Main] ON [EventTime]
(
[Time] ASC,
[EventId] ASC
);
Check the execution plans to see of the non-clustered index is used and drop it of not needed.

SQL Server 2005 query optimization with Max subquery

I've got a table that looks like this (I wasn't sure what all might be relevant, so I had Toad dump the whole structure)
CREATE TABLE [dbo].[TScore] (
[CustomerID] int NOT NULL,
[ApplNo] numeric(18, 0) NOT NULL,
[BScore] int NULL,
[OrigAmt] money NULL,
[MaxAmt] money NULL,
[DateCreated] datetime NULL,
[UserCreated] char(8) NULL,
[DateModified] datetime NULL,
[UserModified] char(8) NULL,
CONSTRAINT [PK_TScore]
PRIMARY KEY CLUSTERED ([CustomerID] ASC, [ApplNo] ASC)
);
And when I run the following query (on a database with 3 million records in the TScore table) it takes about a second to run, even though if I just do: Select BScore from CustomerDB..TScore WHERE CustomerID = 12345, it is instant (and only returns 10 records) -- seems like there should be some efficient way to do the Max(ApplNo) effect in a single query, but I'm a relative noob to SQL Server, and not sure -- I'm thinking I may need a separate key for ApplNo, but not sure how clustered keys work.
SELECT BScore
FROM CustomerDB..TScore (NOLOCK)
WHERE ApplNo = (SELECT Max(ApplNo)
FROM CustomerDB..TScore sc2 (NOLOCK)
WHERE sc2.CustomerID = 12345)
Thanks much for any tips (pointers on where to look for optimization of sql server stuff appreciated as well)
When you filter by ApplNo, you are using only part of the key. And not the left hand side. This means the index has be scanned (look at all rows) not seeked (drill to a row) to find the values.
If you are looking for ApplNo values for the same CustomerID:
Quick way. Use the full clustered index:
SELECT BScore
FROM CustomerDB..TScore
WHERE ApplNo = (SELECT Max(ApplNo)
FROM CustomerDB..TScore sc2
WHERE sc2.CustomerID = 12345)
AND CustomerID = 12345
This can be changed into a JOIN
SELECT BScore
FROM
CustomerDB..TScore T1
JOIN
(SELECT Max(ApplNo) AS MaxApplNo, CustomerID
FROM CustomerDB..TScore sc2
WHERE sc2.CustomerID = 12345
) T2 ON T1.CustomerID = T2.CustomerID AND T1.ApplNo= T2.MaxApplNo
If you are looking for ApplNo values independent of CustomerID, then I'd look at a separate index. This matches your intent of the current code
CREATE INDEX IX_ApplNo ON TScore (ApplNo) INCLUDE (BScore);
Reversing the key order won't help because then your WHERE sc2.CustomerID = 12345 will scan, not seek
Note: using NOLOCK everywhere is a bad practice

how to fix this query performance on SQL Server Compact Edition 4

i have the following SQL Query which runs on SQL Server CE 4
SELECT [Join_ReleaseMinDatePost].[FK_MovieID]
FROM (
SELECT [FK_MovieID], MIN([DatePost]) AS [ReleaseMinDatePost]
FROM [Release]
GROUP BY [FK_MovieID]
) [Join_ReleaseMinDatePost]
INNER JOIN
(
SELECT COUNT([ID]) AS [FolderCount], [FK_MovieID]
FROM [MovieFolder]
GROUP BY [FK_MovieID]
) [Join_MovieFolder]
ON [Join_MovieFolder].[FK_MovieID] = [Join_ReleaseMinDatePost].[FK_MovieID]
this query takes a long time to execute but if i change the Part
SELECT COUNT([ID]) AS [FolderCount], [FK_MovieID] FROM [MovieFolder] GROUP BY [FK_MovieID]
To
SELECT 1 AS [FolderCount], [FK_MovieID] FROM [MovieFolder]
So the full query becomes
SELECT [Join_ReleaseMinDatePost].[FK_MovieID]
FROM ( SELECT [FK_MovieID], MIN([DatePost]) AS [ReleaseMinDatePost] FROM [Release] GROUP BY [FK_MovieID] ) [Join_ReleaseMinDatePost]
INNER JOIN (SELECT 1 AS [FolderCount], [FK_MovieID] FROM [MovieFolder] ) [Join_MovieFolder]
ON [Join_MovieFolder].[FK_MovieID] = [Join_ReleaseMinDatePost].[FK_MovieID]
then the performance becomes very fast.
the problem is that the part that was changed if taken by itself is pretty fast. but for some reason the execution plan of the first query shows that the "actual number of rows" in the index scan is 160,016 while the total number of rows in the table MovieFolder is 2,192.
and the "Estimated number of rows" is 2,192.
so i think the problem is in the number of rows but i cant figure out why its all messed up.
any help will be appreciated :)
thanks
the schema of the tables is below
CREATE TABLE [Release] (
[ID] int NOT NULL
, [FD_ForumID] int NOT NULL
, [FK_MovieID] int NULL
, [DatePost] datetime NULL
);
GO
ALTER TABLE [Release] ADD CONSTRAINT [PK__Release__0000000000000052] PRIMARY KEY ([ID]);
GO
CREATE INDEX [IX_Release_DatePost] ON [Release] ([DatePost] ASC);
GO
CREATE INDEX [IX_Release_FD_ForumID] ON [Release] ([FD_ForumID] ASC);
GO
CREATE INDEX [IX_Release_FK_MovieID] ON [Release] ([FK_MovieID] ASC);
GO
CREATE UNIQUE INDEX [UQ__Release__0000000000000057] ON [Release] ([ID] ASC);
GO
CREATE TABLE [MovieFolder] (
[ID] int NOT NULL IDENTITY (1,1)
, [Path] nvarchar(500) NOT NULL
, [FK_MovieID] int NULL
, [Seen] bit NULL
);
GO
ALTER TABLE [MovieFolder] ADD CONSTRAINT [PK_MovieFolder] PRIMARY KEY ([ID]);
GO
CREATE INDEX [IX_MovieFolder_FK_MovieID] ON [MovieFolder] ([FK_MovieID] ASC);
GO
CREATE INDEX [IX_MovieFolder_Seen] ON [MovieFolder] ([Seen] ASC);
GO
CREATE UNIQUE INDEX [UQ__MovieFolder__0000000000000019] ON [MovieFolder] ([ID] ASC);
GO
CREATE UNIQUE INDEX [UQ__MovieFolder__0000000000000020] ON [MovieFolder] ([Path] ASC);
GO
I think you're running into a correlated subquery problem. The query part you're experimenting with is part of a JOIN condition, so it is fully evaluated for every potentially matching row. You're making your SQL engine do the second 'GROUP BY' for every row produced by the FROM clause. So it's reading 2192 rows to do the group by for each and every row produced by the FROM clause.
This suggest you're getting 73 rows in the FROM clause grouping (2192 * 73 = 160 016)
When you change it to do SELECT 1, you eliminate the table-scan read for grouping.
DaveE is right about the issue with your correlated subquery. When these issues arise you often need to rethink your entire query. If anything else fails you can probably save time extracting your sub-query to a temporary table like this:
/* Declare in-memory temp table */
DECLARE #Join_MovieFolder TABLE (
count INT,
movieId INT )
/* Insert data into temp table */
INSERT INTO #Join_MovieFolder ( count, movieId )
SELECT COUNT([ID]) AS [FolderCount], [FK_MovieID]
FROM [MovieFolder]
GROUP BY [FK_MovieID]
/* Inner join the temp table to avoid excessive sub-quering */
SELECT [Join_ReleaseMinDatePost].[FK_MovieID]
FROM (
SELECT [FK_MovieID], MIN([DatePost]) AS [ReleaseMinDatePost]
FROM [Release]
GROUP BY [FK_MovieID]
) [Join_ReleaseMinDatePost]
INNER JOIN #Join_MovieFolder
ON #Join_MovieFolder.movieId = [Join_ReleaseMinDatePost].[FK_MovieID]
i think if found the problem.
but i would like people to tell me if this indeed the problem.
the problem is that the 2 sub queries create some kind of like temp table (dont know how to call it).
but these 2 temp table dont contain a clustered index on [FK_MovieID].
so when the external join tries to join them it need to scan them several times and and this is mainly the problem.
now if i can only fix this ?

Aggregate Function/Group-By Query Performance

This query works (thanks to those that helped) to generate a 30-day moving average of volume.
SELECT x.symbol, x.dseqkey, AVG(y.VOLUME) moving_average
FROM STOCK_HIST x, STOCK_HIST y
WHERE x.dseqkey>=29 AND x.dseqkey BETWEEN y.dseqkey AND y.dseqkey+29
AND Y.Symbol=X.Symbol
GROUP BY x.symbol, x.dseqkey
ORDER BY x.dseqkey DESC
However the performance is very bad. I am running the above against a view (STOCK_HIST) that brings two tables (A and B) together. Table A contains daily stock volume and the daily date for over 9,000 stocks dating back as far as 40 years (300+ rows, per year, per each of the 9,000 stocks). Table B is a "Date Key" table that links the date in table A to the DSEQKEY (int).
What are my options for performance improvement? I have heard that views are convenient but not performant. Should I just copy the columns needed from table A and B to a single table and then run the above query? I have indexes on the tables A and B on the stock symbol + date (A) and DSEQKEY (B).
Is it the view that's killing my performance? How can I improve this?
EDIT
By request, I have posted the 2 tables and the view below. Also, now there is one clustered index on the view and each table. I am open to any recommendations as this query that produces the deisred result, is still slow:
SELECT
x.symbol
, x.dseqkey
, AVG(y.VOLUME) moving_average
FROM STOCK_HIST x
JOIN STOCK_HIST y ON x.dseqkey BETWEEN y.dseqkey AND y.dseqkey+29 AND Y.Symbol=X.Symbol
WHERE x.dseqkey >= 15000
GROUP BY x.symbol, x.dseqkey
ORDER BY x.dseqkey DESC ;
HERE IS THE VIEW:
CREATE VIEW [dbo].[STOCK_HIST]
WITH SCHEMABINDING
AS
SELECT
dbo.DATE_MASTER.date
, dbo.DATE_MASTER.year
, dbo.DATE_MASTER.quarter
, dbo.DATE_MASTER.month
, dbo.DATE_MASTER.week
, dbo.DATE_MASTER.wday
, dbo.DATE_MASTER.day
, dbo.DATE_MASTER.nday
, dbo.DATE_MASTER.wkmax
, dbo.DATE_MASTER.momax
, dbo.DATE_MASTER.qtrmax
, dbo.DATE_MASTER.yrmax
, dbo.DATE_MASTER.dseqkey
, dbo.DATE_MASTER.wseqkey
, dbo.DATE_MASTER.mseqkey
, dbo.DATE_MASTER.qseqkey
, dbo.DATE_MASTER.yseqkey
, dbo.DATE_MASTER.tom
, dbo.QP_HISTORY.Symbol
, dbo.QP_HISTORY.[Open] as propen
, dbo.QP_HISTORY.High as prhigh
, dbo.QP_HISTORY.Low as prlow
, dbo.QP_HISTORY.[Close] as prclose
, dbo.QP_HISTORY.Volume
, dbo.QP_HISTORY.QRS
FROM dbo.DATE_MASTER
INNER JOIN dbo.QP_HISTORY ON dbo.DATE_MASTER.date = dbo.QP_HISTORY.QPDate ;
HERE IS DATE_MASTER TABLE:
CREATE TABLE [dbo].[DATE_MASTER] (
[date] [datetime] NULL
, [year] [int] NULL
, [quarter] [int] NULL
, [month] [int] NULL
, [week] [int] NULL
, [wday] [int] NULL
, [day] [int] NULL
, [nday] nvarchar NULL
, [wkmax] [bit] NOT NULL
, [momax] [bit] NOT NULL
, [qtrmax] [bit] NOT NULL
, [yrmax] [bit] NOT NULL
, [dseqkey] [int] IDENTITY(1,1) NOT NULL
, [wseqkey] [int] NULL
, [mseqkey] [int] NULL
, [qseqkey] [int] NULL
, [yseqkey] [int] NULL
, [tom] [bit] NOT NULL
) ON [PRIMARY] ;
HERE IS THE QP_HISTORY TABLE:
CREATE TABLE [dbo].[QP_HISTORY] (
[Symbol] varchar NULL
, [QPDate] [date] NULL
, [Open] [real] NULL
, [High] [real] NULL
, [Low] [real] NULL
, [Close] [real] NULL
, [Volume] [bigint] NULL
, [QRS] [smallint] NULL
) ON [PRIMARY] ;
HERE IS THE VIEW (STOCK_HIST) INDEX
CREATE UNIQUE CLUSTERED INDEX [ix_STOCK_HIST] ON [dbo].[STOCK_HIST]
(
[Symbol] ASC,
[dseqkey] ASC,
[Volume] ASC
)
HERE IS THE QP_HIST INDEX
CREATE UNIQUE CLUSTERED INDEX [IX_QP_HISTORY] ON [dbo].[QP_HISTORY]
(
[Symbol] ASC,
[QPDate] ASC,
[Close] ASC,
[Volume] ASC
)
HERE IS THE INDEX ON DATE_MASTER
CREATE UNIQUE CLUSTERED INDEX [IX_DATE_MASTER] ON [dbo].[DATE_MASTER]
(
[date] ASC,
[dseqkey] ASC,
[wseqkey] ASC,
[mseqkey] ASC
)
I do not have any primary keys setup. Would this help performance?
EDIT - After making suggested changes the query is slower than before. What ran in 10m 44s is currently at 30m and still running.
I made all of the requested changes except I did not change name of date in Date_Master and I did not drop the QPDate column from QP_Hist. (I have reasons for this and do not see it impacting the performance since I'm not referring to it in the query.)
REVISED QUERY
select x.symbol, x.dmdseqkey, avg(y.volume) as moving_average
from dbo.QP_HISTORY as x
join dbo.QP_HISTORY as y on (x.dmdseqkey between y.dmdseqkey and (y.dmdseqkey + 29))
and (y.symbol = x.symbol)
where x.dmdseqkey >= 20000
group by x.symbol, x.dmdseqkey
order by x.dmdseqkey desc ;
PK on QP_History
ALTER TABLE [dbo].[QP_HISTORY]
ADD CONSTRAINT [PK_QP_HISTORY] PRIMARY KEY CLUSTERED ([Symbol] ASC, DMDSeqKey] ASC)
FK on QP_History
ALTER TABLE [dbo].[QP_HISTORY] ADD CONSTRAINT [FK_QP_HISTORY_DATE_MASTER] FOREIGN KEY([DMDSeqKey]) REFERENCES [dbo].[DATE_MASTER] ([dseqkey])
PK on Date_Master
ALTER TABLE [dbo].[DATE_MASTER]
ADD CONSTRAINT [PK_DATE_MASTER] PRIMARY KEY CLUSTERED ([dseqkey] ASC)
EDIT
HERE IS THE EXECUTION PLAN
First, separate join an filter.
(edit: fixed ON clause)
SELECT x.symbol, x.dseqkey, AVG(y.VOLUME) moving_average
FROM
STOCK_HIST x
JOIN
STOCK_HIST y ON x.dseqkey BETWEEN y.dseqkey AND y.dseqkey+29
AND Y.Symbol=X.Symbol
WHERE x.dseqkey>=29
GROUP BY x.symbol, x.dseqkey
ORDER BY x.dseqkey DESC
Also, what indexes do you have - I'd suggest an index on (dseqkey, symbol) INCLUDE (VOLUME)
Edit 3: you can't have an INCLUDE in a clustered index, my bad. Your syntax is OK.
Please try these permutations... the aim is find the best index for the JOIN and WHERE, followed with the ORDER BY.
CREATE UNIQUE CLUSTERED INDEX [ix_STOCK_HIST] ON [dbo].[STOCK_HIST] (...
...[Symbol] ASC, [dseqkey] ASC, [Volume] ASC )
...[dseqkey] ASC, [Symbol] ASC, [Volume] ASC )
...[Symbol] ASC, [dseqkey] DESC, [Volume] ASC )
...[dseqkey] DESC, [Symbol] ASC, [Volume] ASC )
SQL Server does not support LAG or LEAD clauses available in Oracle and PostgreSQL, neither does it support session variables like MySQL.
Calculating aggregates against moving windows is a pain in SQL Server.
So God knows I hate to say this, however, in this case a CURSOR based solution may be more efficient.
try putting a clustered index on the view. that will make the view persisted to disk like a normal table and your tables won't have to be accessed every time.
that should speed things up a bit.
for better answer please post the link to your original question to see if a better solution can be found.
OK, so I'll start from the end. I would like to achieve this model.
With this in place, you can run the query on the history table directly, no need for the view and join to the dbo.DATE_MASTER.
select
x.symbol
, x.dseqkey
, avg(y.volume) as moving_average
from dbo.QP_HISTORY as x
join dbo.QP_HISTORY as y on (x.dSeqKey between y.dSeqKey and (y.dSeqKey + 29))
and (y.symbol = x.symbol)
where x.dseqkey >= 15000
group by x.symbol, x.dseqkey
order by x.dseqkey desc
OPTION (ORDER GROUP) ;
The QP_HISTORY is narrower than the STOCK_HISTORY view, so the query should be faster. The "redundant column removal" from joins is scheduled for the next generation of SQL Server (Denali), so for the time being narrower usually means faster -- at least for large tables. Also, the join on .. and the where clause nicely match the the PK(Symbol, dSeqKey).
Now, how to achieve this:
a) Modify the [date] column in dbo.DATE_MASTER to be if the type date instead of datetime. Rename it FullDate to avoid confusion. Not absolutely necessary, but to preserve my sanity.
b) Add PK to the dbo.DATE_MASTER
alter table dbo.DATE_MASTER add constraint primary key pk_datemstr (dSeqKey);
c) In the table QP_HISTORY add column dSeqKey and populate it for matching QPDate dates.
d) Drop the QPDate column from the table.
e) Add PK and FK to the QP_HISTORY
alter table dbo.QP_HISTORY
add constraint pk_qphist primary key (Symbol, dSeqKey)
, add constraint fk1_qphist foreign key (dSeqKey)
references dbo.DATE_MASTER(dSeqKey) ;
f) Drop all those indexes mentioned at the end ouf your question, at least for the time being.
g) I do not see the size of the Symbol field. Define it as narrow as possible.
h) Needles to say, implement and test this on a development system first.