sql 规范

1、任何语句使用前通过 EXPLAIN 查看执行计划是否用到索引
  explain select ...;
-- 问题语句:(去除嵌套查询中无意义排序)
  EXPLAIN SELECT COUNT(*) AS num
  FROM ( SELECT b.id,
  FROM ( SELECT id,
  WHERE yczt = 0 AND hfbz = 0 AND (bm = ‘410045‘ OR bmr = ‘410045‘)
  ORDER BY lysj DESC) b
  GROUP BY b.dhb_id) c
  INNER JOIN dhb a
  ON a.id = c.dhb_id
  LEFT JOIN ydserver.gs d
  ON c.bm = d.bm
  LEFT JOIN ydserver.gs e
  ON c.bmr = e.bm
  WHERE 1
  ORDER BY c.lysj DESC, c.dhb_id;

2、不要从明细表查统计结果,定期统计插入到汇总表
-- 问题语句:
  SELECT COUNT(txm) AS zs,SUM(ifqs) AS qs
  FROM wdcx.yd_scan03
  WHERE dd = 450001 AND rksj >= ‘2014-05-30 00:00:00‘
    AND rksj <= ‘2014-05-30 23:59:59‘ AND (ifdj = 1 OR ifff = 1);
  # Query_time: 18.229131 Lock_time: 0.000474 Rows_sent: 0 Rows_examined: 180139
  注:从明细表查询时检索记录数为 180139,从汇总表查询时检索记录数为 1,需要改为从汇总表查询

3、禁止使用SELECT *,必须指定字段名称.无法完成索引覆盖扫描这类优化,还会给服务器带来额外的I/O、内存和CPU的消耗
  SELECT *  FROM cust_txm
   WHERE txm = ‘3100042251575‘
       CREATE TABLE cust_txm;
      注:全部返回时,很多字段用不到,另数值条件不要加引号

