473,385 Members | 1,356 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,385 software developers and data experts.

Tidying up of sproc

Hi

I've written this sproc. Can any one suggest any optimisations to the
main query and its sub-queries please? Truncated DDL after
CREATE PROCEDURE dbo.spGetUnbilledInfoByClient
(
@ClientID varchar(5),
@Sort int = 0,
@Dir int = 0
) AS

SET NOCOUNT ON

-- The Main Query
SELECT mmatter, mdesc1, tkfirst + ' ' + tklast AS Contact,
CONVERT(varchar(10), mopendt, 103) As [Date], CONVERT(DECIMAL(18,2),
tTime.Cost) AS COST, tTime.Hours, tDisbursements.Disbursements

FROM matter
INNER JOIN timekeep ON mbillaty = timekeep.tkinit
lEFT JOIN (

SELECT cmatter, SUM(Disbursements) AS Disbursements FROM (
SELECT dbo.cost.cmatter,
CASE WHEN dbo.cost.cauth = 'SO' OR dbo.cost.cauth = 'CF'
THEN dbo.cost.cbillamt * 1.175
ELSE
dbo.cost.cbillamt
END AS Disbursements
FROM dbo.cost
WHERE (dbo.cost.cstatus = 'b') AND (dbo.cost.cinvoice IS NULL) AND
(LEFT(dbo.cost.cmatter, 5) = @ClientID)
) AS costs
GROUP BY cmatter

) AS tDisbursements
ON dbo.matter.mmatter = tDisbursements.cmatter

INNER JOIN
(
SELECT dbo.timecard.tmatter, SUM(dbo.timecard.tbillhrs) AS Hours,
SUM(dbo.timecard.tbilldol) AS Cost
FROM dbo.timecard JOIN dbo.batch ON dbo.timecard.tbatch =
dbo.batch.bbatch
WHERE (dbo.timecard.tinvoice IS NULL)
AND (dbo.timecard.tstatus = 'b')
AND (LEFT(dbo.timecard.tmatter, 5) = @ClientID)
AND (dbo.timecard.tbilldt IS NULL)
AND ( NOT dbo.batch.bfindt IS NULL)
GROUP BY tmatter
) AS tTime
ON dbo.matter.mmatter = tTime.tmatter

