星期五, 12月 27, 2013

[SQL] Trigger - 避免大量更新或大量刪除

朋友傳來的一個悲劇討論,大意就是進行更新時沒有下 WHERE 導致全部的資料都被更新,在 MS SQL 內可以在重點 Table 上,利用 instead of Trigger 來避免這種悲劇

利用 instead of Trigger 禁止一筆資料以上的更新或刪除,下面為 Script

星期一, 12月 23, 2013

[SSRS] 格式化條件 - 顯示圖片

網友問題,要分析逗號字串資料,並依資料內容來顯示所屬欄位圖形,如下圖



DataSet T-SQL
CREATE TABLE FormatImage (Data varchar(10))
INSERT INTO FormatImage VALUES
 ('S,O,F,C'),
 ('S,O'),
 ('O,F,C'),
 ('S,O,C'),
 ('O,C'),
 ('C')

報表設計

先利用精靈拉出一個簡單的 Table 並新增 SOFC 四欄位


針對 S 欄位插影像控件


影像控件 => 滑鼠右鍵 => 影像屬性 => 一般,選擇事先建立號的[勾圖形],並內嵌在報表中就好


把圖片內嵌在報表中之後,就可以在[報表資料]=> 影像 中,看見剛剛的[勾圖形]


影像控件 => 滑鼠右鍵 => 影像屬性 => 可見性,點選[fx]button 來設定顯示條件


利用 indexof 來判斷 Data 中有是否有 "S" 字樣,有的話則顯示 S 欄位的勾圖形
= IIF((Fields!Data.Value).indexof("S") > -1 , false , true)

依序完成 OFC 欄位條件設定

第二次插入影像控件,就不需要再進行匯入,直接選取[勾圖形]即可


預覽設定結果


看見預覽結果,根據 VFP 製作報表經驗,只要把背景設定為 Transparent 就可以解決此問題,沒想到 SSRS 影像控件中,根本就沒有 Transparent 設定,>.<

利用矩形包覆影像控件來達到 Transparent 效果

重新設定 SOFC 欄位內的控件,先插入矩形,並在矩形中再插入影像控件,影像控件設定跟之前是完全一樣的,下圖為完成 SOFC 欄位設定後的版面


預覽設定結果


星期五, 12月 20, 2013

[SQL] 觀看執行計畫重點

執行計畫可能會很龐大,要每個路徑都去觀察是一件相當費時的事情,從效能調校角度來觀察的話,可以先開啟 SET STATISTICS IO ON 來了解 Logical Read 最高是發生在哪一個 Table,先針對執行計畫內該 Table 相關執行路徑去觀察;利用 SET SHOWPLAN_ALL ON 改成文字執行計畫,複製至記事本上,再用 Ctrl + F 去搜尋該 Table 並觀察相關路徑也是一個方法,以下列出常見的觀察重點:

1. 減少資料量:

執行計畫中的箭頭符號代表資料量,資料量越大,箭頭會越粗,滑鼠移到箭頭上方會出現明細資料,可以觀察是否沒有適合的 WHERE 條件而抓取太多資料出來處理
2. 統計資訊老舊

觀察 operator 上的[實際資料列數量]和[估計的資料列數量]是否差異太大,差異太大代表,表示統計資訊需要更新
3. Index 使用方式是否合理

Index 使用有兩種方式:Index Seek 或 Index Scan,理論上使用 Index Seek 效能會比 Index Scan 佳,但假如抓取大量資料,利用 Index Scan 反而會比 Index Seek 更佳

4. 書籤查詢(Bookmark lookup)、鍵查詢(Key Lookup )


SQL 2005 SP2 開始 Bookmark lookup 更名為 Key lookup,代表 T-SQL 語法沒有滿足 covering index,可以觀察 T-SQL 是否有抓取多餘欄位資料或建立包含索引來避免


5. missing index


在 SSMS 內執行 T-SQL 語法,假如有 missing index 的話,會利用綠色文字來說明,在綠色文字說明上,點選滑鼠右鍵 => 遺漏索引詳細資料,就會建立 missing index 語法


6. 避免平行處理

OLPT 小交易系統,跑出平行處理通常代表耗能警訊,下左圖執行計畫中的 operator 上有黃色方向左雙箭頭在上面,就代表平行處理

7. 排序


