January 25, 2011 at 1:59 pm
Craig,
I am attaching a text file with the proc's code. Please excuse the fact that I am including the whole proc. I thought it might give a better sense of what I was explaining before. Running this proc produces seven queries, which I have saved and zipped into one execution file.
I call this proc (SalaryCalc) from another proc (sp_helper), which passes the needed parameters. The way I call it is: EXEC dbo.SalaryCalc 'BUL','[some emp #]' or EXEC dbo.SalaryCalc 'HRG','[some emp #]', etc.
I create the temp table #tmpSalary in sp_helper, then I call SalaryCalc to populate it and then join #tmpSalary with other tables.
Here is the for creating the temp table:
CREATE TABLE #tmpSalary
(Empvarchar(15),
CCSalEffDatedatetime,
CurrSalarymoneyDEFAULT 0,
CCIncPctnumeric (8,4) DEFAULT 0,
CCIncAmtmoneyDEFAULT 0,
PreviousSalarymoneyDEFAULT 0,
PriorYearSalary moneyDEFAULT 0,
LastIncPctnumeric (8,4) DEFAULT 0,
LastIncAmtmoneyDEFAULT 0,
PreviousIncPctnumeric(8,4) DEFAULT 0,
NewSalarymoneyDEFAULT 0)
Two points:
1. When I compare the execution plans running this proc under the WinUser and the nonWinUser, I get the same percentages. Exactly the same, even if they run at different times.
2. When I look at the execution plans is seems that query #4, which executes @sSQLUpdateLastYear takes the longest always.
I hope all this made sense.
January 25, 2011 at 3:36 pm
MB-400824 (1/24/2011)
Hi,I have two users on a SQL Server 2005:
1. Windows AD account, which has admin rights to the SQL Server.
2. SQL Server account, which is given individual rights to select stored procedures and tables.
It seems that I get different performance depending on which account runs the stored procedure. With the Windows AD account, I would get results back in 4-5 seconds, and with the SQL server account I am getting 20-25 seconds for the same stored procedure.
Does anybody know why this is happening?
Thanks a lot in advance.
M
It looks to me like the security implemented in the view NuHRS.dbo.Eml is the cause.
The filter on the select from ZZSecRol is 75% of the cost and will only kick in for a non sysadmin user.
For nudevadmin and dbo, I am pretty sure that SQL will ignore the rest of the predicates as they are logical OR'd.
WHERE
user_name () = N'nudevadmin' OR
user_name () = N'dbo' OR
[NuHRS].[dbo].[ZZSecRol].[Sec] = convert (VARCHAR (50), user_name (), 0) AND
([NuHRS].[dbo].[ZZSecRol].[Rol] = 'AllAccess' OR
[NuHRS].[dbo].[ZZSecRol].[Rol] = 'BenefitMgr' OR
[NuHRS].[dbo].[ZZSecRol].[Rol] = 'CompMgr' OR
[NuHRS].[dbo].[ZZSecRol].[Rol] = 'HR' OR
[NuHRS].[dbo].[ZZSecRol].[Rol] = 'HRMgr' OR
[NuHRS].[dbo].[ZZSecRol].[Rol] = 'PerfAdmin' OR
[NuHRS].[dbo].[ZZSecRol].[Rol] = 'SegDBADP' OR
[NuHRS].[dbo].[ZZSecRol].[Rol] = 'SegDBCC' OR
[NuHRS].[dbo].[ZZSecRol].[Rol] = 'SegDBLTC' OR
[NuHRS].[dbo].[ZZSecRol].[Rol] = 'SegDBSW' OR
[NuHRS].[dbo].[ZZSecRol].[Rol] = 'SegDocument' OR
[NuHRS].[dbo].[ZZSecRol].[Rol] = 'SegExecutive' OR
[NuHRS].[dbo].[ZZSecRol].[Rol] = 'SegGenCounsel' OR
[NuHRS].[dbo].[ZZSecRol].[Rol] = 'SegHRAdmin' OR
[NuHRS].[dbo].[ZZSecRol].[Rol] = 'SegHRAssistant' OR
[NuHRS].[dbo].[ZZSecRol].[Rol] = 'SegITBenSupport' OR
[NuHRS].[dbo].[ZZSecRol].[Rol] = 'SegPayrollGen' OR
[NuHRS].[dbo].[ZZSecRol].[Rol] = 'SegTrainRest' OR
[NuHRS].[dbo].[ZZSecRol].[Rol] = 'SystemAdmin' OR
[NuHRS].[dbo].[ZZSecRol].[Rol] = 'TimAttAdmin')
MM
select geometry::STGeomFromWKB(0x0106000000020000000103000000010000000B0000001000000000000840000000000000003DD8CCCCCCCCCC0840000000000000003DD8CCCCCCCCCC08408014AE47E17AFC3F040000000000104000CDCCCCCCCCEC3F9C999999999913408014AE47E17AFC3F9C99999999991340000000000000003D0000000000001440000000000000003D000000000000144000000000000000400400000000001040000000000000F03F100000000000084000000000000000401000000000000840000000000000003D0103000000010000000B000000000000000000143D000000000000003D009E99999999B93F000000000000003D009E99999999B93F8014AE47E17AFC3F400000000000F03F00CDCCCCCCCCEC3FA06666666666FE3F8014AE47E17AFC3FA06666666666FE3F000000000000003D1800000000000040000000000000003D18000000000000400000000000000040400000000000F03F000000000000F03F000000000000143D0000000000000040000000000000143D000000000000003D, 0);
January 25, 2011 at 3:45 pm
I noticed the major difference between the two user types is the additional query against tblYY. (ANDtblX.Div IN (SELECT FieldId FROM tblYY WHERE HRG='''+@EmpId+''') ).
Unfortunately, it seems like all the execution plans you provided are based on @AccessLevel <> 'HRG'. Therefore, we can't compare the related execution plans.
What I also noticed: there are numerous RID lookups, some of them consuming more than 90% of te execution time. It seems like the indexes are not covering the queries. You might want to add the required columns as INCLUDED(), especially to the ZZEml table. This should help overall performance.
January 26, 2011 at 9:45 am
MB, as Lutz pointed out, none of these queries allow us to compare the difference in the queries in your system when you do, or don't, add in the:
AND tblX.Div IN (SELECT FieldId FROM tblYY WHERE HRG='''+@EmpId+''')'
clause. Btw, easier way to re-write this would probably be to set the @sSQLSalary string once, and then merely append the extra where clause. Would save you keeping two definitions of the same base query.
Now, to my earlier question, this does have multiple subcalls. In particular, this component:
EXEC (@sSQLSalary)
EXEC (@sSQLPreviousSalary)
EXEC (@sSQLPriorYearSalary)
EXEC (@sSQLUpdateLastYear)
EXEC (@sSQLUpdateSalary5)
EXEC (@sSQLUpdateCCRecoms)
Here's what I'd like you to do. Create a quick logging table, it doesn't need a lot we just need it for a few minutes. Then create a second copy of the proc that does the following.
First, the logging table:
CREATE TABLE zLoggingDataFromProc ( StepName VARCHAR(100), DurationInMS BIGINT)
Next, wrapper your code as so:
DECLARE @TimeStart AS DATETIME
SET @TimeStart = GETDATE()
EXEC (@sSQLSalary)
INSERT INTO zLoggingDataFromProc VALUES ( '@sSQLSalary', DATEDIFF( ms, @TimeStart, GETDATE()))
SET @TimeStart = GETDATE()
EXEC (@sSQLPreviousSalary)
INSERT INTO zLoggingDataFromProc VALUES ( '@sSQLPreviousSalary', DATEDIFF( ms, @TimeStart, GETDATE()))
SET @TimeStart = GETDATE()
EXEC (@sSQLPriorYearSalary)
INSERT INTO zLoggingDataFromProc VALUES ( '@sSQLPriorYearSalary', DATEDIFF( ms, @TimeStart, GETDATE()))
SET @TimeStart = GETDATE()
EXEC (@sSQLUpdateLastYear)
INSERT INTO zLoggingDataFromProc VALUES ( '@sSQLUpdateLastYear', DATEDIFF( ms, @TimeStart, GETDATE()))
SET @TimeStart = GETDATE()
EXEC (@sSQLUpdateSalary5)
INSERT INTO zLoggingDataFromProc VALUES ( '@sSQLUpdateSalary5', DATEDIFF( ms, @TimeStart, GETDATE()))
SET @TimeStart = GETDATE()
EXEC (@sSQLUpdateCCRecoms)
INSERT INTO zLoggingDataFromProc VALUES ( '@sSQLUpdateCCRecoms', DATEDIFF( ms, @TimeStart, GETDATE()))
SET @TimeStart = GETDATE()
Perform three runs with an NT login, and 3 runs without it. That'll give us baseline data. What we want to see is if all of these sub-calls are running slower, or if it's one or two, allowing us to concentrate either on specific query plans, or on the generic call for:
tblX.Div IN (SELECT FieldId FROM tblYY WHERE HRG='''+@EmpId+''')'
If this code doesn't make sense to you, it's basically just setting the @TimeStart variable to the current moment, then after the subcall runs, it's getting the difference in milliseconds from before and after the call, and dropping it to zLoggingDataFromProc
Do not keep this included in your primary production proc, it's almost always only used for testing.
Never stop learning, even if it hurts. Ego bruises are practically mandatory as you learn unless you've never risked enough to make a mistake.
For better assistance in answering your questions[/url] | Forum Netiquette
For index/tuning help, follow these directions.[/url] |Tally Tables[/url]
Twitter: @AnyWayDBA
January 26, 2011 at 3:58 pm
I have been busy with everyone's suggestions today.
mister.magoo:
Thank you for pointing out that the non-Winuser was making calls to those tables. I am in a situation where I am attaching a module to a vendor application, and this non-Winuser is created and maintained within that vendors application. But I can use it on the server end, and so I have been suspecting that this user is making extra calls to resolve security, but I didn't know how to find that out. I assume that it was obvious in the execution plans, so I will make an effort to understand how to better read those.
LutzM and Craig:
I have to make that extra call to tblYY. (AND tblX.Div IN (SELECT FieldId FROM tblYY WHERE HRG='''+@EmpId+''') ). That is what makes the difference between the users and what they are allowed to see.
As far as the adding indexes, again I am dealing with a vendor app, and I have some control, but not all, so I will see what I can do with that.
Craig, I understand what you mean about the appending the strings into one and making the call once. I also did what you suggested with the logging table. I am attaching the results in a spreadsheet. I had to add a column for the user. I ran the proc with different params, so I have BUL winuser, BUL non-winuser, HRG winuser, HRG non-winuser, and Mgr. I hope that is enough.
I think that the solution for me might be to try and not use the non-WinUser. I was just curious why the difference.
If you have any additional comments for me that would be great, if not I am extremely thankful for everyone's help and patience.
M
January 26, 2011 at 4:19 pm
First of all: you did an excellent job setting up the test scenario and collecting the data!
What I would do as a next step is to compare the actual execution plans for @sSQLUpdateLastYear, run by BUL non-Windows-user and BUL Windows-user.
The reason why I picked those two: the time difference between non-Windows and Windows user is by far the most significant for the @sSQLUpdateLastYear sproc. The duration per user user (BUL, HRG and MGR) is almost identical within a user/non-user group, so this shouldn't really make the difference. I picked BUL since it has the largest difference between Win and non-Win.
Maybe there's something obvious when comparing the two execution plans...
January 26, 2011 at 4:28 pm
This is just from my past experience. If you are on a 64 bit box and using a linked server for your process i have noticed big delays one of the reason is because windows account in a 64 bit server has to go through more number of checks like NTLM, Kerberos..etc...
January 26, 2011 at 4:58 pm
MB-400824 (1/26/2011)
mister.magoo:...I assume that it was obvious in the execution plans, so I will make an effort to understand how to better read those.
Let me help you out with a starter for 10....
You mentioned that Query#4 was slow, so I opened the sql execution plan for Query4 and saw this:
Notice the Filter shows as 75% of the query cost... I saw that and clicked it. Then Press F4 - to open the Properties window - this can be seen on the right.
The properties window shows you a lot more detailed information about the selected item.
In this case it was the "Predicate" I was interested in because the item was a Filter.
Clicking on the ellipses to the right of the Property value (they appear when your mouse is over the value) opens a small window containing the WHERE clause for the query:
Copy this and paste it into a new query window , format it a bit and you can see the contents a bit clearer:
user_name () = N'nudevadmin' OR
user_name () = N'dbo' OR
[NuHRS].[dbo].[ZZSecRol].[Sec] = convert (VARCHAR (50), user_name (), 0) AND
([NuHRS].[dbo].[ZZSecRol].[Rol] = 'AllAccess' OR
... (code omitted for brevity)...
[NuHRS].[dbo].[ZZSecRol].[Rol] = 'TimAttAdmin')
You can then see quite plainly that nudevadmin and dbo always get access, while other users only get access based on the lookup to ZZSecRol.
Hope that helps as a starter to understanding the plans - obviously there is much, much more to it than that, but investigating the properties of objects in the plan will often help to identify potential issues.
MM
select geometry::STGeomFromWKB(0x0106000000020000000103000000010000000B0000001000000000000840000000000000003DD8CCCCCCCCCC0840000000000000003DD8CCCCCCCCCC08408014AE47E17AFC3F040000000000104000CDCCCCCCCCEC3F9C999999999913408014AE47E17AFC3F9C99999999991340000000000000003D0000000000001440000000000000003D000000000000144000000000000000400400000000001040000000000000F03F100000000000084000000000000000401000000000000840000000000000003D0103000000010000000B000000000000000000143D000000000000003D009E99999999B93F000000000000003D009E99999999B93F8014AE47E17AFC3F400000000000F03F00CDCCCCCCCCEC3FA06666666666FE3F8014AE47E17AFC3FA06666666666FE3F000000000000003D1800000000000040000000000000003D18000000000000400000000000000040400000000000F03F000000000000F03F000000000000143D0000000000000040000000000000143D000000000000003D, 0);
January 26, 2011 at 6:25 pm
I agree with Lutz. Nice work on the data collection as well. The next step is to examine the query execution plans for @sUpdateLastYear between the windows and non-windows users. That's a 9 second difference between the two queries. It'll explain the most about what's giving you the biggest problems here.
Take your parameter declarations, set them to the values you're passing, and bring in the two versions of the same statement. Run it with actual execution plans turned on. Then save the plans like you did before.
Never stop learning, even if it hurts. Ego bruises are practically mandatory as you learn unless you've never risked enough to make a mistake.
For better assistance in answering your questions[/url] | Forum Netiquette
For index/tuning help, follow these directions.[/url] |Tally Tables[/url]
Twitter: @AnyWayDBA
February 10, 2011 at 12:39 pm
Hello,
I am sorry to not responded to the last post, but I have been focusing on implementing the suggestions that have been offered here. I was able to eliminate the separate logic needed for the HRG and moved all I needed to move to another part of the logic. In the end, some of your comments made me re-examine why I was doing that extra logic and it turned out that I was doing something twice. 🙂
I wanted to thank everyone who took the time to read my post and help me in such a short period of time. Your comments have helped me a great deal and I feel that my understanding for writing stored procedures is improved tremendously. Hopefully in time I will be able to help someone else.
Thanks again!!!
M
February 10, 2011 at 1:14 pm
MB-400824 (2/10/2011)
Hello,I am sorry to not responded to the last post, but I have been focusing on implementing the suggestions that have been offered here. I was able to eliminate the separate logic needed for the HRG and moved all I needed to move to another part of the logic. In the end, some of your comments made me re-examine why I was doing that extra logic and it turned out that I was doing something twice. 🙂
I wanted to thank everyone who took the time to read my post and help me in such a short period of time. Your comments have helped me a great deal and I feel that my understanding for writing stored procedures is improved tremendously. Hopefully in time I will be able to help someone else.
Thanks again!!!
M
GREAT JOB!!
For several reasons:
You took the advice provided by some of us not only to modify the query but to re-examine the processing logic itself.
You found a way to process your data more efficiently.
This process improved your skills in writing sprocs.
And, last but not least, you posted back that you found a solution.
Did you notice how often I used "YOU"? All we did is sharing some ideas and concepts....
So, again: GREAT JOB!!
February 10, 2011 at 1:32 pm
MB-400824 (2/10/2011)
Your comments have helped me a great deal and I feel that my understanding for writing stored procedures is improved tremendously. Hopefully in time I will be able to help someone else.
Both those things are the best things to hear. Congratulations on finding the result. Hope to see you around again.
Never stop learning, even if it hurts. Ego bruises are practically mandatory as you learn unless you've never risked enough to make a mistake.
For better assistance in answering your questions[/url] | Forum Netiquette
For index/tuning help, follow these directions.[/url] |Tally Tables[/url]
Twitter: @AnyWayDBA
Viewing 12 posts - 16 through 26 (of 26 total)
You must be logged in to reply to this topic. Login to reply