用的方法很简单,比如说一张stuInfo表,我们想将其中的数据随机排序,那么这里就可以写出如下的sql 语句

如果想得到某条随机的记录,稍微变通一下,即可实现:

如果想记某张表中的记录进行随机显示,或者随机抽取某条记录的话,我们没必要去写一大长串的程序代码去先生成随机数,然后再怎么怎么做去做出所谓的“随机记录”,只需要用SQL SERVER中的一个小函数即可实现随机排序或者随机抽取的功能,该函数就是newid()
用的方法很简单,比如说一张stuInfo表,我们想将其中的数据随机排序,那么这里就可以写出如下的sql 语句1select * from tb_stuinfo order by newid()
如果想得到某条随机的记录,稍微变通一下,即可实现:1select top 1 * from tb_stuinfo order by newid()
如果想记某张表中的记录进行随机显示,或者随机抽取某条记录的话,我们没必要去写一大长串的程序代码去先生成随机数,然后再怎么怎么做去做出所谓的“随机记录”,只需要用SQL SERVER中的一个小函数即可实现随机排序或者随机抽取的功能,该函数就是newid()
用的方法很简单,比如说一张stuInfo表,我们想将其中的数据随机排序,那么这里就可以写出如下的sql 语句1select * from tb_stuinfo order by newid()
如果想得到某条随机的记录,稍微变通一下,即可实现:1select top 1 * from tb_stuinfo order by newid()
转载于:https://www.cnblogs.com/RobotTech/archive/2007/10/21/932379.html
排序数据
使用SELECT检索出来的顺序并不是以纯粹的随机顺序显示的, 如果不排序, 数据一般将以它在底层表中出现的顺序显示。这可以是数据最初添加到表的顺序。但是, 如果数据后来进行过更新和删除, 则此顺序将会受到MySQL重用回收存储空间的影响。因此, 如果不明确控制的话, 不能依赖该排序顺序。
关系数据库设计理论认为, 如果不明确规定排序的顺序, 则不应该假定检索出的数据的顺序有意义。
子句:
SQL语句由子句构成, 有些子句是必须的, 而有的是可选的。一个子句通常由一个关键字和所提供的数据组成。
为了明确地排序SELECT语句检索出的数据, 可使用ORDER BY子句。ORDER BY子句取一个或多个列的名字, 据此对输出进行排序。
MariaDB [crashcourse]> SELECT prod_name FROM products ORDER BYprod_name;+----------------+
| prod_name |
+----------------+
| .5 ton anvil |
| 1 ton anvil |
| 2 ton anvil |
| Bird seed |
| Carrots |
| Detonator |
| Fuses |
| JetPack 1000 |
| JetPack 2000 |
| Oil can |
| Safe |
| Sling |
| TNT (1 stick) |
| TNT (5 sticks) |
+----------------+
14 rows in set (0.006sec)
MariaDB[crashcourse]>
通过非选择列进行排序
通常, ORDER BY子句中使用的列将是为显示所选择的列。但是, 实际上并不一定要这样, 用非检索的列排序是完全合法的
按多个列排序
经常需要不止一个列进行数据排序。为了按多个列排序, 只要指定列名, 列名之间用逗号分开即可。
下面代码检索3个列, 并按其中两个列对结果进行排序--首先按照价格, 然后再按照名称排序。
MariaDB [crashcourse]> SELECT prod_id, prod_price, prod_name FROM products ORDER BYprod_price, prod_name;+---------+------------+----------------+
| prod_id | prod_price | prod_name |
+---------+------------+----------------+
| FC | 2.50 | Carrots |
| TNT1 | 2.50 | TNT (1 stick) |
| FU1 | 3.42 | Fuses |
| SLING | 4.49 | Sling |
| ANV01 | 5.99 | .5 ton anvil |
| OL1 | 8.99 | Oil can |
| ANV02 | 9.99 | 1 ton anvil |
| FB | 10.00 | Bird seed |
| TNT2 | 10.00 | TNT (5 sticks) |
| DTNTR | 13.00 | Detonator |
| ANV03 | 14.99 | 2 ton anvil |
| JP1000 | 35.00 | JetPack 1000 |
| SAFE | 50.00 | Safe |
| JP2000 | 55.00 | JetPack 2000 |
+---------+------------+----------------+
14 rows in set (0.000sec)
MariaDB[crashcourse]>
重要的是理解在按多个列排序时, 排序完全按照所规定的顺序进行。
换句话说, 对于上述例子中的输出, 仅在多个行具有相同的prod_price值时才对产品prod_name进行排序。如果prod_price列中所有的值都是唯一的, 那么不会按照prod_name排序。
指定排序方向
MySQL默认的排序方式为升序排序, 可以使用ORDER BY子句以降序排序。为了进行降序排序, 必须指定DESC关键字
MariaDB [crashcourse]> SELECT prod_id, prod_price, prod_name FROM products ORDER BY prod_price DESC;+---------+------------+----------------+
| prod_id | prod_price | prod_name |
+---------+------------+----------------+
| JP2000 | 55.00 | JetPack 2000 |
| SAFE | 50.00 | Safe |
| JP1000 | 35.00 | JetPack 1000 |
| ANV03 | 14.99 | 2 ton anvil |
| DTNTR | 13.00 | Detonator |
| TNT2 | 10.00 | TNT (5 sticks) |
| FB | 10.00 | Bird seed |
| ANV02 | 9.99 | 1 ton anvil |
| OL1 | 8.99 | Oil can |
| ANV01 | 5.99 | .5 ton anvil |
| SLING | 4.49 | Sling |
| FU1 | 3.42 | Fuses |
| FC | 2.50 | Carrots |
| TNT1 | 2.50 | TNT (1 stick) |
+---------+------------+----------------+
14 rows in set (0.003sec)
MariaDB[crashcourse]>
如果打算以多个列排序, 比如: 以降序排序产品(最贵的在最前面), 然后再对产品名进行排序
MariaDB [crashcourse]> SELECT prod_id, prod_price, prod_name FROM products ORDER BY prod_price DESC, prod_name;+---------+------------+----------------+
| prod_id | prod_price | prod_name |
+---------+------------+----------------+
| JP2000 | 55.00 | JetPack 2000 |
| SAFE | 50.00 | Safe |
| JP1000 | 35.00 | JetPack 1000 |
| ANV03 | 14.99 | 2 ton anvil |
| DTNTR | 13.00 | Detonator |
| FB | 10.00 | Bird seed |
| TNT2 | 10.00 | TNT (5 sticks) |
| ANV02 | 9.99 | 1 ton anvil |
| OL1 | 8.99 | Oil can |
| ANV01 | 5.99 | .5 ton anvil |
| SLING | 4.49 | Sling |
| FU1 | 3.42 | Fuses |
| FC | 2.50 | Carrots |
| TNT1 | 2.50 | TNT (1 stick) |
+---------+------------+----------------+
14 rows in set (0.000sec)
MariaDB[crashcourse]>
DESC关键字只作用与直接位于其前面的列名。
在多个列上降序排序
如果想在多个列上进行降序排序, 必须对每个列指定DESC关键字
与DESC想法的是ASC(ASCENDING), 在升序排序时可以指定它。但是升序排序是默认的排序方式。
使用ORDER BY和LIMIT的组合, 可以找到一个列中最高的或最低的值。
MariaDB [crashcourse]> SELECT prod_price FROM products ORDER BY prod_price DESC LIMIT 1;+------------+
| prod_price |
+------------+
| 55.00 |
+------------+
1 row in set (0.000sec)
MariaDB[crashcourse]>
ORDER BY子句的位置
在给出ORDER BY子句时, 应该保证它位于FROM子句之后。如果使用LIMIT, 它必须位于ORDER BY之后, 使用子句的次序不对将产生错误的消息
所有的优化我只写了改动部分,有需要可以参考
一、快排的优化
1、随机取基准点
(1)问题
如果数据基本有序时,快排会一步一步找基准点进行递归遍历,这时递归层次达到了最大,效率最低,对于这种情况,我们怎么优化??
(2)思考
我们知道对于快排,他最适用于数据乱序,如果数据越有序,效率越差,那我们是不是可以认为将顺序的数据扰乱是不是就可以了,将数据扰乱也就是不按顺序找基准点,即就是随机找基准点优化方法。
(3)方法
现在我们给 key 传的是 start ,那随机找基准点也就是在数组中随即找一个值与start交换,这样就能达到随机找基准点的效果。
简单介绍rand()函数
rand()不需要参数,它会返回一个从0到最大随机数的任意整数,如果你想找0~99这100个整数中的一个随机整数,可以表达为:
int num = rand() % 100;
rand() % (b-a+1)+ a ;
就表示 a~b 之间的一个随机数。
所以对于快排的优化一://优化1 随机取基准点 void Swap(int arr[], int first, int second) { int tmp; tmp = arr[first]; arr[first] = arr[second]; arr[second] = tmp; } int parition(int arr[], int start, int end)//找到基准点后的基准点下标 { int boundkey = arr[start]; while (start < end)//元素没有遍历完,如果start<=end表示所有数据比较完成 { while (start < end && arr[end] >= boundkey)//元素没有遍历完并且大数就在后面,不用移动 end--; arr[start] = arr[end];//后面有数据比基准值小 while (start < end && arr[start] <= boundkey)//元素没有遍历完并且小数就在前面,不用移动 start++; arr[end] = arr[start]; } arr[end] = boundkey; return start;//此时start = end } void Quick(int arr[], int start, int end)//控制区间 { int key; if (start < end)//判断这个区间是否需要排序 { Swap(arr, start, rand() % (end - start + 1) + start); key = parition(arr, start, end);//在start和end找到基准点 Quick(arr, start, key - 1);//递归调用把基准点左边划分成一个区间 Quick(arr, key + 1, end);//递归调用把基准点右边划分成一个区间 } } void QuickSort(int arr[], int len)//拿到数据和数组大小,在主函数中被调用 { Quick(arr, 0, len - 1); }
2、三数取中法
1、问题
如果数据本身就是随机的,你在随机找基准点,那万一最坏的效率就是把每个数据都排一遍,那该该如何处理?
2、思考:
如果我们在数据中取第一个数据、中间的数和最后一个数据,然后找到数值为中间大的数据,就相当于找了一个中间大的数据作为轴线,这个轴线左边放比它小的数,右边放比它大的数。
3、方法
选取数组开头,中间和结尾的元素,通过比较,选择中间大的值作为快排的基准点,这样就能有效避免随机找基准点的万一。
//优化1 随机取基准点 void Swap(int arr[], int first, int second) { int tmp; tmp = arr[first]; arr[first] = arr[second]; arr[second] = tmp; } //优化2 三数取中法 void getMiddleMaxNum(int arr[], int start, int mid, int end)//对三数进行调整,找中间大的数 { if (arr[mid] > arr[end]) { Swap(arr, mid, end);//mid中存放mid和end中较大的 } if (arr[start] < arr[end]) { Swap(arr, start, end);//end为中间大 } if (arr[start]>arr[mid]) { Swap(arr, start, mid);//mid中间大 } } int parition(int arr[], int start, int end)//找到基准点后的基准点下标 { int boundkey = arr[start]; while (start < end)//元素没有遍历完,如果start<=end表示所有数据比较完成 { while (start < end && arr[end] >= boundkey)//元素没有遍历完并且大数就在后面,不用移动 end--; arr[start] = arr[end];//后面有数据比基准值小 while (start < end && arr[start] <= boundkey)//元素没有遍历完并且小数就在前面,不用移动 start++; arr[end] = arr[start]; } arr[end] = boundkey; return start;//此时start = end } void Quick(int arr[], int start, int end)//控制区间 { int key; if (start < end)//判断这个区间是否需要排序 { //优化2 三数取中法 int mid = (end - start) / 2 + start;//中间位置的下标 getMiddleMaxNum(arr, start, mid, end); key = parition(arr, start, end);//在start和end找到基准点 Quick(arr, start, key - 1);//递归调用把基准点左边划分成一个区间 Quick(arr, key + 1, end);//递归调用把基准点右边划分成一个区间 } } void QuickSort(int arr[], int len)//拿到数据和数组大小,在主函数中被调用 { Quick(arr, 0, len - 1); }
3、如果是小数据量
当数据量过小时,调用递归会使效率降低,杀鸡焉用牛刀?
所以可以在快排代码中调用插入排序,只需要稍作改动就可
这时候就要插入三个变量,插排的代码就不在写了,可以看
— 点它—> 使用小数据量的插入排序完整代码void Quick(int arr[], int start, int end)//控制区间 { int key; if (start < end)//判断这个区间是否需要排序 { //优化3 小数据量用插排 if ((end - start) < 20)//将数据量控制在20个以内 { InsertSort(arr, start, end);//此时要改变插排的变量 return; } Quick(arr, start, key - 1);//递归调用把基准点左边划分成一个区间 Quick(arr, key + 1, end);//递归调用把基准点右边划分成一个区间 } }
4、聚集优化
(1)问题
当重复数据过多时,用我们之前的代码会一次又一次对相同的数据进行递归排序,这样会导致程序开销大,效率低,该如何处理??
(2)思考
我们可以在一次分组结束后,将与本次基准点的值相等的元素聚集在一起,这样我们就可以认为它是有序的,不用再对聚集过的元素进行分组递归处理。
(3)方法
找到基准点的位置,排序结束后,对于左边区间,把和基准点相同的数据向右侧移动和基准点聚集起来;对于右边区间,把和基准点相同的数据向左侧移动和基准点聚集起来。
5、非递归优化
(1)简单谈一下递归
在调用递归函数的过程中它是在栈中开辟的,所以每次函数调用(自己调用自己)都会产生开栈开销;在函数回溯的过程中,后面的函数回退到调用函数的过程中也会产生清栈开销,调用一层函数就是开辟一层栈帧空间,结束一层函数,就是释放一层栈帧空间,递归函数实际上就是开辟和释放栈帧空间的过程。所以调用递归函数来说,是有额外的开销产生的。
(2)问题
众所周知,一个函数开销越小,他的效率越低,很明显快排中函数调用递归并没有达到最好的效率,那我们有没有一种方法可以提高效率???换一句话说就是有没有一种该方法可以模拟栈的实现???
(3)思考
用常规的方法解决快速排序时,是在每次分区间的时候进行递归调用的,那我们要解决的就是动态开辟一块内存空间,把这些空间先保存下来,再一次一次的处理。
(4)方法
int parition(int arr[], int start, int end)//找到基准点后的基准点下标
{
int boundkey = arr[start];
while (start < end)//元素没有遍历完,如果start<=end表示所有数据比较完成
{
while (start < end && arr[end] >= boundkey)//元素没有遍历完并且大数就在后面,不用移动
end--;
arr[start] = arr[end];//后面有数据比基准值小
while (start < end && arr[start] <= boundkey)//元素没有遍历完并且小数就在前面,不用移动
start++;
arr[end] = arr[start];
}
arr[end] = boundkey;
return start;//此时start = end
}
//优化5
void Quick2(int arr[], int start, int end)
{
//开辟的数组大小是整个数组长度,避免最坏情况一直分区间入栈,没有出栈
int *st = (int*)malloc(sizeof(int)*(end - start + 1));//动态开辟一个数组模拟栈
int top = 0;//模拟栈顶元素
int k;//存放基准点
int left = start;
int right = end;
st[top++] = left;
st[top++] = right;//此时栈顶元素是right
while (top != 0)//当栈顶元素不为空时,表示有区间可以继续划分,然后在出栈进行一趟排序
{
right = st[top - 1];//拿到栈顶元素
top--;//出栈
left = st[top - 1];//一次性有两个元素出栈
top--;
k = parition(arr, left, right);
//栈中元素遵循先进后出,所以先出左边区间边界,就先入右边区间边界
if (k + 1 < right )//表示右区间中至少有两个元素,右区间有元素可排列
{
st[top++] = k + 1;
st[top++] = right;
}
if (left < k - 1)//表示左区间有元素可排列
{
st[top++] = left;
st[top++] = k - 1;
}
}
free(st);
}
void QuickSort(int arr[], int len)//拿到数据和数组大小,在主函数中被调用
{
Quick2(arr, 0, len - 1);
}