盡量避免在 SQL Server 中進行排序

8. 避免隱含式轉換發生


Operator 中的述詞(就是指 WHERE 條件)是否有隱含式轉換,會造成 Index Scan

9. Warning 警訊


SELECT operator 中可見右下角的警訊圖式,滑鼠移到該 operator 上就可以觀察警訊內容


10. 成本較高的 Operator

在執行計畫中每個 operatore 下都會有一個[成本:?%]資訊,代表此 operator 佔整個執行計畫總成本的?%,可以先針對高成本 operator 進行了解


星期一, 12月 16, 2013

[SSRS] 使用 Split 分割字串

被論壇問題誤導,此篇變成練習 SSRS 中利用 VB.NET Split 來分割逗號字串資料

DataSet T-SQL 語法
CREATE TABLE SplitData (Data varchar(10))
INSERT INTO SplitData VALUES
 ('1,2,3,4'),
 ('1,2,3,'),
 ('1,2,,'),
 ('1,,,'),
 ('1,,4'), -- 不滿足四位
 ('2,4')   -- 不滿足四位

星期五, 12月 13, 2013

[SQL] 具有內含資料行的索引

Nonclustered index 是由 Index Key、included column 和 Clustered Key 組成,利用 Nonclustered index 搜尋資料,會先利用 Index Key 來搜尋,假如利用 Index Key 就搜尋完全部資料,此情況稱為 Covering Index(覆蓋索引);相反地,無法在 Index Key 上找到全部資料,就會進一步利用 Clustered Key 進入 Clustered Index 內去搜尋所需要資料,此行為稱為索引鍵查閱(Key Lookup),2005 SP2 之前稱為書籤查詢(Bookmark Lookup),會增加鎖定和執行時間

建立[具有內含資料行的索引](文內簡稱包含索引)來滿足 Covering Index 進而消除 Key Lookup 來提高效能,就是此篇文章要說明的

建立包含索引的簡易語法
CREATE INDEX IndexName
ON SchemaName.TableName (Col1,Col2,Col3.......) 
INCLUDE (ColA,ColB,ColC.......)
利用 AdventureWork 的 Sales.SalesOrderHeader 來說明
-- Step 1:針對 OrderDate 建立 nonclustered index
CREATE INDEX IX_OrderDate ON [Sales].[SalesOrderHeader] (OrderDate)

-- Step 2:找出 OrderDate = '20040729' 的 SalesOrderID、OrderDate 和  CustomerID 這三個欄位資料
SELECT 
    SalesOrderID , 
    OrderDate ,
    CustomerID
FROM [Sales].[SalesOrderHeader]
WHERE OrderDate = '20040729'
下圖為 Step 2 執行計畫,可以看出因為 IX_OrderDate 無法滿足 Covering Index 所以利用 Clustered Index 搭配 Nested Loop(巢狀迴圈)一筆一筆資料進入PK_SalesOrderHeader_SalesOrderID 抓取資料


把滑鼠移到[索引鍵查閱]上,會顯示下列資訊,可以觀察到是為了要找 CustomerID 資料(綠色框框),所以利用[索引鍵查閱](紅色框框)進入PK_SalesOrderHeader_SalesOrderID (綠色框框)抓取資料
-- Step 3:刪除 IX_OrderDate 
DROP INDEX IX_OrderDate ON [Sales].[SalesOrderHeader]

-- Step 4:建立包含索引
CREATE INDEX IX_OrderDate 
ON [Sales].[SalesOrderHeader] (OrderDate)
INCLUDE (CustomerID) -- 包含 CustomerID 欄位

-- Step 5:再次執行查詢語法
SELECT 
    SalesOrderID , 
    OrderDate ,
    CustomerID
FROM [Sales].[SalesOrderHeader]
WHERE OrderDate = '20040729'
下圖為 Step 5 執行計畫,可以觀察到因為 CustomerID 已經包含在 IX_OrderDate 內,不再需要[索引鍵查閱]去搜尋資料


Key Lookup 只會利用 Nested Loop 來抓取資料,是一個極耗成本的動作,是進行 Perfomance Tuning 時的觀察重點,而除了建立包含索引來避免,撰寫 T-SQL 語法時,也要避免抓出不必要的資料,SELECT * 的寫法更是大忌喔
  • 2011 SQL Hero

