微軟的SQL腳本生成令我傷透了心——我一直以為是我的程序上的問題,或者我操作上的失誤,并且,客服人員屢屢埋怨我的程序bug,多次測試之后,才發現,原來都是微軟惹的禍……
Sql Server的腳本生成有不少漏洞,經常由它生成的腳本運行起來會有錯誤。下面舉例說明:
1. 并沒有根據sysdenpends的依賴關系生成SQL代碼,而是根據所謂的“優先級”來生成。
比如:他認為view的優先級就要比function高。
那么,我寫了下面的測試程序,形成如下的依賴關系:fnT1 <-- vwT1 <-- fnT2
就是,view vwT1處于依賴的中間。
create function fnT1()returns Integerasbeginreturn 123endgocreate view vwT1asselect aa=dbo.fnT1()gocreate function fnT2()returns tableasreturn (select * from vwT1)go |
運行到數據庫之后,用Enterprise生成SQL代碼。要注意選項不一樣,生成的代碼會有所不同,在這里我沒有選數據庫和用戶。
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[fnT1]') and xtype in (N'FN', N'IF', N'TF'))drop function [dbo].[fnT1]GOif exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[fnT2]') and xtype in (N'FN', N'IF', N'TF'))drop function [dbo].[fnT2]GOif exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[vwT1]') and OBJECTPROPERTY(id, N'IsView') = 1)drop view [dbo].[vwT1]GOSET QUOTED_IDENTIFIER ON GOSET ANSI_NULLS ON GOCreate view vwT1AsSelect aa=dbo.fnT1()GOSET QUOTED_IDENTIFIER OFF GOSET ANSI_NULLS ON GOSET QUOTED_IDENTIFIER ON GOSET ANSI_NULLS ON GOCreate function fnT1()Returns IntegerAsbeginReturn 123endGOSET QUOTED_IDENTIFIER OFF GOSET ANSI_NULLS ON GOSET QUOTED_IDENTIFIER ON GOSET ANSI_NULLS ON GOCreate function fnT2()Returns TableAsReturn (Select * From vwT1)GOSET QUOTED_IDENTIFIER OFF GOSET ANSI_NULLS ON GO |
從上面你一眼就可以看出來了,建立view要比建立function先,而不是根據依賴關系建立。毫無疑問,將會得到如下的嚴重的錯誤:
服務器: 消息 208,級別 16,狀態 1,過程 vwT1,行 4
對象名 'dbo.fnT1' 無效。
服務器: 消息 208,級別 16,狀態 1,過程 fnT2,行 5
對象名 'vwT1' 無效。
2. 作業腳本
這個我就不說了,bug還不是很嚴重,主要是中文“——”注釋符的問題。英文版我沒有測試過,不過猜想應當沒有這個bug,大家可以試試看。
3. 還有一個SP的問題
spGetIDStr和spAnalyseStrList的關系,是后者依賴于前者。可是spGetIDStr我并沒有調用任何的表,因此,每當運行SQL Server生成的腳本的時候,總是報告類似這樣的信息:
spGetIDStr并不存在,無法在sysdepends里建立依賴關系,存儲過程spAnalyseStrList仍然建立。無論我手工修改其建立順序或是別的方面,用它生成的腳本就是有錯。這里,再看看第三個bug,看我下面的測試程序:
(原理:當sp沒有對表或視圖等數據庫對象有依賴關系時,sp被別的sp引用時也將無法建立依賴關系)
形成依賴關系:spB1 <--- spA1
create proc spB1asreturn 11gocreate proc spA1asbegindeclare @i intexec @i=spB1return @i*2endgo |
生成的腳本就為:
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[spA1]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)drop procedure [dbo].[spA1]GOif exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[spB1]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)drop procedure [dbo].[spB1]GOSET QUOTED_IDENTIFIER ON GOSET ANSI_NULLS ON GOCreate proc spA1AsBeginDeclare @i intExec @i=spB1Return @i*2EndGOSET QUOTED_IDENTIFIER OFF GOSET ANSI_NULLS ON GOSET QUOTED_IDENTIFIER ON GOSET ANSI_NULLS ON GOCreate Proc spB1AsReturn 11GOSET QUOTED_IDENTIFIER OFF GOSET ANSI_NULLS ON GO |
現在,大家應當明白我故意命名為spA1,spB1緣故了。因為如果沒有依賴關系的話,A自然排在B前面,你看微軟可不就是這么做的么?幸好這個bug還不是很大,怎么說SP還是建立了起來。
各位有興趣的可以試試別的,比如sp與fn,sp Group等等。
(T111)