Recruitment test
Solving the recruitment test from Multisoft Solutions
Background *
A couple of days ago (read: early new years eve) I looked through a couple of job ads on (the very ugly) vakanser.se website and stumbled upon (ergh) an ad from swedish firm Multisoft Solutions. While pondering over the solution I thought to myself "hey, why not put it on there". Said and done, here it is:
Problem *
Imagine the following procedure in T-SQL:
CREATE PROCEDURE dbo.ObscureProcedure
@i int
AS
BEGIN
SELECT
CASE
WHEN @i < 193 THEN
COUNT(*) * @i
ELSE
COUNT(*) * 193
END AS ObscureCount
FROM
VacationGadget
WHERE
@i < 886
END
The logic should be simple enough to follow.
The task at hand, is to figure what the following code would return:
long sum = 0;
for (int i = 0; i < 891; i++)
{
sum += GetIntFromSQL("EXEC dbo.ObscureProcedure @i = " + i.ToString());
}
return sum;
*Side note: That piece of code is horrible. Never, ever do something like it. Thank you.
Now, running the following statement (in the sql server):
EXEC dbo.ObscureProcedure @i = 496
Would give you the answer
ObscureCount
------------
1999287
(1 row(s) affected)
Solution *
Now, thing is, they've pretty much already given us the answer. The steps from here to a solution is pretty clear:
Convert the SQL procedure into a function
Test the function
??
Profit (or not..)
Let's start with the test. Set this up in a unit test (I used nunit, but use whatever you want):
Assert.AreEqual(1999287, dbo.ObscureProcedure(496));
This (of course) will fail the first time it's run, but worry not. We'll soon make it spew green into your eyes.
Since the procedure uses COUNT(*)
we need to calculate the number of rows the table's got. Our procedure doesn't use @i
unless
it's lt
193 so we need to 1999287 / 193
which'll give us 10359
.
The next step in our path to glory would be tracing the control flow in the procedure, and rebuild it (in your language of choice):
public static int ObscureProcedure(int i)
{
const int rows = 10359 //remember?
if (i >= 886) //WHERE @i < 886
return 0;
if (i < 193) //CASE WHEN @i < 193 THEN COUNT(*) * @i
return rows * i;
else
return rows * 193;
}
And that should be pretty self-explanatory. Simply put:
The WHERE clause in SQL must be evaluated first since that's what the SQL server does
Continue as in the original procedure
That's it. You could probably do things in another way, like using a switch-case and some clever subtraction or multiplication. But why bother? Besides, using stuff like that in production environments would just be stupid. But if you do, you should probably switch profession.