Link to home
Start Free TrialLog in
Avatar of ussynth
ussynth

asked on

Query fast in Management Studio, slow in Delphi XE3

I have an indexed query that returns in < 1 second when I run it in SQL Server Management Studio (SQL Server 2008), but when it runs in my Delphi XE3 code, it takes 4 - 5 seconds.  

I've never experienced this before.  I'm hoping someone has some idea of what might be going on.  As you can see below, the query is not overly complex.  I tried putting in in a stored procedure and tried getting rid of the "declare" section and nothing seems to make any difference.  

I have other queries that are MUCH more complex, yet perform the same in Delphi as they do in Management Studio.  The only big difference is that this is the first time I've used CTEs in a query (in Delphi).

I'm using a TADOQuery component.  Also, I use the same TADOQuery component for all of my queries, so it's not a component setting.  Well, actually I use 3 because I sometimes need to go off and do somethings and don't want to lose my original result set.  But I use the same component for all of my major queries.  

Thank you for any help you can give me.
Mike

declare
      -- First date
      @dtDate date = '2012-12-15',
      -- Number of days to show
      @numDays integer = 4;
 with DateInspection as
 (
      select * from Inspection where cast(ScheduledDate as date) >= @dtDate and cast(ScheduledDate as date) < dateadd(day, @numDays, @dtDate)
 ),
 DateSummary as
 (
      select InspectionID, BatchID, InspQuantity, ScheduledDate, SortOrder, cast(ScheduledDate as date) SDate,
            -- How many per day?
            (select count(cast(ScheduledDate as date)) from DateInspection where cast(ScheduledDate as date)=cast(i.ScheduledDate as date)) NumPerDay,
            -- How many per hour per day?
            (select count(cast(ScheduledDate as date)) from DateInspection
                  where (cast(ScheduledDate as varchar) + cast(datepart(hh, ScheduledDate) as varchar))=(cast(i.ScheduledDate as varchar) + cast(datepart(hh, ScheduledDate) as varchar))) NumPerHourPerDay
      from DateInspection i
      where ScheduledDate is not null and Cancelled=0 and PrintedByBUID is null
 )
 select tt.StartDate, ds.*, (select max(NumPerHourPerDay) from DateSummary where SDate=ds.SDate) MaxPosPerDay, b.SalesOrder, cast(datepart(hh, ds.ScheduledDate) as varchar) InspHour
 from (select * from TimeTable where cast(StartDate as date) >= @dtDate and cast(StartDate as date) < dateadd(day, @numDays, @dtDate)) tt
       left outer join DateSummary ds on tt.StartDate=cast(ds.ScheduledDate as date)
      left outer join Batch b on ds.BatchID=b.BatchID
 order by tt.StartDate, ds.ScheduledDate, ds.SortOrder;
Avatar of Mike McCracken
Mike McCracken

How are you calculating the query time?

mlmcc
Avatar of ussynth

ASKER

Thanks for asking.  I'm just stepping through the code and watching what happends when I hit the "Open" statement (below).  It takes 4 - 5 seconds before I hit the first line of code after "Open".

         SQL.Clear;
         SQL.Add('declare ' + #13 +
                 '      -- First date ' + #13 +
                 '      @dtDate date = ' + QuotedStr(FormatDateTime('yyyy-mm-dd', datePlanner.Date)) + ', ' + #13 +
                 '      -- Number of days to show ' + #13 +
                 '      @numDays integer = ' + IntToStr(iDayCount) + '; ' + #13 +
                 ' with DateInspection as ' + #13 +
                 ' ( ' + #13 +
                 '      select * from Inspection where cast(ScheduledDate as date) >= @dtDate and cast(ScheduledDate as date) < dateadd(day, @numDays, @dtDate) ' + #13 +
                 ' ), ' + #13 +
                 ' DateSummary as ' + #13 +
                 ' ( ' + #13 +
                 '      select InspectionID, BatchID, InspQuantity, ScheduledDate, SortOrder, cast(ScheduledDate as date) SDate, ' + #13 +
                 '            -- How many per day? ' + #13 +
                 '            (select count(cast(ScheduledDate as date)) from DateInspection where cast(ScheduledDate as date)=cast(i.ScheduledDate as date)) NumPerDay, ' + #13 +
                 '            -- How many per hour per day? ' + #13 +
                 '            (select count(cast(ScheduledDate as date)) from DateInspection ' + #13 +
                 '                  where (cast(ScheduledDate as varchar) + cast(datepart(hh, ScheduledDate) as varchar))=(cast(i.ScheduledDate as varchar) + cast(datepart(hh, ScheduledDate) as varchar))) NumPerHourPerDay ' + #13 +
                 '      from DateInspection i ' + #13 +
                 '      where ScheduledDate is not null and Cancelled=0 and PrintedByBUID is null ' + #13 +
                 ' ) ' + #13 +
                 ' select tt.StartDate, ds.*, (select max(NumPerHourPerDay) from DateSummary where SDate=ds.SDate) MaxPosPerDay, b.SalesOrder, cast(datepart(hh, ds.ScheduledDate) as varchar) InspHour ' + #13 +
                 ' from (select * from TimeTable where cast(StartDate as date) >= @dtDate and cast(StartDate as date) < dateadd(day, @numDays, @dtDate)) tt ' + #13 +
                 '       left outer join DateSummary ds on tt.StartDate=cast(ds.ScheduledDate as date) ' + #13 +
                 '      left outer join Batch b on ds.BatchID=b.BatchID ' + #13 +
                 ' order by tt.StartDate, ds.ScheduledDate, ds.SortOrder;');

         Open;
