搜尋本站文章

顯示具有 SQL Server 2008 Dev 標籤的文章。 顯示所有文章
顯示具有 SQL Server 2008 Dev 標籤的文章。 顯示所有文章

2016-01-29

效能調教:請使用UNION ALL,不用 UNION

以效能觀點,請一律使用 UNION ALL


先讓我們看看UNIONUNION ALL的說明。


UNION ALL
將所有資料列納入結果中。其中包括複本。若未指定,就會移除資料列複本。
UNION
指定組合多個結果集,以及當做單一結果集傳回。會移除資料列複本。

也就是說,若使用UNION,就會額外耗用系統資源,去逐一比對每一筆資料列!

除非,真的需要耗用系統資源去移除重複的資料列,否則就不該使用UNION

請參考以下的SQL陳述式:

--EX1. UNION,移除重複項的資料列
SELECT sid, myDate FROM tblUnion01
UNION
SELECT sid, myDate FROM tblUnion02
GO

--EX2. UNION ALL,不移除重複項的資料列
SELECT sid, myDate FROM tblUnion01
UNION ALL
SELECT sid, myDate FROM tblUnion02
GO


我們分別匯入2千筆、20萬筆資料列來做討論:


以兩千筆資料列為例

SQL陳述式
查詢成本(相對於批次)
實驗筆數
UNION
85%
2,000
UNION ALL
15%
2,000

以二十萬筆資料列為例

SQL陳述式
查詢成本(相對於批次)
實驗筆數
UNION
79%
200,000
UNION ALL
21%
200,000

經由上述的測試結果,使用UNION,就會額外耗用系統資源。
再與UNION ALL比較起來,UNION硬是多耗用4倍的系統資源。

也就是說,如果真的需要耗用系統資源去移除重複的資料列,否則就不該使用 UNION

 分析執行計畫

我們進一步來觀察其所使用的執行計畫:

以兩千筆資料列為例



1:執行計畫--兩千筆資料列

在圖1中,觀察使用UNION陳述式的執行計畫,額外多使用「排序(相異排序)」運算子,光這個運算子就耗用了查詢成本(相對於批次)82%,真是個耗用資源的運算子。


2:「排序(相異排序)」運算子

在圖2中,進一步觀察「排序(相異排序)」運算子,可以看到「估計的CPU成本」是:0.0747874,與「估計的I/O成本」:0.0112613來比較,這是極度耗用CPU資源的運算子,兩者相差七倍。

以二十萬筆資料列為例



3:執行計畫 -- 二十萬筆資料列

在圖3中,觀察使用UNION陳述式的執行計畫,額外多使用了多個運算子,我們以「雜湊比對(彙總)」運算子來討論:

「雜湊比對(彙總)」運算子


4:「雜湊比對(彙總)」運算子

在圖4中,使用「雜湊比對(彙總)」運算子,光這個運算子就耗用了查詢成本(相對於批次)66%,進一步觀察,可以看到這是完全只使用CPU資源的運算子,沒用到任何I/O成本。

結論


以效能觀點,請一律使用 UNION ALL。除非,真的需要耗用系統資源去移除重複的資料列,否則就不該使用UNION




請參考以下的範例程式碼:



-- 建立範例資料表:tblUnion01、tblUnion02,並分別新增20萬筆資料列到其中。
USE tempdb
GO
IF EXISTS(SELECT name FROM sysobjects  WHERE  name = N'tblUnion01' AND type = 'U') 
 DROP TABLE tblUnion01
GO
IF EXISTS(SELECT name FROM sysobjects  WHERE  name = N'tblUnion02' AND type = 'U') 
 DROP TABLE tblUnion02
GO
CREATE TABLE tblUnion01
(sid int,myDate datetime)
GO
CREATE TABLE tblUnion02
(sid int,myDate datetime)
GO
--
SET NOCOUNT ON
GO
DECLARE @CNT INT=1

WHILE @CNT <=200000
BEGIN
 INSERT tblUnion01 VALUES(@CNT, GETDATE());
 INSERT tblUnion02 VALUES(@CNT, GETDATE());

 SET @CNT +=1
END

--
INSERT tblUnion01 VALUES(9999999,GETDATE())

-- 以上是建立資料表






-- 觀察資料表

-- 分別擁有200,000筆資料列
SELECT COUNT(*) FROM tblUnion01; -- 200,001
SELECT COUNT(*) FROM tblUnion02; -- 200,000

--EX1. UNION,移除重複項的資料列
SELECT sid, myDate FROM tblUnion01
UNION
SELECT sid, myDate FROM tblUnion02
GO

--EX2. UNION ALL,不移除重複項的資料列
SELECT sid, myDate FROM tblUnion01
UNION ALL
SELECT sid, myDate FROM tblUnion02
GO




