前言
已經有大概一個多月沒有更新博客,可能是開始變得有點懶散了吧,有時候想寫,但是又需要額外投入更多的時間去學習,感覺精力完全不夠用啊,所以為了彌補這一個多月的潛水,決定寫一篇,銜接9月未寫博客的空缺。
無需定義實體,返回object類型,如何獲取屬性值?
這樣的場景在下拉列表中很常見,在下拉列表中我們只需要Id和Name兩個字段,無需其他字段,同時也是為了節約流量,例如實體和數據如下:
public class Department { public int Id { get; set; } public string Name { get; set; } public DateTime CreatedTime { get; set; } public string Contact { get; set; } public string ContactUser { get; set; } }
var deparments = new List<Department>() { new Department(){ Id=1,Name="department1",CreatedTime=DateTime.Now,Contact="13682687787",ContactUser="Tom"}, new Department(){ Id=2,Name="department2",CreatedTime=DateTime.Now,Contact="13682687788",ContactUser="Jeffcky"}, new Department(){ Id=3,Name="department3",CreatedTime=DateTime.Now,Contact="13682687783",ContactUser="Lily"}, new Department(){ Id=4,Name="department4",CreatedTime=DateTime.Now,Contact="13682687782",ContactUser="Jim"}, new Department(){ Id=5,Name="department5",CreatedTime=DateTime.Now,Contact="13682687781",ContactUser="Allen"}, };
此時為了再額外定義另外的實體,我們通過匿名類型來進行轉換
static IEnumerable<object> Change(List<Department> departments) { var changeDepartments = departments.Select(d => new { id = d.Id, name = d.Name }); return changeDepartments; }
將上述集合傳入到該Change方法中則返回集合中包含id和name的列,這樣在下拉列表就能很好的顯示,但是如果用戶選擇了所在部門,下一次再次進行編輯時需要在該轉換集合中得到用戶的id和name,此時集合返回類型為object,我們如何得到其中的屬性id和用戶id進行比對呢,我們來看看以下兩種方法。
反射
反射應該是我們首先能夠想到的方式了,獲取該實體類型的屬性並獲取其值。例如我們要在上述Change方法返回的集合中獲取到id=1的對象,我們通過如下反射來獲取。
var changes = Change(deparments); var specialDepartment = changes.FirstOrDefault(d => Convert.ToInt32(d.GetType().GetProperty("id").GetValue(d)) == 1);
dynamic關鍵字
上述實現是最原始的方式,當dynamic關鍵字出世之后,我們可以直接將集合中的object類型轉換為dynamic動態對象,在運行時讓編譯器自己去計算,如此一來上述的實現可以更加簡潔實現。
var changes = Change(deparments); var specialDepartment = changes.FirstOrDefault(d => ((dynamic)d).id == 1);
雖然上述利用dynamic關鍵字實現看似很簡潔但是也有其缺點,類似於JavaScript中動態類型,它是區分大小寫的,要是我們將id寫成Id直接拋出異常,如下:
var specialDepartment = changes.FirstOrDefault(d => ((dynamic)d).Id == 1);
為了這樣避免出錯,而且我們還是需要有智能提示,那就老老實實寫一個專門針對下拉列表的轉換類,也未嘗不可。
public class SelectItem { public int Id { get; set; } public string Name { get; set; } }
數據排名
排名這個也是常見不能再常見的場景了,例如在微信運動中就有截止到每天到十點每天所走步數好友的排名,場景有幾種對應的實現方式也有幾種,我們來看看。建立如下簡單示例表。
不同等分數排名
不同等分數即分數都不一樣,如下
首先我們利用Row_Number()函數來實現排名。
SELECT UserId,Score, ROW_NUMBER() OVER(ORDER BY Score DESC) AS [Rank] FROM dbo.Ranks
這樣我們就可以獲取到某個用戶所在排名為多少,這只是針對沒有同樣的分數而言,要是分數有一樣的呢,Row_Number函數還適用?
同等分數並列排名
我們將數據修改成如下:
再來利用Row_Number來進行排名。
此時分數同樣為90則排名一個為第三名,另個卻為第四名,用戶表示我不服,分數不一樣,而且沒有其他維度的判斷邏輯,怎么我就變成第四名了呢,不應該是並列第三名么。這是程序員小哥的bug。我不聽,我不聽。下面再來設計一個課程得分的表ExamResult。
並列分數排名又分為兩種場景,比如上述學生Sam的數學和科學都是為90,則並列第一名,到了社會這門學科是第三名,還是第二名呢,這就看實際應用場景,若是第三名我們就需要用RANK函數來排名。
SELECT Name , Subject , Marks , RANK() OVER (PARTITION BY name ORDER BY Marks DESC ) Rank FROM ExamResult ORDER BY name , subject
若是沒有空缺排名則利用DENSE_RANK函數來實現。
SELECT Name , Subject , Marks , DENSE_RANK() OVER ( PARTITION BY name ORDER BY Marks DESC ) Rank FROM ExamResult ORDER BY name
由上我們知道關於SQL Server中RANK和DENSE_RANK函數的區別在於DENSE_RANK函數沒有排名空缺。在大部分場景下都需要考慮並列排名的情況,其ROW_NUMBER函數不再適用,除非明確知道排名中不會存在分數並列的情況,否則謹慎適用。
刪除數據重復
如果數據庫表設計不夠合理,或者說出現並發很容易導致插入重復情況,此時我們去嘗試刪除數據重復的表。我們首先創建如下測試表。
CREATE TABLE DuplicateRows ( Id INT , Name VARCHAR(20) ) INSERT INTO DuplicateRows VALUES ( 1, 'Andy' ) INSERT INTO DuplicateRows VALUES ( 1, 'Andy' ) INSERT INTO DuplicateRows VALUES ( 2, 'Bill' ) INSERT INTO DuplicateRows VALUES ( 2, 'Bill' ) INSERT INTO DuplicateRows VALUES ( 2, 'Bill' ) INSERT INTO DuplicateRows VALUES ( 3, 'Chris' )
我們看到如上數據Name為Andy的數據重復兩次,同時Name為Bill的數據重復三次。關於數據重復刪除方式總結起來有四種方式,我們一起來總結下。
借助臨時表(方式一)
SELECT DISTINCT * INTO #tmp FROM [dbo].[DuplicateRows] DELETE FROM [dbo].[DuplicateRows] INSERT INTO [dbo].[DuplicateRows] SELECT * FROM #tmp DROP TABLE #tmp
上述邏輯很簡單,我們查詢出不重復的刪除並插入到臨時表中,然后刪除表中數據將臨時表中不重復的數據插入到表中即可。
RANK() OVER( PARTITION BY)(方式二)
上述是最簡單的方式,若是我們表中有一列和另外一行列不同,此時利用DISTINCT關鍵字過濾數據將不再起作用。在這種情況下,我們可以添加一列,作為行唯一標識符(自動遞增升序)。然后獲取每個Id的名稱。 如果Rank大於1,則表示它是重復行然后刪除。 刪除重復的行后,刪除唯一列標識。 如下:
添加唯一標識列且自增長。
ALTER TABLE [dbo].[DuplicateRows] ADD sNo INT IDENTITY(1,1)
利用RANK函數對名稱進行分區並進行sNo排序。
SELECT * , RANK() OVER ( PARTITION BY Id, Name ORDER BY sNo ) [rank] FROM [dbo].[DuplicateRows]
此時再刪除rank大於1即重復的數據。
DELETE E FROM [dbo].[DuplicateRows] E INNER JOIN ( SELECT * , RANK() OVER ( PARTITION BY Id, Name ORDER BY sNo DESC ) [rank] FROM [dbo].[DuplicateRows] ) T ON E.sno = t.sno WHERE T.[rank] > 1
最后刪除唯一標識列sNo.
ALTER TABLE [dbo].[DuplicateRows] DROP COLUMN sNo
完整代碼如下:
ALTER TABLE [dbo].[DuplicateRows] ADD sNo INT IDENTITY(1,1) DELETE E FROM [dbo].[DuplicateRows] E INNER JOIN ( SELECT * , RANK() OVER ( PARTITION BY Id, Name ORDER BY sNo DESC ) [rank] FROM [dbo].[DuplicateRows] ) T ON E.sno = t.sno WHERE T.[rank] > 1 ALTER TABLE [dbo].[DuplicateRows] DROP COLUMN sNo
DELELTE TOP(2)(方式三)
如果我們想刪除特定重復的數據,通過DELETE TOP語句來完成,例如刪除上述id = 2的重復數據。
DELETE TOP ( 2 ) FROM [dbo].[DuplicateRows] WHERE Id = 2
HAVING COUNT(*) >1(方式四)
如果我們需要將所有重復的數據都刪除,只保留未被刪除的數據,利用HAVING COUNT(*) >1完成,若是查詢到重復個數大於1則刪除。
DELETE FROM [dbo].[DuplicateRows] WHERE Id IN ( SELECT Id FROM [dbo].[DuplicateRows] GROUP BY Id HAVING COUNT(*) > 1 )
上述講到了刪除數據重復的四種方式,若是需要查詢並過濾數據重復情況,舉一反三同樣大部分也適用。
總結
本節講述一點基礎知識,回顧了一下,十一期間有時間會講講vue。see u.