i'm not that good any more at sql server, but on the delphi side

do you have any grids attached (and datasource)
> loading the grid takes time too

how many records are you returning ?

the best way is to start a trace and see what is actually happening
the trace will also contain the real time spent on the database
http://www.sqlservercentral.com/Forums/Topic576752-146-1.aspx
Fetch minimum fields - just you need. Try to set in Adoquery:

qry.CursorType:=ctKeyset;
qry.LockType:=ltReadOnly;
qry.DisableControls;
try
     qry.Open;
finally
      qry.EnableControls;
end;

Open in new window

Avatar of ussynth

ASKER

Thank you both for the suggestions.  I'd hoped sinisav's suggestion would help, but it does not.  

Geert_Gruwez - I don't have a component tied to my query.  I never use DB components, I end up doing too many things with the data that's returned.  So instead I just loop through the result set in my code.  The query I'm currently runnign returns 15 records.  I put a check in to time the query and it's taking 8.335 seconds just to do the "open" command.  No time is spent building a list (or retrieving data from the result set).  I take the query (using Clipboard.AsText := SQL.Text;) and paste it into MS Management Studio and that result set is returned in < 1 second.

I've just never seen anything like this before and I'm getting desparate!

Thanks for trying.
Mike
8 seconds to establish a connection seems quite excessive.  
What does your connection string look like?
Where is your program running, relative to the database?
Is your program running with the same authentication as you are running the mgt. console?
Maybe there is some additional parsing/processing of your string.  Try the following.  I tried eliminating the comments and the carriage returns.
SQL.Add('declare ')
SQL.Add('@dtDate date = ' + QuotedStr(FormatDateTime('yyyy-mm-dd', datePlanner.Date)) );
SQL.Add('@numDays integer = ' + IntToStr(iDayCount) + '; ');
SQL.Add('with DateInspection as ( ');
SQL.Add('select * from Inspection where cast(ScheduledDate as date) >= @dtDate and cast(ScheduledDate as date) < dateadd(day, @numDays, @dtDate) ), ');
SQL.Add('DateSummary as ( ');
SQL.Add('select InspectionID, BatchID, InspQuantity, ScheduledDate, SortOrder, cast(ScheduledDate as date) SDate, ');
SQL.Add('(select count(cast(ScheduledDate as date)) from DateInspection where cast(ScheduledDate as date)=cast(i.ScheduledDate as date)) NumPerDay, ');
SQL.Add('(select count(cast(ScheduledDate as date)) from DateInspection ');
SQL.Add('where (cast(ScheduledDate as varchar) + cast(datepart(hh, ScheduledDate) as varchar))=(cast(i.ScheduledDate as varchar) + cast(datepart(hh, ScheduledDate) as varchar))) NumPerHourPerDay ');
SQL.Add('from DateInspection i ');
SQL.Add('where ScheduledDate is not null and Cancelled=0 and PrintedByBUID is null ) ');
SQL.Add('select tt.StartDate, ds.*, (select max(NumPerHourPerDay) from DateSummary where SDate=ds.SDate) MaxPosPerDay, b.SalesOrder, cast(datepart(hh, ds.ScheduledDate) as varchar) InspHour ' );
SQL.Add('from (select * from TimeTable where cast(StartDate as date) >= @dtDate and cast(StartDate as date) < dateadd(day, @numDays, @dtDate)) tt ');
SQL.Add('left outer join DateSummary ds on tt.StartDate=cast(ds.ScheduledDate as date) ');
SQL.Add('left outer join Batch b on ds.BatchID=b.BatchID ');
SQL.Add('order by tt.StartDate, ds.ScheduledDate, ds.SortOrder;');

Open in new window

Probably unrelated to your immediate problem, but:
1. Don't use any form of SELECT * or even SELECT alias.*, get in the habit of defining the columns you need and only the columns you need.

2. Using the something like the following condition is a very bad idea:
             WHERE     CAST(ScheduledDate AS date) >= @dtDate
                        AND CAST(ScheduledDate AS date) < DATEADD(day, @numDays, @dtDate)
If you have and indexes on ScheduledDate they will be ignored as you have an expression on the left hand side.  What is the data type for ScheduledDate?

3.  Instead of this: COUNT(CAST(ScheduledDate AS date)) simply use COUNT(*).  The expression is pointless and even if the Query Optimizer is smart enough to figure that out,  it just makes the code read better for someone else.
ASKER CERTIFIED SOLUTION
Avatar of ussynth
ussynth

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
>>I've already changed what I'm doing just slightly to overcome this issue.  I was just hoping that  someone would have seen this before and have an answer.

I see this every day, but on an oracle database
i never look at the delphi side for tuning the queries (allthough i am a delphi developer)
... all information is stored in the database
> how many buffergets, how many diskreads, and how long the query takes
buffergets relate to cpu, diskreads to io on disk, etc

based on the information on the database i look where indexes are needed
and if those don't help investigate if there is a better way of writing the query
> I rewrite the query and pass the advice on to the delphi devs

for me it looks like you have a query which needs tuning
> you need a mssql dba tuning expert for this
Avatar of ussynth

ASKER

No better solution than to reformat the query.