星期五, 12月 06, 2013

[SQL] Partition Function

Partition Function 會指定如何分割資料表或索引並將資料對應到單一 Partition,建立 Partition Funcation,要決定
  1. DataType:資料型態必須和 Partition Column 的資料型態一致
  2. Boundary Value:資料界限值
  3. Range Left、Range Right:資料界限值是屬於 Boundary Value 的左邊還是右邊的 Partition

星期五, 11月 29, 2013

[SQL] Partition Table - Big Table

論壇網友問題
我都已經到3GB多了,那代表說,更應該馬上針對大的Table做分割,是吧?
即使所謂的大Table筆數還不到200萬筆?
Partition Table 是針對 Big Table 的進階技巧,而所謂的 Big Table 並不單指 Table Size、資料筆數這兩個較直覺的考量因素,還有其他因素應該考量進去,例如
  1. 該 Table 是效能瓶頸 => 分割成多個 Partition 來提升效能
  2. 索引維護困難 => Rebuild Index 耗費時間,用 Partition 可以針對單一 partition index 進行 rebuild
  3. 定期 Insert 資料或定期 Delete 舊資料會很耗時,且影響系統正常運作 => 用 Partition 減少 Lock 層級造成的影響
請把該 Table 對於整體效能的影響也列入考量,對於效能有所提昇的前提下,建立 Partitoon 才有意義,要不然不是只增加維護困難度而已。

星期五, 11月 22, 2013

[SQL] 利用執行計畫比較成本

Perfomance Turning 時,假如要比較 TSQL 語法修改後是否有差異,可以同時執行並觀察執行計畫中的[相對於批次 ? %]來了解成本差異,但[相對於批次 ? %]是根據[估計的子樹成本]來當成比較依據,並非實際執行計畫成本,這一點要特別注意

利用 AdventureWork SalesOrderHeader 來說明:
下面兩個 TSQL 語法都是要搜尋 SalesOrderID = 73460 這筆資料,但 TSQL2 強制指定 Query Optimizer 使用 IX_SalesOrderHeader_CustomerID Index 來搜尋資料
-- TSQL1
SELECT * 
FROM [Sales].[SalesOrderHeader] 
WHERE SalesOrderID = 73460

-- TSQL2
SELECT * 
FROM [Sales].[SalesOrderHeader]
WITH(INDEX(IX_SalesOrderHeader_CustomerID)) -- 強制指定索引
WHERE SalesOrderID = 73460
從執行計畫中的紅框框就可以比較孰優孰劣

[SQL] 利用執行計畫比較 T-SQL 語法效能-1

星期二, 11月 19, 2013

[SSRS] 子報表

利用採購單來練習子報表(內嵌報表)
  • DataSet T-SQL 語法
IF OBJECT_ID('Purch') IS NOT NULL
  DROP TABLE Purch

IF OBJECT_ID('PurchDetail') IS NOT NULL
  DROP TABLE PurchDetail

IF OBJECT_ID('Employ') IS NOT NULL
  DROP TABLE Employ

IF OBJECT_ID('Supplier') IS NOT NULL
  DROP TABLE Supplier

CREATE TABLE Purch(PurNO char(11),PurDate date,PurEmpNO char(5),SPLNO char(5))
CREATE TABLE PurchDetail(PurNO char(11),MtNum char(16),Spec nvarchar(100),PurQty int,Price money,Unit nchar(4),DealDate date)
CREATE TABLE Employ (EmpNO char(5),EmpName nchar(10))
CREATE TABLE Supplier (SPLNO char(5),SPLName nchar(10),Contact nchar(10),Tel nchar(10),Fax nchar(10))

INSERT INTO Employ VALUES
  ('001',N'王小明'),
  ('002',N'李大白')

INSERT INTO Supplier VALUES
  ('001',N'雜物供應商',N'王XX','00-1234567','00-1234444'),
  ('002',N'電熱供應商',N'張OO','00-3217654','00-3217777'),
  ('003',N'噴漆供應商',N'蔡YY','00-9999999','00-9999990'),
  ('004',N'馬達供應商',N'柯ZZ','00-6666666','00-6666666')

INSERT INTO Purch VALUES
  ('20131021001','20131021','001','002'),
  ('20131021002','20131021','002','004')
 