以兩千筆資料列的情境

-- 01_執行計畫_2千筆



-- 02_UNION_成本_0.104821



-- 03_UNION ALL_成本_0.0187726



-- 04_排序_相異排序_成本_0.0860484



以二十萬筆資料列的情境

-- 01_執行計畫_20萬筆



-- 02_UNION_成本_4.96574



-- 03_UNION ALL_1.29249



-- 04_雜湊比對_3.26904



-- 05_平行處理_成本_4.96574





UNION (Transact-SQL)
https://msdn.microsoft.com/zh-tw/library/ms180026(v=sql.120).aspx

2015-12-30

日期時間資料類型:ISNULL函數、IS [NOT] NULL 述詞,對時間值是 NULL 時的處理方式是不同的



SQL Server 的日期時間類型,例如有:datetimeoffset、datetime2、smalldatetime、datetime等。

若輸入資料值時,僅輸入日期值,卻未提供時間值時,系統自動以 1900-01-01 00:00:00.000,午夜零點零分零秒的方式來呈現。

這對 ISNULL函數、IS [NOT] NULL 述詞,在處理這類資料值時,就會有不同的行為。




請參考以下的範例:

USE tempdb
GO
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[atb01]') AND type in (N'U'))
 BEGIN
  DROP TABLE [dbo].[atb01]
  CREATE TABLE [dbo].[atb01](
 [Sid] [int] PRIMARY KEY,
 [Aircode] [varchar](50) NULL,
 [Edate] [datetime] NULL)
 END
ELSE 
BEGIN
 CREATE TABLE [dbo].[atb01](
 [Sid] [int] PRIMARY KEY,
 [Aircode] [varchar](50) NULL,
 [Edate] datetime NULL)
END
GO

-- 新增四筆資料列
INSERT atb01 VALUES(1, 'A201',GETDATE())
INSERT atb01 VALUES(2, 'A202', '1900/01/01') -- 只給日期,未給時間。資料類型:[datetime]。
INSERT atb01(Sid, Aircode) VALUES(3,'A203')
INSERT atb01(Sid, Aircode) VALUES(4,'A204')
GO

-- 00 有 4 筆資料列
/*
已知:第二筆資料列,Edate資料行的資料類型是:[datetime]。
只輸入日期,未給時間。

查詢時,可觀察到,系統自動以 1900-01-01 00:00:00.000,午夜零點零分零秒的方式來呈現。
由於未提供時間值,仍是 NULL。
*/

SELECT Sid, Aircode, Edate FROM atb01



- 01_範例資料表,有4筆資料列







01 使用 ISNULL() 函數,3 筆資料列

-- 01 使用 ISNULL() 函數,3 筆資料列




/*
ISNULL 函數:以指定的取代值來取代 NULL。

使用 ISNULL() 函數,將會抓取到  3 筆資料列,包含:兩筆未提供任何值,以及一筆僅提供日期值的資料列。
*/

SELECT Sid, Aircode, Edate FROM atb01
WHERE ISNULL(Edate,'') = ''


-- 02_使用 ISNULL() 函數,抓到3筆資料列






02 使用 IS NULL 述詞,2 筆資料列



-- 02 使用 IS NULL 述詞,2 筆資料列 
/*

IS [NOT] NULL 述詞:判斷指定的運算式是否為 NULL。
若使用 IS [NOT] NULL 述詞來判斷 Edate 資料行是否為 NULL,將會抓取到 2 筆資料列。
*/

SELECT Sid, Aircode, Edate FROM atb01
WHERE Edate IS NULL



-- 03_IS [NOT] NULL 述詞,抓到2筆資料列






有些專案在設計時,若沒有輸入

1. 若沒有輸入日期時間時,前端程式將自動輸入 1900/01/01。
2. 後續接手的人員,可能改變此規則,改採取不輸入,讓系統存放 NULL 值,這又會是另一套規則。

或許,在某些狀況下, NULL 與 1900/01/01,對於此專案是具相同的意義。
但使用 ISNULL 函數與 IS NULL 述詞,卻是取得不同的結果。

若能修改原始資料值,具備一致的特性,則在 WHERE 條件式就可以精簡許多,也有機會可以使用索引來提升查詢效能。




ISNULL 函數

ISNULL 函數:以指定的取代值來取代 NULL。

語法:
ISNULL ( check_expression , replacement_value )

check_expression
這是要檢查 NULL 的運算式。check_expression 可以是任何類型。

replacement_value
這是 check_expression 是 NULL 時所傳回的運算式。replacement_value 必須是能夠隱含地轉換成 check_expresssion 類型的類型。




