SQL Server 一些使用小技巧


1、查詢的時候把某一個字段的值拼接成字符串

以下是演示數據。

第一種方式:使用自定義變量

DECLARE @Names NVARCHAR(128)

SET @Names=''    -- 需要先賦值為空字符串,不然結果會是 null 

SELECT @Names=@Names+S_Name+','        -- S_Name 類型為字符串類型,如果不能隱示轉換,就需要強制轉換
FROM Student

SELECT @Names

這種方法有一個好處就是,拼接出來的字符串可以賦值給變量或直接插入表中指定字段,可以適用於存儲過程之類的。

 

第二種方式:轉換為 XML 格式

SELECT t.S_Name + ',' 
FROM 
(
    SELECT S_Name 
    FROM Student 
) t 
FOR XML PATH('')

使用這種方式有一個缺點就是,不能直接賦值給變量或插入表,適用於查看時使用。

 如果想要使之能夠賦值給變量或插入表中,那就需要轉換一下。如下:

DECLARE @Names NVARCHAR(128)

SET @Names=''

SELECT @Names=
(
    SELECT t.S_Name + ',' 
    FROM 
    (
        SELECT S_Name 
        FROM Student 
    ) t 
    FOR XML PATH(''),TYPE
).value('.','NVARCHAR(128)')

SELECT @Names 

 

2、查詢一個字段同時滿足多個條件的方法

舉個栗子:比如現在有一些訂單,而每一個訂單有多個產品,現在我要查出同時具有某幾個產品的訂單。

再拿學生和課程來舉例,一個學生可以選擇多門課程,而每一門課程也可以同時被多個學生所選擇,那么我現在要查出選擇了某幾門課程的學生。

下面是演示數據。

現在我要查出同時選擇了 C# 和 SQL 課程的學生信息。如下:

  SELECT s.S_Id,s.S_No,s.S_Name 
  FROM Student s 
  INNER JOIN Student_Course_Relation r ON s.S_Id=r.S_Id
  INNER JOIN Course c ON r.C_Id=c.C_Id
  WHERE c.C_Name='C#' OR c.C_Name='SQL' 
  GROUP BY s.S_Id,s.S_No,s.S_Name 
  HAVING COUNT(1) >= '2'            -- 這個數字是根據 where 條件來定

 

3、SQL Server 實現多行轉列

之前也寫過一篇博客,SQL Server 使用 Pivot 和 UnPivot 實現行列轉換,但是之前都是相對於“單列”的轉換,於是最近碰到一個需要兩列的問題。最后在網上找了一些相關資料,得出了下面的解決方法。下面先建立一個表,插入一些模擬的數據。如下:

create table OrderDemo
(
    ID        int        not null identity(1,1) primary key,
    CustomerCode    nvarchar(16)    not null,
    OrderCount    int        not null default(0),
    TotalAmount  decimal(13,3)    not null default(0),
    YearDate    nvarchar(8)        null,
    MonthDate    nvarchar(8)        null
)

insert into OrderDemo
(
    CustomerCode,
    OrderCount,
    TotalAmount,
    YearDate,
    MonthDate
)
select 'A001','23','28.650','2017','1' union all 
select 'A001','67','123.123','2017','2' union all 
select 'A002','12','28.320','2017','1' union all 
select 'A002','37','51.221','2017','2' union all 
select 'A003','89','452.200','2017','1' union all 
select 'A003','134','523.210','2017','2' union all 
select 'A004','78','230.220','2017','1' union all 
select 'A004','95','180.567','2017','2' union all 
select 'A005','128','230.789','2017','1' union all 
select 'A005','256','340.450','2017','2' 

select * from OrderDemo 

以上的數據是模擬的按客戶、訂單年份和訂單月份統計的訂單數量和金額,由於年月的時間段非固定的,所以這里使用的是動態sql,下面直接上代碼:

 

declare @strSql nvarchar(1024)
declare @strWhere nvarchar(1024)

set @strWhere=''

-- 用作轉換之后的列頭
select @strWhere = @strWhere+TitleCount+','+TitleAmount+','
from 
(
    select distinct '['+YearDate+''+MonthDate+''+'數量'+']' TitleCount,'['+YearDate+''+MonthDate+''+'金額'+']' TitleAmount
    from OrderDemo 
) t 

-- 去掉最后一個逗號
if(CHARINDEX(',',REVERSE(@strWhere))=1)
begin
    set @strWhere=SUBSTRING(@strWhere,1,len(@strWhere)-1)
end

set @strSql='select * 
            from 
            (
                select CustomerCode,ComDate+ColumnName ComDate,CountAndAmount 
                from  
                (
                    select CustomerCode,YearDate+''''+MonthDate+'''' ComDate,
                    cast(OrderCount as nvarchar) ''數量'',
                    cast(TotalAmount as nvarchar) ''金額'' 
                    from OrderDemo 
                ) a 
                UNPIVOT 
                (
                    CountAndAmount for ColumnName
                    IN ([數量],[金額])
                ) b 
            ) c 
            pivot
            (
                max(CountAndAmount) 
                for ComDate 
                in ('+ @strWhere +')
            ) d '

