Friday, August 31, 2007

CLR Integration in SQL Server 2005

CLR Integration in SQL Server 2005

The integration of Common Language Runtime (CLR) in .NET application using SQL Server 2005


ขั้นตอนในการสร้าง CLR stored procedure
  • ทำการ Enable CLR integration ใน SQL Server 2005
  • สร้าง CLR stored procedure Assembly โดยใช้ Visual Studio 2005
  • ติดตั้ง Assembly ใน SQL Server 2005
  • สร้างและ execute ตัว CLR stored procedure ใน SQL Server 2005

อ่านต่อ ...

--NooM--

การส่ง E-mail กับ .NET Framework 2.0

การส่ง E-mail นั้น เดิมใน Visual Basic 6.0 นั้น จะต้องใช้ Microsoft Outlook Object Library ช่วยในการจัดการ E-mail ซึ่งทำให้เครื่องที่จะใช้งาน function ในการส่ง E-mail นั้น จะต้องติดตั้ง Microsoft Outlook และ ทำการ config mail profile ให้เรียบร้อยด้วย แต่ถ้าเป็น .NET 2.0 แล้ว จะทำให้เขียนโปรแกรมได้สบายมากขึ้น

ใน .NET Framwork 2.0 มี System.Net.Mail ไว้ให้ใช้งาน โดยเพียงแค่เรากำหนดค่า property ที่เหมาะสมเท่านั้นก็สามารถส่ง E-mail ได้ทันที แล้วก็ง่ายมากด้วย

อ่านต่อ ....

--NooM--

การทำ Popup help text โดยใช้ Javascript

หลักในการทำ Popup help text 1.สร้าง object div ใน html document โดยกำหนดให้ visibility เป็น hidden 2.กำหนดให้ object div ยกเลิกการซ่อนและแสดงผลความตำแหน่งที่ต้องการเมื่อเกิด event onmouseover บน control ที่ต้องการ Code : popuptext.htm
<html>
<head>
<title>การแสดงกล่องข้อความแนะนำ</title>
<style type="text/css">
#hintBox{
position:absolute;
top:0;
background-color:white;
width:150px;
padding:3px;
border:1px solid black;
font:normal 11px Verdana;
line-height:18px;
z-index:100;
border-right:3px solid black;
border-bottom:3px solid black;
visibility: hidden;
}
.hint {
font-weith:bold;
color:navy;
margin:3px 8px;
}
</style>
<script language="javascript">
var horizontalOffset = "9px"
var verticalOffset = "0"
var ie = document.all
var ns6 = document.getElementById && !document.all
function getposOffset(elm, offsettype) {
var totalOffset = (offsettype == "left") ? elm.offsetLeft:elm.offsetTop;
var parentElement = elm.offsetParent;
while (parentElement != null) {
totalOffset = (offsettype == "left") ? totalOffset + parentElement.offsetLeft:totalOffset + parentElement.offsetTop;
parentElement = parentElement.offsetParent;
}
return totalOffset;
}
function ieTest() {
return(document.compatMode && document.compatMode != "BackCompat") ? document.documentElement:document.body;
}

function clearEdge(obj, whichedge) {
var edgeoffset = (whichedge == "rightedge") ? parseInt(horizontalOffset)*-1 : parseInt(verticalOffset)*-1
if (whichedge == "rightedge") {
var windowEdge = ie && !window.opera ? ieTest().scrollLeft + ieTest().clientWidth - 30 : window.pageXOffset + window.innerWidth - 30;
dropMenu.contentMeasure = dropMenu.offsetWidth;
if(windowEdge - dropMenu.x < dropMenu.contentMeasure)
edgeoffset = dropMenu.contentMeasure + obj.offsetWidth + parseInt(horizontalOffset);
}
else {
var windowEdge = ie && !window.opera ? ieTest().scrollTop + ieTest().clientHeight - 30 : window.pageYOffset + window.innerHeight - 30;
dropMenu.contentMeasure = dropMenu.offsetHeight;
if(windowEdge - dropMenu.y < dropMenu.contentMeasure)
edgeoffset = dropMenu.contentMeasure - obj.offsetHeight;
}
return edgeoffset;
}

function showhint(menuContent, obj, e, tipwidth){
if((ie||ns6) && document.getElementById("hintBox")) {
dropMenu = document.getElementById("hintBox");
dropMenu.innerHTML = menuContent;
dropMenu.x = getposOffset(obj, "left");
dropMenu.y = getposOffset(obj, "top");
dropMenu.style.left = dropMenu.x - clearEdge(obj, "rightedge") + obj.offsetWidth + "px";
dropMenu.style.top = dropMenu.y - clearEdge(obj, "bottomedge") + "px";
dropMenu.style.visibility = "visible";
obj.onmouseout = hideHint;
}
}