IS [NOT] NULL 述詞

判斷指定的運算式是否為 NULL。

語法:
expression IS [ NOT ] NULL

引數

expression
這是任何有效的運算式。

NOT
指定執行布林結果的否定運算。 這個述詞會反轉它的傳回值,如果值不是 NULL,就傳回 TRUE,如果值是 NULL,就傳回 FALSE。

結果類型 Boolean

傳回碼值
如果 expression 的值是 NULL,IS NULL 會傳回 TRUE;否則,它會傳回 FALSE。
如果 expression 的值是 NULL,IS NOT NULL 會傳回 FALSE;否則,它會傳回 TRUE。

備註
若要判斷運算式是否為 NULL,請利用 IS NULL 或 IS NOT NULL 來取代比較運算子 (如 = 或 !=)。
當兩個引數或其中一個引數是 NULL 時,比較運算子會傳回 UNKNOWN。



參考資料

SQL Server:認識 ISNULL 函數
http://sharedderrick.blogspot.tw/2012/06/t-sql-isnull.html

ISNULL (Transact-SQL)
https://msdn.microsoft.com/zh-tw/library/ms184325(v=sql.120).aspx

資料類型 (Transact-SQL)
https://msdn.microsoft.com/zh-tw/library/ms187752(v=sql.120).aspx

IS [NOT] NULL (Transact-SQL)
https://msdn.microsoft.com/zh-tw/library/ms188795(v=sql.120).aspx

2013-09-12

投影片:淺談資料庫物件的基礎設計概念

題目:淺談資料庫物件的基礎設計概念

討論大綱:
認識索引鍵
認識索引













投影片檔案名稱:
20130903_淺談資料庫物件

投影片:認識交易 (Transaction)與鎖定 (Lock)

題目:認識交易 (Transaction)與鎖定 (Lock)

討論大綱:

  • 認識交易(Transaction)
  • 認識鎖定(Lock)











投影片檔案名稱:
20130822_認識交易 (Transaction)與鎖定 (Lock)


2013-08-29

查詢目前資料庫內有哪些 DML 觸發程序(Trigger)

查詢目前資料庫內有哪些 DML 觸發程序(Trigger)

示範版本:SQL Server 2012

可以參考以下的範例程式碼:


-- 查詢:現行資料庫內,有哪些 DML 觸發程序
SELECT SCHEMA_NAME(tb.schema_id) N'結構描述', OBJECT_NAME(t.parent_id) N'資料表', t.name N'觸發程序名稱', parent_class_desc N'觸發程序父類別的描述', 
 t.type_desc N'物件類型的描述',  tEV.type_desc '引發觸發程序的每個事件', is_instead_of_trigger N'是否為 INSTEAD OF 觸發程序'
FROM sys.triggers t INNER JOIN sys.trigger_events tEV ON t.object_id = tEV.object_id 
 INNER JOIN sys.tables tb ON t.parent_id = tb.object_id
GO

-- 01_DML觸發程序



-- 02_DML觸發程序






參考資料

sys.triggers (Transact-SQL)
http://technet.microsoft.com/zh-tw/library/ms188746.aspx

sys.trigger_events (Transact-SQL)
http://technet.microsoft.com/zh-tw/library/ms187322.aspx

2013-03-28

使用 sp_refreshsqlmodule 更新資料庫物件的中繼資料


示範版本:SQL Server 2012

sp_refreshsqlmodule

針對目前資料庫中指定的非結構描述繫結預存程序、使用者自訂函數、檢視、DML 觸發程序、資料庫層級 DDL 觸發程序或伺服器層級 DDL 觸發程序,更新其中繼資料。

 這些物件的基礎物件變更之後,其保存中繼資料也可能會過期,例如參數的資料類型。

sp_refreshsqlmodule 應在模組的基礎物件變更而影響其定義時執行。

否則,在查詢或叫用模組時,可能會產生非預期的結果。

若要重新整理檢視,您可以使用 sp_refreshsqlmodule 或 sp_refreshview 來重新整理相同的結果。

sp_refreshsqlmodule 不會影響與物件相關聯的任何權限、擴充屬性或 SET 選項。

若要重新整理伺服器層級 DDL 觸發程序,請從任何資料庫的內容執行此預存程序。

當您執行 sp_refreshsqlmodule 時,會卸除與物件相關聯的任何簽章。




EX1. 重新整理使用者預存預存程序的中繼資料

下列範例會重新整理使用者預存預存程序的中繼資料。

這個範例會建立別名資料類型:uid,以及使用 uid 的預存程序:usp_udt01。
接著,uid 會重新命名為 uid_old,而且會建立有不同定義的新 uid。

所以,需要對 預存程序:usp_udt01 執行中繼資料的重新整理,以參考 uid 的新實作,而非舊實作。



-- 01_建立「別名資料類型(alias data type)」:uid,其資料類型是:char(3)。
USE Northwind_Dev
GO
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[usp_udt01]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[usp_udt01]
GO
IF  EXISTS (SELECT * FROM sys.types st JOIN sys.schemas ss ON st.schema_id = ss.schema_id WHERE st.name = N'uid' AND ss.name = N'dbo')
DROP TYPE [dbo].[uid]
GO
CREATE TYPE [dbo].[uid] FROM [char](3) NOT NULL
GO


-- 圖01_建立「別名資料類型(alias data type)」



-- 02 僅查詢:別名資料類型、系統資料類型、最大長度(Byte)以及字元數

SELECT ty1.name N'資料類型', ty2.name N'系統資料類型',ty1.max_length N'資料行的最大長度(Byte)',
 CAST(CASE WHEN ty2.name IN (N'nchar', N'nvarchar') AND ty1.max_length <> -1 
  THEN ty1.max_length/2 ELSE ty1.max_length END AS int) N'字元數'
FROM sys.types ty1 LEFT OUTER JOIN sys.types ty2
ON (ty2.user_type_id = ty1.system_type_id and ty2.user_type_id = ty2.system_type_id) or ((ty2.system_type_id = ty1.system_type_id) 
 and (ty2.user_type_id = ty1.user_type_id) and (ty2.is_user_defined = 0) and (ty2.is_assembly_type = 1)) 
WHERE ty1.is_user_defined=1
GO


-- 圖02_查詢:別名資料類型、系統資料類型、最大長度(Byte)以及字元數



-- 03_建立預存程序:usp_udt01,使用此「別名資料類型」。

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[usp_udt01]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[usp_udt01]
GO
CREATE PROCEDURE usp_udt01
 @cid uid 
AS
 SELECT @cid AS N'別名資料類型'
GO

-- 04_執行此預存程序:usp_udt01,輸入的資料長度是 3 個字元

EXEC usp_udt01 'A12'
GO


-- 圖03_執行此預存程序:usp_udt01



-- 05_若嘗試刪除此「別名資料類型」:uid,會遭遇到以下的錯誤訊息:

IF  EXISTS (SELECT * FROM sys.types st JOIN sys.schemas ss ON st.schema_id = ss.schema_id WHERE st.name = N'uid' AND ss.name = N'dbo')
DROP TYPE [dbo].[uid]
GO

/*
錯誤訊息:

訊息 3732,層級 16,狀態 1,行 2
無法卸除類型 'dbo.uid',因為物件 'usp_udt01' 正在參考它。可能還有參考此類型的其他物件。
*/


-- 圖04_若嘗試刪除此「別名資料類型」:uid,會遭遇到錯誤訊息



無法刪除已經被使用的「別名資料類型」:uid,但可以修改其名稱。

-- 06_先修改「別名資料類型」:uid的名稱為:uid_old

EXEC sp_rename @objname= 'uid', @newname = 'uid_old', @objtype= 'USERDATATYPE';
GO

/*
產生的訊息:

注意: 變更物件名稱的任何部分將會中斷指令碼和預存程序。
*/


-- 圖05_先修改「別名資料類型」:uid的名稱為:uid_old



-- 07_建立與原先同名的「別名資料類型(alias data type)」:uid,但其資料類型是:char(5)。

USE Northwind_Dev
GO
IF  EXISTS (SELECT * FROM sys.types st JOIN sys.schemas ss ON st.schema_id = ss.schema_id WHERE st.name = N'uid' AND ss.name = N'dbo')
DROP TYPE [dbo].[uid]
GO
CREATE TYPE [dbo].[uid] FROM [char](5) NOT NULL
GO

-- 08_再度查詢:別名資料類型、系統資料類型、最大長度(Byte)以及字元數

SELECT ty1.name N'資料類型', ty2.name N'系統資料類型',ty1.max_length N'資料行的最大長度(Byte)',
 CAST(CASE WHEN ty2.name IN (N'nchar', N'nvarchar') AND ty1.max_length <> -1 
  THEN ty1.max_length/2 ELSE ty1.max_length END AS int) N'字元數'
FROM sys.types ty1 LEFT OUTER JOIN sys.types ty2
ON (ty2.user_type_id = ty1.system_type_id and ty2.user_type_id = ty2.system_type_id) or ((ty2.system_type_id = ty1.system_type_id) 
 and (ty2.user_type_id = ty1.user_type_id) and (ty2.is_user_defined = 0) and (ty2.is_assembly_type = 1)) 
WHERE ty1.is_user_defined=1
GO


-- 圖06_有兩個別名資料類型



-- 查詢接受參數之物件的每個參數
SELECT *
FROM sys.parameters 

-- 08_檢查使用者預存程序:usp_udt01,卻是使用舊的「別名資料類型」:uid_old,其資料類型是:char(3)。
SELECT OBJECT_NAME(object_id) AS N'物件名稱', name AS N'參數名稱', TYPE_NAME(user_type_id) AS N'資料類型',
 max_length AS N'最大長度 (位元組)', precision AS N'有效位數', scale AS N'小數位數', is_output AS N'OUTPUT參數', 
 has_default_value AS N'是否有預設值', default_value AS N'預設值', is_readonly AS N'READONLY參數'
FROM sys.parameters 
ORDER BY 1 DESC
GO


-- 圖07_檢查使用者預存程序:usp_udt01,卻是使用舊的「別名資料類型」:uid_old,其資料類型是:char(3)



-- 圖08_使用 SSMS觀察使用者預存程序:usp_udt01,其系統資料沒有更新,仍是使用「別名資料類型」:uid_old,但其資料類型是:char(3)



-- 09_執行此預存程序:usp_udt01,輸入的資料長度是 5 個字元,遭遇錯誤

EXEC usp_udt01 'A12' -- 三個字元
GO
EXEC usp_udt01 'A1234' -- 五個字元
GO

/*
錯誤訊息:

訊息 496,層級 16,狀態 1,程序 usp_udt01,行 4
"@cid" 參數的類型和建立時的類型不同。請先卸除,再用兩段式類型名稱重新建立程序。或使用 sp_refreshsqlmodule 重新整理它的參數中繼資料。
訊息 496,層級 16,狀態 1,程序 usp_udt01,行 4
"@cid" 參數的類型和建立時的類型不同。請先卸除,再用兩段式類型名稱重新建立程序。或使用 sp_refreshsqlmodule 重新整理它的參數中繼資料。

*/


-- 圖09_輸入的資料長度是 3或是 5 個字元,遭遇錯誤



-- 10_使用 sys.sp_refreshsqlmodule 更新其中繼資料
EXEC sys.sp_refreshsqlmodule @name = 'dbo.usp_udt01' ;
GO

-- 11_檢查使用者預存程序:usp_udt01,已經改用新的「別名資料類型」:uid_old,其資料類型是:char(5)。
SELECT OBJECT_NAME(object_id) AS N'物件名稱', name AS N'參數名稱', TYPE_NAME(user_type_id) AS N'資料類型',
 max_length AS N'最大長度 (位元組)', precision AS N'有效位數', scale AS N'小數位數', is_output AS N'OUTPUT參數', 
 has_default_value AS N'是否有預設值', default_value AS N'預設值', is_readonly AS N'READONLY參數'
FROM sys.parameters 
ORDER BY 1 DESC
GO


-- 圖10_使用者預存程序:usp_udt01,已經改用新的「別名資料類型」



-- 圖11_使用 SSMS 觀察使用者預存程序:usp_udt01,已經改用新的資料類型:char(5)



-- 12_執行此預存程序:usp_udt01,輸入的資料長度是 5 個字元,正常執行

EXEC usp_udt01 'A1234'
GO

-- 圖12_執行此預存程序:usp_udt01,輸入的資料長度是 5 個字元,正常執行



-- 99_回復原狀

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[usp_udt01]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[usp_udt01]
GO
IF  EXISTS (SELECT * FROM sys.types st JOIN sys.schemas ss ON st.schema_id = ss.schema_id WHERE st.name = N'uid' AND ss.name = N'dbo')
DROP TYPE [dbo].[uid]
GO
IF  EXISTS (SELECT * FROM sys.types st JOIN sys.schemas ss ON st.schema_id = ss.schema_id WHERE st.name = N'uid_old' AND ss.name = N'dbo')
DROP TYPE [dbo].[uid_old]
GO





參考資料


sp_refreshsqlmodule (Transact-SQL)
http://msdn.microsoft.com/zh-tw/library/bb326754.aspx

sp_refreshview (Transact-SQL)
http://msdn.microsoft.com/zh-tw/library/ms187821.aspx

sys.parameters (Transact-SQL)
http://msdn.microsoft.com/zh-tw/library/ms176074.aspx

2013-03-08

請勿使用 sp_depends 查詢物件相依性的資訊,改用:sys.sql_expression_dependencies 或 sys.dm_sql_referenced_entities


適用版本:SQL Server 2008 開始。

請勿使用 sp_depends 查詢物件相依性的資訊,改用:sys.sql_expression_dependencies 或 sys.dm_sql_referenced_entities

系統預存程序:sp_depends

顯示有關資料庫物件相依性的資訊,例如相依於資料表或檢視的檢視和程序,以及檢視或程序所相依的資料表或檢視。
但不會報告對於目前資料庫外之物件的參考。

重要事項:

未來的 Microsoft SQL Server 版本將移除這項功能。
請避免在新的開發工作中使用這項功能,並規劃修改目前使用這項功能的應用程式。

請改用 sys.dm_sql_referencing_entities 和 sys.dm_sql_referenced_entities。

請參考以下範例程式碼。



EX1. 更新「檢視表(VIEW)」的中繼資料(meta data)

-- EX1. 更新「檢視表(VIEW)」的中繼資料(meta data)

-- 00_建立範例資料表:TREF01,僅 2 個資料行
USE Northwind_Dev
GO
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TREF01]') AND type in (N'U'))
DROP TABLE [dbo].[TREF01]
GO
CREATE TABLE TREF01
(SID INT, CDATA VARCHAR(200))
GO
INSERT dbo.TREF01 VALUES(1,'AB'),(2,'CD');
GO
-- 查詢範例資料表:TREF01,僅 2 個資料行
SELECT * FROM TREF01
GO


-- 圖01_查詢範例資料表:TREF01



-- 01_利用 SELECT * 來建立「檢視表(VIEW)」
IF  EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[vREF01]'))
DROP VIEW [dbo].[vREF01]
GO
CREATE VIEW vREF01
AS
 SELECT * FROM dbo.TREF01
GO

-- 02_查詢「檢視表(VIEW)」,僅 2 個資料行
SELECT * FROM vREF01
GO

-- 03_對「檢視表(VIEW)」,使用 sp_depends,顯示有關資料庫物件相依性的資訊
EXEC sp_depends 'dbo.vREF01'
GO


-- 圖02_使用 sp_depends



-- 04_對「檢視表(VIEW)」,使用 sql_expression_dependencies,傳回參考其他實體的相依性之資訊

SELECT OBJECT_NAME(referencing_id) AS N'參考實體的名稱', 
    o.type_desc AS N'參考實體的物件類型', 
    referencing_class_desc N'參考實體之類別的描述',
    referenced_server_name N'受參考實體之伺服器的名稱', 
    referenced_database_name N'受參考實體之資料庫的名稱', 
    referenced_schema_name N'受參考實體所屬的結構描述',
    referenced_entity_name N'受參考實體的名稱' 
FROM sys.sql_expression_dependencies AS sed INNER JOIN sys.objects AS o 
 ON sed.referencing_id = o.object_id
WHERE referencing_id = OBJECT_ID(N'dbo.vREF01');
GO


-- 圖03_使用 sql_expression_dependencies



-- 05_範例資料表:TREF01,增加一個資料行
ALTER TABLE TREF01
 ADD C3 INT
GO

-- 06_查詢資料表:已有 3 個資料行
SELECT * FROM TREF01
GO


-- 圖04_查詢資料表:已有 3 個資料行



-- 07_查詢「檢視表(VIEW)」:卻只有 2 個資料行
SELECT * FROM VREF01
GO


-- 圖05_查詢「檢視表(VIEW)」:卻只有 2 個資料行



-- 08_對「檢視表(VIEW)」,使用 sp_depends,顯示有關資料庫物件相依性的資訊
-- 卻只看到兩個資料行
EXEC sp_depends 'dbo.vREF01'
GO


-- 圖06_使用 sp_depends,卻只看到兩個資料行



-- 09_對「檢視表(VIEW)」,使用 sql_expression_dependencies,傳回參考其他實體的相依性之資訊

SELECT OBJECT_NAME(referencing_id) AS N'參考實體的名稱', 
    o.type_desc AS N'參考實體的物件類型', 
    referencing_class_desc N'參考實體之類別的描述',
    referenced_server_name N'受參考實體之伺服器的名稱', 
    referenced_database_name N'受參考實體之資料庫的名稱', 
    referenced_schema_name N'受參考實體所屬的結構描述',
    referenced_entity_name N'受參考實體的名稱' 
FROM sys.sql_expression_dependencies AS sed INNER JOIN sys.objects AS o 
 ON sed.referencing_id = o.object_id
WHERE referencing_id = OBJECT_ID(N'dbo.vREF01');
GO


-- 圖07_使用 sql_expression_dependencies



-- 10_刪除與重新建立此範例資料表:TREF01
DROP TABLE [dbo].[TREF01]
GO
CREATE TABLE TREF01
(SID INT, CDATA VARCHAR(200))
GO

-- 11_對「檢視表(VIEW)」,使用 sp_depends,顯示有關資料庫物件相依性的資訊
-- 找不到任何資訊
EXEC sp_depends 'dbo.vREF01'
GO

