Everyone knows Implicit Conversion is bad
It can ruin SARGability, defeat index usage, and burn up your CPU like it needs some Valtrex. But what about explicit conversion? Is there any overhead? Turns out, SQL is just as happy with explicit conversion as it is with passing in the correct datatype in the first place.
Here’s a short demo:
SET NOCOUNT ON; SELECT ISNULL([x].[ID], 0) AS [ID] , ISNULL(CAST([x].[TextColumn] AS VARCHAR(10)), 'A') AS [TextColumn] INTO [dbo].[Conversion] FROM ( SELECT TOP 1000000 ROW_NUMBER() OVER ( ORDER BY ( SELECT NULL ) ) , REPLICATE('A', ABS(CONVERT(INT, CHECKSUM(NEWID()))) % 10 + 1) FROM [sys].[messages] AS [m] ) [x] ( [ID], [TextColumn] ); ALTER TABLE [dbo].[Conversion] ADD CONSTRAINT [pk_conversion_id] PRIMARY KEY CLUSTERED ([ID]); CREATE NONCLUSTERED INDEX [ix_text] ON [dbo].[Conversion] ([TextColumn])
One table, one million rows, two columns! Just like real life! Let’s throw some queries at it. The first one will use the wrong datatype, the second one will cast the wrong datatype as the right datatype, and the third one is our control query. It uses the right datatype.
SET STATISTICS TIME, IO ON DECLARE @txt NVARCHAR(10) = N'A', @id INT SELECT @id = [c1].[ID] FROM [dbo].[Conversion] AS [c1] WHERE [c1].[TextColumn] = @txt GO DECLARE @txt NVARCHAR(10) = N'A', @id INT SELECT @id = [c1].[ID] FROM [dbo].[Conversion] AS [c1] WHERE [c1].[TextColumn] = CAST(@txt AS VARCHAR(10) ) GO DECLARE @txt VARCHAR(10) = 'A', @id INT SELECT @id = [c1].[ID] FROM [dbo].[Conversion] AS [c1] WHERE [c1].[TextColumn] = @txt GO
The results shouldn’t surprise most of you. From statistics time and I/O, the first query is El Stinko. The second two were within 1ms of each other, and the reads were always the same over every execution. Very little CPU, far fewer reads.
Query 1: Table 'Conversion'. Scan count 1, logical reads 738, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. SQL Server Execution Times: CPU time = 47 ms, elapsed time = 47 ms. Query 2: Table 'Conversion'. Scan count 1, logical reads 63, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. SQL Server Execution Times: CPU time = 0 ms, elapsed time = 4 ms. Query 3: Table 'Conversion'. Scan count 1, logical reads 63, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. SQL Server Execution Times: CPU time = 0 ms, elapsed time = 5 ms.
So there you go
Explicit conversion of parameter datatypes doesn’t carry any horrible overhead. Is it easier to just pass in the correct the datatype? Yeah, probably, but you might be in a position where you can’t control the parameter datatype that’s incoming, but you can CAST or CONVERT it where it touches data.
Thanks for reading!
Brent says: the key here is that we’re taking an incoming NVARCHAR variable, and casting it in our query to be VARCHAR to match the table definition. This only works if you can guarantee that the app isn’t going to pass in unicode – but in most situations, that’s true, because the same app is also responsible for inserting/updating data in this same table, so it’s already used to working with VARCHAR data. Also, just to be clear, Erik’s talking about casting the variable – NOT every row in the table. That part still blows.