Yet another way to do it, as inspired by http://www.microsoft.com/sql/techinfo/tips/development/queryresults.asp
CREATE PROCEDURE GET_RANDOM_QUOTE
@quote AS varchar(8000) OUTPUT
SET @quote = (SELECT TOP 1 quote FROM quotes ORDER BY NEWID())
On well indexed tables, the performance is generally MUCH worse than my previous code. On the plus side, there's no requirement of a numeric primary key, or even one at all.
Another benefit is that you can use it to grab a different number of random results than 1 without significant additional performance penalties. You could morph the previous code to use a temporary table and some additional logic to build your way up to X number of distinct rows, but there will be a price to pay in terms of performance, which WILL make it the worse choice under certain circumstances (the smaller the table row count and the larger the number of rows pulled, the more likely it is to be suckier... however, it'll still win handily when pulling few enough rows from a table with a large enough row count).
On properly indexed tables, my previous code has equivalent performance for roughly a hundred rows and roughly 20,000 rows. The code above goes from about 50 times slower to over 150 times slower, respectively. (Individual results may vary.)
The reason it sucks increasingly harder as the row count of the table goes up is that the ORDER BY takes much more effort (barring certain special cases, IIRC, sorting performance is at best O(n lg n), depending on the sort used) and MUST be performed, as the number is generated on the fly and is not strictly increasing nor strictly decreasing. The previous code can take advantage of the index on the table to avoid having to sort at all, which is why the performance is equivalent over an order of magnitude increase in the row count. (All it has to do is look up the max autonum using the index, calculate a value, find the first autonum in the index >= to that value, look up the row from the index and return the requested column.)
On a non-indexed table, it might be possible for the code above to beat my previous code, as the previous code would have to search all the rows for the max autonum, grab all the rows with autonum >= to the calculated value, then ORDER BY their autonum, and then return the requested value from the first row.