--Sort (see http://www.sqlteam.com/forums/topic....42&whichpage=3)
ORDER BY
CASE -- numeric fields
WHEN @Sort = 4 AND @Dir = 0
THEN tTime.Cost
WHEN @Sort = 5 AND @Dir = 0
THEN tTime.Hours
WHEN @Sort = 6 AND @Dir = 0
THEN tDisbursements.Disbursements
END DESC,

CASE -- datetime
WHEN @Sort = 0 AND @Dir = 0
THEN mopendt
WHEN @Sort = 3 AND @Dir = 0
THEN mopendt
END DESC,

CASE -- character fields
WHEN @Sort = 1 AND @Dir = 0
THEN mdesc1
WHEN @Sort = 2 AND @Dir = 0
THEN tkfirst + ' ' + tklast

END DESC,

-- ASC
CASE -- numeric fields
WHEN @Sort = 4 AND @Dir = 1
THEN tTime.Cost
WHEN @Sort = 5 AND @Dir = 1
THEN tTime.Hours
WHEN @Sort = 6 AND @Dir = 1
THEN tDisbursements.Disbursements
END ASC,

CASE -- datetime
WHEN @Sort = 0 AND @Dir = 1
THEN mopendt
WHEN @Sort = 3 AND @Dir = 1
THEN mopendt
END ASC,

CASE -- character fields
WHEN @Sort = 1 AND @Dir = 1
THEN mdesc1
WHEN @Sort = 2 AND @Dir = 1
THEN tkfirst + ' ' + tklast

END ASC
GO
CREATE TABLE [matter] (
[mname] [varchar] (24) COLLATE Latin1_General_CI_AS NULL ,
[mmatter] [varchar] (15) COLLATE Latin1_General_CI_AS NULL ,
[mrtcode] [smallint] NULL ,
[mdept] [varchar] (10) COLLATE Latin1_General_CI_AS NULL ,
[mopendt] [datetime] NULL ,
[mbillaty] [varchar] (8) COLLATE Latin1_General_CI_AS NULL ,
[mdesc1] [varchar] (48) COLLATE Latin1_General_CI_AS NULL
) ON [PRIMARY]
GO


CREATE TABLE [cost] (
[cindex] [int] IDENTITY (1, 1) NOT NULL ,
[cmatter] [varchar] (15) COLLATE Latin1_General_CI_AS NULL ,
[camount] [money] NULL ,
[cbilldt] [datetime] NULL ,
[cbillamt] [money] NULL ,
[cinvoice] [int] NULL ,
[ccode] [varchar] (7) COLLATE Latin1_General_CI_AS NULL ,
[cbatch] [int] NULL ,
[cauth] [varchar] (10) COLLATE Latin1_General_CI_AS NULL
) ON [PRIMARY]
GO

CREATE TABLE [costcode] (
[cocode] [varchar] (7) COLLATE Latin1_General_CI_AS NULL ,
[codesc1] [varchar] (48) COLLATE Latin1_General_CI_AS NULL
) ON [PRIMARY]
GO
CREATE TABLE [timecard] (
[tindex] [int] IDENTITY (1, 1) NOT NULL ,
[tmatter] [varchar] (15) COLLATE Latin1_General_CI_AS NULL ,
[ttk] [varchar] (8) COLLATE Latin1_General_CI_AS NULL ,
[tbilldt] [datetime] NULL ,
[tbillhrs] [decimal](16, 2) NULL ,
[tbillrt] [decimal](16, 2) NULL ,
[tbilldol] [money] NULL ,
[tinvoice] [int] NULL ,
[tcode] [varchar] (7) COLLATE Latin1_General_CI_AS NULL ,
[tbatch] [int] NULL
) ON [PRIMARY]
GO
CREATE TABLE [batch] (
[bbatch] [int] IDENTITY (1, 1) NOT NULL ,
[btype] [varchar] (2) COLLATE Latin1_General_CI_AS NULL ,
[bfindt] [datetime] NULL ,
[bop] [varchar] (8) COLLATE Latin1_General_CI_AS NULL ,
[bper] [varchar] (4) COLLATE Latin1_General_CI_AS NULL ,
[bpflag] [varchar] (1) COLLATE Latin1_General_CI_AS NULL ,
[bdopen] [datetime] NULL ,
[btothrs] [decimal](12, 2) NULL ,
[btotdol] [money] NULL
) ON [PRIMARY]
GO
Jul 20 '05 #1
10 1698
Samuel Hon (no*****@samuelhon.co.uk) writes:
I've written this sproc. Can any one suggest any optimisations to the
main query and its sub-queries please? Truncated DDL after
It is difficult to optimize queries when you don't know what's in the
tables, and what the indexes are. But there is one thing here, which
is likely to be a serious issue:
(LEFT(dbo.cost.cmatter, 5) = @ClientID)
...
AND (LEFT(dbo.timecard.tmatter, 5) = @ClientID)


If you have indexes on these two columns, they are not very likely to be
used. Because when you but a table column in an expression, SQL Server
can no longer use that index to search for values, because the index
were built from the original values. At best, SQL Server can scan the
entire index, but this is rarely a winner in a query like this.

You could try rewriting the conditions as

dbo.cost.cmatter LIKE @ClientID + '%'

Another alternative is to add computed columns to the table:

clientid AS left(cmatter, 5)

and then index that column.

Note that for index on computed column to be used, you must all these
settings on: ANSI_NULLS, QUOTED_IDENTIFIER, ANSI_WARNINGS, ANSI_PADDING,
CONCAT_NULLS_YIELDS_NULL and ARITHABORT. Of these two, ANSI_NULLS and
QUOTED_IDENTIFIER are saved with the stored procedure - it is not the
run-time settings that apply. All these are on by default at run-time,
but some tools, Enterprise Manager and OSQL, do not have QUOTED_IDENT
on by default. And most libraries does not set ARITHABORT ON by default.

--
Erland Sommarskog, SQL Server MVP, es****@sommarskog.se

Books Online for SQL Server SP3 at
http://www.microsoft.com/sql/techinf...2000/books.asp
Jul 20 '05 #2
Thanks Erland, my post was sent before I'd finished typing by me being
careless when clicking

I'll investigate the 'LEFT' issue with your suggestions.

Do you think that there is any problem with the sub-selects I've used?
Is this approach normally recommended?

Thanks again

Sam

Erland Sommarskog <es****@sommarskog.se> wrote in message news:<Xn**********************@127.0.0.1>...
Samuel Hon (no*****@samuelhon.co.uk) writes:
I've written this sproc. Can any one suggest any optimisations to the
main query and its sub-queries please? Truncated DDL after


It is difficult to optimize queries when you don't know what's in the
tables, and what the indexes are. But there is one thing here, which
is likely to be a serious issue:
(LEFT(dbo.cost.cmatter, 5) = @ClientID)
...
AND (LEFT(dbo.timecard.tmatter, 5) = @ClientID)


If you have indexes on these two columns, they are not very likely to be
used. Because when you but a table column in an expression, SQL Server
can no longer use that index to search for values, because the index
were built from the original values. At best, SQL Server can scan the
entire index, but this is rarely a winner in a query like this.

You could try rewriting the conditions as

dbo.cost.cmatter LIKE @ClientID + '%'

Another alternative is to add computed columns to the table:

clientid AS left(cmatter, 5)

and then index that column.

Note that for index on computed column to be used, you must all these
settings on: ANSI_NULLS, QUOTED_IDENTIFIER, ANSI_WARNINGS, ANSI_PADDING,
CONCAT_NULLS_YIELDS_NULL and ARITHABORT. Of these two, ANSI_NULLS and
QUOTED_IDENTIFIER are saved with the stored procedure - it is not the
run-time settings that apply. All these are on by default at run-time,
but some tools, Enterprise Manager and OSQL, do not have QUOTED_IDENT
on by default. And most libraries does not set ARITHABORT ON by default.

Jul 20 '05 #3
Samuel Hon (no*****@samuelhon.co.uk) writes:
Do you think that there is any problem with the sub-selects I've used?
Is this approach normally recommended?


To use the correct terminology, they are derived tables, since they
appear where you could have a table in the query.

Derived tables are a very powerful tool when writing SQL queries. SQL Server
is free to reorder the evaluation order, as long as it does not affect the
result, and SQL Server often does a good job. At worst they are just like
temp tables, except that they are never materialized. So I think that
your derived tables are fine.
--
Erland Sommarskog, SQL Server MVP, es****@sommarskog.se

Books Online for SQL Server SP3 at
http://www.microsoft.com/sql/techinf...2000/books.asp
Jul 20 '05 #4
>
You could try rewriting the conditions as

dbo.cost.cmatter LIKE @ClientID + '%'


WOW

It seems to be significantly faster. Currently running on a test
server with limited data so will see what happens on the production
server

Thanks Erland!

Dont suppose you could explain why?
Jul 20 '05 #5
Samuel Hon (no*****@samuelhon.co.uk) writes:
You could try rewriting the conditions as

dbo.cost.cmatter LIKE @ClientID + '%'


WOW

It seems to be significantly faster. Currently running on a test
server with limited data so will see what happens on the production
server

Thanks Erland!

Dont suppose you could explain why?


Actually, I think it in was my previous post, but let's take it again.

When you have an index on a column, SQL Server can use this index in
three ways:

o Seek it - that is look up a value following the index structure.
o Scan it - read all values.
o Don't use it all.

For seeks to be possible, there must be an expression in the query that
is aligned with the index. If you have a table with an index on
the integer columns (col1, col2), and you say:

SELECT COUNT(*) FROM tbl WHERE col1 = 12
SELECT COUNT(*) FROM tbl WHERE col2 = 12

For the first query, SQL Server can seek the index, because all the
index nodes with col1 = 12 is one place. For the second query, SQL
Server can not seek the index, but it will scan the index, because
that is cheaper than reading all the data pages. (The index nodes are
smaller, and thus more fit one page, and thus there are fewer pages
to scan.)

But if you say:

SELECT COUNT(*) FROM tbl WHERE col1 + 1 = 13
SELECT COUNT(*) FROM tbl WHERE col2 + 1 = 13

SQL Server will not seek in the index in either case, this is because
there is no index for (col + 1). In this particular case, SQL Server
could rewrite the query, but assume for instance the query:

SELECT COUNT(*) FROM tbl WHERE col1 % 100 = 12
SELECT COUNT(*) FROM tbl WHERE col2 % 100 = 12

Here it's getting tricky to find out which value in the index to seek.
So SQL Server is taking the easy way out:

If a column is involved in an expression on side of the comparison
operator, NO seeks can be performed on that column, only scans.

In the case:

dbo.cost.cmatter LIKE @ClientID + '%'

The column is on its own on the left side of the LIKE operator. There
is an expresion on the right, but the result of the expression is
constant for the query. Note, though, that:

dbo.cost.cmatter LIKE '%' + @ClientID + '%'

is another case where the index cannot be seeked. This is because you
can get hits on A0001B, AA0001B, Z00001UI etc.
--
Erland Sommarskog, SQL Server MVP, es****@sommarskog.se

Books Online for SQL Server SP3 at
http://www.microsoft.com/sql/techinf...2000/books.asp
Jul 20 '05 #6
On Fri, 20 Aug 2004 21:23:31 +0000 (UTC), Erland Sommarskog wrote:

.... seek the index ...
.... index cannot be seeked ...

The grammar nazi in my head just woke up. :) Since in ordinary English, "to
seek x" means "to look all over the place until you find x", SQL Server
should have no trouble finding the index. :) Obviously you meant "seek in
the index for the value". Likewise, the past tense of "to seek" is
"sought", but to say "the index cannot be sought" is pretty strange ...

