Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p><strong>UPDATED for performance:</strong></p> <p>My original solution worked but requuired a table scan. Am's solution is a good one and doesn't require a table scan but its hard-coded ranges won't work when the only matches are far outliers. Plus it requires de-duping records. But combining both solutions can get you the best of both worlds, provided you have an index on age. (if you don't have an index on age, then all solutions will require a table scan). </p> <p>The combined solution first picks only the rows which might qualify (the desired range, plus the 10 rows over and 10 rows under that range), and then uses my original logic to rank the results. Caveat: I don't have enough sample data present to verify that MySQL's optimizer is indeed smart enough to avoid a table scan here-- MySQL might not be smart enough to weave those three UNIONs together without a scan. </p> <p><em>[just updated again to fix 2 embarrassing SQL typos: DESC where DESC shouldn't have been!]</em></p> <pre><code>SELECT * FROM ( SELECT id, name, age, CASE WHEN age BETWEEN 25 and 35 THEN RAND() ELSE ABS (age-30) END as distance FROM ( SELECT * FROM (SELECT * FROM Person WHERE age &gt; 35 ORDER BY age DESC LIMIT 10) u1 UNION SELECT * FROM (SELECT * FROM Person WHERE age &lt; 25 ORDER BY age LIMIT 10) u2 UNION SELECT * FROM (SELECT * FROM Person WHERE age BETWEEN 25 and 35) u3 ) p2 ORDER BY distance LIMIT 10 ) p ORDER BY RAND() ; </code></pre> <p><strong>Original Solution:</strong></p> <p>I'd approach it this way: </p> <ol> <li>first, compute how far each record is from the center of the desired age range, and order the results by that distance. For all results inside the range, treat the distance as a random number between zero and one. This ensures that records inside the range will be selected in a random order, while records outside the range, if needed, will be selected in order closest to the desired range.</li> <li>trim the number of records in that distance-ordered resultset to 10 records</li> <li>randomize order of the resulting records</li> </ol> <p>Like this:</p> <pre><code>CREATE TABLE Person (id int AUTO_INCREMENT PRIMARY KEY, name varchar(50) NOT NULL, age int NOT NULL); INSERT INTO Person (name, age) VALUES ("Joe Smith", 26); INSERT INTO Person (name, age) VALUES ("Frank Johnson", 32); INSERT INTO Person (name, age) VALUES ("Sue Jones", 24); INSERT INTO Person (name, age) VALUES ("Ella Frederick", 44); SELECT * FROM ( SELECT id, name, age, CASE WHEN age BETWEEN 25 and 35 THEN RAND() ELSE ABS (age-30) END as distance FROM Person ORDER BY distance DESC LIMIT 10 ) p ORDER BY RAND() ; </code></pre> <p>Note that I'm assuming that, if there are not enough records inside the range, the records you want to append are the ones closest to that range. If this assumption is incorrect, please add more details to the question.</p> <p>re: performance, this requires a scan through the table, so won't be fast-- I'm working on a scan-less solution now...</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload