項目升級-oracle改版sql server問題點匯總


最近項目升級,需要把原來的oracle版本改為sql server版本。由於項目的分層設計,主要的修改內容也就是存儲過程,sql語句。如今改的七七八八,整理一下踩過的坑,備忘!

1.符號使用

1.1 :->@

帶參數的sql語句,oracle的參數標識使用前綴":",sql server前綴"@"。

  • oracle版
SELECT * FROM table1 WHERE column1=:column1
  • sql server版
SELECT * FROM table1 WHERE column1=@column1

帶參數的sql語句,在代碼中添加參數時,oracle同一個參數可以添加多次,sql server添加多次會報異常。

  • oracle版本
...
db.AddInParameter(cmd, "column1", DbType.String,column1);
db.AddInParameter(cmd, "column2", DbType.String,column2);
...
db.AddInParameter(cmd, "column1", DbType.String,column1);
...

oracle可以像上面這樣,再次添加同名的參數,但是連接sql server數據庫時不允許。

1.2 mod()->%

求余運算

1.3 ||->+

字符串連接符

oracle的||專門用來做字符串拼接,因此類似下面這樣的字符串和數字的拼接是可以成功的:

select 'abc'||123 from dual

但是在sql server中類似下面的字符串和數字拼接是無法直接進行的:

select 'abc'+123

sql server傾向於將字符串轉為數字,因此報異常。如果要實現字符串拼接的效果,需要做數據類型轉換

select 'abc'+convert(varchar(10),123)

這種情況在存儲過程等復雜的操作中,做動態sql拼接時,最容易忽略。

1.4 off等表別名

oracle中的表別名off在sql server中是關鍵字,導致sql語句錯誤。因此,在使用表別名的時候我們還是盡量避免掉這種類似關鍵字的命名。

1.5 columnnum=1->top 1

oracle在排序時,可以使用columnnum=1獲取第一條記錄,sql server排序時可以使用top 1來獲取。

1.6 minus->except

oracle的minus對應sql server的except,直接替換

1.7 number->decimal

oracle中的number類型,即使設置了小數位數,比如decimal(18,2),但是數據庫不會對插入的數值補零,插入整數就是整數。但是sql server中會根據設置的數據類型,在數字末尾補零以補足小數位。這個差異會影響前端數字的格式化展示。

1.8 date -> datetime

oracle中date數據類型包括日期和時間兩部分。

sql server中date只包括日期部分,datetime包括日期和時間兩部分,timestamp遞增數字,與時間無關。

oracle中timestamp也包括日期和時間兩部分,與date的區別主要是相減時,timestamp相減結果單位是秒,date相減結果是天。兩個類型可以相互轉換。

C#中向sql server中插入日期類型時,C#默認日期是01/01/01 00:00:00,直接插入數據庫會導致數據一出,因為C#向sql server中插入datetime數值類型時(DbType.SqlDateTime),有效日期范圍為1/1/1753 12:00:00 ~ 12/31/9999 11:59:59 PM。數據庫直接導入日期字段,或者直接修改數據庫時,是可以設置為 1/1/1 00:00:00的。

2.函數轉換

2.1 nvl->isnull

這兩個函數完全等價,可以直接替換使用。

2.2 substr->substring

substr(str,startIndex,[length]) oracle中length參數可以省略,默認取到結尾;
substring(str,startIndex,length) sql server中length參數不能省略。
因此做替換時,要根據業務確定截取長度。

2.3 decode->case when end

oracle中的decode函數使用case when end語句替換,對於嵌套的decode函數我們可以通過組合when的邏輯表達式實現。

  • oracle版
select decode(column1,0,'狀態1',1,'狀態2','其他') from table1;

select decode(column1,0,decode(column2,0,'狀態1','狀態2'),'其他') from table1;
  • sql server版
select case column1
        when 0 then '狀態1'
        when 1 then '狀態2'
        else '其他' end
    from table1;
    
select case 
        when column1 = 0 and column2 = 0 then '狀態1'
        when column1 = 0 then '狀態2'
        else '其他' end
    from table1;

2.4 數據類型轉換

  • to_number(str)->convert(int,str)
  • to_char(str)->convert(nvarchar(n),str)
  • to_char(date,'yyyy-MM-dd')->convert(varchar(100),date,23)
  • to_char(date,'yyyy-MM-dd hh24@mi@ss')->convert(varchar(100),date,20)
  • to_char(date,'yyyyMMdd')->convert(varchar(100),date,112)

2.5 sq_executesql第一個參數必須是nvarchar類型

2.6 instr -> charindex

兩個函數的實現功能一樣,但是參數順序卻不同,一定要注意。

oracle版本

select instr('abcde','c') from dual

sql server版本

select charindex('c','abcde')

2.7 分組並合並列

原始數據結構

