天天看點

SQL Server T-SQL進階查詢一、程式員老鳥寫sql語句的經驗之談二、百萬資料查詢優化技巧三十則

進階查詢在資料庫中用得是最頻繁的,也是應用最廣泛的。

Ø 基本常用查詢

--select      
select * from student;      
--all 查詢所有      
select all sex from student;      
--distinct 過濾重複      
select distinct sex from student;      
--count 統計      
select count(*) from student;      
select count(sex) from student;      
select count(distinct sex) from student;      
--top 取前N條記錄      
select top 3 * from student;      
--alias column name 列重命名      
select id as 編号, name '名稱', sex 性别 from student;      
--alias table name 表重命名      
select id, name, s.id, s.name from student s;      
--column 列運算      
select (age + id) col from student;      
select s.name + '-' + c.name from classes c, student s where s.cid = c.id;      
--where 條件      
select * from student where id = 2;      
select * from student where id > 7;      
select * from student where id < 3;      
select * from student where id <> 3;      
select * from student where id >= 3;      
select * from student where id <= 5;      
select * from student where id !> 3;      
select * from student where id !< 5;      
--and 并且      
select * from student where id > 2 and sex = 1;      
--or 或者      
select * from student where id = 2 or sex = 1;      
--between ... and ... 相當于并且      
select * from student where id between 2 and 5;      
select * from student where id not between 2 and 5;      
--like 模糊查詢      
select * from student where name like '%a%';      
select * from student where name like '%[a][o]%';      
select * from student where name not like '%a%';      
select * from student where name like 'ja%';      
select * from student where name not like '%[j,n]%';      
select * from student where name like '%[j,n,a]%';      
select * from student where name like '%[^ja,as,on]%';      
select * from student where name like '%[ja_on]%';      
--in 子查詢      
select * from student where id in (1, 2);      
--not in 不在其中      
select * from student where id not in (1, 2);      
--is null 是空      
select * from student where age is null;      
--is not null 不為空      
select * from student where age is not null;      
--order by 排序      
select * from student order by name;      
select * from student order by name desc;      
select * from student order by name asc;      
--group by 分組      
按照年齡進行分組統計      
select count(age), age from student group by age;      
按照性别進行分組統計      
select count(*), sex from student group by sex;      
按照年齡和性别組合分組統計,并排序      
select count(*), sex from student group by sex, age order by age;      
按照性别分組,并且是id大于2的記錄最後按照性别排序      
select count(*), sex from student where id > 2 group by sex order by sex;      
查詢id大于2的資料,并完成運算後的結果進行分組和排序      
select count(*), (sex * id) new from student where id > 2 group by sex * id order bysex * id;      
--group by all 所有分組      
按照年齡分組,是所有的年齡      
select count(*), age from student group by all age;      
--having 分組過濾條件      
按照年齡分組,過濾年齡為空的資料,并且統計分組的條數和現實年齡資訊      
select count(*), age from student group by age having age is not null;      
按照年齡和cid組合分組,過濾條件是cid大于1的記錄      
select count(*), cid, sex from student group by cid, sex having cid > 1;      
按照年齡分組,過濾條件是分組後的記錄條數大于等于2      
select count(*), age from student group by age having count(age) >= 2;      
按照cid和性别組合分組,過濾條件是cid大于1,cid的最大值大于2      
select count(*), cid, sex from student group by cid, sex having cid > 1 and max(cid) > 2;      

Ø 嵌套子查詢

    子查詢是一個嵌套在select、insert、update或delete語句或其他子查詢中的查詢。任何允許使用表達式的地方都可以使用子查詢。子查詢也稱為内部查詢或内部選擇,而包含子查詢的語句也成為外部查詢或外部選擇。

# from (select … table)示例

将一個table的查詢結果當做一個新表進行查詢      
select * from (      
select id, name from student where sex = 1      
) t where t.id > 2;      

上面括号中的語句,就是子查詢語句(内部查詢)。在外面的是外部查詢,其中外部查詢可以包含以下語句:

     1、 包含正常選擇清單元件的正常select查詢

     2、 包含一個或多個表或視圖名稱的正常from語句

     3、 可選的where子句

     4、 可選的group by子句

     5、 可選的having子句

