查询反模式 - 随机选择

一、问题提出

  随机数在数据库中是经常用到的系统。

  例如,一个广告系统希望随机选择一个广告来显示。随机推荐相关文章等等。

  在SQL Server中查找随机数最简单的方法为:

  SELECT TOP 1 * FROM Person ORDER BY NEWID() 

  以上SQL语句的执行计划如下:

  

  以上这种方法,需要对整个表进行一次排序,而且还无法有效地使用索引。加入我们只需要前几条数据,那么好不容易对整个结果集完成了排序,但绝大多数都浪费了。

  以上的方式缺点如下:

  •   随着数据量的增加,随机数的产生会变慢。
  •   数据主键并不连续,并且随机数生成算法并没有考虑到这一点。

二、合理使用反模式

  随机排序的性能问题在数据量很小的时候是可容忍的。

  假如我们要从中国23个省中随机选择一个,那么这是很快的,并且基本不会有改变。

三、解决方案

  1、从1到最大值之间的随机选择

  这是一种避免对所有数据进行排序的方法,就是在1到最大的主键值之间随机选择1个。

  不过,这个方案有一个非常大的缺点,就是该方案假设主键的值是从1开始并保持连续的,这意味着在1到最大值之间没有任何值是未使用的。如果当中漏掉一些值,那随机获得的主键可能取不到任何数据。当确信主键是从1到最大值连续的时候,可以使用这个方案。

  SELECT p1.* FROM Person AS p1
      INNER JOIN (SELECT FLOOR(RAND() * (SELECT MAX(Id) FROM Person)) AS Rand_Id) AS p2
  ON p1.Id = p2.Rand_Id

  执行计划如下:

  

  由上面的执行计划已经看出,已经不用排序了。

  2、选择下一个最大值

  这个方案和前一个方案类似,但解决了在1到最大值之间主键有缝隙的情况,这个查询会返回它随机找到的第一个有效值。

  SELECT TOP 1 p1.* FROM Person AS p1
    INNER JOIN (SELECT FLOOR(RAND() * (SELECT MAX(Id) FROM Person)) AS Rand_Id) AS p2
  ON p1.Id = p2.Rand_Id
  WHERE p1.Id >= p2.Rand_Id
  ORDER BY p1.Id

  这个方法解决了随机数没有主键的情况,同时也意味着在一个缝隙之后的那个值被选中的概率会增大。

  当队列中的缝隙不大并且每个值要被等概率选择的重要性不高时,可以考虑这种方案。

  3、高级程序获取所有的键值,随机选择一个

  使用主程序代码获取所有的主键值,然后随机选择一个。再使用这个随机选择出来的主键查询完整的记录。可以用如下的.Net代码实现:

  Random ran = new Random(10);
  ran.Next();

  使用高级程序获取随机数有如下缺点:

  读取整个表的主键出来,占用空间过多,可能超过内存上限。

  查询必须执行两次:一次获取主键的列表,第二次获取对应的记录。如果查询太复杂或者太耗时,就会成为问题。

  4、使用偏移量选择随机行

  这种方案能够避免之前几个方案中的问题,那就是计算总的数据行数,随机选择0到总行数之间的一个值,然后用这个值作为位移来获取随机行。

  DECLARE @i int
  --产生0到COUNT()随机数
  SELECT @i = CAST(CEILING(rand() * COUNT(*)) as int) FROM Person1
  --获取行号等于随机数的记录
  SELECT * FROM (
      SELECT PersonId,PersonName,ROW_NUMBER() OVER (ORDER BY PersonId) AS RN
      FROM Person1) AS T1
  WHERE RN = @i

  当需要每行出现的概率相等,并且主键列是不连续的,可以使用这个方案。

  

  5、专有解决方案

  每种数据库都可能针对这个需求提供独有的解决方案,比如SQL Server 2005就增加了TABLESAMPLE()函数。但是这个方法不能够返回指定行数的记录,可能会多点,也可能会少点。具体请了解函数说明:

  SELECT * FROM Person TABLESAMPLE(50 ROWS);
时间: 2024-10-17 19:41:59

查询反模式 - 随机选择的相关文章

查询反模式 - 全文搜索

一.目标:全文搜索 任何存储文本的应用都有针对这个文本进行单词或词组搜索的需求.我们使用数据库存储越来越多的文本数据,同时也需要搜索速度越来越快.Web应用尤其需要高性能和高扩展性数据库搜索技术. SQL基本原理就是一列中的单个数据是原子性的.也就是说,当我们对两个值进行比较时,通常是把两个值当成一个整体来比较,在SQL中比较子字符串总是意味着低效和不精确. 二.反模式:模式匹配断言 SQL提供了模式匹配断言来比较字符串,并且这是很多程序员用来搜索关键字的第一选择.最广泛使用的就是LIKE断言.

查询反模式 - 正视NULL值

