Reputation: 6101
I have a set of tables that have the following structure repeated multiple times:
Sign|Value
|1000
-|1000
So there is a "sign" column followed by the "value" column...and both values are varchar(..)
. I am building tables from this base data and want to convert to a decimal using sign.
At the moment I have been doing the following:
CASE
WHEN LTRIM(RTRIM(s.PreviousPointBalanceSign)) = '-'
THEN -1 * CAST(s.PreviousPointBalance AS decimal(14,2))
ELSE CAST(s.PreviousPointBalance AS decimal(14,2))
END AS PreviousPointBalance
But I would like to know if using a function to achieve this would give me better performance?
Upvotes: 3
Views: 4447
Reputation: 6018
Obviously the best choice would be to convert your two columns into one, but since that's not an option. I think your stuck. If you could alter your table and make [sign] char(1), you'll have 2 bytes less per row. Which is you have 25 million rows * 2 bytes = 50MB. So narrowing that should help at least a little bit.
As far as a function, I don't know if there is a faster way to do it. There is a simpler way though like so:
SELECT CAST(CONCAT(RTRIM([sign]),value) AS DECIMAL(14,2)) AS PreviousPointBalance
FROM yourTable
You only need RTRIM() because SQL Server doesn't care about spaces around the varchar to be casted to decimal. If you were to alter your table to CHAR(1), you wouldn't even have to worry about RTRIM()
SELECT CAST(' -99 ' AS DECIMAL(14,2)) --works
SELECT CAST('- 99 ' AS DECIMAL(14,2)) --fails
Upvotes: 1
Reputation: 5757
What kind of function are you planning to use? Basically functions does not help with the performance. They simplify the code, they give you ability to re-use the code and so on. Plain SQL will always be better or the same. In your case the best solution would be to store the data in the proper way, however if it is not possible then try to do the same thing in different ways. For example:
1) CASE WHEN Sign like '%-%';
2) CASE WHEN LEN(Sign) > 0.
3) You can also avoid CASE at all: SELECT CAST(Value AS Decimal(14,2)) * ((-1 + (LEN(Sign)^1) * 2))
You can also use calculated persisted column to calculate the value when it is inserted.
UPD:
After small tests, optimizer shown absolutely the same plan for all of the ways:
CREATE TABLE #test
(
value VARCHAR(3),
[sign] VARCHAR(3)
);
DECLARE @a INT = 0;
WHILE (@a < 1000)
BEGIN
INSERT INTO #test VALUES (CAST(@a AS VARCHAR(100)), CASE WHEN @a%2 =0 THEN '-' ELSE '' END )
SET @a=@a+1;
END
SELECT
CASE
WHEN LTRIM(RTRIM(s.[sign])) = '-'
THEN -1 * CAST(s.value AS decimal(14,2))
ELSE CAST(s.value AS decimal(14,2))
END AS PreviousPointBalance
FROM #test s
SELECT
CASE
WHEN s.[sign] LIKE '%-%'
THEN -1 * CAST(s.value AS decimal(14,2))
ELSE CAST(s.value AS decimal(14,2))
END AS PreviousPointBalance
FROM #test s
SELECT
CASE
WHEN LEN(s.sign) >0
THEN -1 * CAST(s.value AS decimal(14,2))
ELSE CAST(s.value AS decimal(14,2))
END AS PreviousPointBalance
FROM #test s
SELECT CAST(Value AS Decimal(14,2)) * ((-1 + (LEN(Sign)^1) * 2)) AS PreviousPointBalance
FROM #test s
Upvotes: 1