2018/09/12

C#計算程式執行時間

在寫程式的時候我們常常需要測量程式執行時間 .NET本身有提供所謂的效能計數器 精度非常高 但是使用上比較繁瑣 如果我們需要簡單的測量時間就好 對幾一千分之一秒的時間誤差沒有很刻求 那以下有兩種常用的方法可以簡單的計算目標程式所花費的執行時間
//方法一
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();//引用stopwatch物件
sw.Reset();//碼表歸零
sw.Start();//碼表開始計時
/**************/
/**************/
/***目標程式***/
/**************/
/**************/
sw.Stop();//碼錶停止
//印出所花費的總豪秒數
string result1 = sw.Elapsed.TotalMilliseconds.ToString();



//方法二
DateTime time_start = DateTime.Now;//計時開始 取得目前時間
/***            ***/
/***            ***/
/***目標程式***/
/***            ***/
/***            ***/
DateTime time_end = DateTime.Now;//計時結束 取得目前時間
//後面的時間減前面的時間後 轉型成TimeSpan即可印出時間差
string result2 = ((TimeSpan)(time_end - time_start)).TotalMilliseconds.ToString();


兩種方法最後都會得到一個timespan類別的變數 代表時間的差距 顯示的時候要使用.TotalMilliseconds才是"總毫秒" 如果是使用Milliseconds 則只有"毫秒"的部份

2018/09/06

[SQL]將多筆資料中某一欄的資料轉作一列顯示

「Combine Multiple Rows into One Row using SQL Server」,< br>提供2種做法,覺得第2種很牛,所以註記一下,也跟大家分享。
--建立table變數
DECLARE @T1 AS TABLE (
busid NVARCHAR(20)
, drivername NVARCHAR(20)
, insurlistnum NVARCHAR(10)
, insurchildname NVARCHAR(10)
);
--新增資料
INSERT INTO @T1 ( busid , drivername , insurlistnum , insurchildname )
VALUES
( N'吉A30241' , N'高 明' , N'PICC12345' , N'死亡伤残赔偿' )
, ( N'吉A30241' , N'高 明' , N'PICC12345' , N'医疗费用赔偿' )
,( N'吉A30241' , N'高 明' , N'PICC12345' , N'财产损失赔偿' );
--撈出資料
SELECT * FROM @T1;

做法1,用變數及COALESCE來串接

DECLARE @str NVARCHAR(MAX)
SELECT @str = COALESCE(@str + ', ', '') + [insurchildname] FROM @T1;



SELECT DISTINCT busid , drivername , insurlistnum , @str AS [险种] FROM @T1;




做法2,透過FOR XML及STUFF

FOR XML那行再加上WHERE條件

SELECT DISTINCT busid , drivername , insurlistnum , STUFF( (SELECT ', ' + insurchildname from @T1 FOR XML PATH('')), 1, 1, '') AS [险种] FROM @T1;



SQL Server 中的 CTE 遞迴查詢的上限

SQL Server 中的 CTE 遞迴查詢的上限
T-SQL 的遞迴深度,
Stored Procedure 的最大上限是 32 次
CTE 預設為 100 次,CTE 遞迴的最大上限 32,767 次
Sample:
declare @i_id nvarchar(10);
set @id='0000000001';
with test_sub(id,i_pid,i_level)as (
select id, i_pid, 0 from [info]
where id=@id
union all

-- 遞迴成員
select a.id, a.i_pid, b.i_level+1
from [info] as a inner join test_sub b on a.i_pid=b.id
)
select * from test_sub
OPTION (MAXRECURSION 150);
go

2018/09/05

Stored Procedure 加密

Stored Procedure 加密(with encryption)
with encryption




在某些狀況,我們會希望系統的資料加密,防止其它不明用意的人士偷偷進行資料變更,造成系統錯誤。那我們如何將目前資料庫的資料,進行加密同時系統不需要大幅度進行修改呢?
基本上有四個步驟:
  • 建立資料庫非對稱式憑證 (記得設定密碼)
  • 建立儲存加密資料的資料表
  • 建立維護資料用的 Stored Procedure
  • 建立檢視資料用的 View




1. 建立資料庫非對稱式憑證
CREATE ASYMMETRIC KEY 非對稱式金鑰
WITH ALGORITHM = RSA_2048
ENCRYPTION BY PASSWORD = '1234567890';
GO

你會在Microsoft SQL Server Management Studio 的Object Explorer裏看到下列結果:




2.建立儲存加密資料的資料表

將產生的CREATE TABLE Script中的Table Name 後面加上「_E」以區別這個Table是有應用加密技術。另外要儲存加密資料的欄位,無論是NVARCHAR, INT…,一律都改成NVARCHAR(255),如下圖所示



3.建立維護資料用的 Stored Procedure
由於維護資料時,皆必須額外考慮加密的問題,建議直接使用Stored Procedure處理會比較直覺,所有的人都只呼叫Stored Procedure,把明碼(畫面上的值直接傳入即可),不需要再針對每一支需要變更此Table的程式都進行相關修改。Stored Procedure的寫法可以參考以下的範例,另外有一件事要特別注意:由於加密的欄位都修改成NVARCHAR(255),為了保持程式的一致性,Stored Procedure傳入參數的部份,請依照原本的Table設計,該是NVARCHAR(10),該是INT,該是SMALLINT,一律維持原樣:

   1: SET ANSI_NULLS ON
   2: GO
   3: SET QUOTED_IDENTIFIER ON
   4: GO
   5:  
   6:  
   7: CREATE PROCEDURE sw_ModifyProductModule 
   8:     @action_type    NCHAR(1) = '' ,
   9:     @productId        nvarchar(10) = '', 
  10:     @moduleId        NVARCHAR(10) = '',
  11:     @moduleCName    NVARCHAR(100) = '',
  12:     @moduleEName    NVARCHAR(10) = '',
  13:     @is_active        SMALLINT = 0
  14: AS
  15: BEGIN
  16:     SET NOCOUNT ON;
  17:  
  18:     --傳入資料檢核
  19:     IF (@action_type NOT IN ('A','D','U'))
  20:     BEGIN
  21:         DECLARE @DBID INT;
  22:         SET @DBID = DB_ID();
  23:  
  24:         DECLARE @DBNAME NVARCHAR(128);
  25:         SET @DBNAME = DB_NAME();
  26:  
  27:         RAISERROR
  28:             (N'資料庫 ID:%d, 資料庫名稱: %s。 錯誤訊息:傳入的Action_Mode,必須是 A, U, D 其中一種',
  29:             10, -- Severity.
  30:             1, -- State.
  31:             @DBID, -- First substitution argument.
  32:             @DBNAME); -- Second substitution argument.    
  33:     END
  34:  
  35:     --宣告加密用變數
  36:     DECLARE 
  37:         @encryp_productId        NVARCHAR(255),
  38:         @encryp_moduleId        NVARCHAR(255),
  39:         @encryp_moduleCName        NVARCHAR(255),
  40:         @encryp_moduleEName        NVARCHAR(255),
  41:         @encryp_is_active        NVARCHAR(255)
  42:  
  43:     SET @encryp_productId = ENCRYPTBYASYMKEY(ASYMKEY_ID('SwEncryKey100'),@productId)
  44:     SET @encryp_moduleId = ENCRYPTBYASYMKEY(ASYMKEY_ID('SwEncryKey100'),@moduleId)
  45:     SET @encryp_moduleCName = ENCRYPTBYASYMKEY(ASYMKEY_ID('SwEncryKey100'),@moduleCName)
  46:     SET @encryp_moduleEName = ENCRYPTBYASYMKEY(ASYMKEY_ID('SwEncryKey100'),@moduleEName)
  47:     SET @encryp_is_active = ENCRYPTBYASYMKEY(ASYMKEY_ID('SwEncryKey100'),CONVERT(NVARCHAR(1),@is_active))
  48:  
  49:     IF (@action_type = 'A')
  50:     BEGIN
  51:         INSERT INTO Production.SW_PRODUCT_MODULE_E
  52:         ( PRODUCT_ID ,
  53:           MODULE_ID ,
  54:           MODULE_CNAME ,
  55:           MODULE_ENAME ,
  56:           IS_ACTIVE 
  57:         )
  58:         VALUES  (             
  59:         @productId,
  60:         @moduleId,
  61:         @encryp_moduleCName,
  62:         @encryp_moduleEName,
  63:         @encryp_is_active
  64:         )
  65:          
  66:     END
  67:     
  68:     IF (@action_type = 'U')
  69:     BEGIN
  70:         UPDATE Production.SW_PRODUCT_MODULE_E
  71:         SET MODULE_CNAME = @encryp_moduleCName,
  72:             MODULE_ENAME = @encryp_moduleEName,
  73:             IS_ACTIVE = @encryp_is_active
  74:         WHERE PRODUCT_ID = @productId
  75:         AND MODULE_ID = @moduleId
  76:     END
  77:     
  78:     IF (@action_type = 'D')
  79:     BEGIN
  80:         DELETE Production.SW_PRODUCT_MODULE_E
  81:         WHERE PRODUCT_ID = @productId
  82:         AND MODULE_ID = @moduleId
  83:     END
  84:  
  85: END
  86: GO



接下來新增一筆資料
   1: EXEC dbo.sw_ModifyProductModule 
   2:     @action_type = 'A', -- nchar(1)
   3:     @productId = 'PWP', -- nvarchar(10)
   4:     @moduleId = 'Security', -- nvarchar(10)
   5:     @moduleCName = '權限控管模組', -- nvarchar(100)
   6:     @moduleEName = 'Security Module', -- nvarchar(10)
   7:     @is_active = 1 -- smallint  
   8:   


結果資料被加密




4.建立檢視用的View
為了讀取加密資料,同時又不想修改所有相關的SQL Statement,加上解密相關的語法,讓程式變得更複雜及更難維護,所以會採取建立View的方式解決這個問題。
所以建立一個View
   1: USE [PWP_NEW]
   2: GO
   3:  
   4: /****** Object:  View [dbo].[vwSW_PRODUCT_MODULE]    Script Date: 01/06/2011 13:58:55 ******/
   5: SET ANSI_NULLS ON
   6: GO
   7:  
   8: SET QUOTED_IDENTIFIER ON
   9: GO
  10:  
  11:  
  12:  
  13: CREATE VIEW [Production].[vwSW_PRODUCT_MODULE]
  14: AS
  15:     SELECT     
  16:             DATAID, 
  17:             PRODUCT_ID, 
  18:             MODULE_ID,
  19:             CONVERT(NVARCHAR(10),DECRYPTBYASYMKEY(ASYMKEY_ID('SwEncryKey100'),MODULE_CNAME, N'KevinTsui')) AS MODULE_CNAME,
  20:             CONVERT(NVARCHAR(50),DECRYPTBYASYMKEY(ASYMKEY_ID('SwEncryKey100'), MODULE_ENAME, N'KevinTsui')) AS MODULE_ENAME,
  21:             CONVERT(SMALLINT,CONVERT(NVARCHAR(1), DECRYPTBYASYMKEY(ASYMKEY_ID('SwEncryKey100'), IS_ACTIVE, N'KevinTsui'))) AS IS_ACTIVE,
  22:             DATAFLAG
  23:     FROM Production.SW_PRODUCT_MODULE_E
  24:  
  25: GO


執行結果:
'

Stored Procedure 解密

Stored Procedure 解密


使用WITH ENCRYPTION進行加密,

後來參考了RiCo大的文章,
安裝SQL Decryptor直接登入後,即可察看加密過的內容

https://www.devart.com/dbforge/sql/sqldecryptor/download.html
軟體備份

2017/11/29

Oracle 12c/11g Client 軟體下載 & Tomcat 環境

Database 
Oracle 12c R2 Client Download for Linux 
http://www.oracle.com/technetwork/database/enterprise-edition/downloads/oracle12c-linux-12201-3608234.html


Oracle 11.2.0.4 Client Download Patchset 13390677 (p13390677_112040_platform_4of7.zip)
https://updates.oracle.com/download/13390677.html (需要有Oracle Support帳號)
 Oracle Database Client p13390677_112040_platform_4of7.zip



Oracle Client 安裝方式
http://sajidkhadarabad.blogspot.tw/2015/09/oracle-12c-linux-client-64-bit-step-by.html

*以Oracle所建議的support matrix, 如果要能夠連接12c R2 (12.2) Database, Oracle Client最低版本要求為 11.2.0.3 or 11.2.0.4
ref: Oracle Client 最小要求版本為11.2.0.3 or 11.2.0.4 (11g R2 Client)
Client / Server Interoperability Support Matrix for Different Oracle Versions (Doc ID 207303.1)

Tomcat 8 (jdk 1.8) 下載與安裝方式
https://oracle-base.com/articles/linux/apache-tomcat-8-installation-on-linux

Oracle JDBC Driver 連接建議
若從Tomcat 6 / 7 / 8開發程式需要連接新的oracle 資料庫 例如11g/ 12c Oracle DB
只要確認您的openJDK為相容的最低要求版本、並搭配ojdbc6.jar 就可以連接到11g/ 12c Oracle DB






Oracle JDBC Driver 連接注意事項
1.原環境把ojdbc14.jar 替換為ojdbc6.jar 就可以連接
2.在DB 端需要修改$ORACLE_HOME/network/admin/sqlnet.ora 設定 SQLNET.ALLOWED_LOGON_VERSION_SERVER=8
  ref: Oracle 9i/10g Client 透過odbc連12c DB 無法連線 ORA-28040 no matching authentication protocol
       http://jaychu649.blogspot.tw/2017/07/oracle-9i10g-client-odbc12c-db-ora.html
  ref: Copy ojdbc6.jar and ucp.jar into $TOMCAT_HOME/lib directory.
       http://www.oracle.com/technetwork/articles/oem/ucp-jdbc-tomcat-355431.html
  ref: Tomcat 6/7/8 所限制的mininal java 開發環境 (tomcat 7 可支援 openjdk 1.7 、openjdk 1.6、tomcat 8 可支援 openjdk 1.7)
       http://tomcat.apache.org/whichversion.html

Oracle JDBC Driver 8下載連結 (ojdbc8.jar 下載後放置到tomcat資料夾的lib資料夾)
http://www.oracle.com/technetwork/database/features/jdbc/jdbc-ucp-122-3110062.html

Oracle JDBC Driver 7下載連結 (ojdbc7.jar 下載後放置到tomcat資料夾的lib資料夾)
http://www.oracle.com/technetwork/database/features/jdbc/jdbc-drivers-12c-download-1958347.html

Oracle JDBC Driver 6下載連結 (ojdbc6.jar 下載後放置到tomcat資料夾的lib資料夾)
http://www.oracle.com/technetwork/apps-tech/jdbc-112010-090769.html

Instant Client Package - Precompiler (v12.2.0.1.0) download
http://www.oracle.com/technetwork/topics/precomp-112010-084940.html

Oracle® Database Client Installation Guide 12c Release 2 (12.2) for Linux
https://docs.oracle.com/database/122/LACLI/LACLI.pdf

Database Database Client Installation Guide for IBM AIX on POWER Systems (64-Bit) 
https://docs.oracle.com/database/122/AXCLI/AXCLI.pdf

PS. Oracle PRO*C precomplier 通常是跟Oracle Client一起進行安裝, PROC 設定可以參考此連結 :
 安裝Oracle 12c Client 與Pro*C Precompilers 的方法
 http://jaychu649.blogspot.tw/2017/07/oracle-12c-client-proc-precompilers.html

關於開發者可能會需要在Solaris / Oracle Linux 安裝所需的Oracle JAVA JDK套件
Oracle Developer Studio and Oracle Solaris Studio Component Matrix 
http://www.oracle.com/technetwork/server-storage/solarisstudio/training/index-jsp-141991.html

Download Oracle Developer Studio
https://www.oracle.com/tools/developerstudio/index.html

關於Oracle 12.2 的線上文件總連結
https://docs.oracle.com/database/122/





參考:https://jaychu649.blogspot.tw/2017/07/oracle-12c11g-client-tomcat.html

2011/08/04

[Java] 小心 substring 的 memory leak

簡單的來說,假設我們有一段程式碼:
public class SubstringMemoryLeak {
private String str = new String(new byte[10000]);
public String substring() {
return this.str.substring(0, 2);
}
/**
*@param args
*/
public static void main(String[] args) {
List<String> substringList = new ArrayList<String>(10000);
for (int i=0, n=10000; i<n; i++) {
substringList.add(new SubstringMemoryLeak().substring());
}
}
}

如果執行上面的程式碼,你就會出現 Exception:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at
但是如果將 substring() method 修改成:

public String substring() {
   return new String(this.str.substring(0, 2)); 
}


神奇的事情發生了!程式竟然可以如願的執行!問題出現在哪?上面的程式碼最主要是建立一堆很佔記憶體的 String 物件,然後取其中的一小段!重點就在於 String 的 substring() method 的實做方式。
首先,String 物件在記憶體中會以 char array 方式呈現,當我們每次建立一個 String 時,記憶體就會長出一塊 char array 來存放。String class 有一個 non-public constructor:

String (int offset, int count, char[] value)

當我們使用 substring 時,實際是用這個 constructor 來完成,也就是原先建立很佔記憶體的 String 物件並不會縮小,而是保持原來的 char array 所佔的大小。所以 Java 的 GC 就無法對此 char array 進行回收的動作。所以使用 substring() method 對字串內容來說,我們看到的是部份的,但是在記憶體中卻是佔有原先的大小!

後來,我們改用 new String() constructor 來包裝 substring 的內容,這就會讓 Java 重新建一個 char array 來放置 substring 的內容,相對的,所佔得記憶體就小了,而且原先較大的 char array 就沒有任何 reference ,所以 GC 就可以直接回收了!

老實說,這種情況可能不太常見,至少對我來說啦!我對於 String 的建立都會盡量改用 StringBuffer 物件,這樣不僅可以提供較好得效能,對於記憶體的利用也比較不會有問題!