Why does this cursor produce results in the incorrect order?

The PRINT statement inside the WHILE loop does execute in the order you expect, but the output is buffered before sys.sp_executesql returns. Implementation details mean the buffered output is reversed.

Using RAISERROR (@cmd, 0, 1) WITH NOWAIT; instead of PRINT forces the buffer to flush after each call, giving you the results in the order you expect. IIRC the NOWAIT trick only works per-row for the first 500 rows. In any case, all this is undocumented stuff that could change at any time, so please don't rely on it - I only mention it to explain what you see.

The reversal does not occur if you replace the sp_executesql call with EXEC (@cmd) AT ' + @Server + ' though that does require a USE database command prefixed to @cmd and also the linked server needs to be enabled for RPC. This is not a recommendation either, just showing the output reversal is a quirk of sp_executesql.


Given the new information laid out in the comments, there are at least three solutions to this that don't require this mess of dynamic SQL and manual, after the fact reaction:

use a ruler

Seriously, he's a DBA with job responsibilities at your company. Surely you can implement a policy that tables have clustered indexes except in certain scenarios (and in such case, they must be signed off on). When stated as a policy this becomes a potential quantifiable measure for job performance.

implement a DDL trigger

It is very easy to capture CREATE_TABLE events in DDL triggers, and then check the sys.indexes or sys.partitions catalog views for the presence of a clustered index. No clustered index? Rollback. This will eliminate the possibility of SELECT INTO, since you can't define a clustered index in advance, but maybe that's ok.

make him develop in Azure

You can't create a table in the cloud without a clustered index. If you make him develop there, no rulers or DDL triggers required.


Why are you using ordinals? Instead of:

ORDER BY 1,2,3 DESC;

Try referencing the aliases directly:

ORDER BY TableName, IndexName, ExecOrder;

Also you use a cursor to build up @cmd and I'd expect somewhere for you to be appending it. As it stands it appears that you are looping through the cursor but only ever executing the @cmd from the very last iteration in the loop. So what is the purpose of the cursor? And why aren't you using better cursor options if the cursor is, indeed, necessary? (If it isn't, you can change it to a single TOP query).