/*
物件並未參考任何物件,且沒有物件參考它。
*/


-- 圖08_使用 sp_depends,找不到任何資訊



-- 12_對「檢視表(VIEW)」,使用 sql_expression_dependencies,傳回參考其他實體的相依性之資訊
-- 仍是可以正確顯示資料

SELECT OBJECT_NAME(referencing_id) AS N'參考實體的名稱', 
    o.type_desc AS N'參考實體的物件類型', 
    referencing_class_desc N'參考實體之類別的描述',
    referenced_server_name N'受參考實體之伺服器的名稱', 
    referenced_database_name N'受參考實體之資料庫的名稱', 
    referenced_schema_name N'受參考實體所屬的結構描述',
    referenced_entity_name N'受參考實體的名稱' 
FROM sys.sql_expression_dependencies AS sed INNER JOIN sys.objects AS o 
 ON sed.referencing_id = o.object_id
WHERE referencing_id = OBJECT_ID(N'dbo.vREF01');
GO

-- 13_不影響:查詢「檢視表(VIEW)」
-- 雖然,使用 sp_depends,找不到任何資訊
SELECT * FROM dbo.vREF01
GO





若是要修正以下問題:

(1) 基礎資料表已經改為 3 個資料行,但查詢「檢視表(VIEW)」:卻只有 2 個資料行 。

(2) 使用 sp_depends,找不到任何資訊(資料庫物件相依性的資訊)之問題。

可以使用:
sys.sp_refreshsqlmodule 或 sys.sp_refreshview 來處理。



-- 14_sys.sp_refreshsqlmodule 更新其中繼資料
EXEC sys.sp_refreshsqlmodule @name = 'dbo.vREF01' ;
GO

-- 15_sys.sp_refreshview 更新指定非結構描述繫結檢視的中繼資料。 
EXEC sys.sp_refreshview 'VREF01';
GO




認識 sys.sp_refreshsqlmodule

適用版本:SQL Server 2005 已提供

針對目前資料庫中指定的非結構描述繫結預存程序、使用者自訂函數、檢視、DML 觸發程序、資料庫層級 DDL 觸發程序或伺服器層級 DDL 觸發程序,更新其中繼資料。

這些物件的基礎物件變更之後,其保存中繼資料也可能會過期,例如參數的資料類型。



認識 sys.sp_refreshview

適用版本:SQL Server 2000 已提供

更新指定非結構描述繫結檢視的中繼資料。

檢視所依賴的基礎物件變更之後,檢視的保存中繼資料也可能會過期。



參考資料

sp_depends (Transact-SQL)

sys.dm_sql_referencing_entities (Transact-SQL)

sys.dm_sql_referenced_entities (Transact-SQL)

sp_refreshsqlmodule (Transact-SQL)

sp_refreshview (Transact-SQL)

sp_refreshview -- SQL Server 2000 版本



2013-03-01

刪除所有連線(Kill All Connection),資料庫層級、執行個體層級。以 Cursors(資料指標) 與資料列集(Rowsets) 方式為例。


分成為刪除指定資料庫,以及刪除執行個體上的全部連線兩個部分來說明。

以下整理了 Cursors(資料指標) 版本,以及資料列集(Rowsets)方式來處理。



EX1. 刪除指定資料庫上的所有連線

若是要刪除指定資料庫上的所有連線,可以使用以下方式:

ALTER DATABASE 語法的部分引數:

1. SINGLE_USER 引數

指定每次只能有一位使用者存取資料庫。

如果指定了 SINGLE_USER,且沒有其他使用者連接到資料庫,就會封鎖 ALTER DATABASE 陳述式,直到所有使用者都中斷指定的資料庫為止。

資料庫會保留在 SINGLE_USER 模式中,即使是將選項記錄設為關閉的使用者也是如此。
此時其他使用者可以連接到這個資料庫,但只能有一位。

2. WITH ROLLBACK IMMEDIATE 引數

使用終止選項。所有未完成的交易都會回復,與指定資料庫的所有連接都會立即中斷。

若是要刪除指定資料庫上的所有連線,請參考以下的範例:



-- 01_刪除指定資料庫上的所有連線,並變更為單一使用者連線模式
USE master
GO
ALTER DATABASE [資料庫名稱]
SET SINGLE_USER
WITH ROLLBACK IMMEDIATE;
GO

-- 02_切換回多人使用者連線模式
ALTER DATABASE [資料庫名稱]
SET MULTI_USER;
GO





EX2. CURSOR 版本,刪除 SQL Server 上全部的連線,但保留本身這條連線不會刪除