# 示例

查詢班級資訊,統計班級學生人生      
select *, (select count(*) from student where cid = classes.id) as num      
from classes order by num;      

# in, not in子句查詢示例

查詢班級id大于小于的這些班級的學生資訊      
select * from student where cid in (      
select id from classes where id > 2 and id < 4      
);      
查詢不是班的學生資訊      
select * from student where cid not in (      
select id from classes where name = '2班'      
)      

in、not in 後面的子句傳回的結果必須是一列,這一列的結果将會作為查詢條件對應前面的條件。如cid對應子句的id;

# exists和not exists子句查詢示例

查詢存在班級id為的學生資訊      
select * from student where exists (      
select * from classes where id = student.cid and id = 3      
);      
查詢沒有配置設定班級的學生資訊      
select * from student where not exists (      
select * from classes where id = student.cid      
);      

exists和not exists查詢需要内部查詢和外部查詢進行一個關聯的條件,如果沒有這個條件将是查詢到的所有資訊。如:id等于student.id;

# some、any、all子句查詢示例

查詢班級的學生年齡大于班級的學生的年齡的資訊      
select * from student where cid = 5 and age > all (      
select age from student where cid = 3      
);      
select * from student where cid = 5 and age > any (      
select age from student where cid = 3      
);      
select * from student where cid = 5 and age > some (      
select age from student where cid = 3      
);      

Ø 聚合查詢

1、 distinct去掉重複資料

select distinct sex from student;      
select count(sex), count(distinct sex) from student;      

2、 compute和compute by彙總查詢

對年齡大于的進行彙總      
select age from student      
where age > 20 order by age compute sum(age) by age;      
對年齡大于的按照性别進行分組彙總年齡資訊      
select id, sex, age from student      
where age > 20 order by sex, age compute sum(age) by sex;      
按照年齡分組彙總      
select age from student      
where age > 20 order by age, id compute sum(age);      
按照年齡分組,年齡彙總,id找最大值      
select id, age from student      
where age > 20 order by age compute sum(age), max(id);      

compute進行彙總前面是查詢的結果,後面一條結果集就是彙總的資訊。compute子句中可以添加多個彙總表達式,可以添加的資訊如下:

     a、 可選by關鍵字。它是每一列計算指定的行聚合

     b、 行聚合函數名稱。包括sum、avg、min、max、count等

     c、 要對其執行聚合函數的列

     compute by适合做先分組後彙總的業務。compute by後面的列一定要是order by中出現的列。

3、 cube彙總

cube彙總和compute效果類似,但文法較簡潔,而且傳回的是一個結果集。

select count(*), sex from student group by sex with cube;      
select count(*), age, sum(age) from student where age is not null group by age withcube;      

cube要結合group by語句完成分組彙總

Ø 排序函數

   排序在很多地方需要用到,需要對查詢結果進行排序并且給出序号。比如:

   1、 對某張表進行排序,序号需要遞增不重複的

   2、 對學生的成績進行排序,得出名次,名次可以并列,但名次的序号是連續遞增的

   3、 在某些排序的情況下,需要跳空序号,雖然是并列

基本文法

排序函數 over([分組語句] 排序子句[desc][asc])      
排序子句 order by 列名, 列名      
分組子句 partition by 分組列, 分組列      

# row_number函數

根據排序子句給出遞增連續序号

按照名稱排序的順序遞增      
select s.id, s.name, cid, c.name, row_number() over(order by c.name) as number      
from student s, classes c where cid = c.id;      

# rank函數函數

根據排序子句給出遞增的序号,但是存在并列并且跳空

順序遞增      
select id, name, rank() over(order by cid) as rank from student;      
跳過相同遞增      
select s.id, s.name, cid, c.name, rank() over(order by c.name) as rank      
from student s, classes c where cid = c.id;      

# dense_rank函數

根據排序子句給出遞增的序号,但是存在并列不跳空