一.提出问题 不可避免地,我们都数据库总有一些字段是没有值的.不管是插入一个不完整的行,还是有些列可以合法地拥有一些无效值.SQL 支持一个特殊的空值,就是NULL. 在很多时候,NULL值导致我们的程序出现报错的现象,于是很多人就开始拒绝NULL值,想各种各样的方法来避免使用NULL值,但是很遗憾,NULL值恰恰就是满足我们的需要用于表示空值的. 空值经常存在于我们的数据库当中: 例如一个在职员工的离职时间. 例如一辆电力驱动的车的燃油消耗比. 二.反模式 很多人对于NULL值感觉到恐惧,原因

查询反模式 - 隐式的列

一.减少输入 程序员都喜欢使用通配符,如: SELECT * FROM Person 又或者省略字段名: INSERT INTO Person VALUES('10','张飞'...) 二.捷径会让你迷失方向 对于以上代码,如果你仅仅是在开发过程中用于查看一下数据库信息,又或者你只是写个小程序自己玩玩,这是没有什么问题的. 但是如果一旦你习惯于这样编写正式生产环境中的代码,那问题就随之而来了. 1.破坏代码重构 如果数据库更改了,比如增加了一列: ALTER TABLE Person Add A

查询反模式 - GroupBy、HAVING的理解

为了最简单地说明问题,我特地设计了一张这样的表. 一.GROUP BY单值规则 规则1:单值规则,跟在SELECT后面的列表,对于每个分组来说,必须返回且仅仅返回一个值. 典型的表现就是跟在SELECT后面的列,如果没有使用聚合函数,必须出现在GROUP BY子句后面. 如下面这个查询报错: 因为对于按照部门分组之后,技术部分组有3个编号,销售部分组有2个编号,你让数据库显示哪个呢? 如果假设你使用聚合函数COUNT(编号)之后,对于每个部门分组,就只有一个值 - 该部门下的人数: 下面来实战下

查询反模式 - GroupBy和HAVING的理解

为了最简单地说明问题,我特地设计了一张这样的表. 一.GROUP BY单值规则 规则1:单值规则,跟在SELECT后面的列表,对于每个分组来说,必须返回且仅仅返回一个值. 典型的表现就是跟在SELECT后面的列,如果没有使用聚合函数,必须出现在GROUP BY子句后面. 如下面这个查询报错: 因为对于按照部门分组之后,技术部分组有3个编号,销售部分组有2个编号,你让数据库显示哪个呢? 如果假设你使用聚合函数COUNT(编号)之后,对于每个部门分组,就只有一个值 - 该部门下的人数: 下面来实战下

SQL反模式学习笔记1 开篇

什么是“反模式” 反模式是一种试图解决问题的方法,但通常会同时引发别的问题. 反模式分类 (1)逻辑数据库设计反模式 在开始编码之前,需要决定数据库中存储什么信息以及最佳的数据组织方式和内在关联方式. 这包含了如何设计数据库的表.字段和关系. (2)物理数据库设计反模式 在确定了需要存储哪些数据之后,使用你所知的RDBMS关系型数据库技术特性尽可能高效地实现数据库管理. 这包含了定义表和索引,以及选择数据类型.也需要是要SQL的“数据定义语言”,比如Create Table语句. (3)查询反模

第二天,导出文件sql,查询,视图view,聚合函数,反模式,字符串处理函数

//把数据库导出到脚本文件mysqldump -uroot -p1234 --databases abc > d:/a/abc.sql CREATE TABLE stud( id INT PRIMARY KEY, NAME VARCHAR(32) NOT NULL, score NUMERIC(4,1));//把所有名字都设成"Mike"了UPDATE stud SET NAME="Mike" //只设置分数>=70的记录的NameUPDATE stud

开发反模式(GUID) - 伪键洁癖

一.目标:整理数据 有的人有强迫症,他们会为一系列数据的断档而抓狂. 一方面,Id为3这一行确实发生过一些事情,为什么这个查询不返回Id为3的这一行?这条记录数据丢失了吗?那个Column到底是什么?我要为这条数据的丢失负责吗? 二.反模式:填充角落 大多数人对于断档的第一反应就是想要填补其中的空缺.对此,可能有两种做法: 1.不按照顺序分配编号 你可能想要在插入新行时,通过遍历表,将找到的第一个未分配的主键编号分配给新行,来代替原来自动分配的伪主键机制.随着你不断地插入新行,断档就被填补起来了

开发反模式 - 明文密码

一.目标:恢复或重置密码 每个有密码的程序都会碰到用户忘记密码的情况,现今大多数程序都通过E-mail的回馈机制让用户恢复或者重置密码.这个解决方案有一个前提,这个服务有一个前提,就是这个用户能够访问他在注册时留下的邮箱. 二.反模式:使用明文存储密码 在这种恢复密码的解决方案中,很常见的一个错误是允许用户申请系统发送一封带有明文密码的邮件.这是数据库设计上一个可怕的漏洞,并且会导致一系列安全问题,可能会使得未取得授权的人获得系统访问权限. 1.存储密码 首先我们设计一张表如下: 类似于这张表,