日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

SQL SERVER With语法[转]

發(fā)布時間:2024/1/17 数据库 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SQL SERVER With语法[转] 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

今天在論壇上看到一個舉例,關(guān)于sql server 的示例。1/25/50/100美分,多少種可能拼湊成2美元。

??? 看了其中第一條語法,放在SQL SERVER中測試,發(fā)現(xiàn)真的列舉出所有組合成2美元的方式。

?? 于是仔細研究語法,發(fā)現(xiàn)用了With關(guān)鍵字。

?

?? 發(fā)現(xiàn)很長時間沒有使用SQL SERVER數(shù)據(jù)庫,真的有點落后了。于是見到那學(xué)習(xí)了下 With關(guān)鍵字。

1.引言

?

現(xiàn)實項目中經(jīng)常遇到需要處理遞歸父子關(guān)系的問題,如果把層次關(guān)系分開,放在多個表里通過主外鍵關(guān)系聯(lián)接,最明顯的問題就是擴展起來不方便,對于這種情況,一般我們會創(chuàng)建一個使用自連接的表來存放數(shù)據(jù)。例如存放會員地區(qū)數(shù)據(jù)表結(jié)構(gòu)可能是這樣:

?

列名描述
location_id地區(qū)編號
location_name地區(qū)名稱
parentlocation_id上級地區(qū)編號

?

或者某個部分的職員表結(jié)構(gòu)可能如下所示:

?

列名描述
employee_id職員編號
employee_name職員名稱
manager_id職員的直接上級管理者,和employee_id進行自聯(lián)接

?

通過類似表結(jié)構(gòu),我們就可以通過一個表理論上管理無限級數(shù)的父/子關(guān)系,但是當(dāng)我們需要將這些數(shù)據(jù)讀取出來,不論是填充到一個樹中,或是使用級聯(lián)顯 示出來,需要花費一定的精力。傳統(tǒng)的做法,是做一個遞歸調(diào)用,首先連接數(shù)據(jù)庫將頂層數(shù)據(jù)(也就是parent_xxx為null的記錄)讀取出來,再對每 一條數(shù)據(jù)進行遞歸訪問填充集合,這種做法需要連接數(shù)據(jù)庫多次,顯然不是較好的解決方法,那么我們能不能通過一次數(shù)據(jù)庫訪問,將數(shù)據(jù)全部讀取出來,并且為了 按照父子關(guān)系形成集合,使返回的數(shù)據(jù)滿足某種格式。

2. 分析

?

理想情況下,如果父/子關(guān)系數(shù)據(jù)時嚴格按照關(guān)系結(jié)構(gòu)添加到數(shù)據(jù)庫中,亦即首先添加某條父記錄,接著添加該父記錄的子記錄,如果子記錄還包含子記錄的 話繼續(xù)添加,最終數(shù)據(jù)表中父/子關(guān)系按規(guī)則排列數(shù)據(jù),我們就可以使用某種算法填充集合,但是正如我們所說,這是理想情況,實際情況下數(shù)據(jù)經(jīng)常會發(fā)生改變, 導(dǎo)致數(shù)據(jù)沒有規(guī)律可言,如下圖所示,這樣的話讀取數(shù)據(jù)填充集合就不太容易的。

所以我們要做的就是通過查詢使數(shù)據(jù)庫返回的數(shù)據(jù)滿足這種格式,那么我們的思路是首先查找頂層(0層)記錄,再查詢第1層記錄,接下來是第2層、第3層直到第n層。因為層數(shù)是不確定的,所以仍然需要使用遞歸訪問。

SQL Server 2005中提供了新的with關(guān)鍵字,用于指定臨時命名的結(jié)果集,這些結(jié)果集稱為公用表表達式(CTE)。該表達式源自簡單查詢,并且在SELECT、 INSERT、UPDATE或DELETE 語句的執(zhí)行范圍內(nèi)定義。該子句也可用在 CREATE VIEW 語句中,作為該語句的 SELECT 定義語句的一部分。公用表表達式可以包括對自身的引用。這種表達式稱為遞歸公用表表達式。

其語法為:

[ WITH <common_table_expression> [ ,...n ] ] <common_table_expression>::= expression_name[ ( column_name [ ,...n ] ) ] AS ( CTE_query_definition )

使用with關(guān)鍵子的一個簡單示例,以下代碼將tb_loc表中數(shù)據(jù)源樣輸出:

WITH locs(id,name,parent) AS (SELECT * FROM tb_loc ) SELECT * FROM locs

為了創(chuàng)建良好層次記錄結(jié)構(gòu)集,使用with關(guān)鍵字首先讀取頂層記錄,并且針對每一條頂層記錄讀取其子記錄,直到讀取到最底層級記錄,最后將所有的記錄組合起來,這里用到了UNION ALL關(guān)鍵字,用于將多個查詢結(jié)果組合到一個結(jié)果集中。

接下來就可以使用該關(guān)鍵字創(chuàng)建存儲過程返回結(jié)果集,并附加每條記錄所位于的“層”數(shù),如下圖所示:


最后需要在前臺界面將其顯示出來,由于記錄已經(jīng)按層次返回,需要做的就是按層次首其輸出,首先將第0層數(shù)據(jù)輸出,接下來將遍歷第0層數(shù)據(jù),將第一層 數(shù)據(jù)添加到合適的父對象中,重復(fù)此過程直到填充結(jié)果。那么這里的難題就在于如何查找父對象,我們當(dāng)然可以遍歷集合,但是這么做的話如果數(shù)據(jù)量很大將導(dǎo)致效 率低下。既然可以得到當(dāng)前對象所位于的層的信息,就也是這樹倒置的樹是一層一層向下填充的,我們可以定義一個臨時集合變量,存儲當(dāng)前層上一層的所有父對 象,在插入當(dāng)前層對象時遍歷集合變量以插入到合適的位置,同時我們還必須保證在逐層讀取數(shù)據(jù)時臨時集合變量中持有的始終時當(dāng)前層上一層所有的對象,程序流 程圖如下所示:

根據(jù)以上分析,我們就可以編寫實現(xiàn)代碼了(為了方便,將本文中用到的數(shù)據(jù)表和創(chuàng)建記錄等SQL語句一并給出)。

3. 實現(xiàn)

3.1 打開SQL Server 2005 Management Studio,選擇某個數(shù)據(jù)庫輸入以下語句創(chuàng)建表結(jié)構(gòu):

CREATE TABLE [tb_loc]([id] [int],[name] [varchar](16),[parent] [int] )GO

3.2 創(chuàng)建測試數(shù)據(jù):

INSERT tb_loc(id,name,parent) VALUES( 1,'河北省',NULL) INSERT tb_loc(id,name,parent) VALUES( 2,'石家莊',1) INSERT tb_loc(id,name,parent) VALUES( 3,'保定',1) INSERT tb_loc(id,name,parent) VALUES( 4,'山西省',NULL) INSERT tb_loc(id,name,parent) VALUES( 5,'太原',4) INSERT tb_loc(id,name,parent) VALUES( 6,'新華區(qū)',2) INSERT tb_loc(id,name,parent) VALUES( 7,'北焦村',6) INSERT tb_loc(id,name,parent) VALUES( 8,'大郭村',6) INSERT tb_loc(id,name,parent) VALUES( 9,'河南省',NULL) INSERT tb_loc(id,name,parent) VALUES( 10,'大郭村南',8) INSERT tb_loc(id,name,parent) VALUES( 11,'大郭村北',8) INSERT tb_loc(id,name,parent) VALUES( 12,'北焦村東',7) INSERT tb_loc(id,name,parent) VALUES( 13,'北焦村西',7) INSERT tb_loc(id,name,parent) VALUES( 14,'橋東區(qū)',3) INSERT tb_loc(id,name,parent) VALUES( 15,'橋西區(qū)',3) GO

3.3 創(chuàng)建pr_GetLocations存儲過程:

CREATE PROCEDURE pr_GetLocations AS BEGINWITH locs(id,name,parent,loclevel)AS(SELECT id,name,parent,0 AS loclevel FROM tb_locWHERE parent IS NULLUNION ALLSELECT l.id,l.name,l.parent,loclevel+1 FROM tb_loc l INNER JOIN locs p ON l.parent=p.id)SELECT * FROM locs END

3.4 在Visual Studio 2008里創(chuàng)建解決方案并新建一個網(wǎng)站。

3.5 在網(wǎng)站中添加APP_Code目錄,并創(chuàng)建Location實體類,該類標(biāo)識了所在地編號和名稱,并且保存了父級所在地編號和它所包含的所有子所在地的集合:

public class Location {public int Id{get;set;}public string Name{get;set;}public LocationCollection SubLocations{get;set;}public int ParentId{get;set;}public Location(){Id = 0;Name = string.Empty;SubLocations = new LocationCollection();ParentId=0;} }

3.5 以上代碼使用了LocationCollection集合類,使用泛型集合創(chuàng)建該類(同樣位于APP_Code目錄下):

using System.Collections.Generic;public class LocationCollection:List<Location> {}

3.6 在APP_Code目錄下創(chuàng)建DAO類用于訪問數(shù)據(jù)庫,添加必要的命名空間引用:

using System; using System.Data; using System.Data.SqlClient;public class DAO { }

3.7編寫GetLocations方法,返回所在地集合對象(請根據(jù)實際情況修改數(shù)據(jù)庫連接字符串):

public LocationCollection GetLocations() {LocationCollection locs = new LocationCollection();using (SqlConnection conn = newSqlConnection("server=.;uid=sa;pwd=00000000;database=temp;")){conn.Open();SqlCommand cmd = new SqlCommand();cmd.CommandText = "pr_GetLocations";cmd.CommandType = CommandType.StoredProcedure;cmd.Connection = conn;SqlDataReader reader = cmd.ExecuteReader();int level = 0;int oldlevel = 1;LocationCollection container=new LocationCollection();LocationCollection current = new LocationCollection();while (reader.Read()){Location loc = GetLocationFromReader(reader, out level);if (level == 0){locs.Add(loc);container.Add(loc); }else{if (oldlevel != level){container.Clear();foreach (Location l in current)container.Add(l);current.Clear();oldlevel = level;}current.Add(loc);CreateLocation(container, loc);} }}return locs; }