4、明细统计时,只统计编码,不要关联名称等冗余字段
-- 问题语句:
SELECT CURPLACE AS xcdd,mc,SUM(zczs - zcsum - xczs + xcsum) AS sjzcsm,
    FROM (SELECT aa.LINEID,
          aa.mc
        FROM (SELECT DISTINCT a.LINEID,
          d.mc --名称
              FROM tst_car_line_info a
                LEFT JOIN tst_place_pre_nex b ON a.LINEID = b.LINEID
                LEFT JOIN tst_down_up_shipment c ON a.LINEID = c.LINEID
                LEFT JOIN ydserver.gs d ON b.CURPLACE = d.bm AND d.lb = 3
                LEFT JOIN ydserver.county e ON d.szd = e.CountyID
                LEFT JOIN ydserver.city f ON e.CityID = f.CityID
              WHERE (b.CURPLACE = ‘0‘ OR 0 = ‘0‘)
                  AND IFNULL(a.FACTCAR_D, a.FACTLOADOR_D) >=‘2014-06-14‘
                  AND IFNULL(a.FACTCAR_D, a.FACTLOADOR_D) <=‘2014-06-15‘) aa
                  GROUP BY xcdd;
    注:名称显示可查询全局 hashtable(写300万/秒 读1200万/秒)
-- 其他 KV 工具:
  memcached(读写 8万/秒,多线程更快,适合小数据)
  redis (读写10万/秒,单线程,可多进程,适合大数据和复杂数据结构)

5、联合查询时,每个表必须加别名,关联字段必须是索引(最好是主键),where条件用以过滤主表
-- 问题语句:
  SELECT a.*, b.kilometre
  FROM car.car_line a
  LEFT JOIN car.car_roadline b
  ON a.roadlineid = b.roadlineid AND b.del_flag = 0
  WHERE lineid = ‘31001383713‘ AND a.del_flag = 0;

6、语句中避免子查询
--问题语句
  SELECT t1.*
  FROM ydserver.ic_site_bound t1
  WHERE DEV_ID IN (
    SELECT dev_id
    FROM car.tb_crd t2
    WHERE t2.scan_time >= ‘2014-06-14 00:00:00‘
    AND t2.scan_time <= ‘2014-06-17 00:00:00‘
    );
--改写
  SELECT t1.*
    FROM ydserver.ic_site_bound t1, (
      SELECT DISTINCT dev_id
      FROM car.tb_crd
      WHERE scan_time >= ‘2014-06-14 00:00:00‘
      AND scan_time <= ‘2014-06-17 00:00:00‘
      ) t2
  where t2.dev_id=t1.DEV_ID;

如果 IN 列表太多必须改为关联的方式 , 且通过主键关联

7、大表 join 用临时表代替 (create temporary table)
SELECT *
  FROM (SELECT ROW_NUMBER () OVER (ORDER BY reverttime DESC) AS ROWNUM,
    R.cardid,
    R.moduleid,
    cardtheme,
    cardperson,
    T.revertcontent,
    T.revertperson,
    T.reverttime,
    W.cardnum,
    H.Hid,
    G.Gid
    FROM Card R
   LEFT JOIN
(SELECT revertid,
  cardid,
  revertcontent,
  revertperson,
  reverttime
  FROM Reverts Y
  WHERE NOT EXISTS
  (SELECT 1
    FROM Reverts
    WHERE cardid = Y.cardid
    AND revertid > Y.revertid)) T
    ON R.cardid = T.cardid
    LEFT JOIN ( SELECT cardid, COUNT (*) AS cardnum
    FROM Reverts
      GROUP BY cardid) W
      ON R.cardid = W.cardid
    LEFT JOIN (SELECT id AS Hid, username FROM UserInfoTable) H
    ON R.cardperson = H.username
    LEFT JOIN (SELECT id AS Gid, username FROM UserInfoTable) G
    ON T.revertperson = G.username
    WHERE R.moduleid = CAST (@moduleid as nvarchar(50))) as TEMPRESULT
    where rownum between str((@currentpage-1)*@pagesize)+1 and str(@currentpage*@pagesize)

--使用临时表保存以下结果集数据
SELECT *
FROM (SELECT ROW_NUMBER () OVER (ORDER BY reverttime DESC) AS ROWNUM,
R.cardid,
R.moduleid,
cardtheme,
cardperson,
T.revertcontent,
T.revertperson,
T.reverttime,
W.cardnum,
H.Hid,
G.Gid
FROM Card R
LEFT JOIN
(SELECT revertid,
cardid,
revertcontent,
revertperson,
reverttime
FROM Reverts Y
WHERE NOT EXISTS
(SELECT 1
FROM Reverts
WHERE cardid = Y.cardid
AND revertid > Y.revertid)) T
ON R.cardid = T.cardid
WHERE R.moduleid = CAST (@moduleid as nvarchar(50))) as TEMPRESULT
where rownum between str((@currentpage-1)*@pagesize)+1 and str(@currentpage*@pagesize);
然后,再和其他表进行left join

9.字段设计
(1)尽可能使用更小的数据类型,如 TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT。
更小的数据类型通常更快,因为他们占用更少的磁盘、内存和CPU缓存,并且处理时需要的CPU周期更少。
例如,整型比字符操作代价更低,因为字符集和校对规则使字符比整型比较更复杂。

(2)相同属性对应的数据类型,如字符型,数值型不能混合使用,依赖后期转换
有可能不走索引
varchar(5)和varchar(200)存储‘hello‘的空间开销是一样的。但是,更长的列在使用内存临时表进行排序或操作时,会消耗更多的内存。

(3)相同字段不同表中的类型和长度要一致
导数可能报错
(4)字段名称不能使用关键字
(5)不要指定字段级编码,建议全库统一
CREATE TABLE `delivery`(
  `user_id` VARCHAR(20) CHARACTER SET utf8,
  `order_id` INT(11),
  `order_bn` VARCHAR(32) CHARACTER SET utf8,
  `delivery_bn` VARCHAR(20) CHARACTER SET utf8,
  `is_scan` enum(‘1‘, ‘0‘) CHARACTER SET utf8,
  `scan_time` datetime ,
  `create_time` datetime ‘生成时间‘
);
(6)默认值要规范,例如日期不要使用 0000-00-00
CREATE TABLE yd_cas_org
(
  orgid bigint(10) NOT NULL,
  orgcode VARCHAR(32) NOT NULL,
  status VARCHAR(255) DEFAULT ‘running‘,
  area VARCHAR(30) DEFAULT NULL,
  lastupdatetime timestamp NOT NULL DEFAULT ‘0000-00-00 00:00:00‘
);
注:特有默认值在 ETL 时会导致异常
(7)事务相关记录保留时间戳,建议只增不改;在必须对记录进行修改的时候,保留更改时间戳
例如:
ctime datetime NOT NULL COMMENT ‘创建时间‘,
utime timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT ‘更新时间‘

10. 索引
(1)一般情况下,一次查询只会用到一个索引
(2)每个表索引越少越好
(3)建立组合索引时,WHERE 条件中用到等于的字段放前边,用到范围的字段放后边。多个范围条件建立的索引无法同时使用
如果查询中有某个列的范围查询,则其右边所有列都无法使用索引优化查找,
例如:where last_name=‘Smith‘ and first_name like ‘J%‘ and bob=‘1976-12-23‘,这个查询只能使用索引的钱两列,因为like是一个范围条件。

(4)删除重复字段的索引,减少 DML IO
-- 问题语句:
CREATE TABLE temp_car_roadline (
I  D int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT ‘线路的id‘,
  RoadLineID char(9) NOT NULL COMMENT ‘线路编码‘,
  LineType varchar(11) NOT NULL COMMENT ‘线路类别‘,
  qty_month int(11) DEFAULT ‘30‘ COMMENT ‘月跑趟数‘,
  PRIMARY KEY (ID),
  KEY RoadLineID (RoadLineID),
  KEY RoadLineID2 (RoadLineID,build_date)
)
注:索引过多影响操作效率,重复索引可能导致执行计划异常
索引(RoadLineID,build_date)可以当作索引(RoadLineID)使用
(6)索引中重复的记录数越少,效率越高,效率最高的是主键
  注:如果同一记录超过50%,全表扫描定期 analyze table 收集统计信息和直方图
  选择性高的列放到组合索引的前面。

(7)索引字段最好不要存在 NULL,NULL可用 0 替代,建议把默认值设置为 0,如果可以加 not null 或者 unique 的最好加上
  因为可为NULL的列使得索引、索引统计和值比较都更复杂。
(8)组合索引可以只使用第一个,或者前两个,或者前几个,不能从第二个开始用,也不能跳着使用
注:索引使用从前缀开始,多字段索引到between或者<,>等以后字段不会使用索引,排序最好在索引中实现

11.查询条件
(1)SQL 语句的 WHERE 条件避免使用无效条件、无效括号
-- 问题语句:
SELECT ydserver.gs.BM, ydserver.gs.SHI, ydserver.gs.MC
FROM ydserver.gs
WHERE (1=1)
AND ( (ydserver.gs.SHI LIKE ‘%‘) AND (ydserver.gs.sheng LIKE ‘%‘) )
AND ( ydserver.gs.BM <> 0 )
AND ( ydserver.gs.sjgs <> 0 )
ORDER BY ydserver.gs.BM ASC;
注:示例语句中使用了无效条件、无效括号,对性能有极大影响
(2)SQL语句中不要加用不到的排序
做统计没必要用加"order by ..."
(3)WHERE 条件中 最好不要用 IN 和 LIKE
-- 问题语句:
SELECT yd_cost.yjsm_czz.*
FROM yd_cost.yjsm_czz, gs
WHERE (yd_cost.yjsm_czz.xjdd = gs.bm)
AND (yd_cost.yjsm_czz.dd = ‘8.30001000000000000e+005‘)
AND (yd_cost.yjsm_czz.sj >= ‘2014-06-15 14:00:00‘)
AND (yd_cost.yjsm_czz.sj <= ‘2014-06-15 23:59:59‘)
AND (cast(yd_cost.yjsm_czz.xjdd AS char(19)) LIKE ‘410088‘)
AND (cast(gs.sheng AS char(6)) LIKE ‘%‘)
AND (cast(gs.shi AS char(6)) LIKE ‘%‘);

注:可使用 exists 代替 in, 使用 = 代替 like,即使使用like也是尽可能将“%”放到字符串后面,例如: like ‘car%‘
避免使用 LIKE
-- 问题语句:
SELECT *
FROM t
WHERE lrsj LIKE "2012-09-23%";
注:需要寻找 LIKE 的替代方案,如 SELECT * FROM t WHERE lrsj BETWEEN ‘2012-09-23 00:00:00‘ AND ‘2012-09-23 23:59:59‘

(4)索引相关字段不要使用函数或者进行运算,如 field1 + 1 = field2、ADDDATE(field1,…、CAST
-- 问题语句:
SELECT t1.CarLicNum, t1.RoadLineName, t2.LeaveTime
FROM car_line t1
LEFT JOIN car_roadlinedetail t2
ON t2.roadlineid = t1.RoadLineID AND t2.del_flag = 0
WHERE CAST(CONCAT(t1.Startdate, ‘ ‘, t1.StartTime) AS datetime) BETWEEN ‘2014-06-10 10:30:00‘
AND ‘2014-06-18 10:30:00‘
AND t1.del_flag = 0
ORDER BY t1.LineID, t2.PassNo;

注:大多数字段使用函数不会使用索引,除非加函数索引
所以,始终将索引列单独放在比较符号的一侧。

(5)禁止字段格式转换,如 SELECT x FROM GS WHERE BM=200000,数值两边不要加引号
-- 问题语句:
SELECT cz_pzxx.cp,
cz_pzxx.pzbh,
cz_pzxx.pz,
cz_pzxx.pic_path
FROM cz_pzxx
WHERE (cz_pzxx.lrdd = ‘3.10000000000000000e+005‘) AND (cz_pzxx.scbz = 0);

注:要区分数值、日期和字符串,科学计数法更要慎重使用

12.存储过程
(1)在存储过程的关键步骤开始和结束都要记录信息到日志表,用于监控和调试
(2)过程避免每条语句提交
-- 正确语句:
START TRANSACTION;
INSERT INTO t(datetime, UID, content, TYPE) VALUES (‘0‘, ‘userid_0‘, ‘content_0‘, 0),(‘1‘, ‘userid_1‘, ‘content_1‘, 1);
INSERT INTO t(datetime, UID, content, TYPE) VALUES (‘2‘, ‘userid_2‘, ‘content_2‘, 2),(‘3‘, ‘userid_3‘, ‘content_3‘, 3);
COMMIT;
注:通过事务提交可以提高大数据操作效率,同时有序插入、合并插入也可以大幅提高数据库效率

13.查询技巧
-- 问题语句:
SELECT t.*
FROM (SELECT LineID, SealStatus, count(*) AS num
FROM car_line_dtl
WHERE LineID = ‘31001367902‘ AND del_flag = 0
UNION
--- 省略 70 KByte
SELECT LineID, SealStatus, count(*) AS num
FROM car_line_dtl
WHERE LineID = ‘31001370528‘ AND del_flag = 0
GROUP BY LineID, SealStatus) t;

注:可通过分批查询或者使用临时表方式降低查询语句大小
(2)
WHERE 多个 OR 条件不走一个索引时可通过 UNION
-- 问题语句:
SELECT *
FROM t
WHERE bm1 = 953016 OR bm2 = 953016
注:一次查询一般走一个索引,可通过 UNION ALL 优化,如 SELECT * FROM t WHERE bm1 = 953016 UNION ALL SELECT * FROM t WHERE bm2 = 953016

14. 权限控制
PHP 连接 MYSQL 的用户只分配对应库 SIUD 权限中的必要权限
注:权限越大,被攻击时受到的破坏越大

时间: 2024-12-17 17:09:40

sql 规范的相关文章

hbase查询基于标准sql规范中间件Phoenix

Phoenix是个很好的hbase 查询工具,在hbase中安装也很简单,可以按照 http://www.cnblogs.com/laov/p/4137136.html 这个连接中进行配置客户端和服务端的Phoenix. PhoenixSQL有如下类: 增删数据:ExecutableAddColumnStatement.ExecutableDropColumnStatement 创建/删除表格:ExecutableCreateTableStatement.ExecutableDropTableS

SQL规范

注:本文转载自 MR_ke 的博客 地址:http://justcoding.iteye.com/blog/2007243 我们做软件开发的,大部分人都离不开跟数据库打交道,特别是erp开发的,跟数据库打交道更是频繁,存储过程动不动就是上千行,如果数据量大,人员 流动大,那么我们还能保证下一段时间系统还能流畅的运行吗?我们还能保证下一个人能看懂我们的存储过程吗?那么我结合公司平时的培训和平时个人工作经验和 大家分享一下,希望对大家有帮助. 要知道sql语句,我想我们有必要知道sqlserver查

网络攻防之SQL注入攻击

SQL注入攻击的根源是因为SQL规范的漏洞,但是,因为规范的长期存在以及使用,几乎已经不太可能去修改规范了,只能够从开发者本身去避免攻击,虽然SQL注入之前很严重,但现在相对控制的很好,这里仅仅作为一种学习的内容. 测试过程如下: 1:搭建PHP,mysql开发环境,可以详见我的另一篇博客自定义开发PHP环境 2:添加数据库,表,以及表内容. 3:分别测试 万能密码,万能用户名 数字注入 测试如下: 万能密码:password ' or 1='1 (password可以任意的填写,注:这里如果粘

Phoenix(sql on hbase)简介

Phoenix(sql on hbase)简介 介绍: Phoenix is a SQL skin over HBase delivered as a client-embedded JDBC driver targeting low latency queries over HBase data. Phoenix takes your SQL query, compiles it into a series of HBase scans, and orchestrates the runnin

SQL 注入攻击

一位客户让我们针对只有他们企业员工和顾客能使用的企业内网进行渗透测试.这是安全评估的一个部分,所以尽管我们之前没有使用过SQL注入来渗透网络,但对其概念也相当熟悉了.最后我们在这项任务中大获成功,现在来回顾一下这个过程的每一步,将它记录为一个案例. "SQL注入"是一种利用未过滤/未审核用户输入的攻击方法("缓存溢出"和这个不同),意思就是让应用运行本不应该运行的SQL代码.如果应用毫无防备地创建了SQL字符串并且运行了它们,就会造成一些出人意料的结果. 我们记录下

网页闯关游戏(riddle webgame)--SQL注入的潘多拉魔盒

前言: 之前编写了一个网页闯关游戏(类似Riddle Game), 除了希望大家能够体验一下我的游戏外. 也愿意分享编写这个网页游戏过程中, 学到的一些知识. web开发初学者往往会忽视一些常见的漏洞, 比如SQL注入攻击, XSS攻击. 本文将简述SQL注入攻击的原理, 并分享下关卡设计, 其在打开潘多拉魔盒的情况下, 又能很好地限制危害. 效果展示: 先打下广告: 网页闯关游戏入口(请狠狠地点击我, ^_^). 本文的想法实施于第十一关--健忘的教授. 很直接的呈现一个登陆对话框, 考验玩家

sql点滴42—mysql中的数据结构

MySQL 的数值数据类型可以大致划分为两个类别,一个是整数,另一个是浮点数或小数.许多不同的子类型对这些类别中的每一个都是可用的,每个子类型支持不同大小的数据,并且 MySQL 允许我们指定数值字段中的值是否有正负之分或者用零填补. 表列出了各种数值类型以及它们的允许范围和占用的内存空间. 类型 大小 范围(有符号) 范围(无符号) 用途 TINYINT 1 字节 (-128,127) (0,255) 小整数值 SMALLINT 2 字节 (-32 768,32 767) (0,65 535)

11.联结表---SQL

说明:使用交互式DBMS工具重要的是,要理解联结不是物理实体.换句话说,它在实际的数据库表中并不存在.DBMS会根据需要建立联结,它在查询执行期间一直存在. 一.等值语法:SELECT 字段 FROM 表1,表2 WHERE 表1.字段=表2.字段 SELECT vend_name, prod_name, prod_price FROM Vendors, Products WHERE Vendors.vend_id = Products.vend_id; 分析▼我们来看这段代码.SELECT语句

SQL关键字转换大写核心算法实现

1 不跟你多废话 上代码! /// <summary> /// SQL关键字转换器 /// </summary> public class SqlConverter : IKeywordsConvertible { public SqlConverter(string[] keywords) { Keywords = keywords; } public SqlConverter() { } /// <summary> /// 关键字集合 /// </summar