我正在优化项目中的许多现有查询。Quassnoi的解决方案帮助我大大加快了查询速度!但是,我发现很难在所有查询中都包含上述解决方案,尤其是对于涉及多个大表上许多子查询的复杂查询。
因此,我使用的是优化程度较低的解决方案。从根本上讲,它的工作方式与Quassnoi的解决方案相同。
SELECT accomodation.ac_id,
accomodation.ac_status,
accomodation.ac_name,
accomodation.ac_status,
accomodation.ac_images
FROM accomodation, accomodation_category
WHERE accomodation.ac_status != 'draft'
AND accomodation.ac_category = accomodation_category.acat_id
AND accomodation_category.acat_slug != 'vendeglatohely'
AND ac_images != 'b:0;'
AND rand() <= $size * $factor / [accomodation_table_row_count]
LIMIT $size
$size * $factor / [accomodation_table_row_count]
计算出选择随机行的可能性。rand()将生成一个随机数。如果rand()小于或等于概率,则将选择该行。这有效地执行了随机选择以限制表的大小。由于返回的次数有可能少于定义的限制数,因此我们需要增加概率以确保选择了足够的行。因此,我们将$ size乘以$ factor(我通常设置$ factor = 2,在大多数情况下有效)。最后我们做limit $size
现在的问题是解决accomodation_table_row_count。如果我们知道表的大小,则可以对表的大小进行硬编码。这将以最快的速度运行,但是显然这并不理想。如果您使用的是Myisam,则获取表计数非常有效。因为我使用的是innodb,所以我只是在做一个简单的count + select。在您的情况下,它看起来像这样:
SELECT accomodation.ac_id,
accomodation.ac_status,
accomodation.ac_name,
accomodation.ac_status,
accomodation.ac_images
FROM accomodation, accomodation_category
WHERE accomodation.ac_status != 'draft'
AND accomodation.ac_category = accomodation_category.acat_id
AND accomodation_category.acat_slug != 'vendeglatohely'
AND ac_images != 'b:0;'
AND rand() <= $size * $factor / (select (SELECT count(*) FROM `accomodation`) * (SELECT count(*) FROM `accomodation_category`))
LIMIT $size
棘手的部分是确定正确的概率。如您所见,以下代码实际上仅计算临时表的大小(实际上太粗糙了!):(select (SELECT count(*) FROM accomodation) * (SELECT count(*) FROM accomodation_category))
但是您可以优化此逻辑以给出更接近的表大小近似值。请注意,过度选择比欠选择行更好。即,如果将概率设置得过低,则可能会冒险选择不足的行。
该解决方案的运行速度比Quassnoi的解决方案慢,因为我们需要重新计算表的大小。但是,我发现此编码更易于管理。这是精度+性能与编码复杂度之间的权衡。话虽如此,在大型表上,这仍然比Rand()的Order快得多。
注意:如果查询逻辑允许,请在进行任何联接操作之前尽早执行随机选择。