前言
資料庫的查詢執行,毋庸置疑是程式設計師必備技能之一,然而資料庫查詢執行的過程絢爛多彩,卻是很少被人瞭解,今天哥哥要帶你裝逼帶你飛,深入一下這sql查詢的來龍去脈,為查詢的效能優化處理打個基礎,或許面試你也會遇到,預防不跪還是看看吧。
這篇部落格,摒棄查詢優化效能,作為其基礎,只針對查詢流程講解剖析。
本片部落格闡述的過程為
1、上一個標識過的sql語句,展示查詢執行的流程
2、上一個流程圖
3、做一個例子逐步深入分析,幫助理解
4、做一個裝逼的總結
sql查詢語句的處理步驟,程式碼清單
--查詢組合欄位 (5)select (5-2) distinct(5-3) top(<top_specification>)(5-1)<select_list> --連表 (1)from (1-J)<left_table><join_type> join <right_table> on <on_predicate> (1-A)<left_table><apply_type> apply <right_table_expression> as <alias> (1-P)<left_table> pivot (<pivot_specification>) as <alias> (1-U)<left_table> unpivot (<unpivot_specification>) as <alias> --查詢條件 (2)where <where_pridicate> --分組 (3)group by <group_by_specification> --分組條件 (4)having<having_predicate> --排序 (6)order by<order_by_list>
說明:
1、順序為有1-6,6個大步驟,然後細分,5-1,5-2,5-3,由小變大順序,1-J,1-A,1-P,1-U,為並行次序。如果不夠明白,接下來我在來個流程圖看看。
2、執行過程中也會相應的產生多個虛擬表(下面會有提到),以配合最終的正確查詢。
sql查詢語句的處理步驟,流程圖
例項準備,建立表,插入資料,寫要分析的例項查詢語句
1、首先建立2各表
2、建立兩個表,並插入表資料,指令碼如下
USE [test] GO /****** Object: Table [dbo].[Member] Script Date: 2014/12/22 14:05:17 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[Member]( [id] [int] IDENTITY(1,1) NOT NULL, [Name] [nvarchar](30) NULL, [phone] [varchar](15) NULL, CONSTRAINT [PK_MEMBER] PRIMARY KEY CLUSTERED ( [id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO SET ANSI_PADDING OFF GO /****** Object: Table [dbo].[Order] Script Date: 2014/12/22 14:05:17 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[Order]( [id] [int] IDENTITY(1,1) NOT NULL, [member_id] [int] NULL, [status] [int] NULL, [createTime] [datetime] NULL, CONSTRAINT [PK_ORDER] PRIMARY KEY CLUSTERED ( [id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO SET IDENTITY_INSERT [dbo].[Member] ON GO INSERT [dbo].[Member] ([id], [Name], [phone]) VALUES (1, N'張龍豪', N'18501733702') GO INSERT [dbo].[Member] ([id], [Name], [phone]) VALUES (2, N'Jim', N'15039512688') GO INSERT [dbo].[Member] ([id], [Name], [phone]) VALUES (3, N'Tom', N'15139512854') GO INSERT [dbo].[Member] ([id], [Name], [phone]) VALUES (4, N'Lulu', N'15687425583') GO INSERT [dbo].[Member] ([id], [Name], [phone]) VALUES (5, N'Jick', N'13528567445') GO SET IDENTITY_INSERT [dbo].[Member] OFF GO SET IDENTITY_INSERT [dbo].[Order] ON GO INSERT [dbo].[Order] ([id], [member_id], [status], [createTime]) VALUES (1, 1, 3, CAST(0x0000A40900B3BBFB AS DateTime)) GO INSERT [dbo].[Order] ([id], [member_id], [status], [createTime]) VALUES (2, 2, 1, CAST(0x0000A40900B3CEF2 AS DateTime)) GO INSERT [dbo].[Order] ([id], [member_id], [status], [createTime]) VALUES (3, 3, 4, CAST(0x0000A40900B3D2D0 AS DateTime)) GO INSERT [dbo].[Order] ([id], [member_id], [status], [createTime]) VALUES (4, 4, 0, CAST(0x0000A40900B3D660 AS DateTime)) GO INSERT [dbo].[Order] ([id], [member_id], [status], [createTime]) VALUES (5, 5, 1, CAST(0x0000A40900B3D9B9 AS DateTime)) GO INSERT [dbo].[Order] ([id], [member_id], [status], [createTime]) VALUES (6, 6, 2, CAST(0x0000A40900B3DFEA AS DateTime)) GO INSERT [dbo].[Order] ([id], [member_id], [status], [createTime]) VALUES (7, NULL, 0, CAST(0x0000A40900E34971 AS DateTime)) GO SET IDENTITY_INSERT [dbo].[Order] OFF GO ALTER TABLE [dbo].[Order] ADD DEFAULT (getdate()) FOR [createTime] GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'編號' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'Member', @level2type=N'COLUMN',@level2name=N'id' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'姓名' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'Member', @level2type=N'COLUMN',@level2name=N'Name' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'電話' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'Member', @level2type=N'COLUMN',@level2name=N'phone' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'會員表' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'Member' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'編號' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'Order', @level2type=N'COLUMN',@level2name=N'id' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'會員編號' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'Order', @level2type=N'COLUMN',@level2name=N'member_id' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'訂單狀態' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'Order', @level2type=N'COLUMN',@level2name=N'status' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'下單日期' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'Order', @level2type=N'COLUMN',@level2name=N'createTime' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'訂單表' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'Order' GO
3、編寫我們們要解析的查詢語句,即本篇要查詢的例項語句。
select top(4) status , max(m.id) as maxMemberID from [dbo].[Member] as m right outer join [dbo].[Order] as o on m.id=o.member_id
where m.id>0 group by status having status>=0 order by maxMemberID asc
例項語句分步驟分析
第一步,從from開始。
1.1、載入左表
from [dbo].[Member] as m
查詢結果:member表中的所有資料
1.2、這裡應該是 right outer join ,但是這裡在sql中被定義分解為2個步驟,即join ,right outer join 。表示式關鍵字從左到右,依次執行。
join [dbo].[Order] as o
查詢結果:存入虛擬表vt1,為兩個表的笛卡爾集合。這裡你或許不明白什麼叫笛卡爾集合,我打個比方給說說,還望不要嫌棄,就是小朋友握手問題,A班裡有3個學生(看作一個表的三條資料),B班裡有2個學生(看作另外一個表的2條資料).B班小朋友跟A班小朋友搞聯歡晚會,首先要每個人都要確保跟另外一個班的同學我一下手,那麼交叉出來的集合就是(2*3=6)有6條不同的軌跡。這個軌跡的集合就是笛卡爾集合。如果你還不明白,我再說下,就是m(5條資料)表中的第一條資料跟o(7條資料)表中的所有資料握下手,有7條,然後依次類推共有35條不同的資料。這裡的null值也是要加進來的。
1.3、on 篩選器
on m.id=o.member_id
查詢結果:從上一步的笛卡爾集合35條資料中刪除掉不匹配的行,就得到啦5條資料,存入虛擬表Vt2
1.4、新增外部行(outer row)
right outer join [dbo].[Order] as o
查詢結果為:右表(order)作為保留表,把剩餘的資料重新新增到上一步的虛擬表中vt2,生成虛擬表vt3.
第二部,進入where階段
where m.id>0
查詢結果:存入虛擬表vt4,為篩選的條件為true的結果集,這裡加入一個記憶點,就是,where的篩選刪除為永久的,而on的篩選刪除為暫時的,因為on篩選過後,有可能會經過outer新增外部行,重新把資料載入回來,而where則不能。
第三部,group by分組
group by status
查詢結果:存入vt5,以status列的數值開始分組,即status列,值一樣的分為一組,這裡的兩個null在三值邏輯中被視為true。三值邏輯:true,false,null。此三值,null為未知,是資料的邏輯特色,有的地方兩個null相等為ture,在有些地方則為false。這個你百度下看看有很多講解。
第四步,having篩選器
having status>=0
查詢結果:篩選分好組的組資料,把不滿足條件的刪除掉
第五步,select查詢挑揀計算列
5.1、計算表示式
select status , max(m.id)
查詢結果:從分過組的資料中計算各個組中的最大m.id,列出要篩選顯示的列。
5.2、distinct過濾重複
5.3、top 結合order by 篩選 多少行,但這裡的資料沒有排序只是把多少行資料列出來而已。
第六部,order by 排序顯示。
蛋疼的總結,裝逼是有依據的
本篇部落格參考:《Microsoft SQL Server 2008技術內幕:T-SQL查詢》,感謝閱讀,(C#).NET技術分享QQ群: 232458226,歡迎加入。