在該方法按照以下步驟執(zhí)行:

1. 使用命令對象對象執(zhí)行pr_GetLocations存儲過程返回結(jié)果集

2. 如果數(shù)據(jù)閱讀器讀取了數(shù)據(jù)(reader.Read方法返回true)執(zhí)行:

2.1.從數(shù)據(jù)閱讀器當(dāng)前記錄中讀取Location對象,并返回層數(shù)信息(out level)

2.2.如果是第一層(level等于0)填充locs集合,并加入到container對象

2.3.如果不是第一層根據(jù)層標(biāo)志(oldlevel)判斷當(dāng)前層是否是新的一層

2.4 如果當(dāng)前層是新的一層清空container集合并將current集合中實體復(fù)制到container集合中,清空current集合并置層標(biāo)志(oldlevel)

2.5 將當(dāng)前對象添加到current集合中

2.6 調(diào)用CreateLocation方法從container上層集合中匹配當(dāng)前實體父級對象并加入父對象的子集合中

3. 重復(fù)第2步直到讀取完全部數(shù)據(jù)

可以看到container集合始終保存了當(dāng)前層的上層所有的實體對象,并且為了在更換層數(shù)后能夠正確的更新container集合,使用current集合保存當(dāng)前層的實體對象。

3.8 編寫GetLocationFromReader方法,用于從數(shù)據(jù)閱讀器中返回Location實體對象,并將層數(shù)信息使用out參數(shù)返回:

private Location GetLocationFromReader(SqlDataReader reader, out int level) {Location loc = new Location();loc.Id = Convert.ToInt32(reader["id"]);loc.Name = Convert.ToString(reader["name"]);object o = reader["parent"];if (o != DBNull.Value)loc.ParentId = Convert.ToInt32(o);level = Convert.ToInt32(reader["loclevel"]);return loc; }

3.9 編寫CreateLocation方法,該方法遍歷實體集合找到與當(dāng)前實體對象的父級編號匹配的實體,并將當(dāng)前實體加入到父級實體的子集合中:

private void CreateLocation(LocationCollection container, Location loc) {foreach (Location location in container){if (location.Id == loc.ParentId){location.SubLocations.Add(loc);break;}} }

3.10 向Default.aspx頁面上添加TreeView控件:

<asp:TreeView ID="trvLocation" runat="server" Font-Size="12px"ShowLines="True"> </asp:TreeView>

3.11 在Default.aspx頁面后置代碼中編寫B(tài)indData數(shù)據(jù)綁定方法:

private void BindData() {DAO dao = new DAO();LocationCollection locs = dao.GetLocations();TreeNodeCollection nodes = CreateTreeNodes(locs);foreach (TreeNode node in nodes){trvLocation.Nodes.Add(node);} }

3.12 BindData方法調(diào)用了CreateTreeNode方法返回節(jié)點集合,該方法中遞歸調(diào)用自身以得到全部所在地節(jié)點:

private TreeNodeCollection CreateTreeNodes(LocationCollection locs) {TreeNodeCollection nodeColl = new TreeNodeCollection();foreach (Location loc in locs){TreeNode node = new TreeNode(loc.Name, loc.Id.ToString());if (loc.SubLocations.Count > 0){TreeNodeCollection subColl = CreateTreeNodes(loc.SubLocations);foreach (TreeNode subNode in subColl)node.ChildNodes.Add(subNode);}nodeColl.Add(node);}return nodeColl; }

3.13 最后在頁面加載事件里執(zhí)行數(shù)據(jù)綁定:

protected void Page_Load(object sender, EventArgs e) {if (!IsPostBack){this.BindData();} }

3.14 在瀏覽器中預(yù)覽結(jié)果:

4. 總結(jié)

原來在處理類似父子關(guān)系時總是找不到好的解決辦法,現(xiàn)在通過SQL Server 2005里的新特性可以較為合理的解決該類問題,在這里主要用到了with關(guān)鍵字實現(xiàn)遞歸訪問,并且在輸出數(shù)據(jù)時同樣使用了遞歸的方法。如果各位有更好的實現(xiàn)方式,請不不吝賜教。

本文示例代碼下載:示例代碼

?


?

感謝各位支持,在SQL Server Management Studio中抓取了查詢單表和以上算法的執(zhí)行計劃,供大家參考吧

1.select * from tb_loc

2. exec pr_GetLocations

轉(zhuǎn)載于:https://www.cnblogs.com/ddlzq/p/4171074.html

總結(jié)

以上是生活随笔為你收集整理的SQL SERVER With语法[转]的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。