不跳過,直接遞增      
select s.id, s.name, cid, c.name, dense_rank() over(order by c.name) as dense      
from student s, classes c where cid = c.id;      

# partition by分組子句

可以完成對分組的資料進行增加排序,partition by可以與以上三個函數聯合使用。

利用partition by按照班級名稱分組,學生id排序      
select s.id, s.name, cid, c.name, row_number() over(partition by c.name order by s.id) as rank      
from student s, classes c where cid = c.id;      
select s.id, s.name, cid, c.name, rank() over(partition by c.name order by s.id) asrank      
from student s, classes c where cid = c.id;      
select s.id, s.name, cid, c.name, dense_rank() over(partition by c.name order by s.id) as rank      
from student s, classes c where cid = c.id;      

# ntile平均排序函數

将要排序的資料進行平分,然後按照等分排序。ntile中的參數代表分成多少等分。

select s.id, s.name, cid, c.name,      
ntile(5) over(order by c.name) as ntile      
from student s, classes c where cid = c.id;      

Ø 集合運算

操作兩組查詢結果,進行交集、并集、減集運算

1、 union和union all進行并集運算

--union 并集、不重複      
select id, name from student where name like 'ja%'      
union      
select id, name from student where id = 4;      
--并集、重複      
select * from student where name like 'ja%'      
union all      
select * from student;      

2、 intersect進行交集運算

--交集(相同部分)      
select * from student where name like 'ja%'      
intersect      
select * from student;      

3、 except進行減集運算

--減集(除相同部分)      
select * from student where name like 'ja%'      
except      
select * from student where name like 'jas%';      

Ø 公式表表達式

查詢表的時候,有時候中間表需要重複使用,這些子查詢被重複查詢調用,不但效率低,而且可讀性低,不利于了解。那麼公式表表達式可以解決這個問題。

我們可以将公式表表達式(CET)視為臨時結果集,在select、insert、update、delete或是create view語句的執行範圍内進行定義。

--表達式      
with statNum(id, num) as      
(      
select cid, count(*)      
from student      
where id > 0      
group by cid      
)      
select id, num from statNum order by id;      
with statNum(id, num) as      
(      
select cid, count(*)      
from student      
where id > 0      
group by cid      
)      
select max(id), avg(num) from statNum;      

Ø 連接配接查詢

1、 簡化連接配接查詢

--簡化聯接查詢      
select s.id, s.name, c.id, c.name from student s, classes c where s.cid = c.id;      

2、 left join左連接配接

--左連接配接      
select s.id, s.name, c.id, c.name from student s left join classes c on s.cid = c.id;      

3、 right join右連接配接

--右連接配接      
select s.id, s.name, c.id, c.name from student s right join classes c on s.cid = c.id;      

4、 inner join内連接配接

--内連接配接      
select s.id, s.name, c.id, c.name from student s inner join classes c on s.cid = c.id;      
--inner可以省略      
select s.id, s.name, c.id, c.name from student s join classes c on s.cid = c.id;      

1、 聚合函數

max最大值、min最小值、count統計、avg平均值、sum求和、var求方差

select      
max(age) max_age,      
min(age) min_age,      
count(age) count_age,      
avg(age) avg_age,      
sum(age) sum_age,      
var(age) var_age      
from student;      

2、 日期時間函數

select dateAdd(day, 3, getDate());--加天      
select dateAdd(year, 3, getDate());--加年      
select dateAdd(hour, 3, getDate());--加小時      
--傳回跨兩個指定日期的日期邊界數和時間邊界數      
select dateDiff(day, '2011-06-20', getDate());      
--相差秒數      
select dateDiff(second, '2011-06-22 11:00:00', getDate());      
--相差小時數      
select dateDiff(hour, '2011-06-22 10:00:00', getDate());      
select dateName(month, getDate());--目前月份      
select dateName(minute, getDate());--目前分鐘      
select dateName(weekday, getDate());--目前星期      
select datePart(month, getDate());--目前月份      
select datePart(weekday, getDate());--目前星期      
select datePart(second, getDate());--目前秒數      
select day(getDate());--傳回目前日期天數      
select day('2011-06-30');--傳回目前日期天數      
select month(getDate());--傳回目前日期月份      
select month('2011-11-10');      
select year(getDate());--傳回目前日期年份      
select year('2010-11-10');      
select getDate();--目前系統日期      
select getUTCDate();--utc日期      