INSERT INTO PurchDetail VALUES
  ('20131021001','557D118L050',N'電熱_230V 1180x500W',60,231,N'支','20131028'),
  ('20131021001','557D146L065',N'電熱_230V 1460×650W',60,251,N'支','20131028'),
  ('20131021001','557D194L065',N'電熱_230V 1940x650W',36,278,N'支','20131028'),
  ('20131021002','554819',N'馬達_10"_200~240V 50/60Hz_雙轉向_UL認証',4000,310,N'顆','20140101'),
  ('20131021002','553201B',N'馬達_12"_4P_220V_雙轉向_無保護器',600,340,N'顆','20131128'),
  ('20131021002','552215B',N'馬達  12" 75/46W 4P_200-240V_左右轉_散熱',600,463,N'顆','20131128'),
  ('20131021002','5546257',N'馬達_1/4HP 3ψ  380~420V50/60Hz_三片式外殼',300,828,N'顆','20131101')

-- 子報表 DataSet(dsPurch)T-SQL 語法
SELECT 
  P.* , 
  E.EmpName , 
  S.SPLName , 
  S.Contact , 
  S.Tel , 
  S.Fax
FROM Purch AS P
  JOIN Employ AS E ON P.PurEmpNO = E.EmpNO
  JOIN Supplier AS S ON P.SPLNO = S.SPLNO

-- 子報表明細 DataSet(dsPurchDetail)T-SQL 語法
SELECT * FROM PurchDetail WHERE PurNO = @PurNO

建立新報表:子報表,並新增清單控件,並在清單內 => 滑鼠右鍵 => 矩形屬性


矩形屬性 => 一般 => 勾選[在後方加入分頁符號],利用清單是要把每一筆採購紀錄都放在一個頁面


在清單內建立一個矩形,利用矩形來群組檔頭資訊


在清單內建立一個子報表控件


建立新報表:子報表明細 DataSet T-SQL 語法


dsPurchDetail 的 T-SQL 語法有 WHERE PurNO = @PurNO,在該 dsPurchDetail 的參數可以看見@PurNO 參數


子報表明細排版,拉一個 Table 進來,排版設定後如下圖
  1. 新增資料列:用來放置全部物料總和
  2. 運算式:單一物料小計(採購數量 X 單價)
  3. 運算式:全部物料總和 (採購數量 X 單價的總和)

回到子報表,子報表控件 => 滑鼠右鍵 => 子報表屬性 => 一般 => 設定[將此報表當成子報表]


子報表屬性 => 參數,手動加入參數,名稱:PurNO、值:[PurNO],此參數是要傳入子報表明細中使用


新增採購單注意事項文字,整理排版並預覽設定結果

星期五, 11月 15, 2013

[SQL] View 和 WHERE 條件

論壇上問題,網友疑問
對於「檢視表」,我一直有種「用它是否有錯」的感覺,因為它的優點雖然是「方便我們在寫 SQL 查詢式時,不需要寫一大串 JOIN 的資料表,只要將它們組合成一個檢視表即可」!但問題是,將檢視表用在程式中的SQL查詢式時,例如:

SELECT xxx,ooo FROM View1 WHERE v_date BETWEEN '2013/6/1' AND '2013/6/2'

看起來,雖然有經過WHERE條件的篩選,但是,它是不是「其實是先經過了一個"完全沒有篩選"的"子查詢"」,然後取回了「全部的資料」再給「WHERE」來篩出範圍內的資料呢?

如果原理真的是這樣,那檢視表不是應該少用為妙嗎???......因為一用它就會產生效能問題?

星期三, 11月 13, 2013

[Security] Skype 病毒 - invoice_xxxxxx.pdf.exe

  • 20131105 
中午吃完飯,正想要休息時,收到同事 Skype 訊息,打開一看臉都綠了,一看就是中毒後自動傳送檔案,而且災情已經擴散出去啦,Orz
[Security] Skype 病毒 - invoice_xxxxxx.pdf.exe-1
幸好 Skype 官網已經有網友提供的官方回應,可以解決此問題,步驟如下:
親愛的PChome & Skype使用者,您好:
謝謝您的來信,我們已初步瞭解您的使用狀況,以下為Skype原廠所提供的相關資訊,提供您參考,希望對您有所幫助:

http://community.skype.com/t5/Windows-desktop-client/Invoice-xxxxxx-pdf-virus/m-p/2059261#M198187

