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

2018年9月11日 星期二

利用 Trigger 紀錄資料表異動 (Log)


原始資料表:[xxx]
異動資料表:[xxx_log]

[xxx_log] 比 [xxx] 至少要多兩個欄位:

  • T1,異動時間,Datetime,預設 getdate() 即可。
  • T2,異動指令,Varchar(10),存放 INSERT UPDATE DELETE 做為識別。

2019/03/25 補充
兩個系統資料表 [INSERTED] & [DELETED]
執行指令 insert 後,[INSERTED] 表內有新增的資料,[DELETED] 表內無資料。
執行指令 delete 後,[INSERTED] 表內無資料,[DELETED] 表內有刪除的資料。
執行指令 update 後,[INSERTED] 表內有更新的資料,[DELETED] 表內有更新的資料。
所以執行指令後,可藉由 select 兩個表判斷為哪種行為,並取得資料。
 
建立一個名為 tr_xxx 的 Trigger

CREATE TRIGGER tr_xxx ON [dbo].[xxx] --// 在資料表 xxx 建立一個名為 tr_xxx 的 Trigger
AFTER INSERT, UPDATE --// 如果需要判斷 DELETE 得加進來
AS

SET NOCOUNT ON; --// 這行是為了正確紀錄批次 UPDATE 或 DELETE,如果不寫,只會 LOG 到一筆異動
DECLARE @INS int, @DEL int --// 取得 INSERTED 與 DELETED 的資料數

--// INSERT:@INS > 0 AND @DEL = 0
--// DELETE:@INS = 0 AND @DEL > 0
--// UPDATE:@INS > 0 AND @DEL > 0

SELECT @INS = COUNT(*) FROM INSERTED
SELECT @DEL = COUNT(*) FROM DELETED

IF @INS > 0 AND @DEL > 0 
BEGIN
     INSERT INTO [xxx_log]  ( T2, C1, C2, C3 ... )  
         SELECT 'UPDATE', C1, C2, C3 ... FROM INSERTED
END

ELSE 
BEGIN
     INSERT INTO [xxx_log]  ( T2, C1, C2, C3 ... )  
         SELECT 'INSERT', C1, C2, C3 ... FROM INSERTED
END

參考資料:
https://stackoverflow.com/questions/9931839/create-a-trigger-that-inserts-values-into-a-new-table-when-a-column-is-updated
https://dotblogs.com.tw/jamesfu/2014/06/20/triggersample

2018年8月21日 星期二

INSERT INTO SELECT 與 SELECT INTO FROM

INSERT INTO SELECT

INSERT INTO TableB ( field1, field2... ) SELECT field1, field2... FROM TableA

  • 目標 TableB 必須已存在。
  • 類似一般 INSERT INTO VALUES 寫法,只是來源改為 TableA
  • 如果 TabelB 與 TableA 結構(含欄位順序)完全一致,後半段可用 * 取代欄位名稱。


SELECT INTO FROM

SELECT field1, field2... INTO TableB FROM TableA

  • 目標 TabelB 不存在。
  • 執行語法時會自動建立資料表並加入值。

2018年7月9日 星期一

SQL Datetime Convert Format (日期格式轉換)

常用,但是每次都要Google,備份一下。

參考來源

- Microsoft SQL Server T-SQL date and datetime formats
- Date time formats - mssql datetime 
- MSSQL getdate returns current system date and time in standard internal format
SELECT convert(varchar, getdate(), 100) - mon dd yyyy hh:mmAM (or PM)
                                        - Oct  2 2008 11:01AM          
SELECT convert(varchar, getdate(), 101) - mm/dd/yyyy 10/02/2008                  
SELECT convert(varchar, getdate(), 102) - yyyy.mm.dd - 2008.10.02           
SELECT convert(varchar, getdate(), 103) - dd/mm/yyyy
SELECT convert(varchar, getdate(), 104) - dd.mm.yyyy
SELECT convert(varchar, getdate(), 105) - dd-mm-yyyy
SELECT convert(varchar, getdate(), 106) - dd mon yyyy
SELECT convert(varchar, getdate(), 107) - mon dd, yyyy
SELECT convert(varchar, getdate(), 108) - hh:mm:ss
SELECT convert(varchar, getdate(), 109) - mon dd yyyy hh:mm:ss:mmmAM (or PM)
                                        - Oct  2 2008 11:02:44:013AM   
SELECT convert(varchar, getdate(), 110) - mm-dd-yyyy
SELECT convert(varchar, getdate(), 111) - yyyy/mm/dd
SELECT convert(varchar, getdate(), 112) - yyyymmdd
SELECT convert(varchar, getdate(), 113) - dd mon yyyy hh:mm:ss:mmm
                                        - 02 Oct 2008 11:02:07:577     
SELECT convert(varchar, getdate(), 114) - hh:mm:ss:mmm(24h)
SELECT convert(varchar, getdate(), 120) - yyyy-mm-dd hh:mm:ss(24h)
SELECT convert(varchar, getdate(), 121) - yyyy-mm-dd hh:mm:ss.mmm
SELECT convert(varchar, getdate(), 126) - yyyy-mm-ddThh:mm:ss.mmm
                                        - 2008-10-02T10:52:47.513

- SQL create different date styles with t-sql string functions
SELECT replace(convert(varchar, getdate(), 111), -/-, - -) - yyyy mm dd
SELECT convert(varchar(7), getdate(), 126)                 - yyyy-mm
SELECT right(convert(varchar, getdate(), 106), 8)          - mon yyyy

2018年6月5日 星期二

WITH (NOLOCK) 實作與驗證

建了一個USER資料表,包含ID與金額欄位




撰寫一個簡單的TRANSACTION,更新ID=3的USER金額




Transaction有正常begin與commit,所以再次select時,資料正確。




再來我故意將commit拿掉,此時Transaction執行後將無法結束(也就是TABLE會被LOCK)
從參數 @@TRANCOUNT知道此TRAN已經執行了(一次),但因為沒有commit,資料都還沒真的寫入。




這時候在回頭執行SELECT時,會撈不出資料




但是!
如果此時加入 WITH (NOLOCK),就可以撈出來了!!
可以看到值也已經更改,因為UPDATE有執行到 (請記得資料還沒COMMIT)




這時候回頭加入rollback,再執行一次transaction,
資料會復原,TABLE也會解除LOCK




此時一般的SELECT就可以執行了,不會再TIMEOUT
而數值會恢復到執行前的狀態(因為資料rollback了)




所以如果某個系統程序執行了一段冗長耗時的TRANSACTION
所有相關的TABLE在執行期間都會LOCK,此時某人要撈資料就必須下WITH (NOLOCK)

以上例子:
  • 金額amount會在Transaction內異動,不適合搭配NOLOCK撈取。
  • ID或姓名name,可使用NOLOCK,避免Transaction執行間無法撈取。


附帶一提,如果剛好要撈的資料欄位必須等TRANSACTION結束(不能用NOLOCK),
又不想讓USER看到逾時的話,可以改用NOWAIT判斷,然後提示USER,如下:




關於 NOWAIT 的實作範例可 參考這裡