一、程式員老鳥寫sql語句的經驗之談

做管理系統的,無論是bs結構的還是cs結構的,都不可避免的涉及到資料庫表結構的設計,sql語句的編寫等。是以在開發系統的時候,表結構設計是否合理,sql語句是否标準,寫出的sql性能是否優化往往會成為公司衡量程式員技術水準的标準。

我們程式員不是dba,不需要時刻關注sql運作時間,想方設法優化表結構,存儲空間,優化表讀取速度等等,但是在開發系統時,時刻保持優良的寫sql語句的作風是很有必要的,這關乎到個人在公司的聲譽,嘿嘿,你懂的。。。

新來的程式員老鳥,在一個開發團隊中,需要表現一下自己的水準,奠定在公司的地位,需要努力表現一把,最簡單的從寫的sql語句就很容易表現出來,曾經就有一次,一個老程式員,上面定位是要做團隊上司的,先曆練一下做個制單的子產品,清單sql中有一列這位老鳥直接寫了個select語句從别的表中取之,而不是用表之間關聯得到,一下破壞自己程式員老鳥光輝形象。

做技術的還是要注重自己的内涵,提升内功,哈哈。

閑話少說,總結一點程式員老鳥寫sql順手拈來的功夫吧:

1. 不論一個sql中涉及到多個表,每次都用兩個表(結果集)操作,得到新的結果後,再和下一個表(結果集)操作。

2. 避免在select f1,(select f2 from tableB ).... from tableA 這樣得到字段列。直接用tableA和tableB關聯得到A.f1,B.f2就可以了。

3.避免隐含的類型轉換

 如 

 select id from employee where emp_id='8'  (錯)

 select id from employee where emp_id=8    (對)

 emp_id是整數型,用'8'會預設啟動類型轉換,增加查詢的開銷。

4. 盡量減少使用正規表達式,盡量不使用通配符。

5. 使用關鍵字代替函數

   如:

   select id from employee where UPPER(dept) like 'TECH_DB'  (錯)

   select id from employee where SUBSTR(dept,1,4)='TECH'    (錯)

   select id from employee where dept like 'TECH%'         (對)

6.不要在字段上用轉換函數,盡量在常量上用

  如:

  select id from employee where to_char(create_date,'yyyy-mm-dd')='2012-10-31'  (錯)

  select id from employee where create_date=to_date('2012-10-31','yyyy-mm-dd')   (對)

7.不使用聯接做查詢

 如:select id from employee where first_name || last_name like 'Jo%'  (錯)

8. 盡量避免前後都用通配符

  如:

  select id from employee where dept like '%TECH%' (錯)

  select id from employee where dept like 'TECH%' (對)

9. 判斷條件順序

  如:

  select id from employee where creat_date-30>to_date('2012-10-31','yyyy-mm-dd')   (錯)

    select id from employee where creat_date >to_date('2012-10-31','yyyy-mm-dd')+30   (對)

10. 盡量使用exists而非in

 當然這個也要根據記錄的情況來定用exists還是用in, 通常的情況是用exists

 select id from employee where salary in (select salary from emp_level where....)   (錯)   

 select id from employee where salary exists(select 'X' from emp_level where ....)   (對)

11. 使用not exists 而非not in

    和上面的類似

12. 減少查詢表的記錄數範圍

13.正确使用索引

  索引可以提高速度,一般來說,選擇度越高,索引的效率越高。

14. 索引類型

  唯一索引,對于查詢用到的字段,盡可能使用唯一索引。

  還有一些其他類型,如位圖索引,在性别字段,隻有男女的字段上用。

15. 在經常進行連接配接,但是沒有指定為外鍵的列上建立索引

16. 在頻繁進行排序會分組的列上建立索引,如經常做group by 或 order by 操作的字段。

