关于博客访问量的问题,影响因素有不少,例如你的权重,你的博客数量,包括你的标题是否吸引人都是一个衡量的标准。
这些东西须要的是日积月累,今天咱们从其中的一个维度入手:发帖时间。相信你们都明白,不管是csdn,博客园这种技术博客
仍是今日头条百度贴吧或者抖音快手这种娱乐论坛,都有本身的在线高峰期。例如百度贴吧,用户年龄段广泛偏小,“夜猫子”占据主力。
21-23点是在线高峰期,这个时间的阅读量以及评论量也是最多的,自媒体人确定会选择在这个时间发帖已获得更多的阅读及评论。
那咱们的博客园呢?目前咱们还不知道,既然园子里面都是程序猿,数据统计咱就要拿出点技术人员该有的样子,接下来咱们
写一个爬虫统计全部的发帖时间以及阅读数量。
所需语言:
python
c#
sql server
- 爬取数据
咱们打开博客园首页,首页的文章列表有发帖时间,阅读数,博客园最多只有200页,咱们只要将这200页的全部文章阅读数,发帖时间爬取到就ok。
下面咱们用python+scrapy 来编写爬虫代码。
环境配置:
pip install scrapy 安装爬虫框架,scrapy安装容易遇到坑,scrapy教程与常见坑,不懂scrapy看连接。
scrapy startproject csblog 建立项目
scrapy gensider scblogSpider “csblogs.com” 建立爬虫文件
修改csblog下面的items.py
title:文章标题
read:阅读数
date:发帖时间
# -*- coding: utf-8 -*- # Define here the models for your scraped items # # See documentation in: # https://doc.scrapy.org/en/latest/topics/items.html import scrapy class CnblogsItem(scrapy.Item): title = scrapy.Field() read = scrapy.Field() date = scrapy.Field()
而后咱们编写爬虫代码,首先审查下首页的html结构。
首先吐槽下翻页遇到的坑,https://www.cnblogs.com/#p4,表面看上去#p4是页码,可是屡次尝试变化页码爬取,都无效果,始终为第一页。
通过调试工具查看请求才发现,这个url是被重写过得,想要翻页得这么发请求。
接下来就容易多了,向这个地址发请求,在返回的html中取得相应的数据就行了,贴代码。
# -*- coding: utf-8 -*- import scrapy from cnblogs.items import CnblogsItem class CsblogSpider(scrapy.Spider): name = 'csblog' allowed_domains = ['cnblogs.com'] start_urls= ['https://www.cnblogs.com/mvc/AggSite/PostList.aspx'] PageIndex = 1 def start_requests(self): url = self.start_urls[0] #由于博客园只容许200页 for each in range(1,200): print("抓取页码") print(each) post_data ={ 'CategoryId':'808', 'CategoryType':"SiteHome", 'ItemListActionName':"PostList", 'PageIndex':str(each), 'ParentCategoryId':'0', 'TotalPostCount':'400' } yield scrapy.FormRequest(url=url, formdata=post_data) def parse(self, response): items = [] #全部文章都在<div class="post_item">中 for each in response.xpath("/html/body/div[@class='post_item']"): #提取标题 title = each.xpath('div[@class="post_item_body"]/h3/a/text()').extract() #提取发布日期 date = each.xpath('div[@class="post_item_body"]/div/text()').extract() #提取阅读数 read = each.xpath('div[@class="post_item_body"]/div/span[@class="article_view"]/a/text()').extract() title = title[0] #去除无用的字符 date = str(date).replace("[' \\r\\n ', ' \\r\\n",'').replace(" \\r\\n ']","").replace("发布于 ","").lstrip() read = read[0].replace("阅读(","").replace(")","") item = CnblogsItem() item['title'] = title item['read'] = read item['date'] = date items.append(item) return items
爬虫的代码很简单,这也是python的强大之处。
运行 scrapy crawl csblog -o data.xml 将爬取到的数据保存为xml。
咱们已经将抓取到的数据保存到本地xml了,接下来要作的事情就是数据统计了。所谓“术业有专攻”,作统计没有比sql 更强大的语言了,python的任务到此结束。
- 数据存储
为了方便的对数据进项统计查询,咱们把xml保存到MS Sql Server中,作个这个事情没有比Sql server的老伙计C#更合适的了,没啥好说的简简单单的几个方法。
static void Main(string[] args) { data d = (data)Deserialize(typeof(data), File.OpenRead(@"D:/MyCode/cnblogs/cnblogs/data.xml")); DataTable dt = ToDataTable<data.item>(d.items); dt.TableName = "t_article"; dt.Columns.Remove("date"); SqlHelper.ExecuteNonQuery(dt); } /// <summary> /// Convert a List{T} to a DataTable. /// </summary> private static DataTable ToDataTable<T>(List<T> items) { var tb = new DataTable(typeof(T).Name); PropertyInfo[] props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (PropertyInfo prop in props) { Type t = GetCoreType(prop.PropertyType); tb.Columns.Add(prop.Name, t); } foreach (T item in items) { var values = new object[props.Length]; for (int i = 0; i < props.Length; i++) { values[i] = props[i].GetValue(item, null); } tb.Rows.Add(values); } return tb; } /// <summary> /// Determine of specified type is nullable /// </summary> public static bool IsNullable(Type t) { return !t.IsValueType || (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)); } /// <summary> /// Return underlying type if type is Nullable otherwise return the type /// </summary> public static Type GetCoreType(Type t) { if (t != null && IsNullable(t)) { if (!t.IsValueType) { return t; } else { return Nullable.GetUnderlyingType(t); } } else { return t; } } /// 反序列化 /// </summary> /// <param name="type"></param> /// <param name="xml"></param> /// <returns></returns> public static object Deserialize(Type type, Stream stream) { XmlSerializer xmldes = new XmlSerializer(type); return xmldes.Deserialize(stream); }
数据已经成功的存储到sql server,接下来的数据统计是重头戏了。
- 数据统计
--200页码帖子总数量 select COUNT(*) from t_article
--查询的哪一个时间段阅读量最多 --查询结果显示早9点阅读量是最多的,并不意外 --而早6点(5180)与7点(55144)相差了近10倍 --7点与8点相比差了也有三倍,这说明程序猿们陆续 --开始上班了,上班敲代码必定是查资料的高峰期, --果不其然,8,9,10,11,15,16是阅读量最高峰的几个时间段 --都分布在上班时间,而出乎意料的事22点的阅读量也不低 --看来程序猿们回家后也很努力的嘛(应该是在加班) select CONVERT(INT, CONVERT(varchar(2),time, 108)) as count, SUM([read]) as [read] from t_article group by CONVERT(INT, CONVERT(varchar(2),time, 108)) order by [read] desc
--查询阅读量在一个星期内的分布状况 --结果一点都不意外,星期三比另六天 --高得多,星期一到星期五是工做日 --天天的阅读量都很高,周末阅读量下滑 --的厉害,由于休息了嘛(竟然没在加班) select datename(weekday, time) as weekday, SUM([read]) as [read] from t_article group by datename(weekday, time) order by [read] desc
--按照阅读数量排行 --阅读数量与发帖时间基本成正比 --这意味着,你辛辛苦苦写的文章 --没人看,没有关系。时间不会辜负你 select CONVERT(varchar(100), time, 111), sum([read]) from t_article group by CONVERT(varchar(100), time, 111) order by sum([read])
- 总结
阅读的最高峰时段是早9点,因此这也是发帖的最优时间,8,9,10都是不错的时间,若是你想要更多的阅读,不要错过呦。
阅读数量最少的是星期六跟星期日,这两天能够不用发帖了,能够给本身放个假。
阅读数量会随着时间慢慢变多,也就是说一开始没有阅读也不要紧,只要帖子里有干货,随着时间推移依然还会有许多阅读从搜索引擎跳转过来,阅读量会慢慢上去的。
看完python这段爬虫代码,java流泪了c#沉默了
哈哈,其实很简单,寥寥几行代码网页爬一部小说,不卖关子,马上开始。
首先安装所需的包,requests,BeautifulSoup4
控制台执行
pip install requests
pip install BeautifulSoup4
若是不能正确安装,请检查你的环境变量,至于环境变量配置,在这里再也不赘述,相关文章有不少。
两个包的安装命令都结束后,输入pip list
能够看到,两个包都成功安装了。
好的,咱们马上开始编写代码。
咱们的目标是抓取这个连接下全部小说的章节 https://book.qidian.com/info/1013646681#Catalog
咱们访问页面,用chrome调试工具查看元素,查看各章节的html属性。咱们发现全部章节父元素是<ul class="cf">这个元素,章节的连接以及标题,在子<li>下的<a>标签内。
那咱们第一步要作的事,就是要提取全部章节的连接。
'用于进行网络请求' import requests chapter = requests.get("https://book.qidian.com/info/1013646681#Catalog") print(chapter.text)
页面顺利的请求到了,接下来咱们从页面中抓取相应的元素
'用于进行网络请求' import requests '用于解析html' from bs4 import BeautifulSoup chapter = requests.get("https://book.qidian.com/info/1013646681#Catalog") ul_bs = BeautifulSoup(chapter.text) '提取class为cf的ul标签' ul = ul_bs.find_all("ul",class_="cf") print(ul)
ul也顺利抓取到了,接下来咱们遍历<ul>下的<a>标签取得全部章节的章节名与连接
'用于进行网络请求' import requests '用于解析html' from bs4 import BeautifulSoup chapter = requests.get("https://book.qidian.com/info/1013646681#Catalog") ul_bs = BeautifulSoup(chapter.text) '提取class为cf的ul标签' ul = ul_bs.find_all("ul",class_="cf") ul_bs = BeautifulSoup(str(ul[0])) '找到<ul>下的<a>标签' a_bs = ul_bs.find_all("a") '遍历<a>的href属性跟text' for a in a_bs: href = a.get("href") text = a.get_text() print(href) print(text)
ok,全部的章节连接搞定,咱们去看想一想章节详情页面长什么样,而后咱们具体制定详情页面的爬取计划。
打开一个章节,用chrome调试工具审查一下。文章标题保存在<h3 class="j_chapterName">中,正文保存在<div class="read-content j_readContent">中。
咱们须要从这两个标签中提取内容。
'用于进行网络请求' import requests '用于解析html' from bs4 import BeautifulSoup chapter = requests.get("https://book.qidian.com/info/1013646681#Catalog") ul_bs = BeautifulSoup(chapter.text) '提取class为cf的ul标签' ul = ul_bs.find_all("ul",class_="cf") ul_bs = BeautifulSoup(str(ul[0])) '找到<ul>下的<a>标签' a_bs = ul_bs.find_all("a") detail = requests.get("https:"+a_bs[0].get("href")) text_bs = BeautifulSoup(detail.text) text = text_bs.find_all("div",class_ = "read-content j_readContent") print(text)
正文页很顺利就爬取到了,以上代码仅是用第一篇文章作示范,经过调试文章已经能够爬取成功,全部下一步咱们只要把全部连接遍历逐个提取就行了
'用于进行网络请求' import requests '用于解析html' from bs4 import BeautifulSoup chapter = requests.get("https://book.qidian.com/info/1013646681#Catalog") ul_bs = BeautifulSoup(chapter.text) '提取class为cf的ul标签' ul = ul_bs.find_all("ul",class_="cf") ul_bs = BeautifulSoup(str(ul[0])) '找到<ul>下的<a>标签' a_bs = ul_bs.find_all("a") '遍历全部<href>进行提取' for a in a_bs: detail = requests.get("https:"+a.get("href")) d_bs = BeautifulSoup(detail.text) '正文' content = d_bs.find_all("div",class_ = "read-content j_readContent") '标题' name = d_bs.find_all("h3",class_="j_chapterName")[0].get_text()
在上图中咱们看到正文中的每个<p>标签为一个段落,提取的文章包含不少<p>标签这也是咱们不但愿的,接下来去除p标签。
可是去除<p>标签后文章就没有段落格式了呀,这样的阅读体验很不爽的,咱们只要在每一个段落的结尾加一个换行符就行了
'用于进行网络请求' import requests '用于解析html' from bs4 import BeautifulSoup chapter = requests.get("https://book.qidian.com/info/1013646681#Catalog") ul_bs = BeautifulSoup(chapter.text) '提取class为cf的ul标签' ul = ul_bs.find_all("ul",class_="cf") ul_bs = BeautifulSoup(str(ul[0])) '找到<ul>下的<a>标签' a_bs = ul_bs.find_all("a") '遍历全部<href>进行提取' for a in a_bs: detail = requests.get("https:"+a.get("href")) d_bs = BeautifulSoup(detail.text) '正文' content = d_bs.find_all("div",class_ = "read-content j_readContent") '标题' name = d_bs.find_all("h3",class_="j_chapterName")[0].get_text() txt = "" p_bs = BeautifulSoup(str(content)) '提取每一个<p>标签的内容' for p in p_bs.find_all("p"): txt = txt + p.get_text()+"\r\n"
去掉<p>标签了,全部的工做都作完了,咱们只要把文章保存成一个txt就能够了,txt的文件名以章节来命名。
'用于进行网络请求' import requests '用于解析html' from bs4 import BeautifulSoup def create_txt(path,txt): fd = None try: fd = open(path,'w+',encoding='utf-8') fd.write(txt) except: print("error") finally: if (fd !=None): fd.close() chapter = requests.get("https://book.qidian.com/info/1013646681#Catalog") ul_bs = BeautifulSoup(chapter.text) '提取class为cf的ul标签' ul = ul_bs.find_all("ul",class_="cf") ul_bs = BeautifulSoup(str(ul[0])) '找到<ul>下的<a>标签' a_bs = ul_bs.find_all("a") '遍历全部<href>进行提取' for a in a_bs: detail = requests.get("https:"+a.get("href")) d_bs = BeautifulSoup(detail.text) '正文' content = d_bs.find_all("div",class_ = "read-content j_readContent") '标题' name = d_bs.find_all("h3",class_="j_chapterName")[0].get_text() path = 'F:\\test\\' path = path + name+".txt" txt = "" p_bs = BeautifulSoup(str(content)) '提取每一个<p>标签的内容' for p in p_bs.find_all("p"): txt = txt + p.get_text()+"\r\n" create_txt(path,txt) print(path+"保存成功")
图片二进制转换与存入数据库相关
关于转换问题,刚开始我须要从数据库读出一个二进制数据流,并将其转换成一个Image格式。
在不涉及数据库的状况下,我先将一个图片转换成一个二进制数组显示出来,再写一个方法将其转换成图片image格式。
1、 先不涉及数据库,将图片转换成二进制,在将二进制转换成图片。
1.protected void Button1_Click(object sender, EventArgs e) { string str = null; PictureToBinary ptb = new PictureToBinary(); // str = Convert.ToBase64String( ptb.Data("E:/workspace/asp/TEST/PictureTurnToBinary/PictureTurnToBinary/img/Tulips.jpg")); Image newImage = Image.FromFile(Server.MapPath("Tulips.jpg")); str =Convert.ToBase64String( ptb.PhotoImageInsert(newImage)); Label1.Text = str;
//label能够用response代替(response.write(str);)
}
第一步,我将图片转换成二进制数组,并转换成string格式,用一个Label展示(也能够用Response直接输出到页面显示)。产生的数组并非二进制构成的数据流而是一串字节,以下:
因此建议先将图片压缩一下,否则会很大。
第二步,将获得的二进制字节码转换为图片格式。
2. protected void Button2_Click(object sender, EventArgs e) { PictureToBinary ptb = new PictureToBinary(); Image newImage = Image.FromFile(Server.MapPath("Tulips.jpg")); WritePhoto(ptb.PhotoImageInsert(newImage)); } //图片输出到页面 public void WritePhoto(byte[] streamByte) { Response.ContentType = "image/JPEG"; Response.BinaryWrite(streamByte); }
public class PictureToBinary { public PictureToBinary() { // // TODO: 在此处添加构造函数逻辑 // } public byte[] PhotoImageInsert(System.Drawing.Image imgPhoto) { //将Image转换成二进制流数据 MemoryStream mstream = new MemoryStream(); imgPhoto.Save(mstream, System.Drawing.Imaging.ImageFormat.Bmp); byte[] myData = new Byte[mstream.Length]; mstream.Position = 0; mstream.Read(myData, 0, myData.Length); mstream.Close(); return myData; } }
结果以下:
2、将获得的二进制数据保存到数据库,再将其读出。
须要用到这么几个控件:一个fileLoad控件,两个按钮,分别是存入和读取。
存入图片到数据库:
protected void Saveintodatabase_Click(object sender, EventArgs e) { //将图片保存到数据库 //加载文件,并以字节数组格式存入数据库 HttpPostedFile loadPhoto = FileUpload1.PostedFile; //获取记载图片内容长度 int photoLength = loadPhoto.ContentLength; //存为字节数组 byte[] photoArray = new byte[photoLength]; Stream photoStream = loadPhoto.InputStream; photoStream.Read(photoArray, 0, photoLength); //链接数据库读取文件 SqlConnection conn = new SqlConnection(); conn.ConnectionString = "Data Source=localhost;Database=Test;User Id=sa;Pwd=123456789"; string sql = "insert into Test_Picture values(@image,3)"; SqlCommand cmd = new SqlCommand(sql,conn); cmd.CommandType = System.Data.CommandType.Text; cmd.Parameters.Add("@image",SqlDbType.Image); cmd.Parameters["@image"].Value = photoArray; conn.Open(); //执行sql,成功执行返回1,不然返回0(insert,update,delete),其余命令返回-1 int row = cmd.ExecuteNonQuery(); Response.Write(row); conn.Close(); }
读取文件:
protected void PhotoShowInWebSite_Click(object sender, EventArgs e) { //读取数据库图片文件,并保存到本地。 SqlConnection conn = new SqlConnection(); conn.ConnectionString = "Data Source=localhost;Database=Test;User Id=sa;Pwd=123456789"; //选择你须要的字段值,这边就直接赋值了 string sql = "select Picture from Test_Picture where Number = 3"; SqlCommand cmd = new SqlCommand(sql, conn); byte[] MyData = new byte[0]; try { conn.Open(); SqlDataReader mySqlDataReader; mySqlDataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection); if (mySqlDataReader.Read()) { Response.Clear(); Response.ContentType = "image/JPEG"; Response.BinaryWrite((byte[])mySqlDataReader["Picture"]); /*将图片写入到本地d盘 //图片字节流 MyData = (byte[])mySqlDataReader["Picture"]; int ArraySize = MyData.GetUpperBound(0); FileStream fs = new FileStream(@"d:\02.jpg", FileMode.OpenOrCreate, FileAccess.Write); fs.Write(MyData, 0, ArraySize); fs.Close(); */ } } catch (SqlException SQLexc) { Response.Write(SQLexc.ToString()); } conn.Close(); }
以上为整理的为图片二进制转换与存取数据库相关的内容。
C#7.0--引用返回值和引用局部变量
1、在C#7.0以上版本中,方法的返回值能够经过关键字ref指定为返回变量的引用(而不是值)给调用方,这称为引用返回值(Reference Return Value,或ref returns);
1.与引用参数同样,使用关键字ref声明引用返回值:
public ref int MyFunc(int[] nums) { //do… return ref nums[0]; }
2.使用引用返回值避免了值类型在方法返回时的浅拷贝操做,提升了效率;
3.使用引用返回值一般是为了让调用方有权访问(和修改)此变量,所以引用返回值不支持无返回值方法(即返回值类型为void);
引用返回值所返回变量指向对象的生命周期必须大于方法,即不能返回指向值类型局部变量的变量(值类型局部变量会在方法执行完返回时进行回收),能够返回指向引用类型局部变量的变量、传递给方法的引用参数、静态字段和实例字段;
※引用返回值不能够返回字面量、常量、枚举或按值返回的方法、属性,但能够返回当前值为null的符合上述条件的变量;
※异步方法不能使用引用返回值,由于异步方法可能会在执行还没有完成且返回值未知时就返回;
4.查看声明引用返回值方法的IL代码:
5.在调用引用返回值的方法时,调用方能够选择将方法返回的变量看成按值返回或是按引用返回处理,若是是按值返回处理,则与调用普通方法同样:
int[] myNums = new int[] { 1, 2, 3, 4, 5 }; int myNum = MyFunc(myNums);
6.若是是按引用返回处理,则调用方须要使用引用局部变量(Reference Local Variable,或ref locals)接收,保留对方法返回变量的引用:
ref int myNum = ref MyFunc(myNums);
2、可使用关键字ref声明引用局部变量、初始化引用局部变量及调用引用返回值的方法:
ref int myNum = ref myNums[0]; //此时引用局部变量myNum保存的是数组myNums中索引0的内存地址
1.使用引用局部变值避免了值类型在赋值时的浅拷贝操做,提升了效率;
2.引用局部变量必须在声明时进行初始化,初始化时会确认该引用局部变量的做用范围,这个做用范围决定该引用局部变量可否做为引用返回值返回;
对引用局部变量的赋值操做,将直接改变该变量所指向内存地址中对象的值:
myNum = 10; Console.WriteLine(myNums[0]); //10
3.对引用局部变量的读取操做与普通局部变量同样,将访问该变量所指向内存地址中对象的值:
int num = myNum + 10; //20
4.引用局部变量能够做为引用参数的实参传递,一样须要使用修饰符ref修饰,对于给定的方法:
public void MyFunc(ref int num) { } //使用时: MyFunc(ref myNum);
5.在C#7.3以上版本中,能够对引用局部变量从新分配其它引用:
myNum = ref MyFunc(myNums);
※给引用局部变量从新分配不能改变该引用局部变量在声明时确认的做用范围,所以不能给一个做用范围较大的局部引用变量赋值一个做用范围较小的变量;
1、操做系统用进程(Processe)分隔正在执行的程序,用线程(Thread)做为操做系统分配处理器时间的基本单元,进程上下文中能够运行多个线程,进程的全部线程共享其虚拟地址空间,全部线程都可执行程序代码中的任意部分,包括其余线程正在执行的代码;
1.默认状况下,.NET程序只启动单个线程,被称为主线程(Primary Thread),也能够在运行时开启其它线程,与主线程并行同时执行代码,这些线程被称为工做线程(Worker Thread);由.Net开启和管理的线程一般称为托管线程(Managed Thread);
2.托管线程又分为前台线程和后台线程,二者相似,但前台线程会阻止进程中止,然后台线程不会,即当进程的Main方法结束后,只有在全部前台线程都中止时,CLR才会结束该进程,此时会对仍处于活动状态的后台线程调用Abort方法来结束全部后台进程;
※关闭程序如遇到没法关闭全部线程时,能够在Main方法结束前经过如下代码强制关闭全部线程,详见:
System.Environment.Exit(0);
2、在使用多线程时,不一样线程不只能够同时执行同一段代码,还能够同时访问同一内存中的数据,因此会存在必定的数据冲突问题,称为争用条件(Race Condition):
static int MyNum; static int RaceConditionCount; static void MyFunc() { while (true) { if (MyNum == 0) { MyNum++; if (MyNum == 0) { RaceConditionCount++; //只有在出现争用条件时才会执行此语句 } } MyNum = 0; } } static void Main(string[] args) { for (int i = 0; i < 2; i++) { Thread thread = new Thread(MyFunc); thread.Start(); } Thread.Sleep(1000); Console.WriteLine(RaceConditionCount); //输出1秒内产生争用条件的次数 Console.Read(); }
1.对于争用条件的发生频率,发布版本比调试版本的出现次数多(发布版本编译出的代码被优化过并且开启了JIT优化),多核CPU比单核CPU的出现次数多(多核CPU中多个线程能够同时运行,单核CPU的线程调度是抢占式的,也会出现此问题,只是次数较少);
3、为了不产生争用条件,须要注意数据的同步问题,能够经过给对象加锁,使同一时间内只有一个线程能够执行加锁对象所锁定的代码;
JS直接调用C#后台方法(ajax调用)
1. 先手动引用DLL或者经过NuGet查找引用,这里提供一个AjaxPro.2.dll的下载;
2. 以后的的过程不想写了,网上都大同小异的,直接参考之前大佬写的:
AjaxPro2完整入门教程
Linq To Json
using Newtonsoft.Json.Linq;
/// <summary> /// 递归生成部门树 /// </summary> /// <param name="depList">部门列表</param> /// <param name="parentId">用户部门的父级部门Id</param> /// <returns></returns> private JArray ConvertDepartTree(IList<AspSysDepartmentDto> depList, int parentId) { var jArray = new JArray(from a in depList where a.ParentId == parentId select new JObject( new JProperty("id", a.Id), new JProperty("text", a.Name), new JProperty("children", ConvertDepartTree(depList, a.Id) ))); return jArray; }
JSON.net Linq To Json文档地址:https://www.newtonsoft.com/json/help/html/CreatingLINQtoJSON.htm
SqlServer 递归查询
--查询部门及下属部门列表 WITH TEMP --递归 AS (SELECT Id, Code, Name, ParentId FROM [dbo].[AspSysDepartments] WHERE Id = 38 --查询当前部门 UNION ALL SELECT B.Id, --查询子部门 B.Code, B.Name, B.ParentId FROM TEMP A INNER JOIN [dbo].[AspSysDepartments] B ON B.ParentId = A.Id) SELECT Id, Code, Name, ParentId FROM TEMP --获取递归后的集合
结果: