วันพฤหัสบดีที่ ๒๕ ตุลาคม พ.ศ. ๒๕๕๐

โค้ด Restore Database สำหรับ SQL Server

จากบทความที่แล้วเราสามารถทำการ backup database ออกมาเป็น file ทีนี้ถ้าเราต้องการทำ restore ละ ก็ใช้ T-SQL เหมือนเดิม ลองดู syntax กันก่อนครับ

RESTORE DATABASE { database_name @database_name_var }
[ FROM <> [ ,...n ] ]
[ WITH
[ RESTRICTED_USER ]
[ [ , ] FILE = { file_number @file_number } ]
[ [ , ] PASSWORD = { password @password_variable } ]
[ [ , ] MEDIANAME = { media_name @media_name_variable } ]
[ [ , ] MEDIAPASSWORD = { mediapassword @mediapassword_variable } ]
[ [ , ] MOVE 'logical_file_name' TO 'operating_system_file_name' ]
[ ,...n ]
[ [ , ] KEEP_REPLICATION ]
[ [ , ] { NORECOVERY RECOVERY STANDBY = undo_file_name } ]
[ [ , ] { NOREWIND REWIND } ]
[ [ , ] { NOUNLOAD UNLOAD } ]
[ [ , ] REPLACE ]
[ [ , ] RESTART ]
[ [ , ] STATS [ = percentage ] ]
]

จะเห็นว่ามันมี option เยอะแยะเลย รายละเอียดของ option ไปดูใน Online book นะครับ

ในตัวอย่างผมจะทำการ Full Recovery ไป

สำหรับการ Restore มันมีจุดสำคัญคือ ต้องไม่มี user ใช้งาน datbase ครับ
ดังนั้นก่อนจะทำการ restore ให้บอก user ที่ใช้งานให้ออกไปก่อน (เราสามารถใช้ store procedure ดูรายชื่อคนที่ใช้งาน database อยู่ครับ แล้วจะให้ดีในโค้ดเราควรจะเตะ user ที่ใช้งานอยู่ออกไปด้วย
เพื่อความปลอดภัย

ก่อนอื่นผมจะไปสร้าง table ใหม่ใน Northwind ก่อน สมมติชื่อ table1 จากนั้นก็ backup เป็นไฟล์ชื่อ mybackup.bak

เมื่อ backup เสร็จแล้วก็ทำการ drop table1 ทิ้งไปครับ เดี๋ยวเราจะลอง restore database ถ้าผ่าน table1 ก็จะกลับมาหาเราอีกครั้ง เอาละ หายไปเรียบร้อยแล้วครับ

คราวนี้มาดูโค้ดกันบ้าง จากบทความที่แล้วเราได้สร้างปุ่มเผื่อไว้แล้วชื่อ btnRestore

Private Sub btnRestore_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRestore.Click

Dim strSQL As String
Dim strCon As String

strCon = "Data Source=NITHI;Initial Catalog=master;Integrated Security=True"

Dim cmdRestore As SqlClient.SqlCommand = New SqlClient.SqlCommand

sqlConnection1.ConnectionString = strCon
SqlConnection1.Open()
cmdRestore.Connection = SqlConnection1
Cursor = Cursors.WaitCursor
Try
strSQL = "ALTER DATABASE Northwind SET SINGLE_USER"
cmdRestore.CommandText = strSQL
cmdRestore.ExecuteNonQuery()
strSQL = "RESTORE DATABASE Northwind FROM DISK = 'C:\mybackup.bak' "
cmdRestore.CommandText = strSQL
cmdRestore.ExecuteNonQuery()
MsgBox(
"finish")
Catch ex As Exception
MsgBox(
"Error")
Finally
strSQL = "ALTER DATABASE Northwind SET MULTI_USER"
cmdRestore.CommandText = strSQL
c
mdRestore.ExecuteNonQuery()
End Try

Cursor = Cursors.Arrow
SqlConnection1.Close()
cmdRestore.Dispose()
cmdRestore = Nothing

End Sub


จุดสังเกตุ
1. ใน Connection String ผมกำหนด Initial Catalog เป็น master (คือจริงๆเป็น database ตัวไหนก็ได้ที่ไม่ใช่ตัวที่เราต้องการ restore) ถ้าเรากำหนด Initial Catalog เป็น Northwind ก็เท่ากับว่าเรากำลังล๊อก database ด้วยตัวเองครับ (ผมก็เป็น กว่าจะรู้ตัว error ไปแล้ว)
2. ผมเลือกใช้คำสั่ง ALTER TABLE เพื่อ SET SINGLE_USER เพื่อกัน user อื่นออกจาก database
3. รันคำสั่ง Restore เสร็จแล้วก็อย่าลืม SET กลับเป็น MULTI_USER นะครับ
4. เนื่องจากบางครั้งกระบวนการ restore มันจะนานก็เลยสั่งให้เปลี่ยน cursor จะได้บอกให้ user รู้ว่ายังทำงานไม่เสร็จนะจ๊ะ


ลองรันโค้ดดูครับ



จะเห็นว่าระหว่างทำงาน Database จะเปลี่ยน mode เป็น Single User



เสร็จการ restore



Table1 กลับมาแล้ว การ restore ประสบผลสำเร็จ

Note: อยากให้ไปศึกษา option ต่างๆของการ restore เพิ่มนะครับ เพราะมันทำได้หลายอย่างมาก

วันพุธที่ ๒๔ ตุลาคม พ.ศ. ๒๕๕๐

โค้ด Backup Database สำหรับ SQLServer


โดยปกติแล้วการ Backup หรือ Restore รวมทั้งการ Maintenance RDBMS อย่าง SQLServer หรือ Oracle ควรให้ DBA ทำที่ตัว RDBMS เอง แต่ในบางกรณีเราอาจอยากให้ admin ของ application ที่เราพัฒนาขึ้นสามารถ backup/restore database จากหน้า form ที่เราสร้างขึ้น

กรณีนี้เราสามารถใช้คำสั่ง T-SQL (Transact SQL) ได้ครับ เรามาดู syntax ของคำสั่งก่อนครับ

BACKUP DATABASE { database_name @database_name_var }
TO <> [ ,...n ]
[ WITH
[ BLOCKSIZE = { blocksize @blocksize_variable } ]
[ [ , ] DESCRIPTION = { 'text' @text_variable } ]
[ [ , ] DIFFERENTIAL ]
[ [ , ] EXPIREDATE = { date @date_var }
RETAINDAYS = { days @days_var } ]
[ [ , ] PASSWORD = { password @password_variable } ]
[ [ , ] FORMAT NOFORMAT ]
[ [ , ] { INIT NOINIT } ]
[ [ , ] MEDIADESCRIPTION = { 'text' @text_variable } ]
[ [ , ] MEDIANAME = { media_name @media_name_variable } ]
[ [ , ] MEDIAPASSWORD = { mediapassword @mediapassword_variable } ]
[ [ , ] NAME = { backup_set_name @backup_set_name_var } ]
[ [ , ] { NOSKIP SKIP } ]
[ [ , ] { NOREWIND REWIND } ]
[ [ , ] { NOUNLOAD UNLOAD } ]
[ [ , ] RESTART ]
[ [ , ] STATS [ = percentage ] ] ]
สำหรับรายละเอียดลองดูใน Online Book ของ SQLServer นะครับ

สมมติว่าเราต้องการ backup database ลงใน folder ที่ต้องการ คำสั่งจะประมาณนี้ครับ

BACKUP DATABASE Northwind
TO DISK 'C:\Northwind.bak'
WITH FORMAT, NAME = 'NorthwindBackup'
คราวนี้เราลองมาดูการเขียนโปรแกรมเลยครับ สมมติว่า ผมสร้าง Form มา 1 ฟอร์ม สร้าง ปุ่มชื่อ btnBackup ขึ้นมา 1 ปุ่ม
แล้วก็สร้าง sqlConnection ชื่อ sqlConnection1 สำหรับ Form นี้ด้วยครับ




ทีนี้ก็มาดูโค้ด

Private Sub btnBack_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnBackup.Click

Dim strSQL As String
Dim strCon As StringstrCon = "Data Source=NITHI;Initial Catalog=master;Integrated Security=True"
Dim cmdBackup As SqlClient.SqlCommand = New sqlClient.SqlCommandSqlConnection1.ConnectionString = strConSqlConnection1.Open()
Cursor = Cursors.WaitCursor
Try

strSQL = "BACKUP DATABASE Northwind "
strSQL &= "TO DISK = 'C:\mybackup.bak' "
strSQL &= "WITH FORMAT, "
strSQL &= "NAME = 'myBackup'"
cmdBackup.Connection = SqlConnection1

cmdBackup.CommandText = strSQL
cmdBackup.ExecuteNonQuery()
MsgBox("finish")
Catch ex As Exception
MsgBox("Error")
End Try
Cursor = Cursors.Arrow
SqlConnection1.Close()cmdBackup.Dispose()cmdBackup = Nothing
End Sub
ลองรันดูครับ
เมื่อรันเสร็จ จะมี message box บอกว่า Finish แล้วไปดูที่ C: จะพบว่ามีไฟล์ mybackup.bak



ก็เป็นอันเสร็จเรียบร้อยครับ
ถ้าเราตัองการเก็บไฟล์ backup เป็นหลายๆไฟล์ เราก็อาจจะเขียนโปรแกรมให้สร้างชื่อตามวันที่ก็ได้ครับ เช่น myBackup20070311.bak เป็นต้น

ASP.Net Site Navigation

เมื่อวันก่อนมีคนมาถามที่ http://www.greatfriends.biz เรื่อง Response.Redirect กับ
Server.Transfer ว่าต่างกันอย่างไร ผมเห็นเรื่องนี้มีถามกันบ่อยพอสมควร แม้แต่ใน webboard
ต่างประเทศก็มีถามกันบ่อยๆ เลยถือโอกาสนี้เอามาลงบล๊อกซะเลยครับ

เรื่องนี้ถือว่าเป็นเรื่องสำคัญเรื่องหนึ่ง จนหนังสือ
MCTS 70-528
Microsoft .Net Framework 2.0 Web-based Client Development Book
เขียนเป็นหัวข้อสำคัญบทหนึ่งทีเดียว นั่นคือเรื่อง Site Navigation ในหนังสือกล่าวว่า การทำ Navigate Pages หรือการเปลี่ยนหน้า
page นั้นสามารถทำได้ 4 วิธีครับ ได้แก่

1. สร้าง client-side code หรือ markup สำหรับ request page ใหม่
2. ทำ Cross-page
3. สั่ง client-side browser redirect
4. สั่ง server-side transfer

สำหรับวิธีที่1 ได้แก่การเขียนคำสั่ง javascript เช่น

<script language="javascript" type="text/jscript">
function Button1_OnClick(){
document="Page2.aspx";
}
</script>

หรือจะกำหนดที่ tag ของ Control ก็ได้ เช่น
<asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/test.aspx">HyperLink<asp:HyperLink>

สำหรับวิธีที่2 การทำ Cross-paging หมายถึงการที่มีการใส่ข้อมูลที่
page ที่หนึ่ง และทำการ process ข้อมูลที่ page ที่สอง ซึ่งในกรณีนี้ page ที่สองจะมี
property ชื่อ ProviousPage สำหรับเก็บ object ของ page ที่หนึ่ง เพื่อที่ page
ที่สองจะสามารถเข้าถึง property และ object ต่างๆใน page ที่หนึ่งได้ เช่นการไปเอาข้อมูลจาก
web control เป็นต้น


การทำ Cross-paging ได้แก่การกำหนด PostbackURL เพื่อไปเรียกใช้ page ปลายทางเช่น

<asp:LinkButton ID="LinkButton1" runat="server" PostBackUrl="test.aspx"> LinkButton
asp:LinkButton>

สำหรับวิธีที่ 3 ก็คือการสั่ง Response.Redirect นั่นเอง ตามชื่อหัวข้อ
client-side browser redirect ก็บอกอยู่แล้วครับ ว่าเป็นวิธีที่เกิดขึ้นที่ client-side
หรือ browser นั่นเอง การทำงานวิธีนี้ก็คือ เมื่อเราเขียนโค้ด Response.Redirect ที่
Server-side script ทาง Web Server ซึ่งก็คือ IIS จะทำการส่ง response กลับไปยัง browser
ซึ่งจะระบุ location ของ page ใหม่ไปให้ เมื่อ browser ได้รับ response แล้ว browser
ที่สนับสนุน Redirect (ซึ่งก็ทุกยี่ห้อนั่นแหละ) จะทำการส่ง Request กลับมาที่ Web Server
เพื่อขอข้อมูลของ page ใหม่ที่ต้องการ ทาง Web Server ก็จะ process แล้วส่ง response
ซึ่งก็คือ Page ใหม่ไปให้ ดังนั้นเราจะเห็นได้ว่าวิธีนี้จะเกิด round-trip คือจะมีการรับส่ง
request-response ระหว่าง browser กับ Web Server หลายเที่ยว ทำให้กิน resource ของ network


ทีนี้เราลองมาดูคำสั่ง Response.Redirect จะพบว่ามันมี 2 overload ครับ
คือ

Response.Redirect(url As String) กับ

Response.Redirect(url As String, endResponse as Boolean)

โดย default ถ้าเราไม่กำหนด endResponse มันจะมีค่าเป็น false ครับ หมายความว่าทาง
Web Server จะต้องทำการ Process Response รวมถึงงานต่างๆให้เสร็จก่อน ถึงจะส่ง Reponse.Redirect
กลับไปยัง Server ถ้าเรากำหนดเป็น True ทาง Web Server จะทำการยกเลิกงานอื่นๆทันที หมายความว่าโค้ดใดๆก็ตามที่คุณเขียนหลังบรรทัดนี้จะไม่ถูก
process ครับ Web Server จะส่ง Response กลับไปที่ Browser ทันที

ทีนี้การใช้ Response.Redirect มีจุดหนึ่งที่ต้องระวังครับ คือ ปกติการส่ง
Response กลับไปยัง browser นอกจากคำสั่ง redirect แล้วยังอาจจะมีข้อมูลอื่นๆส่งไปด้วย
ดังนั้นถ้ามี data อื่นๆส่งไปแล้ว browser ทำการ process ก่อนที่จะทำการ redirect จะทำให้เกิด
HttpException ทันที (แม้แต่ส่ง HTTP header ไปก็ไม่ได้ครับ) เพื่อป้องกันการเกิด Error
เราจึงควรกำหนด Response.BufferOutput เป็น True ไว้ด้วยครับ

Response.BufferOutput = True

Response.Redirect("test2.aspx")


สำหรับกรณีที่คุณใช้ MS Ajax เพื่อไม่ให้เกิด Exception คุณต้องกำหนด
endResponse เป็น True ไปด้วยเสมอ เพื่อให้แน่ใจว่า WebServer ส่งแค่ Redirect อย่างเดียวไม่ส่ง
data อย่างอื่นพ่วงไปด้วยครับ


Response.BufferOutput = True

Response.Redirect("test2.aspx", True)

เมื่อ Response.Redirect ทำงานเสร็จสมบูรณ์ Web Server จะส่ง Response code
302 ไปให้ browser พร้อมทั้ง URL ของ page ใหม่ ดังนั้นที่ browser ตรงช่อง address
คุณจะเห็น URL ของ page เปลี่ยนไปเป็น page ปัจจุบัน

การที่คุณใช้ Response.Redirect คุณจะไม่สามารถใช้ Previous Page Property ได้ครับ

สำหรับวิธีที่ 4 ก็คือการสั่ง Server.Transfer
การใช้ Server.Transfer จะต่างจากการใช้ Response.Redirect เนื่องจาก
Server.Transfer จะเป็นการทำงานที่ Web Server คือเมื่อมีการ Postback กลับมาที่ Web
Server แล้ว ทาง Web Server จะทำการ process งานตามลำดับจนเมื่อเจอคำสั่ง Server.Transfer
แล้ว Web Server จะทำการไปเรียกใช้งาน Page ใหม่ทันที ดังนั้นข้อดีก็คือจะไม่มี round-trip
เกิดขึ้น เป็นการประหยัด network traffic ไปในตัวครับ
แต่มีข้อเสียคือ
Server.Transfer สามารถสั่งเรียก page เฉพาะใน Server เท่านั้น ไม่สามารถ Transfer ไปที่
Web Server ตัวอื่นได้ (ไม่เหมือน Response.Redirect คุณสามารถ redirect ไปที่ web ไหนก็ได้
server ตัวไหนก็ได้ครับ)


คำสั่ง Server.Transfer ก็มี 3 overload เหมือนกันครับ ลองไปศึกษาเพิ่มเอง
แต่มี parameter ตัวหนึ่งน่าสนใจคือ preserveForm ถ้าคุณกำหนดเป็น True ค่าของ page แรกจะถูกเก็บไว้ใน PreviousPage Property ของ Page ปลายทางครับ นั่นคือมันจะทำ Cross-paging ให้ด้วยในตัว
ดังนั้นที่ page ปลายทาง คุณสามารถสั่ง
PreviousPage.FindControl
เพื่อไป get control ของ Page แรกได้ครับ