exec (@strSql) 

結果如下:

既然兩列可以這么實現,那么如果在多一列呢,或者多很多列。這里我稍稍修改了一下,多增加了一列進行測試(多列同理),修改之后的代碼如下:

create table OrderDemo
(
    ID        int        not null identity(1,1) primary key,
    CustomerCode    nvarchar(16)    not null,
    OrderCount    int        not null default(0),
    ProductCount int  not null default(0),
    TotalAmount  decimal(13,3)    not null default(0),
    YearDate    nvarchar(8)        null,
    MonthDate    nvarchar(8)        null
)

insert into OrderDemo
(
    CustomerCode,
    OrderCount,
    ProductCount,
    TotalAmount,
    YearDate,
    MonthDate
)
select 'A001','23','35','28.650','2017','1' union all 
select 'A001','67','75','123.123','2017','2' union all 
select 'A002','12','18','28.320','2017','1' union all 
select 'A002','37','42','51.221','2017','2' union all 
select 'A003','89','98','452.200','2017','1' union all 
select 'A003','134','150','523.210','2017','2' union all 
select 'A004','78','99','230.220','2017','1' union all 
select 'A004','95','102','180.567','2017','2' union all 
select 'A005','128','138','230.789','2017','1' union all 
select 'A005','256','280','340.450','2017','2' 

select * from OrderDemo 

        
declare @strSql nvarchar(1024)
declare @strWhere nvarchar(1024)

set @strWhere=''

-- 用作轉換之后的列頭
select @strWhere = @strWhere+TitleCount+','+TitleAmount+','+TitleProduct+','
from 
(
    select distinct '['+YearDate+''+MonthDate+''+'數量'+']' TitleCount,
    '['+YearDate+''+MonthDate+''+'金額'+']' TitleAmount,
    '['+YearDate+''+MonthDate+''+'產品數量'+']' TitleProduct 
    from OrderDemo 
) t 

-- 去掉最后一個逗號
if(CHARINDEX(',',REVERSE(@strWhere))=1)
begin
    set @strWhere=SUBSTRING(@strWhere,1,len(@strWhere)-1)
end

set @strSql='select * 
            from 
            (
                select CustomerCode,ComDate+ColumnName ComDate,CountAndAmount 
                from  
                (
                    select CustomerCode,YearDate+''''+MonthDate+'''' ComDate,
                    cast(OrderCount as nvarchar) ''數量'',
                    cast(TotalAmount as nvarchar) ''金額'',
                    cast(ProductCount as nvarchar) ''產品數量'' 
                    from OrderDemo 
                ) a 
                UNPIVOT 
                (
                    CountAndAmount for ColumnName
                    IN ([數量],[金額],[產品數量])
                ) b 
            ) c 
            pivot
            (
                max(CountAndAmount) 
                for ComDate 
                in ('+ @strWhere +')
            ) d '

exec (@strSql) 

PS:需要注意的是當需要轉換的列的數據類型不同時需要轉換為同一種類型,比如這里的 “數量、金額、產品數量”。

 

4、在子查詢中使用 Order By

比如,我現在有這樣一些數據。如下:

我現在想要使用子查詢按字段 “Stu_Age” 排序,那么就有了如下代碼:

可以看見這樣是有錯誤的,那么下面有兩種解決辦法。

第一種: row_number() over() 排名開窗函數

  select * 
  from 
  (
      select [StudentID],Stu_FullName,Stu_Age,Stu_Address,Stu_Phone,
      ROW_NUMBER() over(order by Stu_Age) RowNum
      from Student 
   ) t 

第二種:TOP 100 PERCENT,英語比較好的朋友就知道 percent 就是百分比的意思,結果顯而易見。top 100 percent 就表示百分之百,即全部的數據。

  select * 
  from 
  (
      select top 100 percent [StudentID],Stu_FullName,Stu_Age,Stu_Address,Stu_Phone 
      from Student 
      order by Stu_Age 
   ) t 

其實我個人的話還是喜歡第一種方式,第二種也是偶爾看到的。

 

5、刪除所有字段都相同的重復的數據

首先創建一個臨時表並插入一些數據,用於模擬該場景。

create table #temp
(
    ID int not null,
    Name varchar(20) null
)

insert into #temp(ID,Name) values('1','測試');
insert into #temp(ID,Name) values('1','測試');
insert into #temp(ID,Name) values('2','測試');

select * from #temp

類似於上面這種數據,第一條和第二條數據明顯屬於完成重復,故需要刪除其中一條。

剛開始第一時間想到的方式是,把兩條數據全部刪除,然后再插入一條。

后來又想到了下面這種方式,個人覺得還不錯。

with tes as
(
    select ID,Name,row_number() over(partition by ID,Name order by ID) RowNum 
    from #temp
)
delete from tes where RowNum > 1

select * from #temp

到此結束,最后再刪除臨時表。

drop table #temp

 


免責聲明!

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



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