17. 在條件表達式中經常用到的不同值較多的列上建立檢索,在不同值少的列上不建立索引。如性别列上隻有男,女兩個不同的值,就沒必要建立索引(或建立位圖索引)。如果建立索引不但不會提高查詢效率,反而會嚴重降低更新速度。

18. 在值比較少的字段做order by時,翻頁會出現記錄紊亂問題,要帶上id字段一起做order by.

19. 不要使用空字元串進行查詢

    如:

    select id from employee where emp_name like '%%' (錯)

20. 盡量對經常用作group by的關鍵字段做索引。

21. 正确使用表關聯

    利用外連接配接替換效率十分低下的not in運算,大大提高運作速度。

    如:

    select a.id from employee a where a.emp_no not in (select emp_no from employee1 where job ='SALE')  (錯)

22. 使用臨時表    

   在必要的情況下,為減少讀取次數,可以使用經過索引的臨時表加快速度。

   如:

   select e.id from employee e ,dept d where e.dept_id=d.id and e.empno>1000 order by e.id   (錯)

   select id,empno from employee into temp_empl where empno>1000 order by id

   select m.id from temp_emp1 m,dept d where m.empno=d.id      (對)

 對于大資料量sql語句性能優化更多的工作就交給dba去實踐,我們程式員做好這些基本功就好了。

以上文章來着部落格園web報表的部落格。

二、百萬資料查詢優化技巧三十則

       1.對查詢進行優化,應盡量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。 

  2.應盡量避免在 where 子句中對字段進行 null 值判斷,否則将導緻引擎放棄使用索引而進行全表掃描,如:

  

select id from t where num is null

  可以在num上設定預設值0,確定表中num列沒有null值,然後這樣查詢:

  select id from t where num=0

  3.應盡量避免在 where 子句中使用!=或<>操作符,否則将引擎放棄使用索引而進行全表掃描。

  4.應盡量避免在 where 子句中使用 or 來連接配接條件,否則将導緻引擎放棄使用索引而進行全表掃描,如:

  select id from t where num=10 or num=20

  可以這樣查詢:

  select id from t where num=10

  union all

  select id from t where num=20

  5.in 和 not in 也要慎用,否則會導緻全表掃描,如:

  select id from t where num in(1,2,3)

  對于連續的數值,能用 between 就不要用 in 了:

  select id from t where num between 1 and 3

  6.下面的查詢也将導緻全表掃描:

  select id from t where name like '%abc%'

  若要提高效率,可以考慮全文檢索。

  7.如果在 where 子句中使用參數,也會導緻全表掃描。因為SQL隻有在運作時才會解析局部變量,但優化程式不能将通路計劃的選擇推遲到運作時;它必須在編譯時進行選擇。然而,如果在編譯時建立通路計劃,變量的值還是未知的,因而無法作為索引選擇的輸入項。如下面語句将進行全表掃描:

  select id from t where num=@num <mailto:num=@num>

  可以改為強制查詢使用索引:

  select id from t with(index(索引名)) where num=@num <mailto:num=@num>

  8.應盡量避免在 where 子句中對字段進行表達式操作,這将導緻引擎放棄使用索引而進行全表掃描。如:

  select id from t where num/2=100

  應改為:

  select id from t where num=100*2

  9.應盡量避免在where子句中對字段進行函數操作,這将導緻引擎放棄使用索引而進行全表掃描。如:

  select id from t where substring(name,1,3)='abc'--name以abc開頭的id

  select id from t where datediff(day,createdate,'2005-11-30')=0--‘2005-11-30’生成的id

  應改為:

  select id from t where name like 'abc%'

  select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'

  10.不要在 where 子句中的“=”左邊進行函數、算術運算或其他表達式運算,否則系統将可能無法正确使用索引。

  11.在使用索引字段作為條件時,如果該索引是複合索引,那麼必須使用到該索引中的第一個字段作為條件時才能保證系統使用該索引,否則該索引将不會被使用,并且應盡可能的讓字段順序與索引順序相一緻。

  12.不要寫一些沒有意義的查詢,如需要生成一個空表結構:

  select col1,col2 into #t from t where 1=0

  這類代碼不會傳回任何結果集,但是會消耗系統資源的,應改成這樣:

  create table #t(...)

  13.很多時候用 exists 代替 in 是一個好的選擇:

  select num from a where num in(select num from b)

  用下面的語句替換:

  select num from a where exists(select 1 from b where num=a.num)

  14.并不是所有索引對查詢都有效,SQL是根據表中資料來進行查詢優化的,當索引列有大量資料重複時,SQL查詢可能不會去利用索引,如一表中有字段sex,male、female幾乎各一半,那麼即使在sex上建了索引也對查詢效率起不了作用。

  15.索引并不是越多越好,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因為 insert 或 update 時有可能會重建索引,是以怎樣建索引需要慎重考慮,視具體情況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有必要。

  16.應盡可能的避免更新 clustered 索引資料列,因為 clustered 索引資料列的順序就是表記錄的實體存儲順序,一旦該列值改變将導緻整個表記錄的順序的調整,會耗費相當大的資源。若應用系統需要頻繁更新 clustered 索引資料列,那麼需要考慮是否應将該索引建為 clustered 索引。

  17.盡量使用數字型字段,若隻含數值資訊的字段盡量不要設計為字元型,這會降低查詢和連接配接的性能,并會增加存儲開銷。這是因為引擎在處理查詢和連接配接時會逐個比較字元串中每一個字元,而對于數字型而言隻需要比較一次就夠了。

  18.盡可能的使用 varchar/nvarchar 代替 char/nchar ,因為首先變長字段存儲空間小,可以節省存儲空間,其次對于查詢來說,在一個相對較小的字段内搜尋效率顯然要高些。

  19.任何地方都不要使用 select * from t ,用具體的字段清單代替“*”,不要傳回用不到的任何字段。

  20.盡量使用表變量來代替臨時表。如果表變量包含大量資料,請注意索引非常有限(隻有主鍵索引)。

  21.避免頻繁建立和删除臨時表,以減少系統表資源的消耗。

  22.臨時表并不是不可使用,适當地使用它們可以使某些例程更有效,例如,當需要重複引用大型表或常用表中的某個資料集時。但是,對于一次性事件,最好使用導出表。

  23.在建立臨時表時,如果一次性插入資料量很大,那麼可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果資料量不大,為了緩和系統表的資源,應先create table,然後insert。

  24.如果使用到了臨時表,在存儲過程的最後務必将所有的臨時表顯式删除,先 truncate table ,然後 drop table ,這樣可以避免系統表的較長時間鎖定。

  25.盡量避免使用遊标,因為遊标的效率較差,如果遊标操作的資料超過1萬行,那麼就應該考慮改寫。

  26.使用基于遊标的方法或臨時表方法之前,應先尋找基于集的解決方案來解決問題,基于集的方法通常更有效。

  27.與臨時表一樣,遊标并不是不可使用。對小型資料集使用 FAST_FORWARD 遊标通常要優于其他逐行處理方法,尤其是在必須引用幾個表才能獲得所需的資料時。在結果集中包括“合計”的例程通常要比使用遊标執行的速度快。如果開發時間允許,基于遊标的方法和基于集的方法都可以嘗試一下,看哪一種方法的效果更好。

  28.在所有的存儲過程和觸發器的開始處設定 SET NOCOUNT ON ,在結束時設定 SET NOCOUNT OFF 。無需在執行存儲過程和觸發器的每個語句後向用戶端發送 DONE_IN_PROC 消息。

  29.盡量避免大事務操作,提高系統并發能力。

  30.盡量避免向用戶端傳回大資料量,若資料量過大,應該考慮相應需求是否合理。

兩篇文章來源:

http://blog.csdn.net/chenleixing/article/details/42610529

http://blog.sina.com.cn/s/blog_78106bb1010108oh.html

版權聲明:本文為CSDN部落客「weixin_34384915」的原創文章,遵循CC 4.0 BY-SA版權協定,轉載請附上原文出處連結及本聲明。

原文連結:https://blog.csdn.net/weixin_34384915/article/details/92093242