操作方式請參考:
請先完全關閉Skype,掃毒並確認病毒全數清除完畢後,再重新依照以下步驟執行Skype。

1. 完全關閉Skype
a) 系統列Skype圖示按右鍵,選擇「結束」
b) ctrl-alt-del 啟動「工作管理員」>「處理程序」>「Skype.exe」>「結束處理程序」
2. 到「開始」>「搜尋」>「執行」,輸入 %appdata%,按「確定」送出
3. 找到「Skype」資料夾,將資料夾重新命名,可改名為「Skype_old」
4. 若您使用以下Skype版本:6.5 / 6.6 / 6.7 / 6.9 / 6.10,請到「開始」>「搜尋」>「執行」> 輸入 %temp%\skype,按「確定」送出
5. 移除 DbTemp 這個資料夾
6. 重啟 Skype

若需要重新建立對話記錄,請參考以下步驟:
1. 結束Skype應用程式
2. 回到先前重新命名的 Skype_old 資料夾
3. 點入有您的帳號名稱的該資料夾
4. 找到 main.db (請確認您可以看到副檔名),並將該檔案完全複製到新的Skype / 您的Skype帳號資料夾下
5. 重新啟動Skype

再次提醒您:請勿點擊不明連結,下載或安裝任何可疑的檔案,它將很有可能為病毒或木馬程式或隱藏性質的收集者,安裝於您的電腦中並進而竊取您的資料。
利用上述方法讓 Skype 恢復正常,只有一台電腦(OS 為 Win7)用此方法後,Chrone 竟然打不開,也無法重新安裝,最後是利用 Windows 還原,還原至前一天的 1900,來解決問題,Orz ~~
  • 20131108
PTT 網友發現 Win7 上的 Chrone 相容性層級被改為 XP SP2 改回來 Chrone 就正常啦
  • 20131113
確定 Skype 都恢復正常,結案 ~~

星期六, 11月 09, 2013

[SSRS] 複選參數

練習 SSRS 複選參數設定
  • DataSet T-SQL 語法
IF OBJECT_ID('Checked') IS NOT NULL
 DROP TABLE Checked
GO

CREATE TABLE Checked (DepID char(3) , DepName char(10) , EmpName char(10))
INSERT INTO Checked VALUES
    ('001','MIS','MIS01') ,
    ('001','MIS','MIS02') ,
    ('001','MIS','MIS03') ,
    ('002','HR','HR01') ,
    ('003','Sales','Sales01') ,
    ('003','Sales','Sales02') ,
    ('003','Sales','Sales03') ,
    ('003','Sales','Sales04') ,
    ('004','Design','Design01') ,
    ('004','Design','Design02') ,
    ('','','UNKOWN') -- 故意建立
在 SSRS 內要設計複選參數功能,有一先決條件是該參數在 T-SQL 語法中的 WHERE 必須使用 IN
WHERE Column IN (@Parameters)

星期五, 11月 08, 2013

[SQL] 大量匯入與 Trigger 觸發

有三種大量匯入資料方式,分別為
  1. BCP
  2. BULK INSERT
  3. INSERT ...SELECT * FROM OPENROWSET(BULK...)
這三種大量匯入進行時對於 Trigger 的影響有所差異,整理在下面表格

大量匯入預設行為限定詞限定詞類別
BCP停用-h " FIRE_TRIGGERS "提示
BULK INSERT停用FIRE_TRIGGERS引數
INSERT ...SELECT * FROM OPENROWSET(BULK...)觸發WITH(IGNORE_TRIGGERS)資料表提示

星期五, 11月 01, 2013

[SQL] 刪除重覆資料

刪除 Table 中重覆的資料並保留一筆資料。
  • Sample Data
DECLARE @Temp TABLE(Data char(1))
INSERT INTO @Temp VALUES
    ('A') ,
    ('B') ,
    ('C') ,
    ('A') , -- 重覆
    ('B') , -- 重覆
    ('D') ,
    ('E') ,
    ('F') ,
    ('G') ,
    ('F')   -- 重覆
  • 方法一:利用暫存資料表
SELECT DISTINCT Data INTO TempData
FROM @Temp
 
DELETE FROM @Temp -- 實體 Table 可以用 TRUNCATE
 