學號 名字 愛好
201012 張三 籃球
201012 張三 乒乓球
201013 李四 唱歌
201012 張三 羽毛球
201013 李四 羽毛球
201013 李四 繪畫

目標視圖

學號 名字 愛好
201012 張三 籃球,乒乓球,羽毛球
201013 李四 唱歌,羽毛球,繪畫

oracle 11g

select 學號,名字,listagg(愛好,',') within group(order by 愛好) from table1 group by 學號,名字

oracle 其他版本

select 學號,名字,wm_concat(愛好) from table1 group by 學號,名字

sql server

select 學號,名字,
stuff((select ','+愛好 from table1 for xml path('')),1,1,'') 愛好
from table1
group by 學號,名字

3. 語法規則

3.1 子查詢別名

oracle中子查詢作為from中查詢目標可以不使用別名,但是sql server中必須命名別名。

oracle版本

select column1,column2,column3,column4
from (select column1,column2,column3,column4 from table1);

sql server版本

select column1,column2,column3,column4
from (select column1,column2,column3,column4 from table1) a;

3.2 sql server樹形查詢

with定義的sql片段作為查詢對象,實現樹形查詢。

with cte(id,parentId,column1,column2,column3) as
(
    select column1,column2,column3 from table1
    --這里可以接收查詢參數
    --where column1 = ''
    union all
    select column1,column2,column3 from table1 inner join cte t1 
    on t1.id = t2.parentId --自下而上查詢
    --on t1.parentId = t2.id --自上而下查詢
)

select * from cte;

3.3 sql server 使用merge info

項目中作為更新或插入場景使用。

merge into table1 t1
using (select @id id,@para1 column1,@para2 column2,@para3 column3) t2
on t1.id = t2.id
when matched then
update set t1.column1 = t2.column1,t1.column2 = t2.column2,t1.column3 = t2.column3
when not matched then
insert(id,column1,column2,column3) values(t2.column1,t2.column2,t2.column3)

其中oracle在一些沒有from目標的操作時,使用dual作為操作目標。sql server在這種情況下,直接不寫from部分即可。

3.4 inserted表與deleted表

The deleted table stores copies of the affected rows during DELETE and UPDATE statements. During the execution of a DELETE or UPDATE statement, rows are deleted from the trigger table and transferred to the deleted table. The deleted table and the trigger table ordinarily have no rows in common.

deleted表存儲受Delete和Update語句操作影響的行的副本。執行delete或update語句期間,受影響的行從觸發器所在表轉移到deleted表。deleted表和觸發器所在表通常沒有共同的行。

The inserted table stores copies of the affected rows during INSERT and UPDATE statements. During an insert or update transaction, new rows are added to both the inserted table and the trigger table. The rows in the inserted table are copies of the new rows in the trigger table.

inserted表存儲受insert和update語句操作影響的行的副本。執行insert或update事務時,新行被添加到inserted表和觸發器所在表。inserted表中的行是觸發器所在表新行的副本。

An update transaction is similar to a delete operation followed by an insert operation; the old rows are copied to the deleted table first, and then the new rows are copied to the trigger table and to the inserted table.

一個update事務類似於一個delete操作后跟一個insert操作。舊行先拷貝到deleted表,然后新行拷貝到觸發器所在表和inserted表。

Use the inserted and deleted Tables

3.5 insert\update\delete中使用output

利用inserted表和deleted表,輸出插入后自動生成的id等字段,避免insert后,在執行select查詢的繁瑣。

insert into table1(column1,column2,column3)
output inserted.id,inserted.time into @para1,@para2
values(@column1,@column2,@column3)

3.6 sql語句或存儲過程數據庫執行很快,程序中執行很慢

3.6.1 程序傳遞參數類型與數據庫不一致

...
db.AddInParameter(cmd, "column1", DbType.Decimal,column1);
db.AddInParameter(cmd, "column2", DbType.String,column2);
db.AddInParameter(cmd, "column3", DbType.String,column3);
...

這里的DbType指定的類型一定要與數據庫的數據類型保持一致,否則在執行大量數據查詢時,會存在數據類型轉換,嚴重影響sql語句執行效率。其中比較以忽略的類型

varchar -> DbType.AnsiString
nvarchar -> DbType.String

這一對是比較容易不一致的,我們一般給varchar字段和nvarchar字段都傳遞的DbType.String。這樣在數據量小時沒有問題,數據量大時會非常慢。

3.6.2 存儲過程執行計划過期

由於存儲過程是預編譯的,在第一次執行的時候,會生成執行計划,以后執行的時候,會使用這個執行計划(除非顯示指定重新編譯),而不是每次執行時都生成執行計划。當存儲過程涉及的對象結構調整,或者相關的數據產生了很大的變化,這可能導致原來的計划不適合當前的現狀(執行計划過期),這種情況下應該重新編譯存儲過程。

exec sp_recompile @objname='存儲過程名'


免責聲明!

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



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