因為專案需求, 前一陣子在進行 SQL Server 的 Tables 設計, 及 Functions, Stored Procedures 的撰寫; 發現版本控管是一件很重要的事情, 萬一沒有作好, 就可能發生以下狀況, 例如:
(1) AP 與 DB 的版本無法相符, 導致程式掛掉.
(2) 個人開發境 DB, 整合測試環境 DB, 正式環境 DB 的資料庫物件, 或多或少存在一些差異, 要如何作同步?
本系列文章, 主要係參考 Will 保哥在 Slide Share 及 Channel 9 分享的 Slides 及 影片; 再加上筆者以往的開發經驗彙整而成.
另外, 相關的方案/專案, 均是採用 Visual Studio Online 的 TFS 作為版控服務, 而不是採用 Git.
名詞定義
茲定義一些名詞或縮寫, 供後續篇章使用.
- SSDT : SQL Server Data Tools
說明: 此為微軟所提供的 Visual Studio 套件, 以便在 Visual Studio 進行 SQL Server 資料庫的 設計, 開發, 建置, 測試, 發行, 就像在 Visual Studio 中開發應用程式一樣容易。可至此下載
- SSMS: SQL Server Management Studio
說明: 此為 SQL Server 的前端管理, 開發整合環境.
- Visual Studio Online
說明: 此為微軟提供的程式開發協同合作服務, 在 Visual Studio 2013 的年代, 稱為 Visual Studio Online; 在 Visual Studio 2015 的年代, 改稱為 Visual Studio Team Services. 其實, 其本質均相同; 本文一律採用 Visual Studio Online 這個名稱.
資料庫環境說明
如下圖:
- 共有 3 位開發人員, 每位在其各自的筆電都安裝有 SQL Express
- 開發及單元測試完成後, 要將修改過的資料庫物件 (ex: Tables, Views …) 同步到 MyDB_Test 進行整合測試
- 整合測試完成, 要將修改過的資料庫物件 (ex: Tables, Views …) 同步到 MyDB_Prod 上線
- 因為設備不足, 本文係以同一個 DB Server 的 3個 不同資料庫來呈現 ( MyDB, MyDB_Test, MyDB_Prod )
- 假設目前 MyDB 及 MyDB_Test 均內含 2 個 Table, 4 個 Function, 1 個 Stored Procedure; 相關細節, 請參考 <附錄一>
應用程式說明
範例應用程式, 係為一個 Console 應用程式, 外加一個 SQL 子資料夾, 內含以下 .sql 檔; 同時加入 Visual Studio Online 進行版控.
- Tables.sql
- Functions.sql
- Stored Procedures.sql
加入至 Visual Studio Online 版控
連結至您的 Visual Studio Online 主頁, 建立 TEAM 專案
https://<youridentifier>.visualstudio.com/#
將 Visual Studio 2013 的方案, 加入到剛才建立的 TEAM 專案
檢視版更記錄
開始進行異動
CASE 1: 加入 Stored Procedure
加入 usp_get_all_emps 這個 stored procedure, 以取得所有的員工資料及所屬部門名稱
CASE 2: 修改 Table Layout
將 DeptName 及 EmpName 由 nvarchar(10) 改為 nvarchar(20)
開發環境與整合測試環境同步
在前一個部份, 我們在開發環境調整了一些資料庫的物件, 但要如何異動至整合測試環境呢?
最初想到的, 當然是自己手工寫相關的異動 Script (ex: ALTER TABLE …), 但這樣也太辛苦了, 萬一異動的內容不少, 很容易漏掉, 也很容易寫錯.
這裡就會用到 SSDT 的功能了.
我們先把目前修改後的版本, 以 SSMS 執行, 並簽入至 Visual Studio Online.
利用 SSDT 提供的功能, 同時連結開發環境與整合測試環境, 再進行比對
下一個頁面, 要特別注意 …
- 來源: 係指較新的版本; 亦即要被採用的版本
- 目標: 係指較舊的版本; 亦即要被蓋掉的版本
目前尚未建立至 MyDB, MyDB_Test 的連接, 所以要先建立一下.
來源資料庫設定
目標資料庫設定
進行比較
下圖顯示了有 3 個地方有差異, 可以選擇 [更新] 或 [產生指令碼]
[產生指令碼] 產生後, 可以用 SSMS 到整合測試環境執行, 但要注意, 此時 SSMS 必須採用 SQLCmd 模式.
[更新] 就是直接把異動套用到目標, 亦即整合測試環境; 作這個動作要小心, 萬一 來源與目標剛好選反了, 那就 …
總結
採用 SQL Script 的文字檔案作為資料庫物件的描述, 看來不錯; 但總少了一些東西, 例如: 在撰寫 SQL Script 時, 沒有像 SSMS 一樣, 可以提供 intellisense 的功能.
因此, SSDT 提供了另外一種 Visual Studio 的專案類型, 稱之為 SQL Server 資料庫專案, 這也是下一篇文章要說明的部份.
參考文件
- Will保哥 SQL Server 資料庫版本控管 (簡報分享)
- Will保哥 SQL Server 資料庫版本控管 (影片)
<附錄一> 相關SQL Script參考
-- ========================================================= -- Tables.sql -- ========================================================= -- --------------------------------------------------------- -- [00] 刪除檔案 -- --------------------------------------------------------- -- 員工資料檔 IF EXISTS ( SELECT * FROM sys.tables JOIN sys.schemas ON sys.tables.schema_id = sys.schemas.schema_id WHERE sys.schemas.name = N'dbo' AND sys.tables.name = N'Emp' ) DROP TABLE dbo.Emp GO -- 部門資料檔 IF EXISTS ( SELECT * FROM sys.tables JOIN sys.schemas ON sys.tables.schema_id = sys.schemas.schema_id WHERE sys.schemas.name = N'dbo' AND sys.tables.name = N'Dept' ) DROP TABLE dbo.Dept GO -- --------------------------------------------------------- -- [01] 部門資料檔 -- --------------------------------------------------------- CREATE TABLE dbo.Dept ( DeptId int not null -- 部門代碼 PKEY , DeptName nvarchar(10) not null default '' -- 部門名稱 CONSTRAINT PK_Dept PRIMARY KEY CLUSTERED ( DeptId ) ); GO -- --------------------------------------------------------- -- [02] 員工資料檔 -- --------------------------------------------------------- CREATE TABLE dbo.Emp ( EmpId int not null -- 員工代碼 PKEY , EmpName nvarchar(10) not null default '' -- 員工名稱 , DeptId int not null -- 部門代碼 FKEY CONSTRAINT PK_Emp PRIMARY KEY CLUSTERED ( EmpId ) CONSTRAINT FK_Emp_Dept FOREIGN KEY ( DeptId ) REFERENCES Dept ( DeptId ) ); GO -- ========================================================= -- Functions.sql -- ========================================================= -- --------------------------------------------------------- -- [00] 刪除 -- --------------------------------------------------------- -- ufn_get_date IF OBJECT_ID (N'dbo.ufn_get_date') IS NOT NULL DROP FUNCTION dbo.ufn_get_date GO -- ufn_tomorrow IF OBJECT_ID (N'dbo.ufn_tomorrow') IS NOT NULL DROP FUNCTION dbo.ufn_tomorrow GO -- ufn_yesterday IF OBJECT_ID (N'dbo.ufn_yesterday') IS NOT NULL DROP FUNCTION dbo.ufn_yesterday GO -- ufn_today IF OBJECT_ID (N'dbo.ufn_today') IS NOT NULL DROP FUNCTION dbo.ufn_today GO -- --------------------------------------------------------- -- [01] ufn_today : 取得系統日期 -- --------------------------------------------------------- CREATE FUNCTION dbo.ufn_today( ) RETURNS date AS BEGIN RETURN DATEADD(day, DATEDIFF(day, 0, GETDATE()), 0); END GO -- --------------------------------------------------------- -- [02] ufn_yesterday : 取得昨天日期 -- --------------------------------------------------------- CREATE FUNCTION dbo.ufn_yesterday( ) RETURNS date AS BEGIN RETURN DATEADD(day, DATEDIFF(day, 0, GETDATE()), -1); END GO -- --------------------------------------------------------- -- [03] ufn_tomorrow : 取得明天日期 -- --------------------------------------------------------- CREATE FUNCTION dbo.ufn_tomorrow( ) RETURNS date AS BEGIN RETURN DATEADD(day, DATEDIFF(day, 0, GETDATE()), 1); END GO -- --------------------------------------------------------- -- [04] ufn_get_date : 取得 n 天 的日期 -- --------------------------------------------------------- CREATE FUNCTION dbo.ufn_get_date(@pi_n int = 0) RETURNS date AS BEGIN RETURN DATEADD(day, DATEDIFF(day, 0, GETDATE()), @pi_n); END GO -- ========================================================= -- Stored Procedures.sql -- ========================================================= -- --------------------------------------------------------- -- [00] 刪除 -- --------------------------------------------------------- IF EXISTS ( SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE SPECIFIC_SCHEMA = N'dbo' AND SPECIFIC_NAME = N'usp_get_all_depts' ) DROP PROCEDURE dbo.usp_get_all_depts GO -- --------------------------------------------------------- -- [01] usp_get_all_depts: 取得所有部門資料 -- --------------------------------------------------------- CREATE PROCEDURE dbo.usp_get_all_depts AS SELECT * FROM Dept; RETURN 0; GO
沒有留言:
張貼留言