INSERT INTO @Temp
SELECT *
FROM TempData

DROP TABLE TempData
  • 方法二:跑迴圈來刪除
WHILE 1 = 1
BEGIN
  DELETE TOP (1) 
  FROM @Temp
  WHERE Data IN
      (
          SELECT Data
          FROM @Temp
          GROUP BY Data
          HAVING COUNT(*) > 1
      )
 
  IF @@ROWCOUNT = 0
      BREAK ;
END

星期二, 10月 29, 2013

[SSRS] 清單和矩形差異

一開始使用清單和矩形時,有點搞不清楚,此篇為研究後的筆記
  • 清單(List)
清單資料區域會隨報表資料集中的每一個群組或資料列重複。 清單可以用於建立自由形式的報表或表單 (如發票),也可以與其他資料區域一起使用。 您可以定義包含任何數目之報表項目的清單。 清單可以巢狀放在另一份清單內,以提供多個資料群組。
  • 矩形(Rectangle)
使用矩形做為其他控件的容器。當移動矩形時,包含在矩形中的項目會跟著它移動。矩形內部的項目會以其 Parent 屬性顯示矩形的名稱。
  • 文字說明兩者差異
清單會逐筆顯示所連接資料集和群組後的詳細資料,而矩形只是一個形狀、容器,用來群組控件用。
  • 設計界面差異
一開始打開書本範例,直覺是長的都一模一樣是要怎麼分辨哪個是清單,那個是矩形
[SSRS] 清單和矩形差異 -1
想說屬性視窗總會顯示是那個控件,沒想到清單和矩形在屬性視窗內都是 Rectangle 矩形

[SSRS] 清單和矩形差異 -2

後來發現,點擊控件就可以看出兩者差異,清單會出現資料列和資料行控點,矩形則是會出現被點選的外框線和十字移動符號。
[SSRS] 清單和矩形差異 -3

星期五, 10月 25, 2013

[SQL] 工作天判斷

利用 CTE 產生完整日期資料表,搭配一個例外 Table 來輔助判斷,例外 Table 的設立主因是無法預測人事行政局到底是如何規劃假期、防災假等原因。
  • 利用 201302 來說明,23 號上班日是補 15 號彈休。
[SQL] 工作天判斷-1

星期四, 10月 24, 2013

[VFP] 更換 VFP IDE 介面字型

字型大小對於閱讀 code 有很大的幫助,記錄一下如何更改 VFP IDE 字型和字型大小
  • Task Panel Manager => Tools => Options
[]VFP] 更換 VFP IDE 介面字型大小-1
  • IDE Tag 內進行設定
[]VFP] 更換 VFP IDE 介面字型大小-2
  1. Type:可以選擇針對哪個 IDE 介面來進行設定修改
  2. Font:選擇字型和字型大小
  3. Override individual settings:勾選此設定,才可以覆蓋各 Form 現有的設定
  4. Apply button:套用上述設定
  5. Set As Default button:把上述設定改為預設值,之後再新增 Form、Program 等時,就會套用此設定。

星期三, 10月 23, 2013

Office 2013 Picture Manager

品管小姐來詢問為甚麼 Office 2013 內沒有 Picture Manager,是不是沒有安裝上去,依自身安裝習慣,只要是 Office 軟體,一定是全部安裝,後來發現 Picture Manager 從 Office 2013 開始被拿掉了,Orz ~~
  • 官方說明
[Office] Office 2013 Picture Manager

星期一, 10月 21, 2013

[SSRS] 隔行換色

閱讀 MSDN SSRS RowNumber() 函數 時發現文章內的範例,動手實作並記錄

語法:RowNumber(scope)
參數:指定評估資料列數的範圍,可能為資料集、資料區域、群組名稱或 Nothing
  • DataSet T-SQL 語法
SELECT FirstName , LastName , EmailAddress , Phone 
FROM [Person].[Contact]

  • 設定介面
先利用精靈建立 Table 表格,並在前方建立一個由 RowNumber() 函數建立的序號欄位,方便驗證設定效果
  • 設定 BackgroundColor 運算式
Tablix 資料區域詳細資料列 => 屬性 => BackgroundColor => 運算式

設定運算式
= IIF(RowNumber(Nothing) MOD 2 = 0 , "LightGrey" , "White")
  • 預覽結果
  • 2013 DBA 天團