function hideHint(e) {
dropMenu.style.visibility = "hidden";
}

function createHint() {
var block = document.createElement("div");
document.body.appendChild(block);
block.setAttribute("id","hintBox");
}

if(window.addEventListener)
window.addEventListener("load", createHint, false);
else if(window.attachEvent)
window.attachEvent("onload", createHint);
else if(document.getElementById)
window.onload = createHint;
</script>
</head>
<body>
<form>
<b>ชื่อในการใช้งาน</b>
<input type="text" class="test" style="width: 152px" /><a href="#" class="hint" onmouseover=" showhint('พิมพ์ชื่อผู้ใช้ โดยให้เป็นอักขระจากแป้นพิมพ์เท่านี้น', this, event, '150px')"><font color="#6666FF">[?]</font></a><br />

<b>รหัสผ่าน</b><input type="text" class="test" /><a href="#" class="hint" onmouseover=" showhint('ใส่รหัสผ่าน โดยมีอย่างน้อย 8 ตัวอักษร', this, event, '200px')"><font color="#6666FF">[?]</font></a><br />

<br/>
<b>e-mail address</b><input type="text" class="test" style="width: 152px" ID="Text1" NAME="Text1" onmouseover=" showhint('กรุณาใส่ e-mail address', this, event, '200px')"/>
</form>
</body>
</html>
ผลลัพท์ เมื่อนำเมาส์ไปวางบน [?] จะขึ้น popup help text ขึ้นมาให้ หรือเมื่อนำเมาส์ไปวางบน text box ของ e-mail address ก็จะขึ้น popup help text ขึ้นมาให้เช่นกัน

Hello World !! AJAX

เริ่มต้นเขียนโปรแกรม AJAX for ASP.NET 2005 กันกับโปรแกรม Hello World AJAX กันครับ
ที่ Link นี้เลย http://docs.google.com/Doc?id=dds4wnnw_22fg2z7v

Author by NooM

Reindex for MS SQL Server 2005

สำหรับ MS SQL 2005 นั้น ก็จะคล้่ายๆ กับ 2000 ครับ
เพียงเปลี่ยนจาก sysobjects และ sysusers เป็น sys.tables และ sys.schemas
declare @name varchar(128), --ประกาศตัวแปรสำหรับเก็บชื่อ ตาราง
@user varchar(128),
--ประกาศตัวแปรสำหรับเก็บชื่อ เจ้าของตาราง
@statement varchar(1000)


declare tablename cursor for
--ประกาศตัวแปร cursor ข้อมูล
select t.name,
s.name
from sys.tables as t, sys.schemas as s
where t.schema_id = s.schema_id

open tablename
--เปิด cursor

fetch next from tablename --อ่านข้อมูลจาก cursor และให้ข้อมูลเข้าตัวแปร
into @name, @user

while @@fetch_status = 0 -- วนลูป cursor จนกว่าหมดข้อมูล

begin
set @statement = 'DBCC DBREINDEX (''[' + @user + '].[' + @name + ']'')' --สร้าง sql statement เพื่อ re-index ตาราง
print @statement + '...'
execute (@statement) -- สั่งให้ execute sql statement ที่ re-index ตาราง
fetch next from tablename
into @name, @user
end

close tablename -- ปิด cursor
deallocate tablename -- เคลียร์ memory

go

Reindex for MS SQL Server 2000

การ maintenance ฐานข้อมูลเป็นเรื่องที่สำคัญมาก ทุกๆวัน ฐานข้อมูลจะผ่านการ Insert, Update, Delete ข้อมูลต่างๆ มากมาย ซึ่งการ Re-index ก็เป็นส่วนหนึ่งของการ maintenance ระบบให้มีประสิทธิภาพ (performance) ที่ดีอยู่ตลอดเวลา

---------------------------------------------------------
Real case
ตอนเช้าของเกือบทุกวัน ผมจะได้รับสายฯ จาก users มาต่อว่า "ทำไมค้นหาข้อมูลนานมากเลย" "ระบบของคุณขึ้น Error ว่า Timeout Expire อีกแล้ว" "ผม Insert ข้อมูลไม่ได้" คำถามต่างๆเหล่านี้ทำให้ผมต้องทำอะไรสักอย่าง เพราะปกติแล้ว ไม่เคยมีปัญหา users สามารถใช้งานระบบมาได้นานเป็นระยะเวลาหนึ่ง

