基礎知識(C#語法、數據庫SQL Server)回顧與總結


前言

已經有大概一個多月沒有更新博客,可能是開始變得有點懶散了吧,有時候想寫,但是又需要額外投入更多的時間去學習,感覺精力完全不夠用啊,所以為了彌補這一個多月的潛水,決定寫一篇,銜接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.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM