‘壹’ 静态sql和动态SQL的区别和测试实例
所谓SQL的动态和静态,是指SQL语句在何时被编译和执行,二者都是用在SQL嵌入式编程中的。
静态SQL:在高级语言中,如果嵌入了SQL语句,而这个SQL语句的主体结构已经明确,例如在c的一段代码中有一个待执行的SQL“select * from t1 where c1>5”,在编译阶段,就可以将这段SQL交给数据库管理系统去分析,数据库软件可以对这段SQL进行语法解析,生成数据库方面的可执行代码,这样的SQL称为静态SQL,即在编译阶段就可以确定数据库要做什么事情。
动态SQL:如果嵌入的SQL没有明确给出,如在c中定义了一个字符数组类型的变量name:char name[32];,然后采用prepared Statement对象的execute方法去执行这个sql,该sql的值可能等于从文本框中读取的一个SQL或者从键盘输入的SQL,但具体是什么,在编译时无法确定,只有等到程序运行起来,在执行的过程中才能确定,这种SQL叫做动态SQL。例如每一种数据库软件都有能够执行SQL语句的界面,那个界面接收的SQL就是动态SQL,因为数据库厂商在做这个界面时,并不知道用户会输入哪些SQL,只有在该界面执行后,接收了用户的实际输入,才知道SQL是什么。
注意:在SQL中如果某些参数没有确定,如”select * from t1 where c1>? and c2#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
EXEC SQL INCLUDE SQLCA;
EXEC SQL INCLUDE SQLDA;
/*此函数属于静态SQL编程*/
int DBSelect_static(){
EXEC SQL BEGIN DECLARE SECTION;
char _typename[32];
short _length;
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT typename,length
INTO :_typename,:_length
FROM syscat.columns
WHERE tabname='SYSCOLUMNS' and colname='DEFAULT';
printf("【typename:%s】【length:%d】\n",_typename,_length);
}
/*此函数属于静态SQL编程:指定函数参数作为SQL语句的变量*/
int DBSelect_static_param(char *tbl_str,char *col_str){
EXEC SQL BEGIN DECLARE SECTION;
char _typename1[32];
short _length1;
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT typename,length
INTO :_typename1,:_length1
FROM syscat.columns
WHERE tabname='tbl_str' and colname='col_str';
printf("【typename:%s】【length:%d】\n",_typename1,_length1);
}
/*此函数属于动态SQL编程:SQL语句的结构是不确定的,需要根据用户的输入补全SQL语句*/
int DBUpdate_dynamic(){
EXEC SQL BEGIN DECLARE SECTION;
char _address1[32];
char _tablename[32];
char _tmp[32];
char buf[256];
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT count(address1) INTO :_tmp FROM cisaddressinfo WHERE customid='100000100000000000178';
if(0==_tmp) {printf("Not Found!\n");return -1;}
memset(buf,0x00,sizeof(buf));
sprintf(buf,"update ");
printf("Pls input : tablename ->");
scanf("%s",&_tablename);
strcat(buf,_tablename);
strcat(buf," set address1=? where customid='100000100000000000178'");
EXEC SQL PREPARE project FROM :buf;
if(sqlca.sqlcode) perror("PREPARE");
printf("Pls input : address1 ->");
scanf("%s",&_address1);
EXEC SQL EXECUTE project USING :_address1;
if(sqlca.sqlcode) perror("EXECUTE");
EXEC SQL COMMIT WORK;
if(sqlca.sqlcode) perror("COMMIT");
}
/*此函数属于动态SQL编程:使用sqlda数据结构组装复杂多变的动态SQL*/
int DBSelect_dynamic(){
EXEC SQL BEGIN DECLARE SECTION;
char hostVarStmt[256];
EXEC SQL END DECLARE SECTION;
// 声明两个 SQLDA 指针,minsqlda 将是一个最小的 SQLDA 结构,用于 PREPARE 语句,
// 此时结果集的字段数量未知,所以只需一个最小的 SQLDA,即包含 HEADER 和一个 SQLVAR
struct sqlda * minsqlda = (struct sqlda*)malloc(SQLDASIZE(1));
struct sqlda * fulsqlda = NULL;
strcpy(hostVarStmt, "select WUID from workprocess where muid = '185001'");
// PREPARE 将填写 minsqlda 的 header,sqldabc 为 SQLDA 总长度,sqln 为 SQLVAR 数量,即字段数量
EXEC SQL PREPARE STMT INTO :*minsqlda FROM :hostVarStmt;
// 根据从 minsqlda 中获取的长度,分配完整的 SQLDA 结构 fulsqlda,其中将包括合适数量的 SQLVAR 结构
//结构sqlda的成员变量sqld返回select查询语句的字段的数目,可以根据此变量分配内存
fulsqlda = (struct sqlda *)malloc(SQLDASIZE(minsqlda->sqld));
// 使用 DESCRIBE 语句,获取结果集中每个字段的描述信息,包括各字段的类型 (sqltype) 和长度 (sqllen)
EXEC SQL DESCRIBE STMT INTO :*fulsqlda;
int i;
for(i=0;i<minsqlda->sqld;i++)
{
// 根据每个字段的长度,分配内存,将地址存储在对应 SQLVAR 的 sqldata 中
// fulsqlda->sqlvar[i].sqldata=malloc(fulsqlda->sqlvar[i].sqllen);
fulsqlda->sqlvar[i].sqldata=malloc(32);
fulsqlda->sqlvar[i].sqlind=malloc(sizeof(short));
}
// 声明游标
EXEC SQL DECLARE c1 CURSOR FOR STMT;
EXEC SQL OPEN c1;
EXEC SQL WHENEVER not found goto no_more_data;
// 读取记录,记录中每个字段的内容将写入 fulsqlda 中对应 SQLVAR 结构的 sqldata 指向的内存
// EXEC SQL FETCH c1 USING DESCRIPTOR :*fulsqlda;
// 循环读取所有记录
for (;;)
{
EXEC SQL FETCH c1 USING DESCRIPTOR :*fulsqlda;
for(i=0;i<minsqlda->sqld;i++){
printf("%d %s\n",fulsqlda->sqlvar[i].sqltype,fulsqlda->sqlvar[i].sqldata);
usleep(10000);
}
}
return 0;
no_more_data:
printf("\nEND of Data\n");
free(minsqlda);
free(fulsqlda);
EXEC SQL CLOSE c1;
return 0;
}
int main(){
/*连接数据库*/
EXEC SQL CONNECT TO ezeelink USER ezeelink USING EA704075ezeelink;
DBSelect_static();
DBSelect_static_param("SYSCOLUMNS","DEFAULT");
DBUpdate_dynamic();
DBSelect_dynamic();
no_more_data:
;
return 0;
}
案例输出结果如下:
【typename:VARCHAR】【length:254】
【typename:VARCHAR】【length:254】
Pls input : tablename ->cisaddressinfo
Pls input : address1 ->ShangHai
452 cis505
452 cis506
452 pub806
452 ips007
452 ips032
452 dps302
END of Data
注意:
如果使用动态SQL编程编写select查询语句并保存结果,需要使用sqlda数据结构的,同时使用SQL的特性和功能,如:PREPARE ,EXECUTE ,DESCRIBE , DECLARE CURSE C1 FOR … , OPEN CURSE , CLOSE CURSE ….等等
建议:
动态SQL适用于表名及查询字段名未知的情况。在已知查询字段名及表名的情况下,使用动态SQL(字符串拼接方式)会增加硬解析的开销,在这种情况下,建议使用静态SQL,这样可以提高执行效率。在过程过程用拼凑的动态sql效率并不高,有时候还不如程序直接传递sql.静态SQL是前置编译绑定,动态SQL是后期执行时才编译绑定
‘贰’ SQL 有哪些函数SQL中有哪些函数
聚合函数是对一组值执行计算并返回单一的值的函数,它经常与SELECT语句的GROUP BY子句一同使用,SQL SERVER 中具体有哪些聚合函数呢?我们来一一看一下:
AVG 返回指定组中的平均值,空值被忽略。
例:select prd_no,avg(qty) from sales group by prd_no
2. COUNT 返回指定组中项目的数量。
例:select count(prd_no) from sales
3. MAX 返回指定数据的最大值。
例:select prd_no,max(qty) from sales group by prd_no
4. MIN 返回指定数据的最小值。
例:select prd_no,min(qty) from sales group by prd_no
5. SUM 返回指定数据的和,只能用于数字列,空值被忽略。
例:select prd_no,sum(qty) from sales group by prd_no
6. COUNT_BIG 返回指定组中的项目数量,与COUNT函数不同的是COUNT_BIG返回bigint值,而COUNT返回的是int值。
例:select count_big(prd_no) from sales
7. GROUPING 产生一个附加的列,当用CUBE或ROLLUP运算符添加行时,输出值为1.当所添加的行不是由CUBE或ROLLUP产生时,输出值为0.
例:select prd_no,sum(qty),grouping(prd_no) from sales group by prd_no with rollup
8. BINARY_CHECKSUM 返回对表中的行或表达式列表计算的二进制校验值,用于检测表中行的更改。
例:select prd_no,binary_checksum(qty) from sales group by prd_no
9. CHECKSUM_AGG 返回指定数据的校验值,空值被忽略。
例:select prd_no,checksum_agg(binary_checksum(*)) from sales group by prd_no
10. CHECKSUM 返回在表的行上或在表达式列表上计算的校验值,用于生成哈希索引。
11. STDEV 返回给定表达式中所有值的统计标准偏差。
例:select stdev(prd_no) from sales
12. STDEVP 返回给定表达式中的所有值的填充统计标准偏差。
例:select stdevp(prd_no) from sales
13. VAR 返回给定表达式中所有值的统计方差。
例:select var(prd_no) from sales
14. VARP 返回给定表达式中所有值的填充的统计方差。
例:select varp(prd_no) from sales
‘叁’ sql里的var函数是干什么的
var...开头的数据类型用来保存可变长度的数据的,以VARCHAR型和CHAR举例来说:假如你向一个长度为四十个字符的VARCHAR型字段中输入数据BIll GAtES。当你以后从这个字段中取出此数据时,你取出的数据其长度为十个字符——字符串Bill Gates的长度。 现在假如你把字符串输入一个长度为四十个字符的CHAR型字段中,那么当你取出数据时,所取出的数据长度将是四十个字符。字符串的后面会被附加多余的空格。VARCHAR型字段较突出的好处是它可以比CHAR型字段占用更少的内存和硬盘空间。当你的数据库很大时,这种内存和磁盘空间的节省会变得非常重要。不知这么解释能否明白?
‘肆’ SQL优化(二)
SQL优化一: sql优化(一)
上片文章已经详细介绍了explain各个字段的含义,以及什么情况应该建立索引,什么情况不需要建立索引以及sql语句性能的判断依据,接下来我介绍下如何合理的建立索引。
sql语句:select id,author_id from article where category_id = 1 and comments>1 order by views desc limit 1;
分析:首先我们根据where后面的条件建立符合索引,然后根据order by后面的字段建立索引,因此建立索引idx_article_ccv,即以(category_id,comments,views)数据列建立复合索引,但由于comments是一个范围,按照BTree索引的原理,先排序category_id,如果遇到相同的category_id则再排序comments,如果遇到相同的comments则再排序views,又因为comments字段在复合索引里处于中间位置,而comments>1是一个条件(是一个范围值),在复合索引的一个范围值的数据列后面的索引全部失效,mysql无法利用索引再对后面的views部分进行检索,也就是说views无法按照索引排序,所以explain下此sql语句,type为range,extra使用的是Using filesort,这是比较糟糕的。所以我们放弃comments这个范围字段,建立索引idx_article_cv,即以(category_id,views)数据列建立复合索引,explain 此sql,type变成了ref,extra的using filesort也变成了using index,这就变得好多了。
索引:idx_article_cv,即以(category_id,views)数据列建立复合索引
前段时间做了一个销售精细化项目,是公司crm项目的一个大模块,大致就是为销售人员制定指标,实现销售目标从区域到团到业务员到客户,实时跟踪业务员所负责客户的下单量的情况。这就存在许多关联关系,区域-团,团-业务员,业务员-客户,这使得sql常常需要关联多张表。
sql语句:SELECT
tu.fuserid,
tu.faccount,
tu.fphone,
tu.fcertificationtype,
tu.fcertificatename,
tu.fkeyarea,
tu.fkeyareatext,
DATE_FORMAT(tcr.fupdatetime,'%Y-%m-%d %H:%i:%s') as fupdatetime,
tag.forggroupid,
tag.forggroupname,
tug.forguserid,
tug.fusername,
tug.fuserphone,
tag.fcitycode
FROM t_finedt_user AS tu
LEFT JOIN t_finedt_customer_relation AS tcr
ON tu.fuserid = tcr.fuserid
LEFT JOIN t_finedt_usergroup AS tug
ON tcr.forguserid = tug.forguserid
and tcr.forggroupid = tug.forggroupid
LEFT JOIN t_finedt_areagroup AS tag
ON tug.forggroupid = tag.forggroupid
where tu.fkeyarea=? and tu.fuserid=? and tug.forggroupid = ?
分析:上面的sql是左连接,左边的表一定是全表查询,所以要建立右边表对应关联字段的索引,在表t_finedt_user上建立tu_fuserid_fkeyarea索引,即以(fuserid,fkeyarea)字段建立索引,在表t_finedt_customer_relation 上建立tcr_forguserid_forggroupid索引,即以(forguserid,forggroupid)字段建立索引,在表t_finedt_usergroup 上建立tug_forguserid_forggroupid索引,即以(forguserid,forggroupid)字段建立索引,在表t_finedt_areagroup上建立tag_forggroupid索引,即以(forggroupid)字段建立索引。建立索引后,sql查询速度明显快了很多
索引:tcr_forguserid_forggroupid,tu_fuserid_fkeyarea,tug_forguserid_forggroupid,tag_forggroupid
1、尽可能减少join语句中的NestedLoop的循环次数,永远用小结果集驱动大结果集
2、优先优化NestedLoop的内层循环
3、保证join语句总被驱动表上的join字段已经被索引
4、当无法保证被驱动表join条件字段被索引,且内存资源充足的前提下,不要太吝啬joinBuffer的设置
1、全值匹配我最爱
2、最佳左前缀原则——如果索引了多列,要遵守最左前缀原则,指的是查询从索引的最左前列开始并且不跳过索引中的列
3、并在索引列上做任何操作(计算、函数、自动or手动类型转换),这些会导致索引失效而转向全表扫描
4、存储引擎不能使用索引中范围条件右边的列,范围之后的索引全失效
5、尽量使用覆盖索引(之访问索引的查询(索引列和查询的列一致)),减少select *
6、mysql在使用不等于(!=、>、<)的时候无法使用索引会导致全表扫描。
7、is null、is not null也无法使用索引。
8、like以通配符开头("%abc.."),mysql索引失效也会变成全表扫描的操作。
9、字符串不加单引号也会引起索引失效
10、少用or,用它来连接时会索引失效。
1、对于单值索引,尽量选择针对当前query过滤性更好的索引
2、在选择组合索引的时候,当前query中过滤性最好的字段在索引字段顺序中,位置越靠前越好
3、在选择组合索引的时候,尽量选择尽可能包含当前query中的where字句中更多字段的索引
4、尽可能通过分析统计信息和调整query的写法来达到选择合适索引的目的。
全值匹配我最爱,最左前缀要遵守
带头大哥不能死,中间兄弟不能断
索引列上少计算,范围之后全失效
like百分写最右,覆盖索引不写里
不等空值还有or,索引失效要少用
var引号不可丢,sql高级也不难
‘伍’ 关于SQL中的方差函数VAR()相关问题!
var函数
var函数,计算指定字段之中符合查询条件的变异数估计值。
语法为var(运算式)。运算式,可为字段名称、运算式、或一个函数,此函数可
以是一个内部或使用者定义的,但不能为其它的sql函数。
如果符合查询条件的记录为两个以下时,var函数将传回一个null
值,该表示不
能计算变异数。
‘陆’ 如何使用sql函数平均值、总数、最小值、最大值、总和、标准差
avg函数:计算查询中某一特定字段资料的算术平均值。
count函数:计算符合查询条件的记录数。
min, max函数:传回指定字段值中符合查询条件的第一条、最末条记录的资料。
first, last函数:传回指定字段值中符合查询条件的最小值、最大值。
stdev函数:计算指定字段值中符合查询条件的标准差。
sum函数:计算指定字段值中符合查询条件的资料总和。
var,函数:计算指定字段值中符合查询条件的变异数估计值。
‘柒’ SQL函数的函数介绍
Aggregate函数的操作面向一系列的值,并返回一个单一的值。
注释:如果在 SELECT 语句的项目列表中的众多其它表达式中使用 SELECT 语句,则这个 SELECT 必须使用 GROUP BY 语句! 函数 描述 AVG(column) 返回某列的平均值 COUNT(column) 返回某列的行数(不包括 NULL 值) COUNT(*) 返回被选行数 FIRST(column) 返回在指定的域中第一个记录的值 LAST(column) 返回在指定的域中最后一个记录的值 MAX(column) 返回某列的最高值 MIN(column) 返回某列的最低值 STDEV(column) 返回某列的标准偏差 STDEVP(column) 返回某列总体的标准偏差 SUM(column) 返回某列的总和 VAR(column) 返回某列非NULL值的方差 VARP(column) 返回某列所有非NULL值的总体方差 【示例】 Name Age Adams, John 38 Bush, George 33 Carter, Thomas 28 AVG 函数返回数值列的平均值。NULL 值不包括在计算中。
SELECT AVG(column_name) FROM table_name
我们拥有 Orders 表(示例1):
我们希望计算 OrderPrice 字段的平均值。
我们使用如下 SQL 语句:
SELECT AVG(OrderPrice) AS OrderAverage FROM Orders结果集类似这样: OrderAverage 950 示例2
我们希望找到 OrderPrice 值高于 OrderPrice 平均值的客户。
我们使用如下 SQL 语句:
SELECT Customer FROM OrdersWHERE OrderPrice>(SELECT AVG(OrderPrice) FROM Orders)结果集类似这样: Customer Bush Carter Adams COUNT() 函数COUNT() 函数返回匹配指定条件的行数。 FORMAT 函数用于对字段的显示进行格式化。
SELECT FORMAT(column_name,format) FROM table_name 参数 描述 column_name 必需。要格式化的字段。 format 必需。规定格式。 我们拥有下面这个 Procts 表: Prod_Id ProctName Unit UnitPrice 1 gold 1000 g 32.35 2 silver 1000 g 11.56 3 copper 1000 g 6.85 我们希望显示每天日期所对应的名称和价格(日期的显示格式是 YYYY-MM-DD)。
我们使用如下 SQL 语句:
SELECT ProctName, UnitPrice, FORMAT(Now(),'YYYY-MM-DD') as PerDateFROM Procts结果集类似这样: ProctName UnitPrice PerDate gold 32.35 12/29/2008 silver 11.56 12/29/2008 copper 6.85 12/29/2008 LAST() 函数返回指定的字段中最后一个记录的值。
提示:可使用 ORDER BY 语句对记录进行排序。
SELECT LAST(column_name) FROM table_name
实例1 Orders 表:
我们希望查找 OrderPrice 列的最后一个值。
我们使用如下 SQL 语句:
SELECT LAST(OrderPrice) AS LastOrderPrice FROM Orders结果集类似这样: LastOrderPrice 100 LCASE 函数把字段的值转换为小写。
SELECT LCASE(column_name) FROM table_name
我们拥有下面这个 Persons 表: Id LastName FirstName Address City 1 Adams John Oxford Street London 2 Bush George Fifth Avenue New York 3 Carter Thomas Changan Street Beijing 我们希望选取 LastName 和 FirstName 列的内容,然后把 LastName 列转换为小写。
我们使用如下 SQL 语句:
SELECT LCASE(LastName) as LastName,FirstName FROM Persons结果集类似这样: LastName FirstName adams John bush George carter Thomas LEN 函数返回文本字段中值的长度。
SELECT LEN(column_name) FROM table_name
同上有 Persons 表:
我们希望取得 City 列中值的长度。
我们使用如下 SQL 语句:
SELECT LEN(City) as LengthOfCity FROM Persons结果集类似这样: LengthOfCity 6 8 7 MAX 函数返回一列中的最大值。NULL 值不包括在计算中。
SELECT MAX(column_name) FROM table_name注释:MIN 和 MAX 也可用于文本列,以获得按字母顺序排列的最高或最低值。
实例1Orders 表:
我们希望查找 OrderPrice 列的最大值。
我们使用如下 SQL 语句:
SELECT MAX(OrderPrice) AS LargestOrderPrice FROM Orders结果集类似这样: LargestOrderPrice 2000 MID 函数用于从文本字段中提取字符。
SELECT MID(column_name,start[,length]) FROM table_name 参数 描述 column_name 必需。要提取字符的字段。 start 必需。规定开始位置(起始值是 1)。 length 可选。要返回的字符数。如果省略,则 MID() 函数返回剩余文本。 同上有 Persons 表:
我们希望从 City 列中提取前 3 个字符。
我们使用如下 SQL 语句:
SELECT MID(City,1,3) as SmallCity FROM Persons结果集类似这样: SmallCity Lon New Bei MIN 函数返回一列中的最小值。NULL 值不包括在计算中。
SELECT MIN(column_name) FROM table_name注释:MIN 和 MAX 也可用于文本列,以获得按字母顺序排列的最高或最低值。
实例1 Orders 表:
我们希望查找 OrderPrice 列的最小值。
我们使用如下 SQL 语句:
SELECT MIN(OrderPrice) AS SmallestOrderPrice FROM Orders结果集类似这样: SmallestOrderPrice 100 NOW 函数返回当前的日期和时间间
SELECT NOW() FROM table_name
貌似没有这函数。
我们拥有下面这个 Procts 表: Prod_Id ProctName Unit UnitPrice 1 gold 1000 g 32.35 2 silver 1000 g 11.56 3 copper 1000 g 6.85 我们希望显示当天的日期所对应的名称和价格。
我们使用如下 SQL 语句:
SELECT ProctName, UnitPrice, Now() as PerDate FROM Procts结果集类似这样: ProctName UnitPrice PerDate gold 32.35 12/29/2008 11:36:05 AM silver 11.56 12/29/2008 11:36:05 AM copper 6.85 12/29/2008 11:36:05 AM ROUND 函数用于把数值字段舍入为指定的小数位数。
SELECT ROUND(column_name,decimals) FROM table_name 参数 描述 column_name 必需。要舍入的字段。 decimals 必需。规定要返回的小数位数。 Procts 表:
我们希望把名称和价格舍入为最接近的整数。
我们使用如下 SQL 语句:
SELECT ProctName, ROUND(UnitPrice,0) as UnitPrice FROM Procts结果集类似这样: ProctName UnitPrice gold 32 silver 12 copper 7 SUM 函数返回数值列的总数(总额)。
SELECT SUM(column_name) FROM table_name
实例1 Orders 表:
我们希望查找 OrderPrice 字段的总数。
我们使用如下 SQL 语句:
SELECT SUM(OrderPrice) AS OrderTotal FROM Orders结果集类似这样: OrderTotal 5700 GROUP BY 语句合计函数 (比如SUM) 常常需要添加 GROUP BY 语句。 UCASE 函数把字段的值转换为大写。
SELECT UCASE(column_name) FROM table_name
Persons 表:
我们希望选取 LastName 和 FirstName 列的内容,然后把 LastName 列转换为大写。
我们使用如下 SQL 语句:
SELECT UCASE(LastName) : LastName FirstName ADAMS John BUSH George CARTER Thomas