I'm not complaining about your language usage, Erland. "Seek" has
definitely taken on some new meanings since the data processing people (and
the librarians before them, I guess) got hold of it. It just struck me
funny for a moment, as a guy who loves words almost as much as databases.
Jul 20 '05 #7
Ross Presser (rp******@imtek.com) writes:
On Fri, 20 Aug 2004 21:23:31 +0000 (UTC), Erland Sommarskog wrote:

... seek the index ...
... index cannot be seeked ...

The grammar nazi in my head just woke up. :) Since in ordinary English,
"to seek x" means "to look all over the place until you find x", SQL
Server should have no trouble finding the index. :) Obviously you meant
"seek in the index for the value". Likewise, the past tense of "to
seek" is "sought", but to say "the index cannot be sought" is pretty
strange ...

I'm not complaining about your language usage, Erland. "Seek" has
definitely taken on some new meanings since the data processing people
(and the librarians before them, I guess) got hold of it. It just
struck me funny for a moment, as a guy who loves words almost as much as
databases.

If you make more grammar snubs like that I am going to default to answer
your questions!
--
Erland Sommarskog, SQL Server MVP, es****@sommarskog.se

Books Online for SQL Server SP3 at
http://www.microsoft.com/sql/techinf...2000/books.asp
Jul 20 '05 #8
I'm truly sorry. I'll keep grammar out of it from now on.
Jul 20 '05 #9
Ross Presser (rp******@NOSPAM.imtek.com.invalid) writes:
I'm truly sorry. I'll keep grammar out of it from now on.


