September 21, 2012 at 4:50 am
Eugene Elutin (9/21/2012)
Another way in c# :
public static int SumDigits(int Input) { return Input.ToString().Sum(c => c - '0'); }
I think that would be slower (and it also requires a higher version of .NET - the function I posted works on SQL Server 2005-2012).
Paul White
SQLPerformance.com
SQLkiwi blog
@SQL_Kiwi
September 21, 2012 at 8:06 am
glad im starting to learn c#. i knew deep down that c# was the way to go but this simiple example shows me im right.
For performance Issues see how we like them posted here: How to Post Performance Problems - Gail Shaw[/url]
Need to Split some strings? Jeff Moden's DelimitedSplit8K[/url]
Jeff Moden's Cross tab and Pivots Part 1[/url]
Jeff Moden's Cross tab and Pivots Part 2[/url]
September 24, 2012 at 3:35 am
SQL Kiwi (9/21/2012)
This is about twice as fast for me:Source:
[font="Courier New"] public static int SumDigits(int Input)
{
int sum = 0;
for (int n = System.Math.Abs(Input); n > 0; sum += n % 10, n /= 10) ;
return sum;
}
[/font]
Unrolling the loop yields a small performance gain. Still doesn't handle NULLs or -2147483648 though.
[Microsoft.SqlServer.Server.SqlFunction]
public static int SumDigits(int Input)
{
int n = (Input >= 0 ? Input : -Input);
return (n % 10) +
((n/10) % 10) +
((n/100) % 10) +
((n/1000) % 10) +
((n/10000) % 10) +
((n/100000) % 10) +
((n/1000000) % 10) +
((n/10000000) % 10) +
((n/100000000) % 10) +
((n/1000000000) % 10);
}
____________________________________________________
Deja View - The strange feeling that somewhere, sometime you've optimised this query before
How to get the best help on a forum
http://www.sqlservercentral.com/articles/Best+Practices/61537September 24, 2012 at 6:22 am
Mark-101232 (9/24/2012)
Unrolling the loop yields a small performance gain. Still doesn't handle NULLs or -2147483648 though.
Unrolling the loop is an interesting idea, I will have a play with that in a minute to see if it works the same for me. I think I prefer the loop though - it's neater to my mind.
Handling NULL and -2147483648 is another good point (though the original test rig can produce neither value, in my defence).
The point I was looking to make is that expression evaluation in T-SQL is interpreted and therefore quite slow.
It is amazing that passing a value out to CLR and back is faster, even without working hard to find an optimal .NET algorithm.
Anyway, updated code to handle all values and NULL below:
CREATE ASSEMBLY Test
FROM 0x4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C010300274C60500000000000000000E00002210B0108000008000000060000000000006E2600000020000000400000000040000020000000020000040000000000000004000000000000000080000000020000000000000300408500001000001000000000100000100000000000001000000000000000000000001C2600004F00000000400000A802000000000000000000000000000000000000006000000C000000802500001C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E7465787400000074060000002000000008000000020000000000000000000000000000200000602E72737263000000A80200000040000000040000000A0000000000000000000000000000400000402E72656C6F6300000C0000000060000000020000000E0000000000000000000000000000400000420000000000000000000000000000000050260000000000004800000002000500A0200000E004000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001330030039000000010000110F00280500000A2C067E0600000A2A160A0F00280700000A0B2B0C06071F0A5D580A071F0A5B0B072DF106162F0406652B0106730800000A2A1E02280900000A2A00000042534A4201000100000000000C00000076322E302E35303732370000000005006C00000044010000237E0000B00100006C01000023537472696E6773000000001C03000008000000235553002403000010000000234755494400000034030000AC01000023426C6F620000000000000002000001471502000900000000FA2533001600000100000007000000020000000200000001000000090000000400000001000000010000000200000000000A0001000000000006003D0036000A00650050000600970084000F00AB0000000600DA00BA000600FA00BA000A003D0122010000000001000000000001000100010010001800000005000100010050200000000096006E000A0001009520000000008618780011000200000001007E00190078001500290078001B0031007800110039007800110011005201680111005D016C01110062017001110078001B000900780011002000230020002E000B0079012E00130082012E001B008B0174010480000000000000000000000000000000001801000002000000000000000000000001002D0000000000020000000000000000000000010044000000000000000000003C4D6F64756C653E004461746162617365332E646C6C0055736572446566696E656446756E6374696F6E73006D73636F726C69620053797374656D004F626A6563740053797374656D2E446174610053797374656D2E446174612E53716C54797065730053716C496E7433320053756D446967697473002E63746F7200496E7075740053797374656D2E446961676E6F73746963730044656275676761626C6541747472696275746500446562756767696E674D6F6465730053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300436F6D70696C6174696F6E52656C61786174696F6E734174747269627574650052756E74696D65436F6D7061746962696C69747941747472696275746500446174616261736533004D6963726F736F66742E53716C5365727665722E5365727665720053716C46756E6374696F6E417474726962757465006765745F49734E756C6C004E756C6C006765745F56616C7565000003200000000000E58AE8812A08674882CB590B906DDF360008B77A5C561934E089060001110911090320000105200101111104200101088146010004005455794D6963726F736F66742E53716C5365727665722E5365727665722E446174614163636573734B696E642C2053797374656D2E446174612C2056657273696F6E3D322E302E302E302C2043756C747572653D6E65757472616C2C205075626C69634B6579546F6B656E3D623737613563353631393334653038390A446174614163636573730000000054557F4D6963726F736F66742E53716C5365727665722E5365727665722E53797374656D446174614163636573734B696E642C2053797374656D2E446174612C2056657273696F6E3D322E302E302E302C2043756C747572653D6E65757472616C2C205075626C69634B6579546F6B656E3D623737613563353631393334653038391053797374656D446174614163636573730000000054020F497344657465726D696E6973746963015402094973507265636973650103200002030611090320000804070208080801000200000000000801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F777301000000000000274C60500000000002000000800000009C2500009C07000052534453FA3B49E3B829D24C859CA14C76F908B103000000633A5C55736572735C5061756C2057686974655C446F63756D656E74735C56697375616C2053747564696F20323031305C50726F6A656374735C4461746162617365335C4461746162617365335C6F626A5C52656C656173655C4461746162617365332E706462004426000000000000000000005E26000000200000000000000000000000000000000000000000000050260000000000000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF2500204000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001001000000018000080000000000000000000000000000001000100000030000080000000000000000000000000000001000000000048000000584000004C02000000000000000000004C0234000000560053005F00560045005200530049004F004E005F0049004E0046004F0000000000BD04EFFE00000100000000000000000000000000000000003F000000000000000400000002000000000000000000000000000000440000000100560061007200460069006C00650049006E0066006F00000000002400040000005400720061006E0073006C006100740069006F006E00000000000000B004AC010000010053007400720069006E006700460069006C00650049006E0066006F0000008801000001003000300030003000300034006200300000002C0002000100460069006C0065004400650073006300720069007000740069006F006E000000000020000000300008000100460069006C006500560065007200730069006F006E000000000030002E0030002E0030002E00300000003C000E00010049006E007400650072006E0061006C004E0061006D00650000004400610074006100620061007300650033002E0064006C006C0000002800020001004C006500670061006C0043006F00700079007200690067006800740000002000000044000E0001004F0072006900670069006E0061006C00460069006C0065006E0061006D00650000004400610074006100620061007300650033002E0064006C006C000000340008000100500072006F006400750063007400560065007200730069006F006E00000030002E0030002E0030002E003000000038000800010041007300730065006D0062006C0079002000560065007200730069006F006E00000030002E0030002E0030002E0030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000C000000703600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
WITH PERMISSION_SET = SAFE;
GO
CREATE FUNCTION dbo.SumDigits
(@Input integer)
RETURNS integer
AS EXTERNAL NAME Test.UserDefinedFunctions.SumDigits;
Source:
[font="Courier New"]public static SqlInt32 SumDigits(SqlInt32 Input)
{
if (Input.IsNull)
{
return SqlInt32.Null;
}
int sum = 0;
for (int n = Input.Value; n != 0; sum += n % 10, n /= 10) ;
return new SqlInt32(sum >= 0 ? sum : -sum);
}
[/font]
Paul White
SQLPerformance.com
SQLkiwi blog
@SQL_Kiwi
September 24, 2012 at 6:39 am
SQL Kiwi (9/24/2012)
Handling NULL and -2147483648 is another good point (though the original test rig can produce neither value, in my defence).
NULL can also be handled by adding 'RETURNS NULL ON NULL INPUT' to the wrapper.
____________________________________________________
Deja View - The strange feeling that somewhere, sometime you've optimised this query before
How to get the best help on a forum
http://www.sqlservercentral.com/articles/Best+Practices/61537September 24, 2012 at 6:50 am
Mark-101232 (9/24/2012)
NULL can also be handled by adding 'RETURNS NULL ON NULL INPUT' to the wrapper.
Yes that is true.
I did wonder about asking what should be returned for NULL input.
Some might prefer a zero return value - I didn't want to assume.
Adding the RETURNS thing does prevent the function being called which is nice.
That reminds me of a very welcome addition to SQL Server 2012: deterministic SQLCLR scalar functions can be constant-folded!
Paul White
SQLPerformance.com
SQLkiwi blog
@SQL_Kiwi
September 25, 2012 at 3:55 am
Just for fun...
Using a lookup table improves performance, a bit suprising (for me at least).
// Pre-calculated sums of 0 to 99
static readonly int[] TwoDigitSums = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
9, 10, 11, 12, 13, 14, 15, 16, 17, 18 };
// Handle NULLs by adding RETURNS NULL ON NULL INPUT to wrapper if required
[Microsoft.SqlServer.Server.SqlFunction]
public static int SumDigits(int Input)
{
return (Input >= 0 ? TwoDigitSums[Input % 100] +
TwoDigitSums[(Input / 100) % 100] +
TwoDigitSums[(Input / 10000) % 100] +
TwoDigitSums[(Input / 1000000) % 100] +
TwoDigitSums[(Input / 100000000) % 100]
: TwoDigitSums[-(Input % 100)] +
TwoDigitSums[(Input / -100) % 100] +
TwoDigitSums[(Input / -10000) % 100] +
TwoDigitSums[(Input / -1000000) % 100] +
TwoDigitSums[(Input / -100000000) % 100]);
}
____________________________________________________
Deja View - The strange feeling that somewhere, sometime you've optimised this query before
How to get the best help on a forum
http://www.sqlservercentral.com/articles/Best+Practices/61537October 25, 2013 at 12:34 pm
I know this is an old thread, but please note that Celko and Improved Celko are the only ones (of the non-CLR ones in the test harness, as written) that survive crossing into BIGINT values (i.e. over 2147483647 but less than 9223372036854775807).
Trivial extensions of some of the others results in errors, probably due to rounding, at least near the maximums.
Viewing 8 posts - 31 through 37 (of 37 total)
You must be logged in to reply to this topic. Login to reply