February 16, 2012 at 8:11 pm
Comments posted to this topic are about the item T-SQL Performance 1 at:
http://www.sqlservercentral.com/Questions
Edited to include the following at 8:23 AM Feb 17, 2012
HAPPY DAYS ARE HERE AGAIN
One of, or perhaps the major objective of the QOD is to teach. In many cases, there is more teaching performed in the discussion, then in the supporting reference(s) cited, as is the case with this question.
No matter how you answered the question, if you drop into this discussion to add a comment I strongly urge you to read on. The comments so far are in themselves a wonderful lesson.
And for that I thank SQL Kiwi, Hugo Kornelis and others to numerous to mention.
February 16, 2012 at 10:29 pm
It is certainly very good practice to match data types, but that is not the only way to obtain a seek here (assuming the default collation is a Windows one like Latin1_General_CI_AS rather than a SQL one like SQL_Latin1_General_CP1_CI_AS):
CREATE PROC QOD_Performance_1
@Get NVARCHAR(20)
AS
SELECT ID,Col
FROM FirstTable WITH (FORCESEEK)
WHERE Col = @Get;
GO
EXECUTE dbo.QOD_Performance_1
@Get = N'XYZ'
Wondering how this works, or what the Constant Scan and Compute Scalar are for? I wrote about the hard work put in by the optimizer with these sorts of data type mismatches here:
Paul White
SQLPerformance.com
SQLkiwi blog
@SQL_Kiwi
February 16, 2012 at 11:07 pm
Nice question. I immediately chose "yes", because it would have been quite sad if you couldn't force a seek somehow 🙂
Need an answer? No, you need a question
My blog at https://sqlkover.com.
MCSE Business Intelligence - Microsoft Data Platform MVP
February 17, 2012 at 12:28 am
Good question
M&M
February 17, 2012 at 12:47 am
Nice, easy question - though I'm sad that as of this time, 8% of respondends got it wrong.
There is an error in the explanation, though. You can not avoid the implicit conversion. You can only change it. Making the parameter varchar(100) instead of nvarchar(20) means that now the implicit conversion from an implicit conversion from varchar to nvvarchar during the scan, to an implicit conversion from nvarchar to varchar during the procedure call.
Or you can leave the parameter as is and add an explicit cast in the query. Again, not avoiding the conversion, but making it explicit.
February 17, 2012 at 1:30 am
Hi All,
Interesting question. I think i must be doing something wrong when testing this myself though.
I've created the table and index like so:
CREATE TABLE TestTable (ID INT, Col VARCHAR(100));
GO
CREATE INDEX IX_Col ON TestTable (Col);
GO
Then created the procedure:
CREATE PROCEDURE Test_Procedure
@Get NVARCHAR(20)
AS
SELECT ID, Col
FROM TestTable
WHERE Col = @Get;
Then populated the table with some random data:
DECLARE @int INT;
SET @int = 1;
WHILE @int <100
BEGIN
INSERT INTO TestTable (id, col)
VALUES (@int, 'name' + CAST(@int AS VARCHAR(100)));
SET @int += 1;
END
Now, this does result in a table scan with the CONVERT_IMPLICIT operator.
If I then change the stored proc to use VARCHAR (or VARCHAR(100)) it still results in a table scan - however the CONVERT_IMPLICIT is gone.
Is this the correct behaviour? or am I doing something silly....
Cheers, 🙂
February 17, 2012 at 1:43 am
I have a table containg a large number
DECLARE @int INT;
SET @int = 1;
WHILE @int <=1000
BEGIN
INSERT INTO TestTable (id, col)
VALUES (@int, 'name' + CAST(@int AS VARCHAR(100)));
SET @int += 1;
I Have Nine Lives You Have One Only
THINK!
February 17, 2012 at 1:48 am
handkot (2/17/2012)
I have a table containg a large number
DECLARE @int INT;
SET @int = 1;
WHILE @int <=1000
BEGIN
INSERT INTO TestTable (id, col)
VALUES (@int, 'name' + CAST(@int AS VARCHAR(100)));
SET @int += 1;
The performant alternative:
Create a Tally or Numbers Table
[/url]
Need an answer? No, you need a question
My blog at https://sqlkover.com.
MCSE Business Intelligence - Microsoft Data Platform MVP
February 17, 2012 at 1:59 am
offtop
The performant alternative:
Create a Tally or Numbers Table
create table dbo.Tally(i int identity(1,1))
go
insert into dbo.Tally DEFAULT VALUES
go 1000000
I Have Nine Lives You Have One Only
THINK!
February 17, 2012 at 2:01 am
Loundy (2/17/2012)
Hi All,Interesting question. I think i must be doing something wrong when testing this myself though.
I've created the table and index like so:
CREATE TABLE TestTable (ID INT, Col VARCHAR(100));
GO
CREATE INDEX IX_Col ON TestTable (Col);
GO
Then created the procedure:
CREATE PROCEDURE Test_Procedure
@Get NVARCHAR(20)
AS
SELECT ID, Col
FROM TestTable
WHERE Col = @Get;
Then populated the table with some random data:
DECLARE @int INT;
SET @int = 1;
WHILE @int <100
BEGIN
INSERT INTO TestTable (id, col)
VALUES (@int, 'name' + CAST(@int AS VARCHAR(100)));
SET @int += 1;
END
Now, this does result in a table scan with the CONVERT_IMPLICIT operator.
If I then change the stored proc to use VARCHAR (or VARCHAR(100)) it still results in a table scan - however the CONVERT_IMPLICIT is gone.
Is this the correct behaviour? or am I doing something silly....
Cheers, 🙂
Ok, it was because my table wasn't big enough 🙂 having 10000 rows in there demonstrated the behaviour correctly 🙂
February 17, 2012 at 2:08 am
handkot (2/17/2012)
offtopThe performant alternative:
Create a Tally or Numbers Table
create table dbo.Tally(i int identity(1,1))
go
insert into dbo.Tally DEFAULT VALUES
go 1000000
You're still performing a loop, aka RBAR (row by agonazing row).
The Tally Table from Jeff executes in less than a second, while this little loop executes for over 1 minute on my machine (see attachment). And I changed the code to 11000, I wouldn't like to know what happened if I kept 1000000.
Need an answer? No, you need a question
My blog at https://sqlkover.com.
MCSE Business Intelligence - Microsoft Data Platform MVP
February 17, 2012 at 2:10 am
Got it right, but I think the answer should be "it depends". On what? The selectivity of the data in Col. You get a seek if you populate your table thus:
INSERT INTO TestTable (id, col)
SELECT TOP 1000000 ROW_NUMBER() OVER (ORDER BY a.name),
'name' + CAST(ROW_NUMBER() OVER (ORDER BY a.name) AS varchar(7))
FROM master.sys.columns a
CROSS JOIN master.sys.columns b
CROSS JOIN master.sys.columns c
... but a scan if you have the following data in the table:
INSERT INTO TestTable (id, col)
SELECT TOP 1000000 ROW_NUMBER() OVER (ORDER BY a.name),
'name' + CAST((ROW_NUMBER() OVER (ORDER BY a.name))/500000 AS char(5))
FROM master.sys.columns a
CROSS JOIN master.sys.columns b
CROSS JOIN master.sys.columns c
Therefore, in the second case, there'd be no way of optimising the stored procedure.
John
February 17, 2012 at 2:29 am
You might also have to remove the ID column to get a seek, depending how selective that column is.
Gail Shaw
Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability
February 17, 2012 at 3:08 am
I immediately thought of using a hint to force the use of the index. Is that wrong somehow?
SELECT ID,Col
FROM FirstTable WITH INDEX(IX_Col)
WHERE Col = @Get;
February 17, 2012 at 3:13 am
cengland0 (2/17/2012)
I immediately thought of using a hint to force the use of the index. Is that wrong somehow?
SELECT ID,Col
FROM FirstTable WITH INDEX(IX_Col)
WHERE Col = @Get;
Not wrong, just less good. With this hint, I expect you to get an index scan, which could be a bit better than a scan of the clustered index, but still far worse than an index seek.
Viewing 15 posts - 1 through 15 (of 56 total)
You must be logged in to reply to this topic. Login to reply