แล้วผมก็สังเกตุได้ว่า ฐานข้อมูล มันมีขนาดที่ใหญ่โตมากขึ้น การค้นหาแต่ละครั้งนั้นใช้เวลานาน ดังนั้นจำเป็นต้องใช้งาน Index ซึ่งผมก็ได้สร้าง Index เพื่อแก้ไขปัญหาดังกล่าว ซึ่งก็ได้ผลดีครับ การค้นหาทำได้เร็วขึ้นกว่าแต่ก่อน แต่แล้ว 1 สัปดาห์ต่อมา ผมก็กลับได้รับโทรศัพท์เช่นเดิมอีก ... แล้วผมต้องทำเช่นไรอีกละเนี่ย ในเมื่อผมก็สร้าง Index แล้วนิ...
---------------------------------------------------------

เนื่องจาก Index ซึ่งโดยส่วนใหญ่แล้วเป็น non-clustered index จะไม่ถูกจัดเรียงใหม่ทุกครั้งที่มีการ Insert, Update, Delete ข้อมูล ต่างจาก Clustered index ที่โดยส่วนใหญ่เป็น Primary Key (ที่ผมบอกว่าโดยส่วนใหญ่นั้น เพราะ Primary Key ไม่จำเป็นต้องเป็น Clustered index เสมอไป) ที่จะถูกจัดเรียงใหม่ทุกครั้งที่มีการกระทำ Insert, Update, Delete ดังนั้น เมื่อมีการสร้าง หรือ ปรับปรุงข้อมูลในตารางที่มี non-clustered index อยู่บ่อยๆแล้ว ถ้าไม่มีการจัดเรียง index ใหม่บ้างแล้ว ก็อาจทำให้ประสิทธิภาพในการค้นหาทำได้ไม่ดีเท่าที่ควร และยิ่งตารางที่มีข้อมูลเป็นจำนวนมากด้วยแล้ว ก็ยิ่งจะเห็นผลกระทบได้ชัดเจน

ใน SQL Server 2000 มี Sql Statement ที่ใช้เพื่อทำการ re-index คือ
Syntax
DBCC DBREINDEX
( [ 'database.owner.table_name'
[, index_name
[ , fillfactor ]
]
]
) [ WITH NO_INFOMSGS ]เช่น
DBCC DBREINDEX('dbo.Orders')
Go

ผลลัพท์ที่ได้คือ

DBCC execution completed. If DBCC printed error messages, contact your system administrator.

จาก Statement ข้างต้น เป็นการ re-index ตารางที่เราต้องทราบไว้ล่วงหน้าว่ามีตารางชื่ออะไร เราจึงสามารถกำหนดคำสั่งได้ ถ้าฐานข้อมูลของเรามีตารางสัก 10 ตารางก็คงไม่เท่าไร แต่ถ้ามีสัก 100 ตารางล่ะ คงให้มากำหนด DBCC DBREINDEX(...) แต่ละตารางคงไม่ไหว ผมมีวิธีที่จะทำการ re-index ตารางทั้งหมดในฐานข้อมูล โดยใช้ sql statement ด้านล่างนี้

declare @name varchar(128), --ประกาศตัวแปรสำหรับเก็บชื่อ ตาราง
@user varchar(128), --ประกาศตัวแปรสำหรับเก็บชื่อ เจ้าของตาราง
@statement varchar(1000)

declare tablename cursor for
--ประกาศตัวแปร cursor ข้อมูล ตาราง จาก sysobjects
select o.name, u.name
from sysobjects as o, sysusers as u
where o.uid = u.uid
and xtype = 'U'

open tablename
--เปิด cursor

fetch next from tablename --อ่านข้อมูลจาก cursor และให้ข้อมูลเข้าตัวแปร
into @name, @user

while @@fetch_status = 0 -- วนลูป cursor จนกว่าหมดข้อมูล
begin
set @statement = 'DBCC DBREINDEX (''[' + @user + '].[' + @name + ']'')' --สร้าง sql statement เพื่อ re-index ตาราง
print @statement + '...'
execute (@statement) -- สั่งให้ execute sql statement ที่ re-index ตาราง
fetch next from tablename
into @name, @user
end

close tablename -- ปิด cursor
deallocate tablename -- เคลียร์ memory

go

จาก statement ข้างต้่น สามารถนำไปวางไว้ใน job agent เพื่อให้ re-index ตารางในฐานข้อมูลตามเวลาที่ต้องการได้ทันที

Function การแปลงตัวเลขเป็นตัวอักษร ด้วย Visual Basic .NET (ฉบับปรับปรุง 2014)

** แก้ไข 12/5/2014 ปรับปรุงไฟล์รูปภาพ **
( ดาวน์โหลดซอร์สโค้ดได้ที่ https://github.com/noomdev/ThaiBaht )

การเขียน Function เพื่อเปลี่ยนจากตัวเลขเป็นตัวอักษร นั้น ถือว่าเป็นเรื่องสุดคลาสสิกที่โปรแกรมเมอร์ทุกคนต้องสามารถ “เขียนได้” ข้อสอบเข้าทำงานของบริษัทต่างๆ ก็มีให้เขียนโปรแกรมแปลงตัวเลขเป็นตัวอักษรทั้งนั้น ดังนั้นจะเป็นการดีไหมที่โปรแกรมทุกคนควรเขียน Function เหล่านี้เอง และทำความเข้าใจ Logic ของโปรแกรมให้ถ่องแท้และปรับแต่งให้ดีสุด ให้โปรแกรมมีขนาดเล็ก และทำงานได้อย่างถูกต้องตามต้องการ ในบทความนี้ผมได้เขียน Function ชื่อ ThaiBaht ขึ้นมา ซึ่งเป็น Function ที่แปลงจากตัวเลขเป็นตัวอักษร เช่น 10 เป็น สิบ, 121 เป็น หนึ่งร้อยยี่สิบเอ็ด, 150.25 เป็น หนึ่งร้อยห้าสิบบาทยี่สิบห้าสตางค์ ตาม Code Listing ด้านล่างนี้

Listing 1 : Function ThaiBaht

Public Shared Function ThaiBaht(ByVal pAmount As Double) As String
        If pAmount = 0 Then
            Return "ศูนย์บาทถ้วน"
        End If

        Dim _integerValue As String ' จำนวนเต็ม
        Dim _decimalValue As String ' ทศนิยม
        Dim _integerTranslatedText As String ' จำนวนเต็ม ภาษาไทย
        Dim _decimalTranslatedText As String ' ทศนิยมภาษาไทย

        _integerValue = Format(pAmount, "####.00") ' จัด Format ค่าเงินเป็นตัวเลข 2 หลัก
        _decimalValue = Mid(_integerValue, Len(_integerValue) - 1, 2) ' ทศนิยม
        _integerValue = Mid(_integerValue, 1, Len(_integerValue) - 3) ' จำนวนเต็ม

        ' แปลง จำนวนเต็ม เป็น ภาษาไทย
        _integerTranslatedText = NumberToText(CDbl(_integerValue))

        ' แปลง ทศนิยม เป็น ภาษาไทย
        If CDbl(_decimalValue) = 0 Then
            _decimalTranslatedText = NumberToText(CDbl(_decimalValue))
        Else
            _decimalTranslatedText = ""
        End If

        ' ถ้าไม่มีทศนิม
        If _decimalTranslatedText.Trim = "" Then
            _integerTranslatedText += "บาทถ้วน"
        Else
            _integerTranslatedText += "บาท" & _decimalTranslatedText & "สตางค์"
        End If

        Return _integerTranslatedText
    End Function


Listing 2 : Function NumberToText

Private Shared Function NumberToText(ByVal pAmount As Double) As String
        ' ตัวอักษร
        Dim _numberText() As String = {"", "หนึ่ง", "สอง", "สาม", "สี่", "ห้า", "หก", "เจ็ด", "แปด", "เก้า", "สิบ"}

        ' หลัก หน่วย สิบ ร้อย พัน ...
        Dim _digit() As String = {"", "สิบ", "ร้อย", "พัน", "หมื่น", "แสน", "ล้าน"}
        Dim _value As String, _aWord As String, _text As String
        Dim _numberTranslatedText As String = ""
        Dim _length, _digitPosition As Integer

        _value = pAmount.ToString
        _length = Len(_value) ' ขนาดของ ข้อมูลที่ต้องการแปลง เช่น 122200 มีขนาด เท่ากับ 6

        For i As Integer = 0 To _length - 1 ' วนลูป เริ่มจาก 0 จนถึง (ขนาด - 1)
            ' ตำแหน่งของ หลัก (digit) ของตัวเลข
            ' เช่น
            ' ตำแหน่งหลักที่0 (หลักหน่วย)
            ' ตำแหน่งหลักที่1 (หลักสิบ)
            ' ตำแหน่งหลักที่2 (หลักร้อย)
            ' ถ้าเป็นข้อมูล i = 7 ตำแหน่งหลักจะเท่ากับ 1 (หลักสิบ)
            ' ถ้าเป็นข้อมูล i = 9 ตำแหน่งหลักจะเท่ากับ 3 (หลักพัน)
            ' ถ้าเป็นข้อมูล i = 13 ตำแหน่งหลักจะเท่ากับ 1 (หลักสิบ)
            _digitPosition = i - (6 * ((i - 1) \ 6))
            _aWord = Mid(_value, Len(_value) - i, 1)
            _text = ""
            Select Case _digitPosition
                Case 0 ' หลักหน่วย
                    If _aWord = "1" And _length > 1 Then
                        ' ถ้าเป็นเลข 1 และมีขนาดมากกว่า 1 ให้มีค่าเท่ากับ "เอ็ด"
                        _text = "เอ็ด"
                    ElseIf _aWord <> "0" Then
                        ' ถ้าไม่ใช่เลข 0 ให้หา ตัวอักษร ใน _numberText()
                        _text = _numberText(CInt(_aWord))
                    End If
                Case 1 ' หลักสิบ
                    If _aWord = "1" Then
                        ' ถ้าเป็นเลข 1 ไม่ต้องมี ตัวอักษร อื่นอีก นอกจากคำว่า "สิบ"
                        '_numberTranslatedText = "สิบ" + _numberTranslatedText
                        _text = _digit(_digitPosition)
                    ElseIf _aWord = "2" Then
                        ' ถ้าเป็นเลข 2 ให้ตัวอักษรคือ "ยี่สิบ"
                        _text = "ยี่" + _digit(_digitPosition)
                    ElseIf _aWord <> "0" Then
                        ' ถ้าไม่ใช่เลข 0 ให้หา ตัวอักษร ใน _numberText() และหาหลัก(digit) ใน _digit()
                        _text = _numberText(CInt(_aWord)) + _digit(_digitPosition)
                    End If
                Case 2, 3, 4, 5 ' หลักร้อย ถึง แสน
                    If _aWord <> "0" Then
                        _text = _numberText(CInt(_aWord)) + _digit(_digitPosition)
                    End If
                Case 6 ' หลักล้าน
                    If _aWord = "0" Then
                        _text = "ล้าน"
                    ElseIf _aWord = "1" And _length - 1 > i Then
                        _text = "เอ็ดล้าน"
                    Else
                        _text = _numberText(CInt(_aWord)) + _digit(_digitPosition)
                    End If
            End Select
            _numberTranslatedText = _text + _numberTranslatedText
        Next

        Return _numberTranslatedText
    End Function


อธิบาย
Function ThaiBaht เป็น function หลักในการทำงาน โดยมีหน้าที่จัดรูปแบบจำนวนเงินที่ส่งเข้ามาซึ่งมี data type เป็น double โดยทำการเปลี่ยนเป็น string ที่มีทศนิยม 2 หลัก และทำการแยกข้อมูล ตัวเลขจำนวนเต็ม กับ ตัวเลขทศนิยม ออกจากกัน จากนั้นจึงส่งข้อมูล ตัวเลขจำนวนเต็ม และ ตัวเลขทศนิยม ไปยัง function NumberToText ทีละตัว แล้วนำข้อความตัวอักษรมารวมกัน (concat) อีกครั้งก่อน return ตัวอักษรกลับไปทั้งหมด

ทดลองใช้งาน
1.สร้าง class ชื่อ sample.vb ใน Visual Basic .NET 2005 โดยมี function ทั้ง 2 ข้างต้น


2.สร้าง Form1 ให้มีหน้าตาตามรูป


3.เขียน code ดังนี้


   Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
       MessageBox.Show(Sample.ThaiBaht(TextBox1.Text))
   End Sub

4.ลอง run และทดสอบผลลัพธ์


ลองนำไปประยุกต์และปรับปรุงได้ตามความเหมาะสมนะครับ

-- NooM --

Thursday, August 9, 2007

Error 14274

ความเดิมตอนที่แล้ว (การย้าย Database SQL Server 2000) ที่ผมกำลังย้ายเครื่อง SQL Server 2000 จากเดิม ไปยังเครื่องใหม่และชื่อใหม่นั้น เมื่อ restore ฐานข้อมูล msdb ลงบนเครื่องใหม่เสร็จแล้ว พบว่าไม่สามารถทำการ Update หรือ Delete ข้อมูล Jobs ได้ โดยจะขึ้น Error ขณะที่กำลัง save หรือ delete job ว่า

"Error 14274: Cannot add, update, or delete a job (or its steps or schedules) that originated from an MSX server."

มีปัญหาก็ย่อมมีทางออก วิธีแก้ไขคือให้ปรับข้อมูลในตาราง msdb.sysjobs ใน column ชื่อ originating_server จากเดิมเป็นชื่อ Database ใหม่ เพียงเท่านี้ก็สามารถปรับปรุง Job ได้ตามปกติแล้วครับ