반응형
반응형

김종열


Query는 집합적으로 처리하지만 순차적으로 처리하고 있는 구조입니다. 예를 들어 세개의 테이블에 데이터를 넣는다고 가정하면 (물론 순서는 필요없고아마도 쿼리는 대략 이러할 것입니다.

INSERT INTO t1 (c1, c2) VALUES (v1, v2)

INSERT INTO t2 (c1, c2) VALUES (v1, v2)

INSERT INTO t3 (c1, c2) VALUES (v1, v2)

그런데 위와 같은 쿼리를 동시에 처리하는 구조를 가지면 어떨까요?

이런 취지로 Procedure를 하나 개발해봤습니다. Procedures는 구분자(세미콜론을 사용했다.)를 기준으로 명령을 배열화 시키고 thread로 처리하는 형식입니다보통 쿼리문은 실행했을 때 리턴이 없는 형식이 있는가 하면 리턴 형식이 존재하는 것이 있는데, 이 구조는 리턴을 무시하는 구조입니다.
또한
병렬 구문중 하나가 에러가 나는 구조라면 그 구문만 에러가 나며 다른 건 실행이 됩니다.


다음은 C# 소스입니다.

using System;

using System.Data;

using System.Data.SqlClient;

using System.Data.SqlTypes;

using Microsoft.SqlServer.Server;

using System.Threading;

 

 

public partial class StoredProcedures

{

    public class thWorker

    {

        private string wName;

        public SqlPipe p = SqlContext.Pipe;

        public thWorker(string name)

        {

 

            wName = name;

            p.Send("Start : " + wName);

 

            SqlConnection c = new SqlConnection("context connection=true");

            c.Open();

            SqlCommand db_cmd;

 

            try

            {

                db_cmd = new SqlCommand(wName, c);

                db_cmd.ExecuteNonQuery();

            }

            catch (Exception e)

            {

                p.Send(e.ToString());

            }

            finally

            {

                c.Close();

                p.Send("Finish : " + wName);

            }

 

 

        }

 

        public void mDmy()

        {

            SqlPipe p = SqlContext.Pipe;

            p.Send(wName);

        }

    }

   

    [Microsoft.SqlServer.Server.SqlProcedure]

    public static int USP_Thread_Proc(string cmd)

    {

        Thread t = null;

        thWorker wth = null;

        string[] strCmd = cmd.Split(';');

        int cmdCount = strCmd.Length;

        Object[] obj = new object[cmdCount];

       

 

        for (int i = 0; i < cmdCount; i++)

        {

 

            wth = new thWorker(strCmd[i]);

 

            t = new Thread(new ThreadStart(wth.mDmy));

            t.Name = "Thread -" + i.ToString() + ":";

            t.Start();

            obj[i] = t;

        }

        for (int i = 0; i < cmdCount; i++)

        {

            t = (System.Threading.Thread)obj[i];

            t.Join();         

        }

        return 0;

    }

 

 

};

위의 코드를 한번 테스트는 아래의 쿼리로 가능합니다.

IF OBJECT_ID('t1') IS NOT NULL

        DROP TABLE t1

 

CREATE TABLE t1 (a INT, b DATETIME)

GO

 

IF OBJECT_ID('t2') IS NOT NULL

        DROP TABLE t2

IF OBJECT_ID('t3') IS NOT NULL

        DROP TABLE T3

       

SELECT * INTO t2 FROM t1

SELECT * INTO t3 FROM t1

GO

 

 

SELECT * FROM t1

SELECT * FROM t2

SELECT * FROM t3

 

 

--이게 우리가 주목해야 부분

EXEC usp_thread_proc 'truncate table t1;truncate table t2;truncate table t3'

EXEC usp_thread_proc 'insert into t1 values(1, getdate());insert into t2 values(1, getdate());insert into t3 values(1, getdate())'

 

 

--시간의차이를확인해주자..

SELECT * FROM t1

SELECT * FROM t2
SELECT * FROM t3

수고하십시오..


반응형

'연구개발 > CLR' 카테고리의 다른 글

CLR - Windows Event Log  (0) 2011.08.27
CLR - 누적합 구하기  (0) 2011.08.27
CLR-이전행가기 udf_lag  (0) 2011.08.27
WMI 객체를 쿼리로 읽어오기  (0) 2010.08.10
파일리스트 불러오기  (0) 2009.06.22
반응형


작업을 처리하고 그 기록을 남기는 방법들은 어느 플랫폼, 어느 데이터베이스, 어느 os든간에 관리자라면 필요한 기능일 것이다. 물론 이러한 Log를 데이터베이스에 바로 쌓아도 되겠지만..

큰 프로시져에서 sub procedure를 호출하는 경우라고 가정해보자. Sub procedure에서 실행하며 그 작업들을 데이터베이스에 바로 쌓으려고 하고 있는데 이 sub procedure를 호출한 큰 프로시져에서 transaction 처리로 rollback을 하고 있다면.. 프로시져의 에러를 어떻게 Tracking 해야 할까?

어떤 곳에서는 파일로 처리하는 경우가 있으며, 이 데이터를 데이터베이스에 올려서 처리하기도 할 것이다.

 

그런데 이번에 하고자 하는 것은 이런 과정이 아니라 윈도우 이벤트에 로그를 남기는 작업을 해볼까 한다.

 

먼저 mssql에서 이미 제공하고 있는 procedure, 함수를 이용해서 로그를 남기려면 아래를 사용하면 된다.


 

DECLARE @desc VARCHAR(100)

SET @desc = 'sp...'

RAISERROR (@desc  ,0, 0) with LOG

 

EXEC xp_logevent 50001, @desc, informational

 

***참고 powershell 새항목 30 항목에 대해 export하기

Get-eventLog -logname "application" -newest 30 | where-object {$_.Source -like "*MSSQLSERVER*"} | Export-csv c:\DBScript\sqlevent.csv



위의 명령으로 실행하면 응용 프로그램이라는 카테고리에 Log가 쌓이게 된다.

 

그런데 이 로그를 다시 읽으려면 어떻게 해야 하는가? 또는 event log를 지우려면 어떻게 해야 할까? 새로운 이벤트 카테고리(나만의)를 생성해서 다른 것과 섞이지 않게 하려면 어떻게 해야할까? 역시 이런 의문으로 함수를 하나 만들어 작업을 했다.


그런데 이 로그를 다시 읽으려면 어떻게 해야 하는가? 또는 event log를 지우려면 어떻게 해야 할까? 새로운 이벤트 카테고리(나만의)를 생성해서 다른 것과 섞이지 않게 하려면 어떻게 해야할까? 역시 이런 의문으로 함수를 하나 만들어 작업을 했다.

 

솔루션을 하나 만들고 디비를 연결한 다음 아래와 같은 코드를 저장하십시오..

아래의 솔루션에는 event view관련 네 개의 함수로 구성되어 있습니다.

Write , Read, Backup, Clear이 넷으로 구성했다. 그리고 Read를 제외한 함수들은 작업의 성공여부 1, 0으로 리턴한다. Read는 지금 탭으로 구분한 데이터에 대해 테이블로 리턴하게 했다.


using System;

using System.Data;

using System.Data.SqlClient;

using System.Data.SqlTypes;

using Microsoft.SqlServer.Server;

using System.Diagnostics;

using System.IO;

using System.Collections;

 

 

public partial class UserDefinedFunctions

{

  

    [Microsoft.SqlServer.Server.SqlFunction(FillRowMethodName = "FillLog", TableDefinition = "Num nvarchar(20), T1 nVarchar(1000), T2 nVarchar(1000)")]

 

    public static IEnumerable udf_read_log(string cs)

    {

        string[] sItems;

        string sData = "";

        EventLog eLog = new EventLog(cs);

 

        foreach (EventLogEntry entry in eLog.Entries)

        {

             sData += entry.Message + "#";

        }

 

        try

        {

            sItems = sData.Split('#');

            return (sItems);

        }

        catch

        {

            return null;

        }

 

       

    }

 

    private static void FillLog(Object obj, out SqlString Num, out SqlString T1, out SqlString T2)

    {

        string sTemp = Convert.ToString(obj);

        string[] arr_temp = sTemp.Split('\t');

        try

        {

            Num = (SqlString)arr_temp[0];

            T1 = (SqlString)arr_temp[1];

            T2 = (SqlString)arr_temp[2];

        }

        catch

        {

            Num = null;

            T1 = null;

            T2 = null;

        }

    }

 

 

    [Microsoft.SqlServer.Server.SqlFunction]

    public static SqlInt16 udf_write_log(string message, string cs)

    {

        try

        {

            if (!EventLog.Exists(cs))

            {

                EventLog.CreateEventSource("MSSQL_Trace", cs);

            }

 

            EventLog.WriteEntry(cs, message);

            return 1;

 

        }

        catch (Exception e)

        {

            return 0;

 

        }

 

    }

 

 

    [Microsoft.SqlServer.Server.SqlFunction]

    public static SqlInt16 udf_clear_log(string cs)

    {

        try

        {

            EventLog elog = new EventLog(cs);

            elog.Clear();

            return 1;

        }

        catch

        {

            return 0;

        }

 

    }

 

 

    [Microsoft.SqlServer.Server.SqlFunction]

    public static SqlInt16 udf_backup_log(string pString, string cs)

    {

        try

        {

            EventLog eLog = new EventLog(cs);

            StreamWriter sw = new StreamWriter(pString, true);

            foreach (EventLogEntry entry in eLog.Entries)

            {

                sw.WriteLine(entry.Message);

            }

            sw.Close();

            return 1;

        }

        catch

        {

            return 0;

        }

 

    }

};





컴파일하고 소스를 배포하면 해당 디비에서 아래와 같은 코드를 실행하면 된다.

-write

select common.dbo.udf_write_log('123 234  procedurename_', 'mssql_trace');

--success 1 ,  error 0

 

--backup

select common.dbo.udf_backup_log('d:\mssql_trace.bak', 'mssql_trace');

 

--clear_log

select common.dbo.udf_clear_log('mssql_trace');

 

--read

select * from common.dbo.udf_read_log('mssql_trace')




실행하고 난 다음의 windows log모습이다. query의 결과보다 이게 더 궁금할 듯하여 캡쳐 ^^



첨부 : udf_read_log.cs

반응형

'연구개발 > CLR' 카테고리의 다른 글

CLR - 병렬로 쿼리를 처리하기  (0) 2011.08.27
CLR - 누적합 구하기  (0) 2011.08.27
CLR-이전행가기 udf_lag  (0) 2011.08.27
WMI 객체를 쿼리로 읽어오기  (0) 2010.08.10
파일리스트 불러오기  (0) 2009.06.22
반응형


김종열

MS SQL Server MVP



 

udf_lag를 나름 만들고 나니 욕심이 나는 쿼리가 하나 있네요. 
누적합을 구하는 건데요.. 아주 lag와 비슷합니다.  코드는 참고하세요.. 

 


using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Collections.Generic;


public partial class UserDefinedFunctions
{
    public readonly static Dictionary<string, List<object>> _dicMemory = new Dictionary<string, List<object>>();

    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlDouble Udf_Agg_Sum(object objValue, int key, string guid)
    {
        Double retValue = 0;

        if (_dicMemory.ContainsKey(guid) == false)
        {
            _dicMemory.Add(guid, new List<object>());
        }

        List<object> tempList = _dicMemory[guid];
        tempList.Add(objValue);


        int valueCount = tempList.Count;

        for (int i = 0; i < tempList.Count; i++)
        {
            retValue = retValue + Double.Parse(Convert.ToString(tempList[i]));
          
        }
       
        if (key == -1)
        {
            _dicMemory.Remove(guid);
        }

        return retValue;
       
    }
};

 

 


여튼 실행의 예시는 다음과 같습니다.


if OBJECT_ID('tempdb..#t') is not null
 drop table #t
go

create table #t ( a int)
go

insert into #t 
 select number from master.dbo.spt_values
 where TYPE= 'p'
go

declare @guid varchar(50) = newid()
 , @endbit int = 0 
 

select
*
, dbo.Udf_Agg_Sum(a,CASE ROW_NUMBER() OVER (ORDER BY a)WHEN 1 THEN 1
                                              WHEN @endbit THEN -1
                                              ELSE 0 END , @guid)
from
 #t



첨부 : Udf_Agg_Sum.cs
        udf_Agg_Sum.sql

반응형

'연구개발 > CLR' 카테고리의 다른 글

CLR - 병렬로 쿼리를 처리하기  (0) 2011.08.27
CLR - Windows Event Log  (0) 2011.08.27
CLR-이전행가기 udf_lag  (0) 2011.08.27
WMI 객체를 쿼리로 읽어오기  (0) 2010.08.10
파일리스트 불러오기  (0) 2009.06.22
반응형


김종열

MS SQL Server MVP



 

정말 오랜만에 글을 올리네요. ^^

쿼리를 다루시다가 보면 이전행의 데이터를 가져오는 부분을 짜는 일이 있을 겁니다.
그 하나의 데이터를 가져오기 위해 다시 join을 사용하면서 이를 지원해주는 함수가 없을까 생각해봤을 겁니다. oracle에는 lag(), lead()라는 window Function을 제공해줍니다.

역시 이런 함수를 mssql에서 다룰 수 있도록 해달라는군요..
이런 메시지를 받으면 고민하고 시도를 해봐야 겠다는 맘이 생기지요..


using
System;

using System.Data;

using System.Data.SqlClient;

using System.Data.SqlTypes;

using Microsoft.SqlServer.Server;

using System.Collections.Generic;

 

public partial class UserDefinedFunctions

{

    public readonly static Dictionary<string, List<object>> _dicMemory = new Dictionary<string, List<object>>();

   

    [Microsoft.SqlServer.Server.SqlFunction]

    public static SqlString Udf_Lag(object objValue, int rowNo, int key, string guid)

    {

        //Return 변수 선언

        string retValue = string.Empty;

 

        //Guid 존재 하지 않을 경우 새로운 시작

        if (_dicMemory.ContainsKey(guid) == false)

        {

            //Dictionary guid 키로 하여 새로운 List 생성 한다.

            _dicMemory.Add(guid, new List<object>());

        }

 

        //Guid 따른 저장 List 값을 가져 온다.

        List<object> tempList = _dicMemory[guid];

 

        //현재 저장된 리스트의 개수

        int valueCount = tempList.Count;

 

        if (valueCount - rowNo -1  < 0)

        {

            //부모 값이 없는 경우

            retValue = string.Empty;

        }

        else

        {

            //부모의 값이 있는 경우 반환 값을 넘긴다.

            retValue = Convert.ToString(tempList[valueCount - rowNo - 1]);

        }

 

        tempList.Add(objValue);

 

        //key -> 1 시작

        //key -> 0 진행중

        //key -> -1 종료

        if (key == -1)

        {

            //종료일 경우 메모리에서 값을 삭제 한다.

            _dicMemory.Remove(guid);

        }

 

        return retValue;

    }

 

};

 


만들며 데이터가 들어온 순서를 메모리에 담기때문에 동시에 두곳에서 실행을 하면 메모리에서
중복이 일어나 제대로 된 결과를 얻을 수 없게 됩니다. 해서 newid를 이용해서 고유한 스티링을 이용해
메모리 충돌이 일어나지 않게 변수를 생성하여 그곳에 데이터를 담아 각자의 session에 대한
제대로 된 결과를 제공해주자는 것입니다.

물론 메모리에 들어갔으므로 실행을 마쳤다면 종료를 시켜줘야 하는데 그게 endbit가 필요한 이유입니다.

여튼 실행의 예시는 다음과 같습니다.


CREATE TABLE tempdb.dbo.t (a INT IDENTITY(1,1))

GO

 

 

INSERT INTO tempdb.dbo.t DEFAULT VALUES

GO 40000

 

 

 

DECLARE @endBit INT = 0

DECLARE @guid NVARCHAR(50) = NEWID()

 

SELECT @endBit = COUNT(*) FROM tempdb.dbo.t

 

/*

udf_lag(데이터열, 몇행앞으로(1, 2, 3.), 시작중간끝(1,0,-1), guid)

 

*/

SELECT

       a

       , dbo.udf_lag(a, 3, CASE ROW_NUMBER() OVER (ORDER BY a) 

                                              WHEN 1 THEN 1

                                              WHEN @endbit THEN -1

                                              ELSE 0 END , @guid)                    

FROM

       tempdb.dbo.t

첨부 : Udf_Lag.cs
        udf_lag.sql

반응형

'연구개발 > CLR' 카테고리의 다른 글

CLR - Windows Event Log  (0) 2011.08.27
CLR - 누적합 구하기  (0) 2011.08.27
WMI 객체를 쿼리로 읽어오기  (0) 2010.08.10
파일리스트 불러오기  (0) 2009.06.22
CLR-server와 통신하기  (1) 2009.06.22
반응형

DB서버를 관리하다 보면 성능카운터를 수집하거나 모니터링 하는 일이 많습니다.

성능 모니터를 사용해서 수집 및 모니터링을 할 수도 있고 써드파티툴을 사용할 수도 있습니다.

 



SQL 2005 
부터는 DMV 등을 통해서 성능 카운터를 모니터링 할 수 있습니다.

  

SELECT  * FROM SYS.DM_OS_PERFORMANCE_COUNTERS

WHERE REPLACE(RIGHT([OBJECT_NAME], PATINDEX('%:%', REVERSE([OBJECT_NAME]))-1), ' ', '') =

 'GeneralStatistics'


 




그러나 DMV 도 모든 WMI 카운터를 제공하지는 않습니다.

특히 하드디스크의 용량 같은 물리적인 부분이나 OS 단의 카운터 중 제공 안 하는 것들이 있습니다.

이 부분을 따로 WMI 툴로 만들어서 볼 수도 있습니다만 이걸 CLR로 등록해서 쿼리로 바로 보도록 하겠습니다.

WMI 개발에 관련된 자료는 쉽게 찾아볼 수가 있고 코드까지 만들어주는 툴도 있습니다.

예전에 한대성님이 ‘Scriptomatic2’ 도 소개해주셨습니다만 이번엔 ‘WMICodeCreator’ 이라는 툴로 해보겠습니다. (#파일첨부)

원하는 WMI클래스와 객체를 선택하면 바로 VB 또는 C# 코드를 만들어줍니다. Condition 설정도 가능합니다.

 



이렇게 만들어진 코드를 가지고 CLR로 등록해서 Function으로 만들어 직접 쿼리를 날려보겠습니다.

이 코드를 CLR로 등록하려면 사전에 한가지 설정을 해야 됩니다.

 

WMI 데이터를 읽기 위해선 System.management 네임스페이스의 클래스들을 사용합니다.

그래서 데이터베이스에 사전에 System.Management 어셈블리가 등록되어 있어야 됩니다.
 

USE TEST

GO

 

--CLR ENABLE

EXEC SP_CONFIGURE 'clr enabled', 1

RECONFIGURE WITH OVERRIDE

GO

 

--TRUSTWORTHY ON

ALTER DATABASE TEST SET TRUSTWORTHY ON

GO

 

--System.Management 등록

IF EXISTS(SELECT 1 FROM SYS.ASSEMBLIES WHERE NAME = 'System.Management')

BEGIN

             DROP ASSEMBLY [System.Management]

END

GO

CREATE ASSEMBLY [System.Management]

             AUTHORIZATION [dbo]

             FROM 'C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Management.dll'

             WITH PERMISSION_SET = UNSAFE

GO


 

System.Management 어셈블리가 등록되지 않은 상태에서 컴파일을 할 경우 아래와 같은 오류를 만나게 됩니다.

 


이 부분 외에 다른 설정은 보통 CLR 모듈을 등록하는 것과 동일합니다.

김종열님께서 다른 강좌에서 많이 보여주셨기 때문에 생략하도록 하겠습니다..


비쥬얼 스튜디오에서 C# SQL Server 프로젝트를 생성하신 후 데이터베이스 참조를 설정하시고

아래 소스를 붙여 넣으시기 바랍니다.

  

using System;

using System.Data;

using System.Data.SqlClient;

using System.Data.SqlTypes;

using Microsoft.SqlServer.Server;

using System.Management;

using System.Net;

using System.Net.Sockets;

using System.Collections;

 

public partial class UserDefinedFunctions

{

    [Microsoft.SqlServer.Server.SqlFunction(FillRowMethodName="fillTable", TableDefinition="Name nvarchar(20), Value nvarchar(20)")]

    public static IEnumerable UDF_WMI(string srvname, string username, string password, string wmiClass, string wmiName, string wmiValue, string condition)

    {

      

        //서버 연결

        ManagementScope scope = ConnServer(srvname, username, password);

 

        //조회 쿼리(WQL) 생성

        if (condition.Length > 0)

        {

            condition = " WHERE " + condition;

        }

 

        try

        {   

          

            ObjectQuery query = new ObjectQuery("SELECT " + wmiName + ", " + wmiValue + " FROM " + wmiClass + condition);

 

            //개체 컬렉션 검색

            ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);

            ManagementObjectCollection oCollection = searcher.Get();

 

            //return 변수 선언

            string[] retArray = new string[oCollection.Count];

 

            //컬렉션 열거자

            ManagementObjectCollection.ManagementObjectEnumerator

                oEnumerator = oCollection.GetEnumerator();

            oEnumerator.MoveNext();

 

            //return 변수 입력

            for (int i = 0; i < oCollection.Count; i++)

            {

                ManagementObject o = (ManagementObject)oEnumerator.Current;

                retArray[i] = Convert.ToString(o[wmiName]) + "," + Convert.ToString(o[wmiValue]);

                oEnumerator.MoveNext();

            }

            return (retArray);

        }

        catch (ManagementException e)

        {

            Console.WriteLine("An error occurred while querying for WMI data: " + e.Message);

            return "-1";

        }

    }

 

    public static ManagementScope ConnServer(string srvname, string username, string password)

    {

        try

        {

            //로컬인지 확인

            bool local = CheckLocal(srvname);

 

            //연결설정

            ConnectionOptions conn = new ConnectionOptions();

            conn.Username = username;

            conn.Password = password;

 

            //관리 범위 지정

            ManagementScope scope = new ManagementScope();

 

            //경로 지정

            ManagementPath path = new ManagementPath();

 

            //로컬일 경우 사용자 자격 증명 제외

            if (local == true)

            {

                path.Path = "root\\CIMV2";

            }

            else

            {

                path.Path = "\\\\" + srvname + "\\root\\CIMV2";

                scope.Options = conn;

            }

            //연결

            scope.Path = path;

            scope.Connect();

 

            return scope;

        }

        catch (ManagementException e)

        {

            Console.WriteLine("Server connecting error: " + e.Message);

            return null;

        }

    }

 

    public static bool CheckLocal(string srvname)

    {

        //로컬의 호스트명과 IP주소를 가져온다.

        IPHostEntry ihe = Dns.GetHostEntry(Dns.GetHostName());

        string strHostName = ihe.HostName;  //호스트명

        foreach (IPAddress ip in ihe.AddressList)

        {

            if (ip.AddressFamily == AddressFamily.InterNetwork)

            {

                if ((srvname == ip.ToString()) || (srvname.ToUpper() == strHostName.ToUpper()))

                {

                    return true;

                }

            }

        }

        return false;

    }

 

    private static void fillTable(Object obj, out SqlString Name, out SqlString Value)

    {

        string sTemp = Convert.ToString(obj);

        string[] arr_temp = sTemp.Split(',');

 

        try

        {

            Name = (SqlString)arr_temp[0];

            Value = (SqlString)arr_temp[1];

        }

        catch

        {

            Name = null;

            Value = null;

        }

    }

 

 

}

 

 

 


그리고 솔루션 탐색기에서 참조를 선택하시고 마우스 오른쪽 버튼 참조추가를 선택하시고

팝업에서 SQL Server탭의 System.Management 를 선택하시기 바랍니다.

 

이제 모든 준비가 끝났으니 dll을 등록하겠습니다. 배포를 하셔도 되고 빌드 후 수동으로 등록하셔도 됩니다.

  

--dll 등록

IF EXISTS(SELECT 1 FROM SYS.ASSEMBLIES WHERE NAME = 'WMICLR')

BEGIN

             DROP ASSEMBLY WMICLR

END

GO

CREATE ASSEMBLY [WMICLR]

             AUTHORIZATION [dbo]

             FROM 'D:\WMICLR\WMICLR\bin\Debug\WMICLR.dll'

             WITH PERMISSION_SET = UNSAFE

GO

 

 

쿼리를 사용하기 위해 SQL Function 을 생성하고 Function 을 실행한 결과입니다..

쿼리에서 서버명, UserID, Password 를 환경에 맞게 수정하신 후 실행 하시기 바랍니다.

참고로 로컬일 경우 계정 검사를 하지 않습니다.
  

--Function 등록

IF EXISTS(SELECT 1 FROM SYS.OBJECTS WHERE NAME = 'UDF_WMI')

BEGIN

             DROP FUNCTION DBO.UDF_WMI

END

GO

CREATE FUNCTION DBO.UDF_WMI

(

             @SRVNAME NVARCHAR(4000) = '',

             @USERNAME NVARCHAR(4000) = '',

             @PASSWORD NVARCHAR(4000) = '',

             @WMICLASS NVARCHAR(4000) = '',

             @WMINAME NVARCHAR(4000) = '',

             @WMIVALUE NVARCHAR(4000) = '',

             @CONDITION NVARCHAR(4000) = ''

)

RETURNS TABLE ([NAME] NVARCHAR(1000),[VALUE] nvarchar(1000))

AS EXTERNAL NAME [WMICLR].UserDefinedFunctions.UDF_WMI

GO

 

SELECT * FROM dbo.UDF_WMI('Servername', 'UserID', 'P@ssword', 'Win32_LogicalDisk', 'Name', 'Size', '')

 



 
C D드라이브의 논리디스크 용량입니다. E F DVD드라이브와 가상드라이브입니다.

 

이번엔 하드디스크만 나오도록 Condition 을 설정하고 총 용량이 아닌 남은 용량을 보도록 하겠습니다.

Condition 이나 WMI객체들은 ‘WMI Code Creator’ 등을 사용해서 확인하시기 바랍니다.
  

SELECT * FROM dbo.UDF_WMI('Servername', 'UserID', 'P@ssword', 'Win32_LogicalDisk', 'Name', 'FreeSpace',

 'DriveType=3')

 


 

 

수집방식이 성능모니터와는 다른지 특정 카운터들은 성능모니터와 다른 값들을 보여주기도 합니다.  제한된 용도내에서 충분히 테스트 하시고 사용한다면 편리하게 쓰실 수 있을 겁니다.

 

참고로 WMI를 사용하기 위해 원격을 접속하는 계정은 윈도우 계정이며 Administrator 권한이 있을 경우엔 별 다른 문제가 없습니다만 보안상 Administrator 권한이 없을 경우 아래와 같이 약간 복잡한 설정을 해줘야 됩니다.


반응형

'연구개발 > CLR' 카테고리의 다른 글

CLR - 누적합 구하기  (0) 2011.08.27
CLR-이전행가기 udf_lag  (0) 2011.08.27
파일리스트 불러오기  (0) 2009.06.22
CLR-server와 통신하기  (1) 2009.06.22
CLR-User Definded Aggregatiion  (0) 2009.06.22
반응형


김종열

MS SQL Server MVP

 


다른 커뮤너티에서 폴더의 파일들을 어떻게 불러오는가에 대한 질문이 곧잘 올라오더군요.
해서 간단하게 모듈하나 만들어봤습니다. (물론 xp_cmdshell등과 함께 써도 되겠지만... )

폴더와 파일타입 두개 정도만 받고 이를 Table형식으로 리턴해주는 함수입니다.


using System;

using System.Data;

using System.Data.SqlClient;

using System.Data.SqlTypes;

using Microsoft.SqlServer.Server;

using System.IO;

using System.Collections;

 

public partial class UserDefinedFunctions

{

    //[Microsoft.SqlServer.Server.SqlFunction]

    [Microsoft.SqlServer.Server.SqlFunction(FillRowMethodName = "f_fileList", TableDefinition = "fName nvarchar(50)")]

    public static IEnumerable UDF_FILE_LIST(SqlString dName, SqlString fType)

    {

        string strPath = dName.ToString();

        string strType = fType.ToString();

        string sItem = "";

 

        DirectoryInfo _dir = new DirectoryInfo(strPath);

 

        FileInfo[] f_info = _dir.GetFiles(strType);

 

        foreach (FileInfo f in f_info)

        {

            sItem += f.Name + "|";

        }

        sItem = Microsoft.VisualBasic.Strings.Left(sItem, sItem.Length - 1);

        string[] arrS = sItem.Split('|');

 

        return (arrS);

 

    }

 

    private static void f_fileList(Object obj, out SqlString fName)

    {

        string sTemp = Convert.ToString(obj);

        try

        {

            fName = sTemp;

        }

        catch

        {

            fName = null;

        }

 

    }

};

 

 

 

실행하는 샘플들입니다.

select * from dbo.UDF_FILE_LIST( 'd:\', 'copy*.*') a

select * from dbo.UDF_FILE_LIST( 'c:\windows\', '*.*') a





 

반응형

'연구개발 > CLR' 카테고리의 다른 글

CLR-이전행가기 udf_lag  (0) 2011.08.27
WMI 객체를 쿼리로 읽어오기  (0) 2010.08.10
CLR-server와 통신하기  (1) 2009.06.22
CLR-User Definded Aggregatiion  (0) 2009.06.22
CLR- 음력을 양력으로 변환하는 함수  (0) 2009.06.22
반응형

김종열

MS SQL Server MVP

 


Prologue

Procedure view를 통해 Database 내에 있는 정보를 Server로 주게 되고 Client에서 처리하게 되고 Client에서 처리한 결과들이 서버를 통해 Database까지 전달되면 Database에 저장하는 것이 통상적인 3-Tier의 처리 과정이라는 것은 모두들 잘 아시리라 생각합니다.

 

그런데 때로 Server에 있는 정보가 궁금했던 적은 없었나요?  악성 user Kickoff시키려면 어떤 과정을 거치나요? 마치 procedure를 실행시키듯 그 유저를 kickoff시키고 싶은 생각을 해 본적은 없나요? 악성 user는 몇 번 서버에 접속해 있는 상태이고 어떤 게임을 하고 있으며, 누구와 게임을 하고 있을까요?  때로는 게임 속 유저를 Trace해보고 싶지는 않나요? 실시간 랭킹을 내기 위해서는 어떻게 해야 할까요? …

 

이런 무수한 질문의 궁극은 Database에서 서버로 접속이 가능해지면 되는 것입니다. 즉 통신 프로그램을 database에 붙여 넣으면 된다는 간단한 결론입니다.


작업 (소스에 대한 설명은 하지 않겠습니다.)

1.       Echoserver 만들기

A.        역할 및 설명 : client에서 메시지를 주면 단순한 답을 주는 Demon

B.        UDP로 작성

C.        Winform_C# (visual studio 2008)

D.        .net Framework 3.5

E.         os : xp professional

F.         소스          

                                     i.    Project c# winform을 선택  다음과 같이 listbox를 올려놓고 objectname lbConnections라고 하십시오.

                                     ii.   Form1_load action에 다음과 같은 Thread를 시작하는 command를 주십시오.

private void Form1_Load(object sender, EventArgs e)

{

            thdUDPserver = new Thread(new ThreadStart(serverThread));            

            thdUDPserver.Start();

}


                                     iii.   Form1_close에서는 다음 서버의 종료를 알려주십시오.

private void Form1_FormClosed(object sender, FormClosedEventArgs e)

        {

            thdUDPserver.Abort();

            _udpClient.Close();

 

        }



                                     iv.   serverThread를 이제 작성해볼까요                   

public void serverThread()

        {

            IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9999);

            _udpClient = new UdpClient(ipep);

            while (true)

            {

                IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);

                Byte[] receiveBytes = _udpClient.Receive(ref RemoteIpEndPoint);

                string returnData = Encoding.Default.GetString(receiveBytes);

                lbConnections.Items.Add(RemoteIpEndPoint.Address.ToString() + ":" + returnData.ToString());

                if (returnData.ToString() == "Hi youly")

                {

                }

                else if (returnData.ToString() == "Ds" )

                {

 

                    string _strR = "a1^a2^a3|1^2^3|^^|a1^a2^a3|1^2^3|^^|a1^a2^a3|1^2^3|^^";

 

 

                    receiveBytes = Encoding.Default.GetBytes(_strR);

                    _udpClient.Send(receiveBytes, receiveBytes.Length, RemoteIpEndPoint);

                   

 

 

 

 

                } else

 

                {

                    receiveBytes = Encoding.Default.GetBytes("SrvMsg " + returnData.ToString());

                    _udpClient.Send(receiveBytes, receiveBytes.Length, RemoteIpEndPoint);

 

                }

 

               

            }

        }



2.       database client만들기

A.        역할 및 설명 : echoserver에 접속하여 세가지형태의 command를 날려줌

B.        UDP로 작성

C.        Database Program with c# (visual studio 2008)

D.        .net Framework 3.5

E.         Database : mssql2008 (sp1)

F.         소스

using System;

using System.Data;

using System.Data.SqlClient;

using System.Data.SqlTypes;

using Microsoft.SqlServer.Server;

using System.Net;

using System.Net.Sockets;

using System.Threading;

using System.Text;

 

 

 

public partial class StoredProcedures

{

    [Microsoft.SqlServer.Server.SqlProcedure]

    public static void usp_echoSrv_Hi()

    {

        UdpClient _udpClient = new UdpClient();

        _udpClient.Connect("127.0.0.1", 9999);

        Byte[] _sendByte = Encoding.Default.GetBytes("Hi youly");

        _udpClient.Send(_sendByte, _sendByte.Length);

 

    }

 

    [Microsoft.SqlServer.Server.SqlProcedure]

    public static void usp_echoSrv_show(string msg)

    {

        UdpClient _udpClient = new UdpClient();

        _udpClient.Connect("127.0.0.1", 9999);

        IPEndPoint ipe = new IPEndPoint(IPAddress.Any, 0);

 

        Byte[] _sendByte = Encoding.ASCII.GetBytes(msg);

        _udpClient.Send(_sendByte, _sendByte.Length);

 

        _sendByte = _udpClient.Receive(ref ipe);

 

        string returnData = Encoding.Default.GetString(_sendByte);

 

 

        SqlConnection _sqlConn = new SqlConnection("Context Connection=True");

        try

        {

            _sqlConn.Open();

            string strsql = "select msg = '" + returnData.ToString() + "'";

            SqlCommand _sqlCmd = new SqlCommand(strsql, _sqlConn);

            SqlContext.Pipe.ExecuteAndSend(_sqlCmd);

 

 

           

        }

        catch (SqlException e)

        {

            SqlContext.Pipe.Send(e.Message.ToString());

        }

        finally

        {

            _sqlConn.Close();

        }

    }

 

 

    [Microsoft.SqlServer.Server.SqlProcedure]

    public static void usp_echoSrv_ds(string msg)

    {

        UdpClient _udpClient = new UdpClient();

        _udpClient.Connect("127.0.0.1", 9999);

        IPEndPoint ipe = new IPEndPoint(IPAddress.Any, 0);

 

        Byte[] _sendByte = Encoding.ASCII.GetBytes(msg);

        _udpClient.Send(_sendByte, _sendByte.Length);

 

        _sendByte = _udpClient.Receive(ref ipe);

 

        string returnData = Encoding.Default.GetString(_sendByte);

        string[] arr_1 = returnData.Split('|');

        string[] arr_2 = arr_1[0].Split('^');

 

        string strsql = "";

        string strQry = "";

 

        for (int i = 0; i <= arr_1.Length - 1; i++)

        {

            arr_2 = arr_1[i].Split('^');

            strsql = "";

            for (int j = 0; j <= arr_2.Length - 1; j++)

            {

                strsql += "a_" + j.ToString() + " = '" + arr_2[j].ToString() + "'";

                    if (j != arr_2.Length-1)

                    {

                        strsql += ",";

                    }

            }

            strQry  += " select " + strsql;

            if (i != arr_1.Length - 1)

            {

                strQry += " union all ";

 

            }           

        }

 

 

        SqlConnection _sqlConn = new SqlConnection("Context Connection=True");

        try

        {

            _sqlConn.Open();

            SqlCommand _sqlCmd = new SqlCommand(strQry, _sqlConn);

            SqlContext.Pipe.ExecuteAndSend(_sqlCmd);

 

        }

        catch (SqlException e)

        {

            SqlContext.Pipe.Send(e.Message.ToString());

        }

        finally

        {

            _sqlConn.Close();

        }

    }

};



좀 긴가요? procedure는 세가지 command 구조를 실행하는 각각의 procedure 입니다.

 

배포를 하신 다음 echoServer를 실행하세요. SSMS를 실행한 다음 쿼리를 실행하면 만든 것들에 대한 결과를 볼 수 있습니다.

쿼리 및 실행결과  Echo server Capture

use youlydb

go

 

 

exec usp_echoSrv_Hi

/*

명령이완료되었습니다.

*/

use youlydb

go

 

exec usp_echoSrv_show 'sqlleader, sqlleader'

/*

msg

---------------------------

SrvMsg sqlleader, sqlleader

 

(1개행이영향을받음)

*/

--제가 두 번 실행을 해서 두번찍혀있네요

use youlydb

go

 

exec usp_echoSrv_ds 'Ds'

/*

a_0  a_1  a_2

---- ---- ----

a1   a2   a3

1    2    3

     

a1   a2   a3

1    2    3

     

a1   a2   a3

1    2    3

     

 

(9개행이영향을받음)

*/



Epilogue

말은 거창하게 한 거 같지만 너무 미미한 프로그램을 보여드린 것은 아닌지 모르겠습니다. 이런 접근이 Active한 서비스를 그리고 살아있는 따근한 정보를 다룰 수 있게 하는 계기가 되었으면 합니다.



반응형

'연구개발 > CLR' 카테고리의 다른 글

WMI 객체를 쿼리로 읽어오기  (0) 2010.08.10
파일리스트 불러오기  (0) 2009.06.22
CLR-User Definded Aggregatiion  (0) 2009.06.22
CLR- 음력을 양력으로 변환하는 함수  (0) 2009.06.22
CLR Utf8String - UDT  (0) 2009.06.22
반응형


김종열

MS SQL Server MVP

엔트리브 소프트



user Defined aggregate(UDA)는 Mssql 2005의 새롭게 등장한 겁니다.
mssql 2000이나 그 이전 버젼에서 aggregate를 하기 위해서는 Looping이나 T-sql문으로만 했을겁니다.
특히나 문자열관련이면 더더욱 그러했을 겁니다.

요즘들어 커뮤너티에서 문자열을 합하는 과정을 많이 보게 되는데요.
이번에 만들고자 하는 것이 바로 그 문자열 관련 concat의 과정입니다. 먼저 소개하는 것은 t-sql문입니다. 
 

IF OBJECT_ID('TEST') IS NOT NULL

       DROP TABLE TEST

GO

 

CREATE TABLE TEST

(

   ID   INT       NOT NULL,

   NM    NVARCHAR(10) NOT NULL

)

GO

 

INSERT TEST VALUES(1, '홍길동')

INSERT TEST VALUES(2, '김종영')

INSERT TEST VALUES(3, '최석중')

INSERT TEST VALUES(2, '임꺽정')

INSERT TEST VALUES(3, '송헉')

INSERT TEST VALUES(3, '한대송')

GO

 

 

IF OBJECT_ID('UDF_CONCAT_SQL') IS NOT NULL

       DROP FUNCTION UDF_CONCAT_SQL

GO

 

 

CREATE FUNCTION UDF_CONCAT_SQL (

       @BOOKID INT

)

RETURNS VARCHAR(8000)

AS

BEGIN

       DECLARE @STRTEMP VARCHAR(8000)

 

       SELECT @STRTEMP = ISNULL(@STRTEMP, '') + NM + ','

       FROM

             TEST

       WHERE

             ID = @BOOKID

      

       SET @STRTEMP = LEFT(@STRTEMP, LEN(@STRTEMP)-1)

       RETURN @STRTEMP

END

GO

 

SELECT id, DBO.UDF_CONCAT_SQL(id)

FROM test

GROUP BY id

GO


물론 이렇게 만들게 됐을 때 문제가 될 만한 건 실행계획일것이고 코드의 재활용은 거의 0일 겁니다.
그래서 UDA로 가는 건 어떨까? 하는 생각을 해봅니다.  (다음의 c#소스는 msdn에서 제공하는 uda입니다.)

using System;

using System.Data;

using Microsoft.SqlServer.Server;

using System.Data.SqlTypes;

using System.IO;

using System.Text;

 

[Serializable]

[SqlUserDefinedAggregate(

    Format.UserDefined,                 //use clr serialization to serialize the intermediate result

    IsInvariantToNulls = true,          //optimizer property

    IsInvariantToDuplicates = false,    //optimizer property

    IsInvariantToOrder = false,         //optimizer property

    MaxByteSize = 8000)                 //maximum size in bytes of persisted value

]

public class UDF_Concat : IBinarySerialize

{

 

    private StringBuilder strTemp;

 

    public void Init()

    {

        strTemp = new StringBuilder();

    }

 

    public void Accumulate(SqlString value)

    {

        if (value.IsNull)

        {

            return;

        }

 

        strTemp.Append(value.Value).Append(',');

    }

 

    public void Merge(UDF_Concat other)

    {

        this.strTemp.Append(other.strTemp);

    }

 

    public SqlString Terminate()

    {

        string output = string.Empty;

 

        if (strTemp != null && strTemp.Length > 0)

        {

            output = strTemp.ToString(0, strTemp.Length - 1);

        }

 

        return new SqlString(output);

    }

 

    public void Read(BinaryReader r)

    {

        strTemp = new StringBuilder(r.ReadString());

    }

 

    public void Write(BinaryWriter w)

    {

        w.Write(strTemp.ToString());

    }

}


여기까지가 C#코드입니다.
컴파일 이후 쿼리를 실행하면 다음과 같습니다.

IF OBJECT_ID('TEST') IS NOT NULL

       DROP TABLE TEST

GO

 

CREATE TABLE TEST

(

   ID   INT       NOT NULL,

   NM    NVARCHAR(10) NOT NULL

)

GO

 

INSERT TEST VALUES(1, '홍길동')

INSERT TEST VALUES(2, '김종열')

INSERT TEST VALUES(3, '최석준')

INSERT TEST VALUES(2, '임꺽정')

INSERT TEST VALUES(3, '송혁')

INSERT TEST VALUES(3, '한대성')

GO

 

 

SELECT ID, DBO.UDF_CONCAT(NM)

FROM TEST

GROUP BY ID


쿼리플랜을 보게 되면 T-sql문과 사뭇 달라진 과정을 확인 할 수 있을 것이며
또한 재사용(예를 들면- 테이블명이 틀려지거나 조합하고자 하는 칼럼명이 틀려질 경우,
그룹 by의 기준이 되는 칼럼의 변경등)의 측면에서도 T-sql문과는 비교가 되지 않을겁니다.




참고)  uda를 사용하기 위해서 몇가지 필수 사항
1. 반드시 SqlUserDefinedAggregate 를 선언
2. aggregation method중 init, accumulate, merge, terminate를 가지고 있어야 함

각각의 method의 기능
1. init() : T-sql에서 uda로 들어오는 진입 포인트의 역할을 함
2. Accumulate() : 각각의 value를 검사하여 aggregated을 계산하는 역할
3. Merge() : 멀티 프로세서가 결과를 조합하거나 더할때 병렬로 처리할수 있도록 함
4. Terminate() : aggregate를 마친 UDA가 value를 리턴하는 역할

첨부 : udf_Concat.cs
        uda.sql

반응형

'연구개발 > CLR' 카테고리의 다른 글

파일리스트 불러오기  (0) 2009.06.22
CLR-server와 통신하기  (1) 2009.06.22
CLR- 음력을 양력으로 변환하는 함수  (0) 2009.06.22
CLR Utf8String - UDT  (0) 2009.06.22
CLR - Mail 보내기  (0) 2009.06.22
반응형

김종열

MS SQL Server MVP

엔트리브 소프트


회원을 관리하면서 음력의 생일을 가진 회원을 양력기준으로 메시지를 보내는 경우가 있었나요?
머 특별하게 함수를 만들어 사용하셔도 되겠지만, 이런 간단한 이슈로 음력을 양력으로 변환해봅니다.

머 왜 양력을 음력으로 변환하지 않는가?
아마도 양력을 음력으로 변환하는 것은 특별히 신년운세를 보는 그런 곳에 제가 종사를 해본적이
없기에 필요하신 분들이 직접 만들기를 기원하며..

C#을 아주 끝에만 다루어보지만  함수가 필요해서 msdn을 뒤져보면 이미 정의가 다 되어 있는 참 편리(?)하다는 생각을 해봅니다.

달력과 관련되는 부분을 사용하기 위해서는
"using System.Globalization"
이런 선언을 하고 난뒤 달력 class를 사용하시면 됩니다.

using System;

using System.Data;

using System.Data.SqlClient;

using System.Data.SqlTypes;

using Microsoft.SqlServer.Server;

using System.Globalization;

 

public partial class UserDefinedFunctions

{

    [Microsoft.SqlServer.Server.SqlFunction]

    public static SqlDateTime Udf_Lunar2Solar(SqlDateTime sDt)

    {

        DateTime dt = (DateTime)sDt;

        bool bExistLeap = false;

 

        KoreanLunisolarCalendar kr_Lunnar = new KoreanLunisolarCalendar();

        int _lunnarYY = kr_Lunnar.GetYear(dt);

        int _lunnarMM = kr_Lunnar.GetMonth(dt);

        int _lunnarDD = kr_Lunnar.GetDayOfMonth(dt);

 

        if (kr_Lunnar.GetMonthsInYear(_lunnarYY) > 12)                  //12보다 큰달은 윤달이 있다는.

        {

            bExistLeap = kr_Lunnar.IsLeapMonth(_lunnarYY, _lunnarMM);   // 윤달에 대한 true or false

            int intLeap_mm = kr_Lunnar.GetLeapMonth(_lunnarYY);         //윤달 추출

            if (_lunnarMM >= intLeap_mm)

            {

                _lunnarMM--;

            }

        }

        return  Convert.ToDateTime(_lunnarYY.ToString() + "-" + _lunnarMM.ToString() + "-" + _lunnarDD.ToString());  

 

    }

};

 

 


그리고 이를 확인하는 소스코드입니다.

with sTB as (

select x = dbo.Udf_Lunar2Solar(cast('1985-03-19' as datetime))

       , y = dbo.Udf_Lunar2Solar('19850319')

       , z = dbo.Udf_Lunar2Solar('1985/03/19')

)

select * from sTB

 

 

/*

x                       y                       z

----------------------- ----------------------- -----------------------

1985-01-28 00:00:00.000 1985-01-28 00:00:00.000 1985-01-28 00:00:00.000

 

(1개행이영향을받음)

 

*/





첨부 : Udf_Lunar2Solar.cs
        Udf_Lunar2Solar.sql

반응형

'연구개발 > CLR' 카테고리의 다른 글

CLR-server와 통신하기  (1) 2009.06.22
CLR-User Definded Aggregatiion  (0) 2009.06.22
CLR Utf8String - UDT  (0) 2009.06.22
CLR - Mail 보내기  (0) 2009.06.22
CLR - FTP download(FTP 2탄)  (0) 2009.06.22
반응형

김종열

MS SQL Server MVP

엔트리브 소프트


안녕하세요. CLR에 오랜만에 하나를 올리는 듯하네요.
강좌를 목표로 하지 않고 실무에 필요한 것을 하나 하나 만들고 적용하다 보니 그렇습니다.
그리고 Database & C#의 수준이 한층 높아졌을 꺼라는 아니면 관심도라도 높아졌으리라는 희망을 가져봅니다.

이번 강좌는 sql2005에서 제공하는 User Defined Type(사용자정의데이터형)을 만들어볼까 합니다.

User Defined Type을 C#에서 만드시려면 C#에서 Datatype에 대한 옵션을 줄 때 
Optimiser에 대한 간략한 이해가 선행이 되면 훨씬 좋구요.
또한 DataType이기에 Transaction Isolation에 대한 이해도 선행되면 훨씬 좋을 듯합니다.

그럼 C#코드와 SQL 코드의 샘플들입니다.

using System;

using System.Xml;

using System.Data.Sql;

using System.Data.SqlTypes;

using System.Globalization;

using Microsoft.SqlServer.Server;

 

[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.Samples.SqlServer")]

 

namespace Microsoft.Samples.SqlServer

{

    [Serializable]

    [Microsoft.SqlServer.Server.SqlUserDefinedType(Microsoft.SqlServer.Server.Format.UserDefined, IsByteOrdered = true, MaxByteSize = 8000)]

    [CLSCompliant(false)]

    public class Utf8String : INullable, IComparable, Microsoft.SqlServer.Server.IBinarySerialize

    {

 

        public static Utf8String Parse(SqlString sqlString)

        {

            if (sqlString.IsNull)

                return Utf8String.Null;

 

            return new Utf8String(sqlString.Value);

        }

 

        public SqlBinary Utf8Bytes

        {

            get

            {

                if (this.IsNull)

                    return SqlBinary.Null;

 

                if (this.m_Bytes != null)

                    return this.m_Bytes;

 

                if (this.m_String != null)

                {

                    this.m_Bytes = System.Text.Encoding.UTF8.GetBytes(this.m_String);

                    return new SqlBinary(this.m_Bytes);

                }

 

                throw new NotSupportedException("cannot return bytes for empty instance");

            }

            set

            {

                if (value.IsNull)

                {

                    this.m_Bytes = null;

                    this.m_String = null;

                }

                else

                {

                    this.m_Bytes = value.Value;

                    this.m_String = null;

                }

            }

        }

 

        [Microsoft.SqlServer.Server.SqlMethod(IsDeterministic = true, IsPrecise = true, DataAccess = Microsoft.SqlServer.Server.DataAccessKind.None, SystemDataAccess = Microsoft.SqlServer.Server.SystemDataAccessKind.None)]

        public override string ToString()

        {

            if (this.IsNull)

                return null;

 

            if (this.m_String != null)

                return this.m_String;

 

            if (this.m_Bytes != null)

            {

                this.m_String = System.Text.Encoding.UTF8.GetString(this.m_Bytes);

                return this.m_String;

            }

 

            throw new NotSupportedException("dont know how to return string from empty instance");

        }

 

 

        public SqlString ToSqlString()

        {

            if (this.IsNull)

                return SqlString.Null;

 

            return new SqlString(this.ToString());

        }

 

 

        private SqlString GetSortKeyUsingCultureInternal(CultureInfo culture, bool ignoreCase,

            bool ignoreNonSpace, bool ignoreWidth)

        {

            if (this.IsNull)

                return SqlString.Null;

 

            SqlCompareOptions compareOptions = SqlCompareOptions.None;

            if (ignoreCase)

                compareOptions = compareOptions | SqlCompareOptions.IgnoreCase;

 

            if (ignoreNonSpace)

                compareOptions = compareOptions | SqlCompareOptions.IgnoreNonSpace;

 

            if (ignoreWidth)

                compareOptions = compareOptions | SqlCompareOptions.IgnoreWidth;

 

            return new SqlString(this.ToString(), culture.LCID, compareOptions);

        }

 

        [Microsoft.SqlServer.Server.SqlMethod(IsDeterministic = true, IsPrecise = true)]

        public SqlString GetSortKeyUsingCulture(string cultureName, bool ignoreCase,

            bool ignoreNonSpace, bool ignoreWidth)

        {

            CultureInfo culture = CultureInfo.CreateSpecificCulture(cultureName);

            if (culture == null)

                throw new ArgumentException(string.Format(

                    CultureInfo.InvariantCulture,

                    "Culture {0} not recognized.", cultureName));

 

            return this.GetSortKeyUsingCultureInternal(culture, ignoreCase,

                ignoreNonSpace, ignoreWidth);

        }

 

        [Microsoft.SqlServer.Server.SqlMethod(IsDeterministic = false)]

        public SqlString GetSortKey(bool ignoreCase, bool ignoreNonSpace, bool ignoreWidth)

        {

            return this.GetSortKeyUsingCultureInternal(CultureInfo.CurrentCulture,

                ignoreCase, ignoreNonSpace, ignoreWidth);

        }

 

 

        #region comparison operators

        public override bool Equals(object obj)

        {

            return this.CompareTo(obj) == 0;

        }

 

        public static bool operator ==(Utf8String utf8String, Utf8String other)

        {

            return utf8String.Equals(other);

        }

 

        public static bool operator !=(Utf8String utf8String, Utf8String other)

        {

            return !(utf8String == other);

        }

 

        public static bool operator <(Utf8String utf8String, Utf8String other)

        {

            return (utf8String.CompareTo(other) < 0);

        }

 

        public static bool operator >(Utf8String utf8String, Utf8String other)

        {

            return (utf8String.CompareTo(other) > 0);

        }

 

        private int CompareUsingCultureInternal(Utf8String other, CultureInfo culture, bool ignoreCase,

            bool ignoreNonSpace, bool ignoreWidth)

        {

            if (other == null)

                return 1;

 

            if (this.IsNull)

                if (other.IsNull)

                    return 0;

                else

                    return -1;

 

            if (other.IsNull)

                return 1;

 

            return this.GetSortKeyUsingCultureInternal(culture, ignoreCase, ignoreNonSpace,

                ignoreWidth).CompareTo(other.GetSortKeyUsingCultureInternal(culture, ignoreCase,

                ignoreNonSpace, ignoreWidth));

        }

 

        public int CompareUsingCulture(Utf8String other, string cultureName, bool ignoreCase,

            bool ignoreNonSpace, bool ignoreWidth)

        {

            CultureInfo culture = CultureInfo.CreateSpecificCulture(cultureName);

            if (culture == null)

                throw new ArgumentException(string.Format(

                    CultureInfo.InvariantCulture,

                    "Culture {0} not recognized.", cultureName));

 

            return this.CompareUsingCultureInternal(other, culture, ignoreCase,

                ignoreNonSpace, ignoreWidth);

        }

 

        public int Compare(Utf8String other, bool ignoreCase,

            bool ignoreNonSpace, bool ignoreWidth)

        {

            return this.CompareUsingCultureInternal(other, CultureInfo.CurrentCulture, ignoreCase,

                ignoreNonSpace, ignoreWidth);

        }

 

        public override int GetHashCode()

        {

            if (this.IsNull)

                return 0;

 

            return this.ToString().GetHashCode();

        }

 

        public int CompareTo(object obj)

        {

            if (obj == null)

                return 1;

 

            Utf8String s = obj as Utf8String;

 

            if (s == null)

                throw new ArgumentException("the argument to compare is not a Utf8String");

 

            if (this.IsNull)

            {

                if (s.IsNull)

                    return 0;

 

                return -1;

            }

 

            if (s.IsNull)

                return 1;

 

            return this.ToString().CompareTo(s.ToString());

        }

 

        #endregion

 

        #region private state and constructors

        private string m_String;

 

        private byte[] m_Bytes;

 

        public Utf8String(string value)

        {

            this.m_String = value;

        }

 

        public Utf8String(byte[] bytes)

        {

            this.m_Bytes = bytes;

        }

        #endregion

 

        #region UserDefinedType boilerplate code

 

        public bool IsNull

        {

            get

            {

                return this.m_String == null && this.m_Bytes == null;

            }

        }

 

        public static Utf8String Null

        {

            get

            {

                Utf8String str = new Utf8String((string)null);

 

                return str;

            }

        }

 

        public Utf8String()

        {

        }

        #endregion

 

        #region IBinarySerialize Members

        public void Write(System.IO.BinaryWriter w)

        {

            byte header = (byte)(this.IsNull ? 1 : 0);

 

            w.Write(header);

            if (header == 1)

                return;

 

            byte[] bytes = this.Utf8Bytes.Value;

 

            w.Write(bytes.Length);

            w.Write(bytes);

        }

 

        public void Read(System.IO.BinaryReader r)

        {

            byte header = r.ReadByte();

 

            if ((header & 1) > 0)

            {

                this.m_Bytes = null;

                return;

            }

 

            int length = r.ReadInt32();

 

            this.m_Bytes = r.ReadBytes(length);

        }

        #endregion

    }

}


조금 어렵죠? 저는 이런 복잡한 것은 ㅋ , SQL2005에서 이미 제공하는 UDT입니다.

use youlyDB

 

DECLARE @u1 Utf8String

       , @u2  Utf8String

      

select @u1 = CONVERT(Utf8String, ',“')

       , @u2 = CONVERT(Utf8String, ', “')

 

select PATINDEX('%,%', @u1.ToString())

       , PATINDEX('%,%', @u2.ToString())

 

go

 

 

Create Table utfTest(u Utf8String)

go

 

insert into utfTest values (cast('SQLLeader.Com' as Utf8String))

go

 

select

       u

       , u.ToString()

       , u.Utf8Bytes

from utfTest



참고 파일 : Utf8String.cs
              Utf8Test.sql

반응형

'연구개발 > CLR' 카테고리의 다른 글

CLR-User Definded Aggregatiion  (0) 2009.06.22
CLR- 음력을 양력으로 변환하는 함수  (0) 2009.06.22
CLR - Mail 보내기  (0) 2009.06.22
CLR - FTP download(FTP 2탄)  (0) 2009.06.22
CLR - FTP upload 구현(FTP 1탄)  (0) 2009.06.22
반응형


김종열

MS SQL Server MVP

엔트리브 소프트


Ms SQL SERVER에서 Email을 보내려고 하실때 어떻게 하시나요?
혹시 불편하신 점은 없었나요? 간간히 Community에 올라오는 것 중에 하나더군요.
SQL server에서 mail을 보낼때 에러가 많이 나느니..
작업의 결과를 email로 보내고 싶다는 등의

역시 만들면 됩니다.
True / False를 리턴하는 함수하나를 만들었습니다.
회사 smtp를 공개할 수도 없는 일이고, 그렇다고 이 때문에 smtp기능을 지원하는 곳의 서비스를 이용할 수 도 없고..
google은 이미 무료의 서비스를 제공하고 있기에 가입하고 사용하기로 했습니다.
smtp는 환경에 맞게 설정 및 변경하시어 사용하시기 바랍니다.


using System;

using System.Data;

using System.Data.SqlClient;

using System.Data.SqlTypes;

using Microsoft.SqlServer.Server;

using System.Net;

using System.Net.Mail;

 

public partial class UserDefinedFunctions

{

    [Microsoft.SqlServer.Server.SqlFunction]

    public static SqlBoolean UDF_MAIL_SEND(string toAddr, string mailSubj, string mailCont)

    {

        string frAddr = "id@gmail.com";  //setting

        string frPwd = "password";      //setting

 

        MailMessage cMail = new MailMessage(frAddr, toAddr, mailSubj, mailCont);

        SmtpClient cSmtp = new SmtpClient("smtp.gmail.com", 587);

 

        cSmtp.Credentials = new NetworkCredential(frAddr, frPwd);

        cSmtp.EnableSsl = true;

 

        try

        {

            cSmtp.Send(cMail);

            return true;

        }

        catch

        {

            return false;

        }

    }

};

 

 


쿼리로 확인하기.

use youlyDB

go

 

declare @rst bit

set @rst = dbo.udf_mail_send('youly_92@hotmail.com', 'testMail', 'Go Mail Test Content')

select @rst

 


udf_mail_send의 param은 Reciever Address, Mail subject, Mail Content입니다. 
Return은 당연 True / False 입니다.
(메일을 받은 hotmail의 결과를 그림으로 올리려 했는데
왠지 그림파일이 올라가지 않네요..)

참고파일 :  udf_mail_send.cs
              udf_mail_send.sql


설정 중에 데이터베이스 권한 수준을 '안전하지 않음' (UNSAFE) 로 설정해야 되네요
처음에 '외부' (external) 로 설정하고 했더니 아래와 같은 오류가 나더군요.
System.Security.SecurityException: 'System.Net.Mail.SmtpPermission, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken= ' 형식의 사용 권한을 요청하지 못했습니다.

반응형

'연구개발 > CLR' 카테고리의 다른 글

CLR- 음력을 양력으로 변환하는 함수  (0) 2009.06.22
CLR Utf8String - UDT  (0) 2009.06.22
CLR - FTP download(FTP 2탄)  (0) 2009.06.22
CLR - FTP upload 구현(FTP 1탄)  (0) 2009.06.22
CLR - EXCEL 파일 읽기  (0) 2009.06.22
반응형

김종열

MS SQL Server MVP

엔트리브 소프트


FTP로 업로드를 했다면 다운로드도 가능해야 하지 않을까요?
그러면 역시 만들면 되죠 ^^
코드나 사용법은 udf_upload와 동일한 구조로 했습니다.

간단한 c#코드, sql파일을 첨부하겠습니다.

워낙 뜸했던 지라 clr에 관해 잘 모르겠다구요?
그렇다면 복습도 할겸 해서 다시 처음부터 하나씩 열어보시면서 기억을 상기해보세요.


using System;

using System.Data;

using System.Data.SqlClient;

using System.Data.SqlTypes;

using Microsoft.SqlServer.Server;

using System.Collections;

using System.Collections.Generic;

using System.IO;

using System.Net;

using System.Xml;

using System.Text;

 

public partial class UserDefinedFunctions

{

    [Microsoft.SqlServer.Server.SqlFunction]

    public static SqlInt32 UDF_DOWNLOAD(SqlString ftp_server

                                        , SqlString ftp_user

                                        , SqlString ftp_pwd

                                        , SqlString local_file

                                        , SqlString rmt_file

                                        , SqlInt32 ftp_mode)

    {

        /* Init Vairable */

        string ftpServer = Convert.ToString(ftp_server);

        string ftpUser = Convert.ToString(ftp_user);

        string ftpPwd = Convert.ToString(ftp_pwd);

        string localFile = Convert.ToString(local_file);

        string rmtFile = Convert.ToString(rmt_file);

        int ftpMode = Convert.ToInt32(Convert.ToString(ftp_mode)); /* asc(1), bin(else) */

 

        FtpWebRequest reqFtp;

        try

        {

            FileStream outputStream = new FileStream(localFile, FileMode.Create);

            reqFtp = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://" + ftpServer + rmtFile));

            reqFtp.Method = WebRequestMethods.Ftp.DownloadFile;

            if (ftpMode == 1)

            {

                reqFtp.UseBinary = false;

            }

            else

            {

                reqFtp.UseBinary = true;

            }

            reqFtp.Credentials = new NetworkCredential(ftpUser, ftpPwd);

            FtpWebResponse response = (FtpWebResponse)reqFtp.GetResponse();

            Stream ftpStream = response.GetResponseStream();

 

 

            int buffersize = 2048;

            int readCount;

            byte[] buffer = new byte[buffersize];

 

            readCount = ftpStream.Read(buffer, 0, buffersize);

            while (readCount > 0)

            {

                outputStream.Write(buffer, 0, readCount);

                readCount = ftpStream.Read(buffer, 0, buffersize);

 

            }

 

            ftpStream.Close();

            outputStream.Close();

            response.Close();

 

            return 1;

        }

        catch

        {

            return 0;

        }       

       

    }

};

 

 


실행예입니다.


DECLARE @RET_down INT

SELECT @RET_down = DBO.udf_Download ('server'

                                     , 'id'

                                     , 'pwd'

                                     , 'local_save_full_path'

                                     , 'download_file_full_path'

                                     , 1)

SELECT @RET_DOWN

 



참고파일 : udf_Download.cs
             udf_download.sql

반응형

'연구개발 > CLR' 카테고리의 다른 글

CLR Utf8String - UDT  (0) 2009.06.22
CLR - Mail 보내기  (0) 2009.06.22
CLR - FTP upload 구현(FTP 1탄)  (0) 2009.06.22
CLR - EXCEL 파일 읽기  (0) 2009.06.22
CLR - 쿼리의 결과를 파일로 저장하기  (0) 2009.06.22
반응형

김종열

MS SQL Server MVP

엔트리브 소프트


그동안 회사사정으로 인해 조금 바빴는데요. 물론 지금도 바쁘지만 핑계삼아...
망중한으로 ftp upload를 하는 funtion을 하나 만들어봤습니다.
기능은 upload를 하고 성공(1), 실패(0)를 리턴하는 함수입니다.

이쯤되면 파일에 대한 작업이나 ftp등의 네트웍프로그램까지 확장이 되어버렸다는..
CLR의 끝은 자신이 하고자 하는 작업이나 상상력이 한계인듯하군요.. ㅋㅋ

다음은 c#코드입니다. 역시 설명은 드리지 않겠습니다.

using System;

using System.Data;

using System.Data.SqlClient;

using System.Data.SqlTypes;

using Microsoft.SqlServer.Server;

using System.Collections;

using System.Collections.Generic;

using System.IO;

using System.Net;

using System.Xml;

using System.Text;

 

public partial class UserDefinedFunctions

{

    [Microsoft.SqlServer.Server.SqlFunction]

    public static SqlInt32 UDF_UPLOAD(SqlString ftp_server

                                      , SqlString ftp_user

                                      , SqlString ftp_pwd

                                      , SqlString local_file

                                      , SqlString rmt_file

                                      , SqlInt32 ftp_mode)

    {

        /* Init Vairable */

        string ftpServer = Convert.ToString(ftp_server);

        string ftpUser = Convert.ToString(ftp_user);

        string ftpPwd = Convert.ToString(ftp_pwd);

        string localFile = Convert.ToString(local_file);

        string rmtFile = Convert.ToString(rmt_file);

        int ftpMode = Convert.ToInt32(Convert.ToString(ftp_mode)); /* asc(1), bin(else) */

 

 

        FileInfo fileinf = new FileInfo(localFile);

        string uri = "ftp://" + ftpServer + rmtFile;

        FtpWebRequest reqFtp = (FtpWebRequest)FtpWebRequest.Create(new Uri(uri));

        reqFtp.Credentials = new NetworkCredential(ftpUser, ftpPwd);

        reqFtp.KeepAlive = false;

        reqFtp.Method = WebRequestMethods.Ftp.UploadFile;

 

        if (ftpMode == 1)

        {

            reqFtp.UseBinary = false;

        }

        else

        {

            reqFtp.UseBinary = true;

        }

 

        reqFtp.ContentLength = fileinf.Length;

 

        int buffLength = 2048;

        byte[] buff = new byte[buffLength];

        int contentLen;

 

        FileStream fs = fileinf.OpenRead();

 

        try

        {

            Stream strm = reqFtp.GetRequestStream();

            contentLen = fs.Read(buff, 0, buffLength);

 

            while (contentLen != 0)

            {

                strm.Write(buff, 0, contentLen);

                contentLen = fs.Read(buff, 0, buffLength);

            }

            strm.Close();

            fs.Close();

 

            return 1;

        }

        catch

        {

            return 0;

        }       

    }

};

 

 

 




그리고 나서 역시 배포를 하고 나시면 database에서 그 명령을 실행할 수 있겠죠..

DECLARE @RET_up INT

SELECT @RET_up = DBO.UDF_UPLOAD('<test_ftp_server_name> or <server ip>'

                                 , 'id'

                                 , 'pwd'

                                 , 'upload_file_full_path'

                                 , 'remote_save_full_path'

                                 , 1) --1 asc, else bin

SELECT @RET_up 

 





참고파일 : udf_upload.cs
             udf_upload.sql

반응형

'연구개발 > CLR' 카테고리의 다른 글

CLR - Mail 보내기  (0) 2009.06.22
CLR - FTP download(FTP 2탄)  (0) 2009.06.22
CLR - EXCEL 파일 읽기  (0) 2009.06.22
CLR - 쿼리의 결과를 파일로 저장하기  (0) 2009.06.22
CLR - 10진수를 다른 진법의 수로 표현  (0) 2009.06.22
반응형

김종열

MS SQL Server MVP

엔트리브 소프트


DATABASE이전의 파일들, 정리된 파일들 중 우리가 가장 쉽게 접할 수 있는 파일이 아마도
엑셀이 아닐까 싶습니다.  이 엑셀의 내용을 바로 읽을 수 있다면 참으로 편리할 겁니다.
LINKED SERVER등등을 하셔도 되고 아니면 SSIS로 읽으온 다음 읽으셔도 되고..
직접 열어서 DATABASE로 보내주는 VBA를 만드셔도 되고..

방법은 여러가지가 있습니다. 또한 용도가 다르기에 어느 하나가 좋다라고 말할수도 없습니다.

저는 이번에 CLR로의 접근을 해보려고 합니다.
물론 안에는 OPENDATASET이라는 QUERY문을 사용하고 있습니다.
이는 CLR에서 직접 EXCEL을 CONNECT하기 위해서는 OLE개체를 참조할 수 있어야 하는데요. 
CLR WITH DATABASE(MSSQL)을 할 떄에 개체 참조는 지극히 제한적입니다.

해서 이런 변칙을 사용할 수 밖에 없었습니다.

그리고 OFFICE이기에 지극히 PRODUCT 종속적인 부분-DRIVER, PROVIDER등..-이 변경되어야 합니다.
(저는 VISTA, OFFICE2007을 사용하고 있습니다.)

이번에는 새로운 PROCEDURE 개체를 하나 추가해주십시오.
그런 다음 아래의 코딩을 입력하시고 배포하십시오. 그리고 추가적으로 해야 할 작업이 남아 있습니다.

using System;

using System.Data;

using System.Data.SqlClient;

using System.Data.SqlTypes;

using Microsoft.SqlServer.Server;

using System.IO;

 

 

 

public partial class StoredProcedures

{

    [Microsoft.SqlServer.Server.SqlProcedure]

    public static void USP_EXCEL_READ(string fname, string sname)

    {

        SqlConnection _sqlConn = new SqlConnection("Context Connection=True");

        try

        {

            _sqlConn.Open();

 

            //제품종속적*****************

            string strSql = "SELECT * FROM OPENROWSET('MICROSOFT.ACE.OLEDB.12.0', 'Excel 12.0;Database=" + fname + "', [" + sname + "$])";

            //제품종속적*****************

 

 

            //SqlContext.Pipe.Send(strSql);

            SqlCommand _sqlCmd = new SqlCommand(strSql, _sqlConn);

            SqlContext.Pipe.ExecuteAndSend(_sqlCmd);

        }

        catch (SqlException e)

        {

            SqlContext.Pipe.Send(e.Message.ToString());

        }

        finally

        {

            _sqlConn.Close();

        }

    }

};

 



이렇게 입력하고 나면 바로 실행을 할 수 있으면 좋겠는데요.
OPENDATASOURCE, OPENQUERY, OPENDATASET등의 쿼리를 실행하기 위해서는
SERVER에서 ADHOC DISTRIBUTED QUERY를 실행할 수 있는 세팅을 해주어야 합니다.

USE YOULYDB

GO

 

--adhoc distributed query를실행하기위한환경을세팅

SP_CONFIGURE 'show advanced options', 1

RECONFIGURE WITH OVERRIDE

GO

 

SP_CONFIGURE 'Ad Hoc Distributed Queries', 1

RECONFIGURE WITH OVERRIDE

GO

 

 

 

--실행

EXEC YOULYDB..USP_EXCEL_READ 'C:\LECTURE_CLR\PRJ_CLR\TEST.XLS', 'SHEET2'

 

 

 

/*

a     b     c     d    e    f     g  

----- ----- ----- ---- ---- ----- ----

80    55    39    41   20   25    39

96    79    42    50   22   47    78

40    18    98    51   42   51    78

43    87    31    57   93   93    24

57    91    69    86   32   78    90

92    78    5     17   53   13    51

26    2     26    99   45   49    51

60    14    22    51   19   5     31

8     68    98    76   50   53    80

83    39    12    88   98   66    46

82    89    95    95   8    70    95

89    95    42    13   57   18    5

81    37    49    12   38   85    7

 

(13 row(s) affected)

 

*/


참고파일 : USP_EXCEL_READ.CS
             EXCEL_READ.SQL

엑셀파일은 대충 만들고 테스트 해보시면 됩니다.
저는 RAND를 이용해서 대강의 데이터를 SHEET1, SHEET2에 각각 채우고
테스트를 해봤습니다.

반응형
반응형


김종열

MS SQL Server MVP

엔트리브 소프트


커뮤니티에서 자주 오르는 질문 중의 하나지요. 쿼리의 결과를 파일로 만들수 없는지..
물론 bcp등을 이용한 동적SQL문으로 만들면 얼마든지 가능합니다.
이번에 만들어본 저장프로시져가 그런 의도로 만들어본 겁니다.
좀더 쉽게 만들어야 한다는 압박을 가지며 작성을 했습니다.
또한 파일은 overWrite모드로 되어 있기에 실무에 사용하실 때는 검증후 하시길 추천드립니다.
간략한 구조를 설명드리면
1. 인자를 받아 검사를 해주고
2. Dataset을 만드는 부분
3. Dataset을 이용해서 파일에 기록하는 부분
이 세구조로 되어 있습니다.

역시 자세한 설명을 드리지 않는 것은 프로그램적인 부분이기 때문에 논외 사항이라 여기기 떄문입니다.


using System;

using System.Data;

using System.Data.SqlClient;

using System.Data.SqlTypes;

using Microsoft.SqlServer.Server;

using System.Collections;

using System.Text;

using System.IO;

using System.Security;

using System.Security.Permissions;

 

public partial class StoredProcedures

{

    [Microsoft.SqlServer.Server.SqlProcedure]

    public static void USP_WRITE_FILE(SqlString SqlSelect, SqlString FullPath)

    {

       

        if (SqlSelect.IsNull || FullPath.IsNull)

        {

            SqlContext.Pipe.Send("Wrong Parameter!");

        }

        else

        {

            string select = SqlSelect.ToString();

 

            DataSet _sqlDS = GetDataSetWithSQL(select);

            CreateFile(_sqlDS, FullPath.ToString());

            SqlContext.Pipe.Send("Saving Complete");

            _sqlDS.Dispose();

        }

 

    }

 

    public static DataSet GetDataSetWithSQL(string _sql)

    {

        SqlDataAdapter _sqlDA = new SqlDataAdapter();

        DataSet _sqlDS = new DataSet();

        SqlConnection _sqlConn = new SqlConnection("Context Connection=True");

 

        SqlCommand _sqlCmd = new SqlCommand(_sql, _sqlConn);

        _sqlDA.SelectCommand = _sqlCmd;

        _sqlDA.Fill(_sqlDS, "TABLE");

        _sqlConn.Close();

 

        return _sqlDS;       

    }

 

    public static void CreateFile(DataSet _myDS, String FullPath)

    {

        int i = 0;

        int fldCnt = 0;

 

        StreamWriter sw = new StreamWriter(FullPath, true);

 

        fldCnt = _myDS.Tables[0].Columns.Count;

 

        //Head 출력

        for (int x = 0; x <= fldCnt - 1; x++)

        {

            sw.Write("[" + _myDS.Tables[0].Columns[x].ColumnName + "]");

            if (x < fldCnt - 1)

                sw.Write(",");           

        }

 

        sw.Write(sw.NewLine);

 

        //Table Data

        for (int x = 0; x <= _myDS.Tables["TABLE"].Rows.Count - 1; x++)

        {

            while (i < fldCnt)

            {

                if (!Convert.IsDBNull(_myDS.Tables["TABLE"].Rows[x][i]))

                {

                    sw.Write(Convert.ToString(_myDS.Tables["TABLE"].Rows[x][i]));

                }

                if (i < fldCnt - 1)

                    sw.Write(",");

                i++;

            }

            i = 0;

            sw.Write(sw.NewLine);

        }

 

        sw.Close();

    }

};

 



다 입력을 하셨다면 배포를 해주십시오.

그리고 query analyze에서

declare @sql varchar(4000)

       , @path varchar(4000)

      

select @sql = 'select * from pubs.dbo.titles'

       , @path = 'C:\LECTURE_CLR\pubs_title.txt'

 

exec dbo.usp_write_file @sql, @path


이런 쿼리하나 실행해보십시오.
그리고 경로에 저장되어 있는 파일을 확인해보십시오.

좋은 하루 되십시오.
오랜만에 파일도 하나 첨부해볼께요.

반응형
반응형

김종열

MS SQL Server MVP

엔트리브 소프트


다른 커뮤너티에서 10진수를 다른 진법의 수로 변환하는가에 대한 질문이 있더군요.
해서 간단한 FUNCTION을 하나 만들어봤습니다.

다음은 UDF_NOTATION.CS의 파일입니다.

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

public partial class UserDefinedFunctions
{
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlString UDF_NOTATION(int oNum, int nNum)
    {
        try
        {
            string _not = Convert.ToString(oNum, nNum);
            return (SqlString)_not;

        }
        catch
        {
            return null;
        }       
    }
};




인자가 수가 먼저 들어온다는 점을 감안하셔야겠죠 ^^,
즉 16진수의 경우는 문자가 포함된 숫자임에도 이는 다시 10진수로 못 나타낸다는 이야기겠죠.

배포를 하시고 나시면 다음과 같은 쿼리로 확인을 해보십시오.


SELECT DBO.UDF_NOTATION(255, 16)


-----
ff
(1 row(s) affected)

반응형
반응형

 

조금 뜸 했네요. 이번엔 간단히 CLR 통합에 대한 ACHITECUTRE를 간단히 이야기 해볼까 합니다. 실은 제가 모르는..  

 

SQL SERVER에서의 CLR은 특별한 환경적 조건이 갖추어져야 실행할 수 있는 “SANDBOX”와 같은 것입니다. (SANDBOX는 놀이터에서 모래가 있는 곳에서 기능을 발휘하는 장난감에서 유래한 단어로 특별한 환경이 아니면 실행할 수 없는 것을 일컫습니다.) 예를 들어 USERCLR OBJECT를 호출하게 되면 SQL SERVER .NET 실행엔진인 MSCOREE.DLL을 메모리에 LOAD하게 되고 사용하게 되고 이후에는 즉시 메모리에서 UNLOAD를 하게 됩니다.

이는 CLR의 환경에서 SQL SERVERCLR OPERATION에 대한 KEY를 컨트롤 하고 있음을 의미한다. CLRSQL SERVER OPERATION SYSTEM(SQLOS)에 새로운 쓰레드와 메모리에 대한 리소스할당을 요구하게 되지만 SQL SERVER는 이러한 요구를 거절하게 될 수도 있습니다. (예를 들어 SQL SERVER의 메모리 한계 상황이거나 CLR에 추가 메모리를 할당할 수 없을 때)

SQL SERVER는 또한 장시간 실행되는 CLR 쓰레드를 모니터링 하면서 장시간 실행되는 CLR 쓰레드를 별견하게 되면 SQL SERVER THREAD를 멈추게 될 것입니다.

SQLOS is not a topic for the novice; we are talking about the “guts” of SQL Server here.

SQLOS is an abstraction layer over the base operating system and its corresponding hardware. SQLOS enables SQL Server (and future server applications from Microsoft) to take advantage of new hardware breakthroughs without having to understand each platform’s complexities and uniqueness. SQLOS enables such concepts as locality (fully utilizing local hardware resources to support high levels of scalability) and advanced parallelism. SQLOS is new with SQL Server 2005. If you wish to learn more about SQLOS you can visit Slava Oks’s weblog at http://blogs.msdn.com/slavao. Slava is a developer on the SQLOS team for Microsoft.








참고 : WROX PROFESSIONAL SQL SERVER 2005 CLR PROGRAMMING


반응형
반응형


그동안 김종열님의 CLR 강좌를 따라 하면서 저에게 필요한 function 을 만들어보았습니다.

기존 김종열님 강좌에서 사용되었던 ‘PRJ_CLR’ 프로젝트와 ‘youlydb’ DB를 사용하였습니다.

Function : UDF_GET_NUM

Function 기능 : 입력된 문자열 중 숫자만을 골라서 리턴

문자열의 길이만큼 for 문을 사용하여 루핑을 돌면서 한 문자씩 숫자인지 확인 후

숫자일 경우 따로 변수에 저장한 후 리턴하는 형식입니다.
소스파일 첨부 : UDF_GET_NUM.cs


using System;

using System.Data;

using System.Data.SqlClient;

using System.Data.SqlTypes;

using Microsoft.SqlServer.Server;

 

 

public partial class UserDefinedFunctions

{

    [Microsoft.SqlServer.Server.SqlFunction]

    public static SqlInt16 UDF_GET_NUM(SqlString str)

    {

        // Put your code here

        try

        {

            string str_s = Convert.ToString(str);

            string _num = "";

            for (int i = 0; i <= str_s.Length-1; i++) //문자열 길이 만큼 반복

            {

                if (Char.IsNumber(str_s, i) == true) //숫자인지 확인

                {

                    _num = _num + str_s.Substring(i, 1); //숫자일 경우 변수에 추가

                }

            }

            return (SqlInt16)Convert.ToInt16(_num); //숫자 형식으로 변환 후 리턴

        }

        catch

        {

            return 0;

        }

    }

};

 





Function 을 생성하고 컴파일, 배포 하는 과정은 기존에 김종열님 강좌에 있으므로 생략하도록 하겠습니다.

 

--결과

WITH TEST

AS

(

     SELECT STR_S = 'RUBY V1.0' UNION ALL

     SELECT STR_S = '1997315' UNION ALL

     SELECT STR_S = '1011106'

)

 

SELECT STR_S, [NUM] = DBO.UDF_GET_NUM(STR_S) FROM TEST

 

 

--RESULT

STR_S            NUM

---------------  -----------

RUBY V1.0        10

1997315     1997315

1011106       1011106

 

(3 row(s) affected)

 

반응형
반응형


김종열

MS SQL Server MVP

엔트리브 소프트


CLR에서 몇 개의 함수를 만들어 보며 CLR을 느꼈으면 합니다.

이번에 해볼건 얼마전부터 만들어보고 싶었던 것인데요.

칼럼의 최대값과 최소값은 MAX, MIN이라는 함수를 이용해서 쉽게 알수 있습니다.

그런데 칼럼간의 최대,  최소를 알고 싶을 때는 어떻게 해야 할까요?

ORACLE에서는 GREATEST, LEAST라는 함수를 제공합니다. 문법도 아주 단순합니다.

GREATEST(A1, A2, A3, …., An) 이라고 하면 그 최대값을 리턴하게 됩니다.

ORACLE에서는 이 함수가 문자열과 수치형 모두 사용가능한 것으로 되어 있습니다.

LEAST라는 함수는 이와 반대로 최소값을 RETURN해주는 것인데요.

 

이번에 제가 만들고자 하는 함수는 GREATEST, LEAST 함수를 만들어보는 것이구요.

저는 단순히 입력은 STRING으로 받고 RETURNINT로 받는 걸로 해서 만들었구요.

그리고 실행계획을 비교해보려고 합니다.

 

1.     기존 프로젝트인 PRJ_CLR OPEN합니다.

2.     사용자 정의 함수를 추가시킵니다. (PROJECT에서 오른 마우스를 클릭 > ADD > NEW ITEM)

3.     NEW ITEM에서 사용자 정의 함수를 선택하고 이름을 “GREATEST_LEAST.CS”라고 입력

4.     여기까지 하고 아래와 같은 소스를 입력합니다. (소스는 첨부하였습니다.)

using System;

using System.Data;

using System.Data.SqlClient;

using System.Data.SqlTypes;

using Microsoft.SqlServer.Server;

 

public partial class UserDefinedFunctions

{

    [Microsoft.SqlServer.Server.SqlFunction]

    public static SqlInt32 GREATEST(string strNum)

    {

        // 여기에 코드를 입력합니다.

        string[] sNum;

        Int32 _maxNum = 0;

 

        try

        {

            sNum = strNum.Split(' ');

 

            for (int i = 0; i <= sNum.Length - 1; i++)

            {

                if (_maxNum < Convert.ToInt32(sNum[i]))

                {

                    _maxNum = Convert.ToInt32(sNum[i]);

                }

            }

 

            return (SqlInt32)_maxNum;

        }

        catch

        {

            return 0;

 

        }

 

    }

 

    [Microsoft.SqlServer.Server.SqlFunction]

    public static SqlInt32 LEAST(string strNum)

    {

        // 여기에 코드를 입력합니다.

        string[] sNum;

        Int32 _minNum = 999999999;

 

        try

        {

            sNum = strNum.Split(' ');

 

            for (int i = 0; i <= sNum.Length - 1; i++)

            {

                if (_minNum > Convert.ToInt32(sNum[i]))

                {

                    _minNum = Convert.ToInt32(sNum[i]);

                }

            }

 

            return (SqlInt32)_minNum;

        }

        catch

        {

            return 0;

 

        }

 

    }

 

};

 

 


    간단히 함수에 대해 설명하면 GREATEST는 문자열을 받아서

구분자로 된 문자열을 SPLIT하여 배열로 만들고 MAXNUM의 초기값을 0으로 한 다음

순환을 하며 값을 비교하여 최대값을 리턴하게 했습니다.


5    
Visual Studio의 컴파일 및 배포를 해줍니다.

 

6.     위의 쿼리에 대한 것을 검증하기 위한 T-SQL문을 만들고 CLR과 비교하겠습니다.

--준비용테이블

create table test(이름varchar(3),성적int,성적int,성적int,성적int,성적int)

GO

 

--TEST DATA 입력

insert test

        select 'AAA',52,78,34,0,43 union all

        select 'BBB',46,54,75,65,24 union all

        select 'CCC',78,86,45,56,12 union all

        select 'DDD',53,77,13,44,67 union all

        select 'EEE',34,24,63,23,83

GO

 

 

 

 

--T-SQL (MADE BY 최석준)

select row_number() over(order by 성적desc) 순위,*

  from (select 이름,

               max(case b.id when 1 then 성적

                             when 2 then 성적

                             when 3 then 성적

                             when 4 then 성적

                             when 5 then 성적end) 성적       

              from test a cross join

                  (select 1 id union all

                   select 2 union all

                   select 3 union all

                   select 4 union all

                   select 5) b

            group by 이름) t

 order by 성적desc

GO

 

--QUERY WITH CLR

select

        이름

        , 성적

        , 성적

        , 성적

        , 성적

        , 성적

        ,  MAX_SCORE = dbo.GREATEST(cast(성적as varchar) + ' ' +

               cast(성적as varchar) + ' ' +

               cast(성적as varchar) + ' ' +

               cast(성적as varchar) + ' ' +

               cast(성적as varchar) )

from test

order by max_score desc

GO


     실행계획 비교 (HOT_KEY : CTRL + L, CTRL + K)


SET SHOWPLAN_TEXT ON

T-SQL (MADE BY 최석준)

 

/*

  |--Sequence Project(DEFINE:([Expr1010]=row_number))

       |--Compute Scalar(DEFINE:([Expr1013]=(1)))

            |--Segment

                 |--Sort(ORDER BY:([Expr1009] DESC))

                      |--Stream Aggregate(GROUP BY:([a].[이름]) DEFINE:([Expr1009]=MAX(CASE WHEN [Union1008]=(1) THEN [youlyDB].[dbo].[test].[성적] as [a].[성적] ELSE CASE WHEN [Union1008]=(2) THEN [youlyDB].[dbo].[test].[성적] as [a].[성적] ELSE CASE WHEN [Union1008]=(3) THEN [youlyDB].[dbo].[test].[성적] as [a].[성적] ELSE CASE WHEN [Union1008]=(4) THEN [youlyDB].[dbo].[test].[성적] as [a].[성적] ELSE CASE WHEN [Union1008]=(5) THEN [youlyDB].[dbo].[test].[성적] as [a].[성적] ELSE NULL END END END END END)))

                           |--Nested Loops(Inner Join)

                                |--Sort(ORDER BY:([a].[이름] ASC))

                                |    |--Table Scan(OBJECT:([youlyDB].[dbo].[test] AS [a]))

                                |--Constant Scan(VALUES:(((1)),((2)),((3)),((4)),((5))))

 

*/

 

QUERY WITH CLR

 

/*

  |--Sort(ORDER BY:([Expr1004] DESC))

       |--Compute Scalar(DEFINE:([Expr1004]=[youlyDB].[dbo].[GREATEST](CONVERT_IMPLICIT(nvarchar(4000),(((((((CONVERT(varchar(30),[youlyDB].[dbo].[test].[성적],0)+' ')+CONVERT(varchar(30),[youlyDB].[dbo].[test].[성적],0))+' ')+CONVERT(varchar(30),[youlyDB].[dbo].[test].[성적],0))+' ')+CONVERT(varchar(30),[youlyDB].[dbo].[test].[성적],0))+' ')+CONVERT(varchar(30),[youlyDB].[dbo].[test].[성적],0),0))))

            |--Table Scan(OBJECT:([youlyDB].[dbo].[test]))

*/

 

SET SHOWPLAN_TEXT OFF


     SET SHOWPLAN_ALL ON / OFF를 이용해서 세부적인 사항도 한번 비교해보십시오.



도움파일 : GREATEST_LEAST.CS
 GREATEST_LEAST.SQL


반응형
반응형

 

김종열

MS SQL Server MVP

엔트리브 소프트


Mssql에 접속하는 Host_name을 아는 건 별 어려운 건 아닙니다.

그런데 접속자의 IP를 구하기 위해서는 어떻게 하나요?

Computername (흔히들 hostname이라는 함수로 얻어지는)을 이용해서 IP를 구하는 function을 하나 만들었습니다.

 

1.     기존 프로젝트인 PRJ_CLR OPEN합니다.

2.     사용자 정의 함수를 추가시킵니다. (PROJECT에서 오른 마우스를 클릭 > ADD > NEW ITEM)

3.     NEW ITEM에서 사용자 정의 함수를 선택하고 이름을 “UDF_GET_IP.CS”라고 입력

4.     여기까지 하고 아래와 같은 소스를 입력한다. (소스는 첨부하였습니다.)



using
System;

using System.Data;

using System.Data.SqlClient;

using System.Data.SqlTypes;

using Microsoft.SqlServer.Server;

using System.Net;

using System.Collections;

 

public partial class UserDefinedFunctions

{

    [Microsoft.SqlServer.Server.SqlFunction]

    public static SqlString Udf_Get_IP(string hname)

    {      

        try

        {

            IPHostEntry _IP = Dns.GetHostEntry(hname);

            string str_ip = _IP.AddressList[0].ToString();

            return (SqlString)str_ip;

        }

        catch

        {

            return null;

        }

    }

}

 

 

 

소스가 너무 간단해서 놀라고 있는 건 아니겠죠?

물론 이 말고도 여러가지가 있어요. Ping Test를 이용한 ResultIP로 그 값을 구하는 것이나

WMI를 이용해서 IP를 이용할 수 있습니다.

또한 유무선이나 virtualmachine IP를 두개 이상 가지고 계신분의 것은 정확하지 않아요..

_IP.AddressList[0].ToString()이런 말에서 보듯 이미 복수개 이상의 IP 있을 있다는 거죠.

이건 상위 하나만 잡는 다는 뜻이겠죠. 만약 리스트를 받으시려고 한다면

당연히 Table Return 만드셔야 겠죠.

 

 

5.     Visual Studio의 컴파일 및 배포를 해줍니다.

 

USE YOULYDB

GO

 

SELECT IP_ADDR = DBO.UDF_GET_IP('YOULYN')

/*

ip_addr

---------------

192.168.**.***

 

(1 row(s) affected)

*/

 

 

참고로 select ip_addr = dbo.udf_get_ip(host_name()) 이런 구문도 얼마든지 가능합니다.

당연히 hostname이 없다면 Null을 리턴합니다
실제로는 보이실 겁니다. 사무실의 내부 아이피지만 IP는 가렸습니다.  

 

 

도움파일 : Udf_Get_IP.CS


점점 이미지 파일이 없고 딱딱해져 가죠..
그리고 성능 검증편을 이쯤되면 해줘야 할 듯한데.. 흐흠.. 귀찮으네요 ^^

반응형
반응형



 

김종열

MS SQL Server MVP

엔트리브 소프트


이번에도 TABLE RETURN함수 입니다.

이번 함수의 역할은 특별한 구분자-저는 여기서 “,”를 사용했습니다.-의 데이터로 구분되어진 파일이 있습니다.

텍스트 파일의 내용은 대강 이렇습니다. (첨부하였습니다.)

1, www.sqllearder.com

2, www.naver.co.kr

3, www.empas.kr



이 파일의 결과를 쿼리하게 되면 아래와 같은 식이 되게 하는 함수입니다.

NUM

TXT

1

www.sqllearder.com

2

www.naver.co.kr

3

www.empas.kr

 

1.     기존 프로젝트인 PRJ_CLR OPEN합니다.

2.     사용자 정의 함수를 추가시킵니다. (PROJECT에서 오른 마우스를 클릭 > ADD > NEW ITEM)

3.     NEW ITEM에서 사용자 정의 함수를 선택하고 이름을 “UDF_READ_FILE_2_ARRAY.CS”라고 입력

4.     여기까지 하고 아래와 같은 소스를 입력한다. (소스는 첨부하였습니다.)

using System;

using System.Data;

using System.Data.SqlClient;

using System.Data.SqlTypes;

using Microsoft.SqlServer.Server;

using System.IO;

using System.Collections;

 

 

public partial class UserDefinedFunctions

{

    [Microsoft.SqlServer.Server.SqlFunction(FillRowMethodName = "fillTable2", TableDefinition = "Num nvarchar(20), Txt nVarchar(1000)")]

    public static IEnumerable UDF_READ_FILE_2_ARRAY(SqlString fname)

    {

        string[] sItems;

        StreamReader oSr;

        char cDelimeter = Convert.ToChar("\n");

        string c_fname = Convert.ToString(fname);

 

        try

        {

            if (File.Exists(c_fname))

            {

                oSr = new StreamReader(c_fname, System.Text.Encoding.Default);

                sItems = oSr.ReadToEnd().Split(cDelimeter);

                oSr.Close();

 

                return (sItems);

            }

            else

            {

                throw new System.Exception("File Not found");

            }

        }

        catch

        {

            return null;

        }

    }

 

 

    private static void fillTable2(Object obj, out SqlString Num, out SqlString Txt)

    {

        string sTemp = Convert.ToString(obj);

        string[] arr_temp = sTemp.Split(',');

        try

        {

            Num = (SqlString)arr_temp[0];

            Txt = (SqlString)arr_temp[1];

        }

        catch

        {

            Num = null;

            Txt = null;

        }

 

    }

}

 


TABLE DEFINITION부분과 FILLTABLE에서 인자를 갑자기 둘로 주는 부분이 보이시죠?

그 부분이 이번 강의 요입니다. 그리고 저 SPLIT이라는 함수를 눈여겨 봐주십시오. 간단히 설명하면 구분자로 분리하여 문자열(STRING) 배열로 잡는 함수입니다.


5.    
Visual Studio의 컴파일 및 배포를 해줍니다.

 

이전 강의를 하셨다면 permition level을 외부 라고 지정이 되어 있을 겁니다.
그렇지 않다면 이전 강의를 참고하시기 바랍니다.

 

USE YOULYDB

GO

 

 

SELECT * FROM DBO.UDF_READ_FILE_2_ARRAY('C:\TEMP\SAMPLE2.TXT')

/*

Num                  Txt

1                     www.sqllearder.com

2                     www.naver.co.kr

3                     www.empas.kr

NULL                 NULL

(4 row(s) affected)

*/

 

 

FILLTABLE이나 TABLE DEFINITION은 실무에 맞게 변경하셔서 사용하시기 바랍니다.



도움파일 : UDF_READ_FILE_2_ARRAY.CS

          SAMPLE.TXT


반응형
반응형


SELECT A.* FROM SYS.ASSEMBLY_FILES A
INNER JOIN SYS.ASSEMBLIES B
ON A.ASSEMBLY_ID = B.ASSEMBLY_ID
GO

 

SELECT * FROM SYS.ASSEMBLY_MODULES A
INNER JOIN SYS.ASSEMBLIES B
ON A.ASSEMBLY_ID = B.ASSEMBLY_ID
GO

 

SELECT * FROM SYS.ASSEMBLY_REFERENCES
GO

 

SELECT * FROM SYS.MODULE_ASSEMBLY_USAGES
GO

반응형

'연구개발 > CLR' 카테고리의 다른 글

CLR 강좌 - Hostname을 이용한 IP구하기  (0) 2009.06.22
CLR 강좌 - TEXT파일 읽기2  (0) 2009.06.22
CLR 강좌 - TEXT파일 읽기  (0) 2009.06.22
CLR 강좌_ 함수 인자 주고 받기  (0) 2009.06.22
CLR 권한 처리 문제  (0) 2009.06.22
반응형


김종열

MS SQL Server MVP

엔트리브 소프트


함수 만드는 거 하나 더 해볼까요?

 

앞서 이야기 했던 함수 종류인 Table을 리턴 하는 함수를 하나 만들어볼까 합니다.

함수의 기능은 아주 단순합니다. ASCII 파일을 읽어서 QUERY의 결과에 TABLE 형식으로 리턴해주는
 Table Return 함수입니다.
또한 앞강에서 인자의 사용법을 알았으니 파일의 경로를 인자로 처리하여 경로에 있는 파일을 읽는 것으로 하겠습니다.

 

주의하실 점은 지금까지는 대부분의 C# 코드는 하나의 CLASS 되었었는데
이번엔 하나의 METHOD가 더 있어야 합니다.
TABLE
을 리턴하므로 TABLE을 만들어주는 METHOD-
FillRowMethodName”- 있어야 합니다.


1.     기존 프로젝트인 PRJ_CLR OPEN합니다.

2.     사용자 정의 함수를 추가시킵니다. (PROJECT에서 오른 마우스를 클릭 > ADD > NEW ITEM)
    (이미지는 전 강의를 참고하십시오.)

3.     NEW ITEM에서 사용자 정의 함수를 선택하고 이름을 “UDF_READ_FILE.CS”라고 입력

4.     여기까지 하고 아래와 같은 소스를 입력한다. (소스는 첨부하였습니다.)

using System;

using System.Data;

using System.Data.SqlClient;

using System.Data.SqlTypes;

using Microsoft.SqlServer.Server;

using System.IO;

using System.Collections;

 

 

public partial class UserDefinedFunctions

{

    [Microsoft.SqlServer.Server.SqlFunction(FillRowMethodName = "fillTable", TableDefinition="txt nvarchar(4000)")]

    public static IEnumerable UDF_READ_FILE(SqlString fname)

    {

        string[] sItems;

        StreamReader oSr;

        char cDelimeter = Convert.ToChar("\n");

        string c_fname = Convert.ToString(fname);

 

        try

        {

            if (File.Exists(c_fname))

            {

                oSr = new StreamReader(c_fname, System.Text.Encoding.Default);

                sItems = oSr.ReadToEnd().Split(cDelimeter);

                oSr.Close();

 

                return (sItems);

            }

            else

            {

                throw new System.Exception("File Not found");

            }

        }

        catch

        {

            return null;

        }

    }

 

    private static void fillTable(object obj, out SqlString sItem)

    {

        string sTemp = Convert.ToString(obj);

        sItem = (SqlString)sTemp;

    }

};

 


갑자기 기존의 코드보다 복잡하다고 혹시 당황해하시고 있는 것은 아닌지 모르겠습니다.
관심이 있으신 분은 C#에 대한 공부를 다른 루틴으로 하시기 바랍니다. 공부를 하시게 되면 위의 코드는 그렇게 어려운 소싱이 아니게 될 것이라 믿고 싶습니다. (개인적으로는..)

5.     Visual Studio의 컴파일 및 배포를 이용하자.

6. 여기까지 이상없었다면 바로 Query에서 샘플텍스트를 읽고자 할 것입니다.

 

SELECT * FROM DBO.UDF_READ_FILE('C:\TEMP\SAMPLE.TXT');

 


그런데 예전처럼 한번에 결과가 나오지 않네요.


이유는 조금 있다가 설명을 드리기로 하고 제대로 나오게 다음과 같은 설정을 더해줍시다.
  Solution  Exploer에서 prject에 오른 마우스를 누르고 property를 선택하십시오.



데이터베이스를 선택한 다음 Database 그리고 permition level을 외부 라고 지정합니다.




pub한 뒤 배포를 하면 텍스트에 직접 쿼리를 하신 결과를 만나게 됩니다. (5, 6과정)

 

USE YOULYDB

GO

 

 

SELECT * FROM DBO.UDF_READ_FILE('C:\TEMP\SAMPLE.TXT');

 

/*

txt

--------------------

www.sqllearder.com

www.naver.co.kr

www.empas.kr

 

 

(4 row(s) affected)

*/

 

 

 

 

Permission level이란? (Query에서는 Permission_set {safe|Extrnal_access | Unsafe}

CLR은 결국 외부에서 만든 dll의 모듈을 사용하게 되는데요. 이 코드에 액세스할 때는 부여된 코드에 액세스 권한을  지정합니다. 지정하지 않으면 기본적으로 safe가 됩니다. 

 

Safe는 가장 제한적인 권한으로 파일, 네트워크, 환경변수, 레지스트리, 외부 시스템 리소스에 액세스 할 수 없습니다. 즉 자신의 시스템에서 가벼운(강의로 따지면 이 이전까지?) 레벨의 처리라고 할 수 있으며, 시스템에 영향을 줄 수 있는 것을 못 쓰게 하는 것입니다. 즉 시스템에게는 safe라고 할 수 있겠네요.

External access safe와 반대로 외부의 리소스 및 파일, 네트워크 등등을 호출할 수 있슴을 나타냅니다.

Unsafe를 사용하게 되면 sql server 인스턴스의 내부 리소스와 외부 리소스 모두 제한 없이 액세스하게 되며, unsafe mode에서 실행되는 코드는 비관리 코드를 호출할 수 있습니다

 

 

도움파일 : UDF_READ_FILE.CS

          SAMPLE.TXT

반응형

'연구개발 > CLR' 카테고리의 다른 글

CLR 강좌 - TEXT파일 읽기2  (0) 2009.06.22
등록된 DLL 정보 확인하기  (0) 2009.06.22
CLR 강좌_ 함수 인자 주고 받기  (0) 2009.06.22
CLR 권한 처리 문제  (0) 2009.06.22
CLR 강좌 - CLR_수동배포  (0) 2009.06.22
반응형


김종열

MS SQL Server MVP

엔트리브 소프트



오늘은 기존 프로젝트에 함수를 추가 시키는 과정과

함수에서 인자를 주고 받는 예제를 하나 만들어 볼까 합니다.

 

MSSQL 에서는 함수를 크게 2가지로 나눌수 있습니다.

단일 VALUE RETURN하는 SCALAR함수와 TABLE을 리턴하는 함수가 그것인데요.

이번엔 단일 VALUE RETURN하는 SCALAR함수 하나 만들어보려고 합니다.

 

기능은 EMAIL 주소의 VALID를 체크하는 함수인데요.

또한 이 함수의 기능은 MSSQL2005 WHITE PAPER에 소개된 함수라 많이들 적용하고 있으리라 짐작이 듭니다.

 
1.     기존 프로젝트인 PRJ_CLR OPEN합니다.

2.     사용자 정의 함수를 추가시킵니다. (PROJECT에서 오른 마우스를 클릭 > ADD > NEW ITEM)

 - 개인적으로는 소스를 한 곳에서 관리하시는 것이 개발이나 유지보수 측면에서는 훨씬 더 좋지 않을까 추천합니다.

3.     NEW ITEM에서 사용자 정의 함수를 선택하고 이름을 “UDF_Mail_Check.cs”라고 입력



4.  여기까지 하고 아래와 같은 소스를 입력한다. (소스는 첨부하였습니다.)


부연 설명을 조금 한다면 C# 코드중
using System.Text.RegularExpressions; 부분은정규식을 사용하기 위해 C# Library 사용하겠다는 코드입니다. 붉은 색으로 표시된 부분인 함수 부분에는 SqlBoolean 데이터 처리 Return되는 형식을 sql bool형태로 하겠다는 선언이며 string email 이라는 부분은 함수를 처리할 입력되는 인자는 email이라는 string이라는 선언입니다

C# 정규식이나 함수의 설명, Method 대한 사용은 하지 않겠습니다. (이는 SQL CLR 영역이 아닌 C# 영역이라 생각되어)


5.    Visual Studio의 컴파일 및 배포를 이용하자. (수동으로 하고자 한다면 2강을 참조)


6. 여기까지 이상없었다면 아래의 쿼리로 메일 주소의 VALID를 검사해보자(첨부하였슴)

USE YOULYDB

GO

 

WITH EMAIL_TMP AS (

        SELECT EMAIL_ADDR = 'YOULY_92@HOTMAIL.COM' UNION ALL

        SELECT EMAIL_ADDR = 'YOULY_92HOTMAIL.COM'  UNION ALL

        SELECT EMAIL_ADDR = 'YOULY_92.HOTMAIL.COM'  UNION ALL

        SELECT EMAIL_ADDR = 'YOULY_92@HOTMAIL.CO.KR'  UNION ALL

        SELECT EMAIL_ADDR = 'YOULY_92.COM'  UNION ALL

        SELECT EMAIL_ADDR = 'YOULY_92@COM'

)

SELECT

        EMAIL_ADDR

        , VALID = DBO.UDF_MAIL_CHECK(EMAIL_ADDR)

FROM

        EMAIL_TMP

GO

 

/*

EMAIL_ADDR             VALID

---------------------- -----

YOULY_92@HOTMAIL.COM   1

YOULY_92HOTMAIL.COM    0

YOULY_92.HOTMAIL.COM   0

YOULY_92@HOTMAIL.CO.KR 1

YOULY_92.COM           0

YOULY_92@COM           0

 

(6 row(s) affected)



 

도움파일 : UDF_Mail_Check.cs

          메일주소검증예제.SQL


반응형

'연구개발 > CLR' 카테고리의 다른 글

등록된 DLL 정보 확인하기  (0) 2009.06.22
CLR 강좌 - TEXT파일 읽기  (0) 2009.06.22
CLR 권한 처리 문제  (0) 2009.06.22
CLR 강좌 - CLR_수동배포  (0) 2009.06.22
SQL 2005 따라하기 6 (CLR)  (0) 2009.06.22
반응형

어셈블리 'SqlServerProject'에 PERMISSION_SET = EXTERNAL_ACCESS에 대한 권한이 없으므로 어셈블리 'SqlServerProject'에 대한 CREATE ASSEMBLY가 실패했습니다. 어셈블리는 DBO(데이터베이스 소유자)에게 EXTERNAL ACCESS ASSEMBLY 권한이 있고 데이터베이스에 TRUSTWORTHY 데이터베이스 속성이 있는 경우 또는 어셈블리가 현재 인증서로 서명되어 있거나 EXTERNAL ACCESS ASSEMBLY 권한이 있는 관련 로그인을 소유한 비대칭 키로 서명되어 있는 경우에 권한이 부여됩니다. 이 데이터베이스를 복원하거나 연결한 경우 데이터베이스 소유자가 이 서버의 올바른 로그인에 매핑되어 있는지 확인하십시오. 그렇지 않으면 sp_changedbowner를 사용하여 문제를 해결하십시오.

라는 엿같은 메시지가 나온다면...

또는 비스무리한 권한 문제가 나온다면 아래와 같이 실행한다.

EXEC SP_CONFIGURE 'CLR ENABLED', 1
GO
RECONFIGURE WITH OVERRIDE  (OR RECONFIGURE)
GO

ALTER DATABASE [해당데이터베이스]
SET TRUSTWORTHY ON
반응형

'연구개발 > CLR' 카테고리의 다른 글

CLR 강좌 - TEXT파일 읽기  (0) 2009.06.22
CLR 강좌_ 함수 인자 주고 받기  (0) 2009.06.22
CLR 강좌 - CLR_수동배포  (0) 2009.06.22
SQL 2005 따라하기 6 (CLR)  (0) 2009.06.22
CLR 강좌 - CLR 환경 설정 및 맛보기  (0) 2009.06.22
반응형


CLR에 대한 맛보기 강좌 후 1주일인가요?

 

될 수 있으면 정기적 Update를 하고 싶은데 그것도 뜻대로 잘 안되죠?

이번에 소개 시켜드리고자 하는 과정은 두 가지 입니다.

 

실행을 했을 때 결과는 크게 두 가지 형태가 있습니다.

하나는 Grid형식(or text)의 결과 또 다른 하나는 메시지 형태입니다.

지난 과정에서는 Grid 형식으로 출력했습니다.

이런 Method를 사용했죠. SqlContext.Pipe.ExecuteAndSend

 

SqlContext.Pipe.Send 이번엔 이런 과정을 통해 메시지 창에 결과가 뿌려지게 해보죠

 

 

두번째는 컴파일을 visual studio 통해 직접 배포하는 과정을 했으니 이번엔

Compile 직접하여 Assembly 불러들이고, Procedure에서 Assembly

불러들이는 배포과정을 수동으로 처리해보겠습니다.

 

 

먼저 과정과 동일하게 visual Studio 실행하여 c# SqlServer Project 하나 만드세요.
Project
Add > New Item Stored Procedure 추가하십시오. (그림참고)



위의 과정이 되셨다면 간단한 코드를 추가해볼까요?

코드를 입력하라는 곳에 다음과 같은 코드를 추가해보세요.


SqlContext
.Pipe.Send("hello World From Sql clr");




이번엔 컴파일 배포를 하지 마시고 저장만 하고 나오세요.

배포를
하기 위해 먼저 dll 컴파일을 해야 합니다. Compiler 위해 2005 명령프롬프트를 실행하세요.


실행을 마치셨다면 command prompt 같은 창이 하나 나올겁니다.

이것을 실행하지 않으려면 Path설정을 하시면 되는데 그런건 이야기 하지 않겠습니다.

그냥 제공되는 것만 쓰겠습니다.

 

아까 우리가 닫았던 파일의 project 있던 , 정확히 이야기하면 cs 있던 곳으로 가서 dll 하나 만들어볼까 합니다.

 

저의 경우는 Root:\Lecture_CLR\Hello2 있습니다.


Root:\Lecture_CLR\Hello2\csc /t:library StoredProcedure1.cs



경로에서 이렇게 입력하여 실행하면 c# compiler version등등과 함께 아무 메시지도 없는 것을 보게 됩니다. (dir *.dll dll 생성된 것을 확인하십시오.)

 

여기까지가 수동 컴파일입니다.

이제 DB 배포하는 과정이 남았습니다.

 

먼저 dll Database load하는 것을 Assembly라고 하는데요

 

USE YOULYDB

GO

 

CREATE ASSEMBLY ASM_HELLO2 FROM 'C:\LECTURE_CLR\Hello2\StoredProcedure1.dll'

WITH PERMISSION_SET = EXTERNAL_ACCESS

go





이제 PROCEDURE 하나 만들고 실행하는 과정만 남았네요


CREATE
PROC USP_HELLO_CLR2

AS

EXTERNAL NAME ASM_HELLO2.StoredProcedures.USP_HELLO_CLR2

GO





실행하면 다음과 같은 결과를 보시게 됩니다.




도움파일 : StoredProcedure1.CS

           배포.SQL

반응형

'연구개발 > CLR' 카테고리의 다른 글

CLR 강좌 - TEXT파일 읽기  (0) 2009.06.22
CLR 강좌_ 함수 인자 주고 받기  (0) 2009.06.22
CLR 권한 처리 문제  (0) 2009.06.22
SQL 2005 따라하기 6 (CLR)  (0) 2009.06.22
CLR 강좌 - CLR 환경 설정 및 맛보기  (0) 2009.06.22
반응형


CLR 통합
데이터베이스 개발에 .NET Framework을 활용하고,
관리되는 코드 권한을 이해하며,
관리되는 코드를 사용해 트리거를 생성하고,
사용자 정의 형식과 집계를 생성하며,
T-SQL과 관리되는 코드를 비교, 대조하고,
ADO.NET에서 새로운 기능 탐색의 내용이 포함되어 있습니다. 
반응형

'연구개발 > CLR' 카테고리의 다른 글

CLR 강좌 - TEXT파일 읽기  (0) 2009.06.22
CLR 강좌_ 함수 인자 주고 받기  (0) 2009.06.22
CLR 권한 처리 문제  (0) 2009.06.22
CLR 강좌 - CLR_수동배포  (0) 2009.06.22
CLR 강좌 - CLR 환경 설정 및 맛보기  (0) 2009.06.22
반응형

김종열

MS SQL Server MVP

엔트리브 소프트

 

 

MSSQL  사용하며  FUNCTION이나  PROCEDURE으로  여러기능을  구현하며

이런  기능이  있었으면  좋겠는데…“

라는  생각을  한번쯤  해보신  적이있나요?  

물론    이상의  기능을  가지고  있음에도  불구하고 저는  이런  작은  의문이나  구현의  욕심으로  CLR & SQL  공부하게  되었고    흔적들을 공유하고자  강좌를  시작하게  되었습니다.

 

부족함이  많기에    부분을  공부하신  분들이  같이  채워주시리라  믿습니다.

또한  실무에  적용시  에러에  대한  것은  책임지지  않음을..

 

 

개발환경

 

OS : WINDOWS VISTA ULTIMATE (SP2)

DATABASE : MSSQL2005 DEVELOPER

TOOL : VISUAL STUDIO 2005

FRAMEWORK : 2.0이상

LANGUAGE : C#

 

 

MSSQL2005  TEST  DATABAS 하나를  만들고  가겠습니다.

한번  설정된  기능은  바꾸지  않는한  특별히  변경되지는  않습니다.

 

CREATE DATABASE YOULYDB

GO

 

ALTER DATABASE youlyDB SET TRUSTWORTHY ON

GO

 

USE YOULYDB

GO

 

--CLR  사용할    있도록  DATABASE CONFIGURE  변경

SP_CONFIGURE 'CLR ENABLED', 1

RECONFIGURE WITH OVERRIDE

GO

 

--CLR  사용할    있는   확인

SP_CONFIGURE  

GO

 

/**********************************************************************************

name                                minimum     maximum     config_value run_value

----------------------------------- ----------- ----------- ------------ -----------

clr enabled                         0           1           1            1

**********************************************************************************/

 

 

 

주의)

SP_CONFIGURE 'CLR ENABLED', 0

RECONFIGURE WITH OVERRIDE

GO

 

위의  명령으로  DATABASE CONFIGURE  바꾸시면  LOAD 되어있는  CLR  모듈들이  전부  UNLOAD 됩니다.

 

 

 

DATABASE  옵션  변경하기

 

CLR  사용자정의함수, 프로시져등이  외부에서  만든개체에  접근하여  사용하기  때문에

DATABASE  옵션은  외부개체의  접근을  허용해주어야합니다.

 

ALTER DATABASE YOULYDB SET TRUSTWORTHY ON

GO

 

 

--설정 확인  하기

SELECT NAME, IS_TRUSTWORTHY_ON FROM SYS.DATABASES WHERE NAME = 'YOULYDB'

 

 

 

여기까지가  DATABASE설정부분입니다.

USP_HELLO  한번  만들어  볼까요?  아래의  과정에  대한  설명은  차후에  설명하도록  하겠습니다.

이번  강좌는  맛보기의  HELLO이므로    의미는 두지  않으시길  ^^



만드는  과정

 

                  1. C# DATABASE PROJECT 생성  

                  2. 사용자저장프로시져  생성  

                  3. COMPILE

                  4. DATABASE에서  실행하기  

 

 

1. C# DATABASE PROJECT 생성  하는 과정입니다.
 


2. C# Project Save

 



3. SELECT DATABASE(
적절한 데이터베이스를 선택하십시오. 생성되어지는 object 들어갈 DB입니다.)

 

 

 

4. solution explore에서 사용자 저장 프로시져를 추가해주십시오.

 

5. 추가하는 stored_procedure cs파일은 usp_hello_clr.cs 저장하십시오 

 

 

6. 코딩하고 컴파일 하는 과정입니다. (cs파일의 소스는 첨부하였습니다.)

  

 

7. build/배포 하십시오. (menu > build > build prj_clr)

 

8. Run

 

 

                  EXEC USP_HELLO_CLR

                  go

 

                  clr_text

                  --------

                  Hello

 

                  (1 row(s) affected)



 

메타데이터 확인하기

SELECT A.* FROM SYS.ASSEMBLY_FILES  A

INNER JOIN SYS.ASSEMBLIES B

ON A.ASSEMBLY_ID = B.ASSEMBLY_ID

WHERE B.NAME = 'PRJ_CLR'

GO

 

SELECT * FROM SYS.ASSEMBLY_MODULES  A

INNER JOIN SYS.ASSEMBLIES B

ON A.ASSEMBLY_ID = B.ASSEMBLY_ID

WHERE B.NAME = 'PRJ_CLR'

GO

 

SELECT * FROM SYS.ASSEMBLY_REFERENCES

GO

 

SELECT * FROM SYS.MODULE_ASSEMBLY_USAGES

GO

 

반응형

'연구개발 > CLR' 카테고리의 다른 글

CLR 강좌 - TEXT파일 읽기  (0) 2009.06.22
CLR 강좌_ 함수 인자 주고 받기  (0) 2009.06.22
CLR 권한 처리 문제  (0) 2009.06.22
CLR 강좌 - CLR_수동배포  (0) 2009.06.22
SQL 2005 따라하기 6 (CLR)  (0) 2009.06.22

+ Recent posts

반응형