Oh, nothing to be sorry for. There was a pun, in case you didn't notice!
--
Erland Sommarskog, SQL Server MVP, es****@sommarskog.se

Books Online for SQL Server SP3 at
http://www.microsoft.com/sql/techinf...2000/books.asp
Jul 20 '05 #10
:) Thanks Erland
Jul 20 '05 #11

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

12
by: scott | last post by:
In LISTING 2, I have a SPROC that returns a recordset and a recordcount in SQL QA. I can access the Recordset with no problem. How can I grab the Recordcount with ASP code at the same time I'm...
1
by: teddysnips | last post by:
SQL Server 2000 I have a stored procedure that uses an extended SPROC to send an email notification to my customers when a document is distributed. However, the SPROC has an unexpected side...
4
by: Radu | last post by:
Hi. It seems to be very simple, actually, but I don't know if it is feasible in TSQL. I have a sproc which gathers in one place many calls to different other sprocs, all of them taking a...
3
by: Radu | last post by:
Hi. I have lots of processing to do on the server - from the client (Access) I call a sproc which returns a recordset (the sproc is essentially a big "select"). With the obtained data , I need to...
4
by: Chad Micheal Lawson via .NET 247 | last post by:
I'm stumped at this point and I'm tired of trying things so I'mposting in hopes of some guru with a sharp eye. I have anasp.net app running on a local Win XP Pro box. Within the app,I call a SPROC...
7
by: Mike L. | last post by:
Hi, I got this 'EXECUTE permission denied on object <mySproc>' error message everytime I try executing my SQL server Sproc. What's this and how to fix this err? many thnaks in advance, mike
0
by: seevion | last post by:
My first post (beginner).. I hope it is clear enough and appreciate your taking the time to consider helping. I have an existing sproc that takes a cart transaction from a table and inserts shared...
5
by: =?Utf-8?B?UlBhcmtlcg==?= | last post by:
I used the wizard to generate a typed dataset for my table and let it create my SPROCs. It created everything, and the GetData() method and the custom GetByUserName query works great, but when I...
1
by: Looch | last post by:
Hi All, I originally wrote a sproc with one of the parameters set as SqlDBType.nvarchar,8. The parameter in the sproc was defined as 8 cahracters and the column in the table was also set to...
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.