views:

124

answers:

5

is there a way to show a certain amount of random records from a database table, but heavily influenced by the date and time of creation.

for example:

  • showing 10 records at random, but
  • showing latest with more frequency than the earliest

  • say there are 100 entries in the news table

  • latest (by date time) record would have an almost 100% chance of being selected
  • first (by date time) record would have an almost 0% chance of being selected
  • 50th (by date time) record would have a 50% chance of being selected

is there such a thing in mssql directly? or is there some function (best practice) in c# i can use for this?

thnx

** edit: the title is really horrible, i know. please edit if you have a more descriptive one. thnx

A: 

You can pick an N using exponential distribution (for instance), and than SELECT TOP(N) ordered by date, and choose the last row. You can choose the exponent according to the number of existing rows.

EFraim
This will just give you a varying number recent results...
Michael Mior
@Michael - I said to pick only the last row.
EFraim
Sorry, you're right. Then do you suggest repeating this operation over and over again to get the desired number of recent results? That sounds expensive.
Michael Mior
A: 

Unfortunately I don't know MSSQL, but I can give a high-level suggestion.

  1. Get the date in UNIX time (or some other increasing integral representation)
  2. Divide this value by the max for each column to get a percentage.
  3. Get a random number and multiply by the percentage above.
  4. Sort your columns by this value and take the top N.

This will give more weight to the most recent results. If you want to adjust the relative frequency of older vs later results, you can apply an exponential or logarithmic function to the values before taking the ratio. If you're interested, let me know and I can provide more info.

Michael Mior
A: 

If you can filter results after DB access, or you can submit a query with order by and process results with a reader, then you can add a probabilistic bias to the selection. You see that the higher the bias, the harder the test inside the if, the more random the process.

var table = ...  // This is ordered with latest records first
int nItems = 10;  // Number of items you want
double bias = 0.5;  // The probabilistic bias: 0=deterministic (top nItems), 1=totally random
Random rand = new Random();

var results = new List<DataRow>();  // For example...

for(int i=0; i<table.Rows.Count && results.Count < nItems; i++) {
    if(rand.NextDouble() > bias)
        // Pick the current item probabilistically
        results.Add(table.Rows[i]);  // Or reader.Next()[...]
}
Mau
What if the table is bigger than you client process viable address space?
Remus Rusanu
Good point. It works with sequential access via a reader too.
Mau
+4  A: 

A quite simplistic way might be something like the following. Or at least it might give you a basis to start with.

WITH N AS
(
SELECT id,
       headline,
       created_date,
       POWER(ROW_NUMBER() OVER (ORDER BY created_date ASC),2) * /*row number squared*/
          ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) AS [Weight] /*Random Number*/
  FROM news
  )
  SELECT TOP 10 
       id,
       headline,
       created_date FROM N 
  ORDER BY [Weight] DESC
Martin Smith
+1 that's the way to go, but why complicate yourself with NEWID() and not use RAND()? http://msdn.microsoft.com/en-us/library/ms177610.aspx
Remus Rusanu
@Remus. I think the Rand() is only evaluated once so the same set of results will always be returned http://www.sql-server-helper.com/tips/generate-random-numbers.aspx
Martin Smith
I see. I guess you can always force it to depend on a column, eg: `select rand(binary_checksum(getdate(), object_id)), * from sys.tables`, but then is not much different from using newid()
Remus Rusanu
@martin like the idea, but not getting it all. why is weight a random number?
b0x0rz
@b0x0rz It isn't. `row number squared multiplied by a random number` is what I called the weight maybe I should have called it score or something. I'm not sure that the formula I've given is necessarily appropriate but its something that you may be able to tweak.
Martin Smith
+2  A: 

For a random sample, see Limiting Result Sets by Using TABLESAMPLE. Eg. Select a sample of 100 rows from a table:

SELECT FirstName, LastName
FROM Person.Person 
TABLESAMPLE (100 ROWS);

For weighted sample, with preference to most recent records (I missed this when first reading the question), then Martin's solution is better.

Remus Rusanu