-- EX1. CURSOR 版本,刪除 SQL Server 上全部的連線,但保留本身這條連線不會刪除

-- 01_適用 SQL Server 2005、2008、2008 R2、2012 等版本

USE master
GO
CREATE PROC KillUserConnections
AS
DECLARE @spid int, @SQLstr nvarchar(128)
 
DECLARE spids_cr CURSOR FOR
SELECT session_id FROM sys.dm_exec_connections
WHERE session_id<>@@SPID
FOR READ ONLY
 
OPEN spids_cr
 
FETCH spids_cr INTO @spid
 
WHILE (@@FETCH_STATUS=0)
BEGIN
SET @SQLstr = 'KILL ' + CAST(@spid AS varchar)
EXEC sp_executesql @SQLstr
FETCH spids_cr INTO @spid
END
 
CLOSE spids_cr
DEALLOCATE spids_cr
GO
 
-- 檢視 SQL Server 目前所有的連線 session ;所有目前的處理序
SELECT * FROM sys.dm_exec_connections
 
-- 刪除所有的處理序、連線、session
EXEC KillUserConnections
 
-- 再度檢視 SQL Server 目前所有的連線 session ;所有目前的處理序
SELECT * FROM sys.dm_exec_connections
GO


-- 02_以下是 SQL Server 2000 與 2005 通用的版本

USE master
GO
CREATE PROC KillUserConnections
AS
DECLARE @spid int, @SQLstr nvarchar(128)
 
DECLARE spids_cr CURSOR FOR
SELECT spid
FROM master..sysprocesses
WHERE spid<>@@SPID AND net_address<>''
FOR READ ONLY
 
OPEN spids_cr
 
FETCH spids_cr INTO @spid
 
WHILE (@@FETCH_STATUS=0)
BEGIN
SET @SQLstr = 'KILL ' + CAST(@spid AS varchar)
EXEC sp_executesql @SQLstr
FETCH spids_cr INTO @spid
END
 
CLOSE spids_cr
DEALLOCATE spids_cr
 
GO
 
-- 檢視 SQL Server 目前所有的連線 session ;所有目前的處理序
EXEC sp_who2
 
-- 刪除所有的處理序、連線、session
EXEC KillUserConnections
 
-- 再度檢視 SQL Server 目前所有的連線 session ;所有目前的處理序
EXEC sp_who2
GO


其中 sysprocesses 此為 SQL Server 2000 系統資料表。




EX3. 資料列集(Rowsets)方式:刪除 SQL Server 上全部的連線,但保留本身這條連線不會刪除

使用資料列集(Rowsets)方式,無須使用 CURSOR 方式。

適用 SQL Server 2005、2008、2008 R2、2012 等版本。

-- EX2. 資料列集(Rowsets)方式:刪除 SQL Server 上全部的連線,但保留本身這條連線不會刪除

-- 01_檢視 SQL Server 目前所有的連線 session ;所有目前的處理序
USE master
GO
SELECT * FROM sys.dm_exec_connections
GO

-- 02_使用 ISNULL() 函數:刪除 SQL Server 上全部的連線,但保留本身這條連線不會刪除
DECLARE @str1 nvarchar(MAX)

SELECT @str1 = ISNULL(@str1, '') + 'KILL ' + CAST(session_id AS nvarchar(10)) + ';'
FROM sys.dm_exec_connections
WHERE session_id<>@@SPID

-- PRINT @str1
EXEC sp_executesql @str1
GO

/*
PRINT 陳述式所回傳的結果:
KILL 52;KILL 51;KILL 53;KILL 54;KILL 56;KILL 57;KILL 58;KILL 59;KILL 60;KILL 61;KILL 62;KILL 63;KILL 64;KILL 65;
*/


-- 03_使用 COALESCE() 函數:刪除 SQL Server 上全部的連線,但保留本身這條連線不會刪除
DECLARE @str1 nvarchar(MAX)

SELECT @str1 = COALESCE(@str1, '') + 'KILL ' + CAST(session_id AS nvarchar(10)) + ';'
FROM sys.dm_exec_connections
WHERE session_id<>@@SPID

-- PRINT @str1
EXEC sp_executesql @str1
GO


若是要改為 SQL Server 2000 版本:

請置換資料表為:master..sysprocesses。

請置換資料行:spid。



參考資料


刪除所有連線; Kill All Connection
http://sharedderrick.blogspot.tw/2008/01/kill-all-connection.html

SQL Server:認識 COALESCE() 函數,補充範例
http://sharedderrick.blogspot.tw/2012/06/t-sql-coalesce_14.html

初探Cursors(資料指標) 與資料列集(Rowsets)
http://sharedderrick.blogspot.tw/2013/02/cursors-rowsets.html