ตัวอย่างเคอร์เซอร์ Ms sql เคอร์เซอร์ในขั้นตอนการจัดเก็บ MySQL คุณสมบัติของการใช้เคอร์เซอร์ใน Oracle
สวัสดีเพื่อนผู้อ่านบล็อกในชุมชน
ฉันอยากจะพูดคุยเกี่ยวกับประสบการณ์ล่าสุดของฉันในการเพิ่มประสิทธิภาพเคอร์เซอร์ใน SQL Server
สิ่งแรกที่คุณต้องรู้ก็คือ เคอร์เซอร์ไม่ดี แต่แย่. ในกรณีที่เป็นไปได้ที่จะแทนที่เคอร์เซอร์ด้วย INSERT SELECT หรือใช้ตารางชั่วคราว ก็ควรจะดำเนินการนี้ (โดยมีข้อยกเว้นที่ไม่ค่อยเกิดขึ้น) เคอร์เซอร์มักจะหมายถึงทรัพยากรเซิร์ฟเวอร์เพิ่มเติมและประสิทธิภาพที่ลดลงอย่างมากเมื่อเทียบกับโซลูชันอื่นๆ
ประการที่สอง บางครั้งคุณไม่สามารถทำได้โดยไม่มีเคอร์เซอร์ โดยที่คุณไม่สามารถทำได้โดยไม่ต้องผ่านผลลัพธ์การเลือกทีละบรรทัด ในกรณีเช่นนี้ สิ่งสำคัญมากคือต้องสร้างเคอร์เซอร์ประเภทที่ต้องการอย่างถูกต้อง ซึ่งเป็นประเภทที่ตรงกับปัญหาที่กำลังแก้ไข ไวยากรณ์ทั่วไปสำหรับการประกาศเคอร์เซอร์คือ:
ประกาศเคอร์เซอร์_ชื่อเคอร์เซอร์
[ท้องถิ่น | ทั่วโลก]
[ ส่งต่อ_เท่านั้น | เลื่อน ]
[คงที่ | คีย์เซ็ต | ไดนามิก | เร็ว_ไปข้างหน้า ]
[อ่าน_อย่างเดียว | SCROLL_LOCKS | มองโลกในแง่ดี]
[TYPE_WARNING]
สำหรับ select_statement
[ สำหรับการอัปเดต [ ของ column_name [ ,... n ] ] ] [ ;]
ฉันจะเน้นไปที่พารามิเตอร์หลักสามบรรทัดแรก
LOCAL หรือ GLOBAL: หากเราต้องการให้เคอร์เซอร์สามารถเข้าถึงขั้นตอน ฟังก์ชัน แพ็คเกจอื่นๆ ภายในเซสชันของเราได้ ก็ GLOBAL - ในกรณีนี้ เราจะจัดการลบเคอร์เซอร์ด้วยตัวเอง (คำสั่ง DEALLOCATE) ในกรณีอื่นๆ ทั้งหมด (เช่น ในกลุ่มคนส่วนใหญ่อย่างท่วมท้น) - ท้องถิ่น. โปรดทราบ ตามค่าเริ่มต้นแล้ว เคอร์เซอร์ GLOBAL จะถูกสร้างขึ้น!
FORWARD_ONLY หรือ SCROLL: หากเราต้องการเลื่อนเคอร์เซอร์อย่างบ้าคลั่ง กลับไปกลับมา ให้ SCROLL หรืออย่างอื่น - ส่งต่อ_เท่านั้น. โปรดทราบ ตามค่าเริ่มต้นเคอร์เซอร์ SCROLL จะถูกสร้างขึ้น!
STATIC หรือ KEYSET, DYNAMIC, FAST_FORWARD: หากเราต้องการให้ข้อมูลปัจจุบันจากตารางแสดงเมื่อผ่านเคอร์เซอร์ (เช่น หากหลังจากเปิดเคอร์เซอร์แล้ว เราได้เปลี่ยนข้อมูลในช่องใดช่องหนึ่งของตารางและเราต้องการให้เป็นเช่นนั้น เมื่อผ่านเคอร์เซอร์ในบรรทัดเคอร์เซอร์ที่ต้องการมีข้อมูลที่อัปเดตแล้ว) จากนั้นเราจะใช้ KEYSET (หากทุกตารางที่เข้าร่วมในการเลือกมีดัชนีไม่ซ้ำกัน) หรือ DYNAMIC (ประเภทที่ช้าที่สุด) หากเราต้องการสแน็ปช็อตของผลลัพธ์ตัวอย่างหลังจากเปิดเคอร์เซอร์ - คงที่(ประเภทที่เร็วที่สุด - สำเนาของผลลัพธ์ตัวอย่างจะถูกคัดลอกไปยังฐานข้อมูล tempdb และเราจะดำเนินการแก้ไข) FAST_FORWARD = KEYSET+FORWARD_ONLY+READ_ONLY – พวกอินเทอร์เน็ตเขียนว่า STATIC ใช้เวลาเปิดนานกว่า (เนื่องจากสำเนาถูกสร้างขึ้นใน tempdb) แต่ทำงานได้เร็วกว่า และ FAST_FORWARD ตรงกันข้าม ดังนั้นหากจำนวนบันทึกมีขนาดใหญ่ (เท่าที่แสดงให้เห็นในทางปฏิบัติ) เราจะใช้ STATIC ไม่เช่นนั้นเราจะใช้ FAST_FORWARD ข้อควรสนใจ ตามค่าเริ่มต้นเคอร์เซอร์ DYNAMIC จะถูกสร้างขึ้น
ดังนั้น สำหรับบันทึกจำนวนมาก ในกรณีส่วนใหญ่ ตัวเลือกของฉันคือ:
ประกาศ cursor_name CURSOR LOCAL FORWARD_ONLY STATIC FOR
select_statemenที
สำหรับบันทึกจำนวนเล็กน้อย:
ประกาศ cursor_name CURSOR LOCAL FAST_FORWARD FOR
select_statement
ตอนนี้เรามาฝึกซ้อมกันต่อ (ซึ่งจริงๆ แล้วกระตุ้นให้ฉันเขียนสิ่งนี้)
ตั้งแต่สมัยโบราณ เมื่อประกาศเคอร์เซอร์ ฉันใช้ DECLARE ... CURSOR LOCAL FOR... โครงสร้าง
เมื่อพัฒนาการรวมเข้ากับฐานข้อมูลที่แย่มากซึ่งไม่มีดัชนีเดียวและไม่มีคีย์เดียว ฉันใช้วิธีการเดียวกันนี้เมื่อประกาศเคอร์เซอร์เช่นเคย ตัวอย่างเคอร์เซอร์ตัวหนึ่งมีข้อมูล 225,000 รายการ เป็นผลให้กระบวนการนำเข้าข้อมูลจากฐานข้อมูลดังกล่าวใช้เวลา 15 ชั่วโมง 14 นาที!!! และถึงแม้ว่าการนำเข้าจะเป็นการนำเข้าขั้นต้น (เช่น ครั้งเดียว) แม้แต่การทดสอบการนำเข้าตามปกติก็ยังต้องใช้เวลาหลายวัน! หลังจากแทนที่โครงสร้างด้านบนเมื่อประกาศเคอร์เซอร์ด้วย DECLARE .. CURSOR LOCAL FORWARD_ONLY STATIC FOR.. กระบวนการนำเข้าทั้งหมดเอา... ความสนใจ... 10 นาที 5 วินาที!!! ดังนั้นเกมนี้จึงคุ้มค่ากับเทียนอย่างแน่นอน
ฉันอยากจะย้ำอีกครั้งว่าตัวเลือกในอุดมคติคือไม่ใช้เคอร์เซอร์เลย - สำหรับ MS SQL DBMS วิธีการเชิงสัมพันธ์มากกว่าการนำทางนั้นมีความเป็นธรรมชาติมากกว่ามาก
เคอร์เซอร์ที่ชัดเจนคือคำสั่ง SELECT ที่กำหนดไว้อย่างชัดเจนในส่วนการประกาศของโปรแกรม เมื่อคุณประกาศเคอร์เซอร์ที่ชัดเจน เคอร์เซอร์นั้นจะถูกตั้งชื่อ ไม่สามารถกำหนดเคอร์เซอร์ที่ชัดเจนสำหรับคำสั่ง INSERT, UPDATE, MERGE และ DELETE
ด้วยการกำหนดคำสั่ง SELECT ให้เป็นเคอร์เซอร์ที่ชัดเจน โปรแกรมเมอร์จะสามารถควบคุมขั้นตอนหลักในการดึงข้อมูลจากฐานข้อมูล Oracle โดยจะกำหนดเวลาเปิดเคอร์เซอร์ (OPEN) เมื่อใดที่ควรเลือกแถวจากเคอร์เซอร์ (FETCH) จำนวนแถวที่จะเลือก และเมื่อใดที่ควรปิดเคอร์เซอร์โดยใช้คำสั่ง CLOSE ข้อมูลเกี่ยวกับสถานะปัจจุบันของเคอร์เซอร์สามารถดูได้จากแอตทริบิวต์ การควบคุมที่มีรายละเอียดสูงนี้เองที่ทำให้เคอร์เซอร์ที่ชัดเจนเป็นเครื่องมืออันล้ำค่าสำหรับโปรแกรมเมอร์
ลองดูตัวอย่าง:
1 ฟังก์ชัน อิจฉา_ระดับ (2 NAME_IN ใน friends.NAME%TYPE) ส่งคืนหมายเลข 3 เป็น 4 เคอร์เซอร์ อิจฉา_cur 5 คือ 6 เลือกตำแหน่งจากเพื่อน 7 โดยที่ NAME = UPPER (NAME_IN); 8 8 อิจฉา_rec อิจฉา_cur%ROWTYPE; 9 ย้อนกลับ NUMBER; 10 เริ่มต้น 11 เปิดความอิจฉาริษยา; 13 12 ดึงความอิจฉา_cur เข้าสู่ความอิจฉา_rec; 15 13 IF อิจฉา_cur% พบ 14 แล้ว 15 IF อิจฉา_rec.location = "เปอร์โตริโก" 16 แล้ว retval:= 10; 17 ELSIF อิจฉา_rec.location = "ชิคาโก" 18 แล้ว retval:= 1; 19 จบ ถ้า; 20 จบ ถ้า; 24 21 ปิดความอิจฉาริษยา; 26 22 การส่งคืนกลับ; 23 ข้อยกเว้น 24 เมื่อคนอื่นแล้ว 25 ถ้า อิจฉา_cur%ISOPEN แล้ว 26 ปิด อิจฉา_cur; 27 สิ้นสุด ถ้า; 28 จบ;
ในส่วนถัดไปจะกล่าวถึงแต่ละการดำเนินการเหล่านี้โดยละเอียด คำว่า "เคอร์เซอร์" ในคำเหล่านี้หมายถึงเคอร์เซอร์ที่ชัดเจน เว้นแต่ข้อความจะระบุไว้เป็นอย่างอื่นอย่างชัดเจน
ประกาศเคอร์เซอร์ที่ชัดเจน
เพื่อให้สามารถใช้เคอร์เซอร์ที่ชัดเจนได้ จะต้องประกาศไว้ในส่วนการประกาศของบล็อกหรือแพ็คเกจ PL/SQL:
เคอร์เซอร์ cursor_name [ ([ พารามิเตอร์ [, พารามิเตอร์...]) ] [ RETURN specification_reEirn ] IS SELECT_command ];
ในที่นี้ชื่อเคอร์เซอร์คือชื่อของเคอร์เซอร์ที่ประกาศ spifition_te?it - ส่วน RETURN ที่เป็นทางเลือก KOMaHdaSELECT - คำสั่ง SQL SELECT ที่ถูกต้อง พารามิเตอร์ยังสามารถส่งผ่านไปยังเคอร์เซอร์ได้ (ดูส่วน "พารามิเตอร์เคอร์เซอร์" ด้านล่าง) สุดท้าย หลังจากคำสั่ง SELECT...FOR UPDATE คุณสามารถระบุรายการคอลัมน์ที่จะอัปเดตได้ (ดูด้านล่างด้วย) หลังจากการประกาศ เคอร์เซอร์จะเปิดขึ้นด้วยคำสั่ง OPEN และแถวจะถูกดึงออกมาด้วยคำสั่ง FETCH
ตัวอย่างการประกาศเคอร์เซอร์ที่ชัดเจน
- เคอร์เซอร์ที่ไม่มีพารามิเตอร์. ชุดผลลัพธ์ของแถวจากเคอร์เซอร์นี้คือชุดรหัสบริษัทที่เลือกจากแถวทั้งหมดในตาราง:
- เคอร์เซอร์พร้อมพารามิเตอร์ชุดแถวผลลัพธ์ของเคอร์เซอร์นี้ประกอบด้วยแถวเดียวที่มีชื่อบริษัทที่สอดคล้องกับค่าของพารามิเตอร์ที่ส่งผ่าน:
- เคอร์เซอร์ที่มีส่วนคำสั่ง RETURN. ชุดแถวผลลัพธ์ของเคอร์เซอร์นี้มีข้อมูลทั้งหมดในตารางพนักงานสำหรับ ID แผนก 10:
ชื่อเคอร์เซอร์
ชื่อเคอร์เซอร์ที่ชัดเจนต้องมีความยาวไม่เกิน 30 อักขระและเป็นไปตามกฎเดียวกันกับตัวระบุ PL/SQL อื่นๆ ชื่อเคอร์เซอร์ไม่ใช่ตัวแปร แต่เป็นตัวระบุของตัวชี้ไปยังคำขอ ชื่อเคอร์เซอร์ไม่ได้กำหนดค่าและไม่สามารถใช้ในนิพจน์ได้ เคอร์เซอร์จะใช้เฉพาะในคำสั่ง OPEN, CLOSE และ FETCH เท่านั้น และเพื่อให้มีคุณสมบัติแอตทริบิวต์เคอร์เซอร์
ประกาศเคอร์เซอร์ในแพ็คเกจ
เคอร์เซอร์ที่ชัดเจนจะถูกประกาศในส่วนการประกาศของบล็อก PL/SQL เคอร์เซอร์สามารถประกาศได้ที่ระดับแพ็คเกจ แต่ไม่สามารถประกาศภายในขั้นตอนหรือฟังก์ชันแพ็คเกจเฉพาะได้ ตัวอย่างการประกาศเคอร์เซอร์สองตัวในแพ็คเกจ:
PACKAGE book_info เป็น CURSOR titles_cur คือ SELECT ชื่อจากหนังสือ; CURSOR books_cur (title_filter_in IN books.title%TYPE) คืนหนังสือ%ROWTYPE IS SELECT * จากหนังสือ WHERE title LIKE title_filter_in; จบ;
เคอร์เซอร์ titles_cur ตัวแรกจะแสดงเฉพาะชื่อหนังสือเท่านั้น ประการที่สอง books_cur ส่งคืนแถวทั้งหมดของตารางหนังสือซึ่งมีชื่อหนังสือตรงกับรูปแบบที่ระบุเป็นพารามิเตอร์เคอร์เซอร์ (เช่น "หนังสือทั้งหมดที่มีสตริง 'PL/SQL'") โปรดทราบว่าเคอร์เซอร์ตัวที่สองใช้ส่วน RETURN ซึ่งประกาศโครงสร้างข้อมูลที่ส่งคืนโดยคำสั่ง FETCH
ส่วน RETURN สามารถมีโครงสร้างข้อมูลใดๆ ต่อไปนี้:
- บันทึกที่กำหนดจากแถวตารางข้อมูลโดยใช้แอตทริบิวต์ %ROWTYPE
- รายการที่กำหนดจากเคอร์เซอร์อื่นที่ประกาศไว้ก่อนหน้านี้ รวมถึงใช้แอตทริบิวต์ %rowtype
- รายการที่กำหนดโดยโปรแกรมเมอร์
จำนวนนิพจน์ในรายการการเลือกเคอร์เซอร์ต้องตรงกับจำนวนคอลัมน์ใน table_name%ROWTYPE, Kypcop%ROWTYPE หรือเรกคอร์ดประเภทเรกคอร์ด ประเภทข้อมูลขององค์ประกอบจะต้องเข้ากันได้ด้วย ตัวอย่างเช่น หากองค์ประกอบที่สองของรายการที่เลือกเป็นประเภท NUMBER คอลัมน์ที่สองของรายการในส่วน RETURN จะต้องไม่เป็นประเภท VARCHAR2 หรือ BOOLEAN
ก่อนที่จะไปยังการตรวจสอบโดยละเอียดของส่วน RETURN และข้อดีของมัน ก่อนอื่นเรามาทำความเข้าใจก่อนว่าทำไมจึงจำเป็นต้องประกาศเคอร์เซอร์ในแพ็คเกจ ทำไมไม่ประกาศเคอร์เซอร์อย่างชัดเจนในโปรแกรมที่ใช้งาน - ในโพรซีเดอร์, ฟังก์ชัน หรือบล็อกที่ไม่ระบุชื่อ?
คำตอบนั้นง่ายและน่าเชื่อถือ ด้วยการกำหนดเคอร์เซอร์ในแพ็คเกจ คุณสามารถนำการสืบค้นที่กำหนดไว้ในแพ็คเกจกลับมาใช้ใหม่ได้ โดยไม่ต้องทำซ้ำโค้ดเดียวกันในตำแหน่งต่างๆ ในแอปพลิเคชัน การนำคำขอไปใช้ในที่เดียวทำให้การแก้ไขและการบำรุงรักษาโค้ดง่ายขึ้น ประหยัดเวลาได้บ้างโดยการลดจำนวนคำขอที่ประมวลผล
นอกจากนี้ยังควรพิจารณาสร้างฟังก์ชันที่ส่งคืนตัวแปรเคอร์เซอร์ตาม REF CURSOR โปรแกรมที่เรียกดึงข้อมูลแถวผ่านตัวแปรเคอร์เซอร์ สำหรับข้อมูลเพิ่มเติม โปรดดูส่วน "ตัวแปรเคอร์เซอร์และเคอร์เซอร์อ้างอิง"
เมื่อประกาศเคอร์เซอร์ในแพ็คเกจที่ใช้ซ้ำได้ มีสิ่งสำคัญสิ่งหนึ่งที่ต้องพิจารณา โครงสร้างข้อมูลทั้งหมด รวมถึงเคอร์เซอร์ที่ประกาศที่ "ระดับแพ็กเกจ" (ไม่ใช่ภายในฟังก์ชันหรือขั้นตอนเฉพาะ) จะคงค่าไว้ตลอดเซสชัน ซึ่งหมายความว่าเคอร์เซอร์แบบแบตช์จะยังคงเปิดอยู่จนกว่าคุณจะปิดอย่างชัดเจน หรือจนกว่าเซสชันจะสิ้นสุด เคอร์เซอร์ที่ประกาศในบล็อกท้องถิ่นจะถูกปิดโดยอัตโนมัติเมื่อบล็อกเหล่านั้นเสร็จสมบูรณ์
ตอนนี้เรามาดูที่ส่วน RETURN สิ่งที่น่าสนใจอย่างหนึ่งเกี่ยวกับการประกาศเคอร์เซอร์ในแพ็คเกจก็คือ ส่วนหัวของเคอร์เซอร์สามารถแยกออกจากเนื้อหาได้ ส่วนหัวนี้ชวนให้นึกถึงส่วนหัวของฟังก์ชันมากกว่า ประกอบด้วยข้อมูลที่โปรแกรมเมอร์จำเป็นต้องใช้ในการทำงาน: ชื่อของเคอร์เซอร์ พารามิเตอร์ และประเภทของข้อมูลที่ส่งคืน เนื้อความของเคอร์เซอร์คือคำสั่ง SELECT เทคนิคนี้แสดงให้เห็นในการประกาศเคอร์เซอร์ books_cur เวอร์ชันใหม่ในแพ็คเกจ book_info:
PACKAGE book_info IS CURSOR books_cur (title_filter_in IN books.title%TYPE) ส่งคืนหนังสือ%ROWTYPE; จบ; เนื้อหาแพคเกจ book_info IS CURSOR books_cur (title_filter_in IN books.title%TYPE) คืนหนังสือ%ROWTYPE IS SELECT * จากหนังสือ WHERE title LIKE title_filter_in; จบ;
อักขระทุกตัวก่อนคีย์เวิร์ด IS จะกำหนดเป็นข้อกำหนด และหลังจาก IS จะเป็นตัวเคอร์เซอร์ การแยกการประกาศเคอร์เซอร์สามารถใช้เพื่อวัตถุประสงค์สองประการ
- การซ่อนข้อมูล. เคอร์เซอร์ในแพ็คเกจคือ "กล่องดำ" ซึ่งสะดวกสำหรับโปรแกรมเมอร์เพราะไม่ต้องเขียนหรือดูคำสั่ง SELECT ก็เพียงพอแล้วที่จะทราบว่าเคอร์เซอร์นี้ส่งคืนสิ่งใดในลำดับใดและอยู่ในคอลัมน์ใด โปรแกรมเมอร์ที่ทำงานกับแพ็คเกจใช้เคอร์เซอร์เหมือนกับองค์ประกอบสำเร็จรูปอื่นๆ
- การคอมไพล์ขั้นต่ำ. ด้วยการซ่อนคำจำกัดความของคิวรีในเนื้อความของแพ็คเกจ การเปลี่ยนแปลงคำสั่ง SELECT สามารถทำได้โดยไม่ต้องเปลี่ยนส่วนหัวของเคอร์เซอร์ในข้อกำหนดเฉพาะของแพ็คเกจ ซึ่งช่วยให้โค้ดได้รับการปรับปรุง แก้ไข และคอมไพล์ใหม่โดยไม่ต้องคอมไพล์ใหม่ตามข้อกำหนดแพ็คเกจ ดังนั้นโปรแกรมที่ขึ้นอยู่กับแพ็คเกจนั้นจะไม่ถูกทำเครื่องหมายว่าไม่ถูกต้องและไม่จำเป็นต้องคอมไพล์ใหม่
การเปิดเคอร์เซอร์ที่ชัดเจน
การใช้เคอร์เซอร์เริ่มต้นด้วยการกำหนดในส่วนการประกาศ จากนั้นจะต้องเปิดเคอร์เซอร์ที่ประกาศไว้ ไวยากรณ์ของคำสั่ง OPEN นั้นง่ายมาก:
OPEN cursor_name [ (อาร์กิวเมนต์ [, อาร์กิวเมนต์...]) ];
ในที่นี้ ชื่อเคอร์เซอร์คือชื่อของเคอร์เซอร์ที่ประกาศไว้ก่อนหน้านี้ และอาร์กิวเมนต์คือค่าที่ส่งไปยังเคอร์เซอร์หากมีการประกาศพร้อมกับรายการพารามิเตอร์
Oracle ยังสนับสนุนไวยากรณ์ FOR เมื่อเปิดเคอร์เซอร์ ซึ่งใช้สำหรับตัวแปรเคอร์เซอร์ทั้งสอง (ดูหัวข้อ "ตัวแปรเคอร์เซอร์และ REF CURSOR") และ SQL ไดนามิกแบบฝัง
เมื่อ PL/SQL เปิดเคอร์เซอร์ จะดำเนินการสืบค้นที่มีอยู่ นอกจากนี้ยังระบุชุดข้อมูลที่ใช้งานอยู่ - แถวของตารางทั้งหมดที่เข้าร่วมในการสืบค้นที่ตรงกับเกณฑ์ WHERE และเงื่อนไขการรวม คำสั่ง OPEN จะไม่ดึงข้อมูล - นั่นคืองานของคำสั่ง FETCH
ไม่ว่าการดึงข้อมูลครั้งแรกจะเกิดขึ้นเมื่อใด โมเดลความสมบูรณ์ของข้อมูลของ Oracle ช่วยให้มั่นใจได้ว่าการดำเนินการดึงข้อมูลทั้งหมดจะส่งคืนข้อมูลในสถานะที่เปิดเคอร์เซอร์ กล่าวอีกนัยหนึ่ง ตั้งแต่การเปิดจนถึงการปิดเคอร์เซอร์ เมื่อดึงข้อมูลจากเคอร์เซอร์ การดำเนินการแทรก อัปเดต และลบที่ทำในช่วงเวลานี้จะถูกละเว้นโดยสิ้นเชิง
ยิ่งไปกว่านั้น หากคำสั่ง SELECT มีส่วน FOR UPDATE แถวทั้งหมดที่ระบุโดยเคอร์เซอร์จะถูกล็อคเมื่อเปิดเคอร์เซอร์
หากคุณพยายามเปิดเคอร์เซอร์ที่เปิดอยู่แล้ว PL/SQL จะแสดงข้อความแสดงข้อผิดพลาดต่อไปนี้:
ORA-06511: PL/SQL: เคอร์เซอร์เปิดอยู่แล้ว
ดังนั้น ก่อนที่จะเปิดเคอร์เซอร์ คุณควรตรวจสอบสถานะโดยใช้ค่าแอตทริบิวต์ %เปิด:
หากไม่ใช่ company_cur% ISOPEN ให้เปิด company_cur; เอนดิฟ;
คุณลักษณะของเคอร์เซอร์ที่ชัดเจนมีการอธิบายไว้ด้านล่างในหัวข้อเฉพาะสำหรับเคอร์เซอร์เหล่านี้
ถ้าโปรแกรมรัน FOR loop โดยใช้เคอร์เซอร์ เคอร์เซอร์ก็ไม่จำเป็นต้องเปิด (ดึงข้อมูล ปิด) อย่างชัดเจน กลไก PL/SQL ทำสิ่งนี้โดยอัตโนมัติ
กำลังดึงข้อมูลจากเคอร์เซอร์ที่ชัดเจน
คำสั่ง SELECT จะสร้างตารางเสมือน - ชุดของแถวที่กำหนดโดยส่วนคำสั่ง WHERE พร้อมด้วยคอลัมน์ที่กำหนดโดยรายการคอลัมน์ SELECT ดังนั้นเคอร์เซอร์จึงแทนตารางนี้ในโปรแกรม PL/SQL วัตถุประสงค์หลักของเคอร์เซอร์ในโปรแกรม PL/SQL คือการเลือกแถวสำหรับการประมวลผล การดึงข้อมูลแถวเคอร์เซอร์ทำได้ด้วยคำสั่ง FETCH:
FETCH cursor_name เข้าสู่ record_or_variable_list;
ในที่นี้ ชื่อเคอร์เซอร์คือชื่อของเคอร์เซอร์ที่เลือกบันทึก และรายการบันทึกหรือตัวแปรคือโครงสร้างข้อมูล PL/SQL ที่จะคัดลอกแถวถัดไปของชุดบันทึกที่ใช้งานอยู่ ข้อมูลสามารถวางในบันทึก PL/SQL (ประกาศด้วยแอตทริบิวต์ %ROWTYPE หรือการประกาศ TYPE) หรือในตัวแปร (ตัวแปร PL/SQL หรือตัวแปรผูก - เช่นในองค์ประกอบ Oracle Forms)
ตัวอย่างของเคอร์เซอร์ที่ชัดเจน
ตัวอย่างต่อไปนี้สาธิตวิธีต่างๆ ในการสุ่มตัวอย่างข้อมูล
- การดึงข้อมูลจากเคอร์เซอร์ลงในบันทึก PL/SQL:
- การดึงข้อมูลจากเคอร์เซอร์เข้าสู่ตัวแปร:
- การดึงข้อมูลจากเคอร์เซอร์ไปยังแถวของตาราง PL/SQL, ตัวแปร และตัวแปรการผูกของ Oracle Forms:
ข้อมูลที่ดึงมาจากเคอร์เซอร์ควรถูกวางไว้ในบันทึกที่ประกาศภายใต้เคอร์เซอร์เดียวกันกับแอตทริบิวต์ %ROWTYPE หลีกเลี่ยงการเลือกรายการตัวแปร การดึงข้อมูลลงในบันทึกจะทำให้โค้ดมีขนาดกะทัดรัดและยืดหยุ่นมากขึ้น ช่วยให้คุณสามารถเปลี่ยนรายการดึงข้อมูลได้โดยไม่ต้องเปลี่ยนคำสั่ง FETCH
การสุ่มตัวอย่างหลังจากประมวลผลแถวสุดท้าย
เมื่อคุณเปิดเคอร์เซอร์ คุณจะเลือกบรรทัดจากบรรทัดนั้นทีละบรรทัดจนกระทั่งหมด อย่างไรก็ตาม คุณยังคงสามารถใช้คำสั่ง FETCH ได้หลังจากนี้
น่าแปลกที่ PL/SQL ไม่มีข้อยกเว้นในกรณีนี้ เขาแค่ไม่ทำอะไรเลย เนื่องจากไม่มีอะไรให้เลือก ค่าของตัวแปรในส่วน INTO ของคำสั่ง FETCH จึงไม่เปลี่ยนแปลง กล่าวอีกนัยหนึ่ง คำสั่ง FETCH ไม่ได้ตั้งค่าตัวแปรเหล่านี้เป็น NULL
นามแฝงคอลัมน์เคอร์เซอร์ที่ชัดเจน
คำสั่ง SELECT ในการประกาศเคอร์เซอร์ระบุรายการคอลัมน์ที่ส่งคืน นอกจากชื่อคอลัมน์ของตารางแล้ว รายการนี้ยังสามารถมีนิพจน์ที่เรียกว่าคอลัมน์จากการคำนวณหรือคอลัมน์เสมือนได้
นามแฝงของคอลัมน์เป็นชื่อสำรองที่ระบุในคำสั่ง SELECT สำหรับคอลัมน์หรือนิพจน์ ด้วยการกำหนดนามแฝงที่เหมาะสมใน SQL*Plus คุณสามารถแสดงผลลัพธ์ของการสืบค้นตามอำเภอใจในรูปแบบที่มนุษย์สามารถอ่านได้ ในสถานการณ์เหล่านี้ นามแฝงไม่จำเป็น ในทางกลับกัน เมื่อใช้เคอร์เซอร์ที่ชัดเจน จำเป็นต้องใช้นามแฝงของคอลัมน์จากการคำนวณในกรณีต่อไปนี้:
- เมื่อดึงข้อมูลจากเคอร์เซอร์ลงในบันทึกที่ประกาศด้วยแอตทริบิวต์ %ROWTYPE ตามเคอร์เซอร์เดียวกัน
- เมื่อโปรแกรมมีการอ้างอิงไปยังคอลัมน์จากการคำนวณ
พิจารณาคำถามต่อไปนี้ คำสั่ง SELECT เลือกชื่อของบริษัททั้งหมดที่สั่งซื้อสินค้าระหว่างปี 2544 รวมถึงจำนวนคำสั่งซื้อทั้งหมด (สมมติว่ามาสก์การจัดรูปแบบเริ่มต้นสำหรับอินสแตนซ์ฐานข้อมูลปัจจุบันคือ DD-MON-YYYY):
เลือก company_name, SUM (inv_amt) จากบริษัท c, ใบแจ้งหนี้ i WHERE c.company_id = i.company_id AND i.invoice_date ระหว่าง "01-JAN-2001" และ "31-DEC-2001";
การรันคำสั่งนี้ใน SQL*Plus จะสร้างเอาต์พุตต่อไปนี้:
ชื่อ บริษัท | ผลรวม (INV_AMT) |
แอคมี เทอร์โบ อิงค์ | 1000 |
วอชิงตัน แฮร์ บจก. | 25.20 |
อย่างที่คุณเห็น SUM ส่วนหัวของคอลัมน์ (INV_AMT) ไม่เหมาะกับรายงานมากนัก แต่ก็เหมาะสำหรับการดูข้อมูลเพียงอย่างเดียว ตอนนี้เรามาเรียกใช้แบบสอบถามเดียวกันในโปรแกรม PL/SQL โดยใช้เคอร์เซอร์ที่ชัดเจนและเพิ่มนามแฝงของคอลัมน์:
ประกาศเคอร์เซอร์ comp_cur คือ SELECT c.name, SUM (inv_amt) Total_sales จากบริษัท C, ใบแจ้งหนี้ I WHERE C.company_id = I.company_id AND I.invoice_date BETWEEN "01-JAN-2001" AND "31-DEC-2001"; comp_rec comp_cur%ROWTYPE; เริ่มเปิด comp_cur; ดึงข้อมูล comp_cur เข้าสู่ comp_rec; จบ;
หากไม่มีนามแฝง ฉันจะไม่สามารถอ้างอิงคอลัมน์ในโครงสร้างบันทึก comp_rec ได้ ถ้าคุณมีนามแฝง คุณสามารถทำงานกับคอลัมน์จากการคำนวณได้เช่นเดียวกับที่คุณทำกับคอลัมน์คิวรีอื่นๆ:
หาก comp_rec.total_sales > 5000 แล้ว DBMS_OUTPUT.PUT_LINE (" คุณมีวงเงินเครดิตเกิน $5,000 ด้วย " || TO_CHAR (comp_rec.total_sales - 5,000, "$9999")); เอนดิฟ;
เมื่อเลือกแถวในเรคคอร์ดที่ประกาศด้วยแอตทริบิวต์ %ROWTYPE คอลัมน์ที่คำนวณแล้วสามารถเข้าถึงได้ด้วยชื่อเท่านั้น เนื่องจากโครงสร้างของเรคคอร์ดถูกกำหนดโดยโครงสร้างของเคอร์เซอร์เอง
การปิดเคอร์เซอร์ที่ชัดเจน
กาลครั้งหนึ่งในวัยเด็ก เราได้รับการสอนให้ทำความสะอาดตัวเอง และนิสัยนี้ยังคงอยู่กับเรา (แม้ว่าจะไม่ใช่ทุกคน) ไปตลอดชีวิต ปรากฎว่ากฎนี้มีบทบาทสำคัญในการเขียนโปรแกรม และโดยเฉพาะอย่างยิ่งเมื่อต้องจัดการเคอร์เซอร์ อย่าลืมปิดเคอร์เซอร์เมื่อคุณไม่ต้องการมันอีกต่อไป!
ไวยากรณ์คำสั่งปิด:
ปิดเคอร์เซอร์_ชื่อ;
ด้านล่างนี้คือเคล็ดลับและข้อควรพิจารณาที่สำคัญบางประการที่เกี่ยวข้องกับการปิดเคอร์เซอร์ที่ชัดเจน
- หากมีการประกาศเคอร์เซอร์และเปิดในโพรซีเดอร์ อย่าลืมปิดเคอร์เซอร์เมื่อคุณดำเนินการเสร็จแล้ว มิฉะนั้นรหัสของคุณจะทำให้หน่วยความจำรั่ว ตามทฤษฎีแล้ว เคอร์เซอร์ (เช่นเดียวกับโครงสร้างข้อมูลอื่นๆ) ควรถูกปิดและทำลายโดยอัตโนมัติเมื่ออยู่นอกขอบเขต โดยทั่วไปแล้ว เมื่อออกจากโพรซีเดอร์ ฟังก์ชัน หรือบล็อกที่ไม่ระบุชื่อ PL/SQL จะปิดเคอร์เซอร์ที่เปิดอยู่ทั้งหมดภายในนั้น แต่กระบวนการนี้ใช้ทรัพยากรจำนวนมาก ดังนั้นด้วยเหตุผลด้านประสิทธิภาพ บางครั้ง PL/SQL จึงมีความล่าช้าในการระบุและปิดเคอร์เซอร์ที่เปิดอยู่ เคอร์เซอร์ประเภท REF CURSOR ตามคำจำกัดความ ไม่สามารถปิดโดยปริยายได้ สิ่งเดียวที่คุณมั่นใจได้ก็คือเมื่อบล็อก PL/SQL "ด้านนอกสุด" เสร็จสมบูรณ์และส่งคืนการควบคุมไปยัง SQL หรือโปรแกรมที่เรียกอื่น PL/SQL จะปิดเคอร์เซอร์ทั้งหมดที่เปิดโดยบล็อกนั้นหรือบล็อกที่ซ้อนกันโดยปริยาย ยกเว้น REF CURSOR . บทความ "การใช้เคอร์เซอร์ซ้ำใน PL/SQL static SQL" จาก Oracle Technology Network ให้การวิเคราะห์โดยละเอียดเกี่ยวกับวิธีการและเวลาที่ PL/SQL ปิดเคอร์เซอร์ บล็อกที่ไม่ระบุชื่อที่ซ้อนกันเป็นตัวอย่างของสถานการณ์ที่ PL/SQL ไม่ได้ปิดเคอร์เซอร์โดยปริยาย สำหรับข้อมูลที่น่าสนใจในหัวข้อนี้ โปรดดูบทความของ Jonathan Gennick เรื่อง “Does PL/SQL Implicitly Close Cursors?”
- หากมีการประกาศเคอร์เซอร์ในแพ็คเกจที่ระดับแพ็คเกจและเปิดอยู่ในบล็อกหรือโปรแกรมบางโปรแกรม เคอร์เซอร์นั้นจะยังคงเปิดอยู่จนกว่าคุณจะปิดอย่างชัดเจนหรือจนกว่าเซสชันจะสิ้นสุดลง ดังนั้นหลังจากเสร็จสิ้นการทำงานด้วยเคอร์เซอร์ระดับแบตช์คุณควรปิดเคอร์เซอร์ทันทีด้วยคำสั่ง CLOSE (และควรทำเช่นเดียวกันในส่วนข้อยกเว้น):
- เคอร์เซอร์สามารถปิดได้เฉพาะเมื่อเปิดไว้ก่อนหน้านี้เท่านั้น มิฉะนั้น ข้อยกเว้น INVALID_CURS0R จะถูกส่งออกไป สถานะเคอร์เซอร์ถูกตรวจสอบโดยใช้แอตทริบิวต์ %ISOPEN:
- หากมีเคอร์เซอร์ที่เปิดอยู่มากเกินไปในโปรแกรม จำนวนเคอร์เซอร์อาจเกินค่าของพารามิเตอร์ฐานข้อมูล OPEN_CURSORS หากคุณได้รับข้อความแสดงข้อผิดพลาด ขั้นแรกตรวจสอบให้แน่ใจว่าเคอร์เซอร์ที่ประกาศในแพ็คเกจถูกปิดเมื่อไม่จำเป็นต้องใช้อีกต่อไป
คุณลักษณะเคอร์เซอร์ที่ชัดเจน
Oracle รองรับแอตทริบิวต์สี่รายการ (%FOUND, %NOTFOUND, %ISOPEN, %ROWCOUNTM) เพื่อรับข้อมูลเกี่ยวกับสถานะของเคอร์เซอร์ที่ชัดเจน การอ้างอิงแอ็ตทริบิวต์มีไวยากรณ์ต่อไปนี้: cursor%attribute
เคอร์เซอร์ในที่นี้คือชื่อของเคอร์เซอร์ที่ประกาศ
ค่าที่ส่งคืนโดยแอตทริบิวต์เคอร์เซอร์ที่ชัดเจนจะแสดงในตาราง 1.
ตารางที่ 1.คุณลักษณะเคอร์เซอร์ที่ชัดเจน
ค่าของแอตทริบิวต์เคอร์เซอร์ก่อนและหลังดำเนินการต่างๆ จะแสดงในตาราง 2.
เมื่อทำงานกับแอตทริบิวต์เคอร์เซอร์ที่ชัดเจน ให้พิจารณาสิ่งต่อไปนี้:
- หากคุณพยายามเข้าถึงแอตทริบิวต์ %FOUND, %NOTFOUND หรือ %ROWCOUNT ก่อนที่จะเปิดเคอร์เซอร์หรือหลังจากปิดแล้ว Oracle จะส่งข้อผิดพลาด CURSOR ที่ไม่ถูกต้อง (ORA-01001)
- ถ้าครั้งแรกที่คำสั่ง FETCH ถูกดำเนินการ ชุดแถวผลลัพธ์จะว่างเปล่า แอ็ตทริบิวต์เคอร์เซอร์ส่งคืนค่าต่อไปนี้: %FOUND = FALSE , %NOTFOUND = TRUE และ %ROWCOUNT = 0
- เมื่อใช้ BULK COLLECT แอตทริบิวต์ %ROWCOUNT จะส่งกลับจำนวนแถวที่ดึงข้อมูลในคอลเลกชันที่กำหนด
|
ตารางที่ 2.ค่าแอตทริบิวต์เคอร์เซอร์
การดำเนินการ | %พบ | %ไม่พบ | %เปิด | %ROWCOUNT |
ก่อนเปิด | ข้อยกเว้น โอรา-01001 |
ข้อยกเว้น โอรา-01001 |
เท็จ | ข้อยกเว้น โอรา-01001 |
หลังจากเปิด | โมฆะ | โมฆะ | จริง | 0 |
ก่อน FETCH ตัวอย่างแรก | โมฆะ | โมฆะ | จริง | 0 |
หลังจากตัวอย่างแรก ดึงข้อมูล |
จริง | เท็จ | จริง | 1 |
ก่อนต่อมา ดึงข้อมูล |
จริง | เท็จ | จริง | 1 |
หลังจาก FETCH ตามมา | จริง | เท็จ | จริง | ขึ้นอยู่กับข้อมูล |
ก่อนตัวอย่าง FETCH สุดท้าย | จริง | เท็จ | จริง | ขึ้นอยู่กับข้อมูล |
หลังจากตัวอย่าง FETCH สุดท้าย | จริง | เท็จ | จริง | ขึ้นอยู่กับข้อมูล |
ก่อนปิด | เท็จ | จริง | จริง | ขึ้นอยู่กับข้อมูล |
หลังจากปิด | ข้อยกเว้น | ข้อยกเว้น | เท็จ | ข้อยกเว้น |
การใช้คุณลักษณะทั้งหมดเหล่านี้แสดงให้เห็นในตัวอย่างต่อไปนี้:
บล็อกก่อนหน้านี้ได้ให้ตัวอย่างการใช้ขั้นตอนและพารามิเตอร์ฟังก์ชันซ้ำแล้วซ้ำอีก พารามิเตอร์เป็นวิธีการส่งข้อมูลเข้าและออกจากโมดูลโปรแกรม เมื่อใช้อย่างถูกต้อง จะทำให้โมดูลมีประโยชน์และยืดหยุ่นมากขึ้น
PL/SQL ช่วยให้คุณสามารถส่งพารามิเตอร์ไปยังเคอร์เซอร์ได้ พวกมันทำหน้าที่เหมือนกับพารามิเตอร์ของโมดูลซอฟต์แวร์รวมถึงฟังก์ชันเพิ่มเติมอีกหลายตัว
- ขยายความสามารถในการนำเคอร์เซอร์กลับมาใช้ใหม่. แทนที่จะฮาร์ดโค้ดค่าที่กำหนดเงื่อนไขการเลือกข้อมูลในส่วนคำสั่ง WHERE คุณสามารถใช้พารามิเตอร์เพื่อส่งค่าใหม่ไปยังส่วนคำสั่ง WHERE ทุกครั้งที่เปิดเคอร์เซอร์
- การแก้ไขปัญหาขอบเขตเคอร์เซอร์. ถ้าแบบสอบถามใช้พารามิเตอร์แทนค่าฮาร์ดโค้ด ชุดผลลัพธ์ของแถวเคอร์เซอร์จะไม่เชื่อมโยงกับโปรแกรมหรือตัวแปรบล็อกเฉพาะ หากโปรแกรมของคุณมีบล็อกที่ซ้อนกัน คุณสามารถกำหนดเคอร์เซอร์ที่ระดับบนสุดและใช้ในบล็อกที่ซ้อนกันโดยมีการประกาศตัวแปรไว้
จำนวนพารามิเตอร์เคอร์เซอร์ไม่จำกัด เมื่อเรียก OPEN จะต้องระบุพารามิเตอร์ทั้งหมด (ยกเว้นที่มีค่าเริ่มต้น) สำหรับเคอร์เซอร์
เคอร์เซอร์ต้องการพารามิเตอร์เมื่อใด กฎทั่วไปที่นี่เหมือนกับขั้นตอนและฟังก์ชัน: หากคาดว่าจะใช้เคอร์เซอร์ในตำแหน่งที่แตกต่างกันและมีค่าต่างกันในส่วน WHERE ควรกำหนดพารามิเตอร์ให้ ลองเปรียบเทียบเคอร์เซอร์ที่มีและไม่มีพารามิเตอร์กัน ตัวอย่างเคอร์เซอร์ที่ไม่มีพารามิเตอร์:
เคอร์เซอร์ joke_cur คือชื่อ, หมวดหมู่, Last_used_date จากเรื่องตลก;
ชุดผลลัพธ์ของเคอร์เซอร์จะรวมรายการทั้งหมดในตารางเรื่องตลก หากเราต้องการเพียงแถวย่อยบางแถว ส่วน WHERE จะรวมอยู่ในแบบสอบถาม:
เคอร์เซอร์ joke_cur คือ SELECT ชื่อ, หมวดหมู่, Last_used_date จากเรื่องตลก WHERE category = "สามี";
ในการดำเนินการนี้ เราไม่ได้ใช้พารามิเตอร์ และไม่จำเป็น ในกรณีนี้ เคอร์เซอร์จะส่งกลับแถวทั้งหมดที่อยู่ในหมวดหมู่เฉพาะ แต่จะเกิดอะไรขึ้นถ้าหมวดหมู่เปลี่ยนแปลงทุกครั้งที่คุณเข้าถึงเคอร์เซอร์นี้
เคอร์เซอร์พร้อมพารามิเตอร์
แน่นอนว่า เราจะไม่กำหนดเคอร์เซอร์แยกกันสำหรับแต่ละหมวดหมู่ ซึ่งจะไม่สอดคล้องกับวิธีการทำงานของการพัฒนาแอปพลิเคชันที่ขับเคลื่อนด้วยข้อมูลโดยสิ้นเชิง เราต้องการเคอร์เซอร์เพียงอันเดียว แต่อีกอันหนึ่งสำหรับเปลี่ยนหมวดหมู่ - และมันจะยังคงส่งคืนข้อมูลที่จำเป็น และวิธีแก้ปัญหาที่ดีที่สุด (แม้ว่าจะไม่ใช่วิธีเดียว) สำหรับปัญหานี้คือการกำหนดเคอร์เซอร์แบบกำหนดพารามิเตอร์:
กระบวนการ expl_joke (main_category_in IN joke_category.category_id%TYPE) IS /* || เคอร์เซอร์พร้อมรายการพารามิเตอร์ที่ประกอบด้วย || จากพารามิเตอร์สตริงเดียว */ CURSOR joke_cur (category_in IN VARCHAR2) คือ SELECT ชื่อ, หมวดหมู่, Last_used_date จาก Joke WHERE category = UPPER (category_in); joke_rec joke_cur%ROWTYPE; BEGIN /* ตอนนี้เมื่อเปิดเคอร์เซอร์ อาร์กิวเมนต์จะถูกส่งผ่านไป */ OPEN joke_cur (main_category_in); ดึง joke_cur เข้าสู่ joke_rec;
ระหว่างชื่อเคอร์เซอร์และคีย์เวิร์ด IS ขณะนี้มีรายการพารามิเตอร์ ค่า HUSBAND แบบฮาร์ดโค้ดในส่วนคำสั่ง WHERE ถูกแทนที่ด้วยการอ้างอิงถึงพารามิเตอร์ UPPER (category_in) เมื่อคุณเปิดเคอร์เซอร์ คุณสามารถตั้งค่าเป็น HUSBAND , สามี หรือ HuSbAnD - เคอร์เซอร์จะยังคงทำงานอยู่ ชื่อของหมวดหมู่ที่เคอร์เซอร์ควรส่งคืนแถวตารางตลกจะถูกระบุในคำสั่ง OPEN (ในวงเล็บ) ในรูปแบบตัวอักษร ค่าคงที่ หรือนิพจน์ เมื่อเปิดเคอร์เซอร์ คำสั่ง SELECT จะถูกแยกวิเคราะห์และพารามิเตอร์จะเชื่อมโยงกับค่า ชุดผลลัพธ์ของแถวจะถูกกำหนด และเคอร์เซอร์ก็พร้อมสำหรับการดึงข้อมูล
การเปิดเคอร์เซอร์ด้วยตัวเลือก
สามารถเปิดเคอร์เซอร์ใหม่เพื่อระบุหมวดหมู่ใดก็ได้:
เปิด joke_cur(Jokes_pkg.category); เปิด joke_cur("สามี"); เปิด joke_cur("นักการเมือง"); เปิด joke_cur (Jokes_pkg.relation || "-ในกฎหมาย");
พารามิเตอร์เคอร์เซอร์มักใช้ในส่วนคำสั่ง WHERE แต่สามารถอ้างอิงถึงที่อื่นในคำสั่ง SELECT:
ประกาศเคอร์เซอร์ joke_cur (category_in ใน ARCHAR2) คือชื่อที่เลือก, category_in, Last_used_date จากเรื่องตลก WHERE category = UPPER (category_in);
แทนที่จะอ่านหมวดหมู่จากตาราง เราเพียงแค่แทนที่พารามิเตอร์ category_in ลงในรายการที่เลือก ผลลัพธ์ยังคงเหมือนเดิมเนื่องจากส่วนคำสั่ง WHERE จำกัดหมวดหมู่ตัวอย่างไว้ที่ค่าพารามิเตอร์
ขอบเขตพารามิเตอร์เคอร์เซอร์
ขอบเขตของพารามิเตอร์เคอร์เซอร์ถูกจำกัดไว้ที่เคอร์เซอร์นั้น ไม่สามารถอ้างอิงพารามิเตอร์เคอร์เซอร์ภายนอกคำสั่ง SELECT ที่เชื่อมโยงกับเคอร์เซอร์ได้ ตัวอย่าง PL/SQL ต่อไปนี้ไม่ได้คอมไพล์เนื่องจาก program_name ไม่ใช่ตัวแปรท้องถิ่นในบล็อก นี่คือพารามิเตอร์เคอร์เซอร์อย่างเป็นทางการที่กำหนดไว้ภายในเคอร์เซอร์เท่านั้น:
ประกาศเคอร์เซอร์ scariness_cur (program_name VARCHAR2) คือผลรวมที่เลือก (scary_level) Total_scary_level จาก tales_from_the_crypt โดยที่ prog_name = program_name; BEGIN program_name:= "THE BREATHING MUMMY"; /* ลิงก์ไม่ถูกต้อง */ OPEN scariness_cur (program_name); .... ปิด Scariness_cur; จบ;
โหมดพารามิเตอร์เคอร์เซอร์
ไวยากรณ์สำหรับพารามิเตอร์เคอร์เซอร์จะคล้ายกับของขั้นตอนและฟังก์ชันมาก - ยกเว้นว่าพารามิเตอร์เคอร์เซอร์สามารถเป็นพารามิเตอร์ IN เท่านั้น พารามิเตอร์เคอร์เซอร์ไม่สามารถตั้งค่าเป็นโหมด OUT หรือ IN OUT โหมดเหล่านี้อนุญาตให้ส่งและส่งค่าจากโพรซีเดอร์ซึ่งไม่สมเหตุสมผลสำหรับเคอร์เซอร์ มีทางเดียวเท่านั้นที่จะรับข้อมูลจากเคอร์เซอร์: ดึงข้อมูลบันทึกและคัดลอกค่าจากรายการคอลัมน์ในส่วน INTO
ค่าพารามิเตอร์เริ่มต้น
พารามิเตอร์เคอร์เซอร์สามารถกำหนดค่าเริ่มต้นได้ ตัวอย่างของเคอร์เซอร์ที่มีค่าพารามิเตอร์เริ่มต้น:
CURSOR emp_cur (emp_id_in NUMBER:= 0) คือ SELECT Employee_id, emp_name จากพนักงาน WHERE Employee_id = emp_id_in;
เนื่องจากพารามิเตอร์ emp_id_in มีค่าเริ่มต้น จึงสามารถละเว้นได้ในคำสั่ง FETCH ในกรณีนี้เคอร์เซอร์จะส่งคืนข้อมูลเกี่ยวกับพนักงานด้วยรหัส 0
เคอร์เซอร์คือลิงก์ไปยังพื้นที่หน่วยความจำตามบริบท ในการใช้งานภาษาการเขียนโปรแกรม SQL บางอย่าง (Oracle, Microsoft SQL Server) - ชุดผลลัพธ์ที่ได้รับเมื่อดำเนินการค้นหาและตัวชี้บันทึกปัจจุบันที่เกี่ยวข้อง ฉันจะบอกว่าเคอร์เซอร์เป็นตารางเสมือนที่แสดงถึงการจัดเก็บข้อมูลทางเลือก ในกรณีนี้ เคอร์เซอร์ช่วยให้คุณเข้าถึงข้อมูลได้เหมือนกับว่าเป็นข้อมูลของอาร์เรย์ปกติ
เคอร์เซอร์ถูกใช้ในขั้นตอนการจัดเก็บ ทฤษฎีพอแล้ว ลองดูตัวอย่าง:
เรามีฐานข้อมูล (ฐานข้อมูลไม่ค่อยดีนิดหน่อย นี่เป็นหนึ่งในผลงานในห้องปฏิบัติการของฉัน แต่ครูฐานข้อมูลของเรายืนยันโครงสร้างนี้)
/*ข้อมูลธนาคาร*/
สร้างตาราง `ธนาคาร` (
`ชื่อธนาคาร` VARCHAR (50) COLLATE utf8_bin ไม่ใช่ค่าเริ่มต้นที่เป็นโมฆะ ""
คีย์หลัก (`BankId`))เครื่องยนต์=อินโนดีบี
ชุดอักขระ "utf8" เรียงกัน "utf8_bin" ;
/*ข้อมูลเกี่ยวกับเงินฝาก */
สร้างตาราง `การกระจายทางธนาคาร` (
`BankId` จำนวนเต็ม (11) ไม่เป็นโมฆะ
`จำนวนเต็มถาวร (11) ค่าเริ่มต้น NULL
`จำนวนการมีส่วนร่วม` ทศนิยม (10,0) ไม่เป็นโมฆะ
`ClientId` จำนวนเต็ม (11) ไม่เป็นโมฆะ
คีย์หลัก(`BankId`, `ClientId`),
คีย์ `BankId` (`BankId`)
คีย์ `ClientId` (`ClientId`)
ข้อจำกัด `bankdistribution_fk` คีย์ต่างประเทศ (`BankId`) การอ้างอิง `bank` (`BankId`)
ข้อจำกัด `bankdistribution_fk1` คีย์ต่างประเทศ (`ClientId`) การอ้างอิง `client` (`ClientId`)
)เครื่องยนต์=อินโนดีบี
/*ข้อมูลเกี่ยวกับนักลงทุน*/
สร้างตาราง `ลูกค้า` (
`ClientId` จำนวนเต็ม (3) ไม่เป็นโมฆะ AUTO_INCREMENT
`CreditCardId` BIGINT(10) ไม่เป็นโมฆะ
`นามสกุล` VARCHAR (50) COLLATE utf8_bin ไม่ใช่ค่าเริ่มต้น "" ,
`ชื่อ` VARCHAR (50) COLLATE utf8_bin ไม่เป็นค่าเริ่มต้น ""
`ชื่อ` VARCHAR (50) COLLATE utf8_bin ไม่เป็นค่าเริ่มต้น "" ,
`โทรศัพท์` VARCHAR (50) COLLATE utf8_bin ไม่เป็นค่าเริ่มต้น ""
`ที่อยู่ ` VARCHAR (50) COLLATE utf8_bin ไม่เป็นค่าเริ่มต้น "" ,
`SafeId` จำนวนเต็ม (5) ไม่เป็นโมฆะ
คีย์หลัก(`ClientId`, `CreditCardId`),
คีย์ `รหัสลูกค้า` (`รหัสลูกค้า`))เครื่องยนต์=อินโนดีบี
AUTO_INCREMENT=ชุดอักขระ 11 ตัว "utf8" COLLATE "utf8_bin"
สมมติว่าเราจำเป็นต้องได้รับธนาคารแต่ละแห่งตามลำดับและดำเนินการบางอย่างกับธนาคาร คำถามต่อไปนี้สามารถช่วยเราได้ในเรื่องนี้
เลือก `ธนาคาร`* จาก `ธนาคาร` จำกัดจำนวน THE_RECORD_WE NEED,1. ดังนั้น เมื่อใช้ LIMIT WE NEED_RECORD NUMBER, 1 เราจะแยกแต่ละระเบียนในลูปจากตารางธนาคารและดำเนินการตามที่เราต้องการ ในขณะที่เพิ่มค่าของ WE NEED_RECORD NUMBER ขึ้น 1 ตอนนี้เราจะทำเช่นเดียวกัน แต่ใช้ เคอร์เซอร์
เริ่ม
/* ตัวแปรที่เราดึงข้อมูลออกมา */
ประกาศจำนวนเต็ม vBankId ;
ประกาศ vBankName VARCHAR(50);
ประกาศ vAddress VARCHAR(50);
ประกาศ vPhone VARCHAR (50);
/* ตัวแปรแฮดเลอร์ - a*/
ประกาศค่าเริ่มต้นจำนวนเต็มเสร็จสิ้น 0;
/*ประกาศเคอร์เซอร์*/
ประกาศเคอร์เซอร์ BankCursor เพื่อเลือก `bank`.`BankId`,`bank`.`BankName`,`bank`.`Address`,`bank`.`Phone`, จาก `bank` โดยที่ 1;
/*วัตถุประสงค์ของ HANDLER ซึ่งจะอธิบายด้านล่าง*/
ประกาศตัวจัดการต่อสำหรับ SQLSTATE "02000" SET เสร็จสิ้น = 1;
/* เปิดเคอร์เซอร์ */
เปิดเคอร์เซอร์ธนาคาร;
/*ดึงข้อมูล*/
ในขณะที่ทำเสร็จ = 0 DO
เราดำเนินการตามที่เราต้องการ
สิ้นสุดในขณะที่ ;
/*ปิดเคอร์เซอร์*/
ปิดเคอร์เซอร์ธนาคาร;
จบ ;* ซอร์สโค้ดนี้ถูกเน้นด้วย Source Code Highlighter
ข้อผิดพลาด: 1329 SQLSTATE: 02000 (ER_SP_FETCH_NO_DATA)
ข้อความ: ไม่มีข้อมูล - ดึง เลือก หรือประมวลผลแถวเป็นศูนย์
SQLSTATE: 02000 เริ่มทำงานเมื่อถึงจุดสิ้นสุดของเคอร์เซอร์ หรือเมื่อการเลือกหรืออัปเดตส่งคืนสตริงว่าง
บรรทัดถัดไปเราประกาศเคอร์เซอร์ DECLARE cursor_name CURSOR FOR select_statement;
เปิดเคอร์เซอร์ เปิด cursor_name;
จากนั้นจนกว่าเราจะไปถึงจุดสิ้นสุดของเคอร์เซอร์ (ในขณะที่ทำ = 0 DO) เราจะแยกข้อมูลและประมวลผล
คุณต้องปิดเคอร์เซอร์ก่อนที่จะออกจากขั้นตอนการจัดเก็บ ปิด cursor_name;
ดูเหมือนจะไม่ซับซ้อน แต่มีข้อผิดพลาดมากมายที่เกี่ยวข้องกับ SQLSTATE "02000"
ในขณะที่ทำเสร็จ = 0 DO
ดึง BankCursor เข้าสู่ vBankId, vBankName, vAddress, vPhone;
เลือก (ContributeAmount) ลงใน vContributeAmountSUM จากการกระจายทางธนาคาร โดยที่ BankId = vBankId จำกัด 1;
เราดำเนินการบางอย่าง
สิ้นสุดในขณะที่ ;* ซอร์สโค้ดนี้ถูกเน้นด้วย Source Code Highlighter
ทุกอย่างเรียบร้อยดีและถูกต้องจากมุมมองทางไวยากรณ์ แต่จากมุมมองเชิงตรรกะไม่มี อาจเกิดขึ้นได้ว่าผู้ฝากเงินยังไม่ได้เปิดบัญชีในบางธนาคาร ดังนั้นสำหรับเลือก (ContributeAmount) เข้าสู่ vContributeAmountSUM จากการกระจายของธนาคาร โดยที่ BankId = vBankId จำกัด 1; SQLSTATE: 02000 จะเริ่มการทำงาน ตัวแปรที่ทำเสร็จแล้วจะถูกตั้งค่าเป็น 1 และลูป while จะสิ้นสุดเร็วกว่าที่เราคาดไว้ สิ่งนี้สามารถหลีกเลี่ยงได้โดยทำดังต่อไปนี้
ในขณะที่ทำเสร็จ = 0 DO
ดึง BankCursor เข้าสู่ vBankId, vBankName, vAddress, vPhone;
/* แยกจำนวนเงินฝากใด ๆ ให้กับธนาคาร */
ถ้า (vContributeAmountSUM > 0) แล้ว
/* แยกจำนวนเงินฝากใด ๆ ให้กับธนาคาร */
สิ้นสุดถ้า ;
เราดำเนินการบางอย่าง
สิ้นสุดในขณะที่ ;* ซอร์สโค้ดนี้ถูกเน้นด้วย Source Code Highlighter
ในคำขอแรก เราจะตรวจสอบว่ามีส่วนสนับสนุนหรือไม่ (หากไม่มี จากนั้น vContributeAmountSUM == 0) และหากมี เราจะดึงข้อมูลเท่านั้น
ตอนนี้ สมมติว่าเราจำเป็นต้องลบจำนวนเงินทั้งหมดในบัญชีในธนาคารต่างๆ สำหรับลูกค้าแต่ละราย
ประกาศเคอร์เซอร์ ClientSummCursor สำหรับผลรวมที่เลือก
ประกาศเคอร์เซอร์ ClientSummCursor สำหรับผลรวมที่เลือก (`bankdistribution`.`ContributeAmount`), `bankdistribution`.`ClientId` จาก `bankdistribution` เข้าร่วมภายในไคลเอนต์บน (client.ClientId = bankdistribution.`ClientId`) โดยที่ 1 กลุ่มโดย `bankdistribution` `รหัสลูกค้า`;เปิด ClientSummCursor;
ในขณะที่ทำเสร็จ = 0 DO
ดึง BankCursor เข้าสู่ vBankId, vBankName, vAddress, vPhone;
/* แยกจำนวนเงินฝากใด ๆ ให้กับธนาคาร */
เลือกจำนวน(ContributeAmount) ลงใน vContributeAmountSUM จากการกระจายทางธนาคาร โดยที่ BankId = vBankId จำกัด 1;
/* ตรวจสอบว่ามีเงินฝากในธนาคารนี้จริงหรือไม่ */
ถ้า (vContributeAmountSUM > 0) แล้ว
/* แยกจำนวนเงินฝากใด ๆ ให้กับธนาคาร */
เลือก ContributeAmount ลงใน vContributeAmountSUM จากการกระจายทางธนาคาร โดยที่ BankId = ขีดจำกัด vBankId 1;
สิ้นสุดถ้า ;
เราดำเนินการบางอย่าง
สิ้นสุดในขณะที่ ;* ซอร์สโค้ดนี้ถูกเน้นด้วย Source Code Highlighter
สถานการณ์เดียวกันอาจเกิดขึ้นเมื่อข้อมูลในเคอร์เซอร์ ClientSummCursor สิ้นสุดเร็วกว่าข้อมูลใน BankCursor, SQLSTATE: 02000 ถูกทริกเกอร์, ตัวแปรที่ทำเสร็จแล้วถูกตั้งค่าเป็น 1 และลูป while สิ้นสุดเร็วกว่าที่เราคาดไว้ สิ่งนี้สามารถหลีกเลี่ยงได้โดยทำดังต่อไปนี้
เปิด ClientSummCursor;
ในขณะที่ทำเสร็จ = 0 DO
ดึง BankCursor เข้าสู่ vBankId, vBankName, vAddress, vPhone;
/* แยกจำนวนเงินฝากใด ๆ ให้กับธนาคาร */
เลือกจำนวน(ContributeAmount) ลงใน vContributeAmountSUM จากการกระจายทางธนาคาร โดยที่ BankId = vBankId จำกัด 1;
/* ตรวจสอบว่ามีเงินฝากในธนาคารนี้จริงหรือไม่ */
ถ้า (vContributeAmountSUM > 0) แล้ว
/* แยกจำนวนเงินฝากใด ๆ ให้กับธนาคาร */
เลือก ContributeAmount ลงใน vContributeAmountSUM จากการกระจายทางธนาคาร โดยที่ BankId = ขีดจำกัด vBankId 1;
สิ้นสุดถ้า ;
/* ก่อนที่จะแยกข้อมูลจากเคอร์เซอร์ตัวที่สอง ให้จำสถานะ sqlstate */
SET old_status = เสร็จสิ้น;
/* ดึงข้อมูลที่เราต้องการ */
ดึง ClientSummCursor เข้าสู่ vSum, vClientId;
/* ตรวจสอบว่าข้อมูลถูกดึงมาหรือไม่ และ sqlstate 0200 ล้มเหลวหรือไม่ */
ถ้า (เสร็จสิ้น = 0) แล้ว
เราดำเนินการบางอย่าง
สิ้นสุดถ้า ;
/* ก่อนสิ้นสุด while ให้คืนค่าของตัวแปรที่ทำเสร็จแล้ว */
ตั้งค่าเสร็จแล้ว = old_status;
สิ้นสุดในขณะที่ ;* ซอร์สโค้ดนี้ถูกเน้นด้วย Source Code Highlighter
ขอบคุณทุกคนที่อ่านมาจนถึงตอนนี้ ฉันหวังว่าสิ่งนี้จะเป็นประโยชน์กับใครบางคน
อาจเป็นไปได้ว่าการตอบสนองต่อคำขอของลูกค้าธรรมดาจะเป็นตัวอย่างแถวหลายแสนแถว ซึ่งลูกค้าส่วนใหญ่ไม่สามารถย่อยได้ ในกรณีนี้ วิธีแก้ปัญหาการโต้ตอบกับไคลเอนต์คือการใช้เคอร์เซอร์เป็นกลไกสากลในการแลกเปลี่ยนข้อมูลระหว่างเซิร์ฟเวอร์และไคลเอนต์ เคอร์เซอร์ทำงานร่วมกับชุดผลลัพธ์ของข้อมูล (ผลลัพธ์ของการสืบค้น) ทำให้ผู้ใช้มีความสามารถในการประมวลผลข้อมูลเพิ่มเติม:
เคอร์เซอร์ช่วยให้คุณทำงานกับแถวของตารางโดยระบุหมายเลขซีเรียลในชุดข้อมูล
เคอร์เซอร์ช่วยให้คุณสามารถดำเนินการแก้ไขข้อมูลที่ซับซ้อนได้ เช่น เมื่อการเปลี่ยนแปลงค่าของคอลัมน์จำเป็นต้องเข้าถึงค่าของคอลัมน์อื่นซ้ำๆ
วงจรชีวิตของเคอร์เซอร์:
การสร้างเคอร์เซอร์:ประกาศ<имя курсора>[ ไม่รู้สึก ] [ เลื่อน ] เคอร์เซอร์เพื่อ< SELECT -оператор>สำหรับ (อ่านอย่างเดียว | อัปเดต)
ในที่นี้ คีย์เวิร์ด INSENSITIVE หมายความว่าเคอร์เซอร์จะเป็นแบบคงที่ (ภาพรวมของข้อมูล) ในขณะที่ตามค่าเริ่มต้น เคอร์เซอร์จะถูกสร้างขึ้นแบบไดนามิก (การดึงข้อมูลจะดำเนินการทุกครั้งที่มีการเข้าถึงแถว) คีย์เวิร์ด SCROLL หมายความว่าเคอร์เซอร์สามารถเลื่อนไปในทิศทางใดก็ได้ มิฉะนั้นเคอร์เซอร์จะถูกสร้างขึ้นเป็นเคอร์เซอร์ "ตามลำดับ"
เคอร์เซอร์เปิด:เปิด [ทั่วโลก]<имя курсора>. เคอร์เซอร์ที่ระบุเป็น GLOBAL จะไม่ถูกลบโดยอัตโนมัติเมื่อขั้นตอนหรือแพ็คเกจที่ถูกเรียกว่าสิ้นสุด
การอ่านข้อมูล : ดึงข้อมูล [[ ถัดไป | ก่อน | ครั้งแรก | สุดท้าย | สัมบูรณ์ n | n ที่เกี่ยวข้อง ] จาก ] [ ทั่วโลก ]<имя курсора>[INTO@variable_name,...] SQL Server 2000 อนุญาตให้คุณอ่านเพียงแถวเดียวจากเคอร์เซอร์ คีย์เวิร์ด FIRST คือการส่งคืนแถวแรกของเคอร์เซอร์ LAST – บรรทัดสุดท้ายของเคอร์เซอร์ NEXT – บรรทัดถัดไปหลังจากบรรทัดปัจจุบัน บรรทัดที่ส่งคืนจะกลายเป็นบรรทัดปัจจุบัน PRIOR – ก่อนหน้าก่อนปัจจุบัน; ABSOLUTE n – ส่งคืนบรรทัดด้วยหมายเลขลำดับสัมบูรณ์ในเคอร์เซอร์ RELATIVE – n บรรทัดหลังจากบรรทัดปัจจุบัน ข้อมูลคอลัมน์จะถูกจัดเก็บไว้ในตัวแปรที่ระบุแต่ละตัวตามลำดับที่แสดง
เปลี่ยนข้อมูล:รันคำสั่ง UPDATE ด้วยไวยากรณ์ที่ออกแบบมาเพื่อทำงานกับเคอร์เซอร์
การลบข้อมูล:รันคำสั่ง DELETE ด้วยไวยากรณ์ที่ออกแบบมาเพื่อทำงานกับเคอร์เซอร์
การปิดเคอร์เซอร์:ปิด [ทั่วโลก]<имя курсора>
ปล่อยเคอร์เซอร์:จัดสรรคืน [ทั่วโลก]<имя курсора>
ตัวอย่างการใช้เคอร์เซอร์:
ประกาศ fo_curs เคอร์เซอร์คงที่สำหรับ
เลือก name_rus จากสำหรับ ORDER BY name_rus
ประกาศ @name varchar(50)
ดึงข้อมูลจาก fo_curs เข้าสู่ @name ก่อน
ในขณะที่ @@FETCH_STATUS=0
ดึงข้อมูลถัดไปจาก fo_curs เข้าสู่ @name
ยกเลิกการจัดสรร fo_curs
2.7. มั่นใจในความปลอดภัยของข้อมูลและความสมบูรณ์ใน Microsoft SQL Server การจัดการฐานข้อมูล บทบาท การกำหนดสิทธิ์ให้กับผู้ใช้ (GRANT, DENY, REVOKE) วิธีการและเทคโนโลยีสำหรับการปกป้องข้อมูลใน SQL Server
การรักษาความปลอดภัยและการดูแลระบบเซิร์ฟเวอร์ SQL .
ภารกิจหลักของ DBMS คือการตรวจสอบความสมบูรณ์และความสอดคล้องของข้อมูลภายในสาขาวิชาที่เลือก ปัจจัยหนึ่งที่ทำให้ระบบไม่สามารถแก้ไขปัญหานี้คือการกระทำของผู้ใช้ที่พยายามทำลายโครงสร้างข้อมูลหรือเปลี่ยนแปลงข้อมูลโดยไม่ตั้งใจหรือโดยเจตนา ด้วยเหตุนี้ DBMS จะต้องได้รับการปกป้องไม่เพียงแต่จากความล้มเหลวทางกายภาพเท่านั้น แต่ยังรวมถึงจากผู้ใช้ที่ไม่เพียงพอสำหรับงานที่กำลังดำเนินการอีกด้วย ในการดำเนินการนี้ จำเป็นต้องออกแบบและเชื่อมต่อระบบรักษาความปลอดภัยเข้ากับฐานข้อมูลที่จะป้องกันไม่ให้ผู้ใช้ดำเนินการใดๆ นอกเหนืออำนาจของตน
การจัดการฐานข้อมูล
หากต้องการสร้างฐานข้อมูลโดยใช้ TSQL ให้ใช้คำสั่ง CREATE DATABASE แต่โดยปกติแล้วความสามารถของ SQL Server Management Studio จะถูกนำมาใช้เพื่อจุดประสงค์นี้ มีการดำเนินการฐานข้อมูลค่อนข้างน้อยที่กำหนดไว้ใน SQL Server: การเพิ่ม (ลด) ขนาดไฟล์, การเปลี่ยนแปลงการกำหนดค่า (คำสั่ง ALTER), การแนบและการแยกออก, การโอนความเป็นเจ้าของ, การเปลี่ยนชื่อ, การดูคุณสมบัติและสุดท้ายคือการลบ (DROP DATABASE)
เช่นเดียวกับเซิร์ฟเวอร์ฐานข้อมูลส่วนใหญ่ SQL Server มีผู้ใช้ที่มีสิทธิ์การดูแลระบบเต็มรูปแบบ - นี่คือ ผู้ดูแลระบบหรือ 'sa'. หลังจากการติดตั้งเซิร์ฟเวอร์ครั้งแรก รหัสผ่าน sa จะว่างเปล่า ผู้ใช้ที่สร้างฐานข้อมูลใหม่จะกลายเป็นเจ้าของโดยอัตโนมัติ ('dbo' - เจ้าของฐานข้อมูล) ในขณะที่สร้างฐานข้อมูล ผู้ใช้ "แขก" จะถูกกำหนดด้วย หากบัญชีผู้ใช้ไม่ได้รับการแมปอย่างชัดเจนกับผู้ใช้ของ ฐานข้อมูลเฉพาะ ผู้ใช้จะได้รับอนุญาตให้เข้าถึงโดยนัยโดยใช้ชื่อแขก แขก แขกมักถูกห้าม
ผู้ใช้ที่สร้างอ็อบเจ็กต์ในฐานข้อมูลจะกลายเป็นเจ้าของโดยอัตโนมัติ และไม่มีใคร รวมถึง dbo และ sa ที่สามารถใช้อ็อบเจ็กต์นั้นได้จนกว่าเจ้าของจะกำหนดสิทธิ์ให้กับพวกเขา แต่เพื่อให้ผู้ใช้สร้างวัตถุ เจ้าของฐานข้อมูลจะต้องให้สิทธิ์ที่เหมาะสมแก่เขาก่อน
บทบาท ช่วยให้คุณสามารถรวมผู้ใช้ที่ทำหน้าที่เดียวกันเพื่อทำให้การดูแลระบบง่ายขึ้น บทบาทสามารถมีอยู่แล้วภายในหรือกำหนดเองได้ บทบาทในตัวจะถูกนำไปใช้ในระดับเซิร์ฟเวอร์และระดับฐานข้อมูล ด้านล่างนี้เป็นตารางบทบาทฐานข้อมูลในตัว:
db_owner. มีสิทธิทั้งหมดในฐานข้อมูล
Db_accessadmin. สามารถเพิ่มหรือลบผู้ใช้ได้
db_securityadmin. จัดการสิทธิ์ วัตถุ บทบาท และผู้ใช้ทั้งหมด
Db_ddladmin. สามารถดำเนินการคำสั่ง DDL ทั้งหมด ยกเว้น GRANT, DENY, REVOKE
Db_backupoperator. Archiver สามารถรันคำสั่งได้ ข้อมูล
db_datareader. บางทีก็ดู.. ข้อมูลใด ๆ ในตารางใด ๆ
db_datawriter บางทีอาจมีการปรับเปลี่ยน ข้อมูลใด ๆ ในตารางใด ๆ
Db_denydatareader ต้องห้าม ดู รัก ข้อมูลในใด ๆ ตาราง
Db_denydatawriter ห้ามแก้ไขข้อมูลใดๆ ในตารางใดๆ
การกำหนดสิทธิ์ให้กับผู้ใช้ พื้นฐานของการรักษาความปลอดภัยของ SQL Server คือ (1) บัญชี; (2) ผู้ใช้; (3) บทบาท; (4) กลุ่ม
เมื่อผู้ใช้เชื่อมต่อกับ SQL Server การกระทำที่เขาสามารถทำได้จะถูกกำหนดโดยสิทธิ์ที่มอบให้แก่เขาในฐานะผู้ใช้และสมาชิกของบทบาท สิทธิ์จะได้รับจากผู้ดูแลระบบ DBMS เจ้าของฐานข้อมูล หรือเจ้าของออบเจ็กต์ฐานข้อมูลเฉพาะ สิทธิ์ในฐานข้อมูลสามารถแบ่งออกเป็นสามประเภท: (1) สิทธิ์ในการเข้าถึงวัตถุฐานข้อมูล; (2) สิทธิ์ในการรันคำสั่ง TSQL; (3) สิทธิโดยนัย เซิร์ฟเวอร์อนุญาตให้คุณโอนความเป็นเจ้าของจากผู้ใช้รายหนึ่งไปยังอีกรายหนึ่ง
คำสั่งต่อไปนี้ใช้ในการจัดการสิทธิ์ของผู้ใช้ในการเข้าถึงวัตถุฐานข้อมูล:
ยินยอม(ทั้งหมด |< вид действия >,…}
( บน (<имя таблицы или представления>} [(<имя столбца>,…)]
| บน (< имя хранимой процедуры >}
| บน (< имя пользовательской функции >}
ถึง ( สาธารณะ |<имя объекта системы безопасности>,…}
[ เช่น<имя группы> | <имя роли>]
– การกำหนดสิทธิ์ให้กับผู้ใช้, ที่ไหน
ทั้งหมด – ผู้ใช้จะได้รับสิทธิ์ที่เป็นไปได้ทั้งหมด มิฉะนั้นจะระบุ
<вид действия>– สิทธิ์ในการดำเนินการที่มีให้กับผู้ใช้ ได้แก่ :
SELECT – สำหรับมุมมอง สำหรับคอลัมน์ตาราง และสำหรับตาราง (มุมมอง)
INSERT – เพื่อเพิ่มสำหรับตาราง (มุมมอง) โดยรวม
UPDATE – สำหรับการเปลี่ยนแปลง สำหรับคอลัมน์ตาราง และสำหรับตาราง (มุมมอง)
DELETE – เพื่อลบตาราง (มุมมอง) โดยรวม
EXECUTE – เพื่อดำเนินการตามขั้นตอนที่เก็บไว้
การอ้างอิง – ความสามารถในการอ้างอิงถึงวัตถุที่ระบุ (รวมเป็นส่วนหนึ่งของคีย์ต่างประเทศ)
<имя объекта системы безопасности>– บัญชี SQL Server, ผู้ใช้โดเมน Windows; สาธารณะ – สำหรับผู้ใช้ทุกคน
ด้วยตัวเลือกการให้สิทธิ์ - อนุญาตให้ผู้ใช้ที่ได้รับสิทธิ์ในปัจจุบันสามารถกำหนดสิทธิ์การเข้าถึงวัตถุให้กับผู้ใช้รายอื่น
เช่น<имя группы> | <имя роли>– การมีส่วนร่วมของผู้ใช้ในบทบาทที่ได้รับความสามารถในการให้สิทธิ์แก่ผู้ใช้รายอื่น
ให้สิทธิ์เลือกแก่ผู้เขียนสู่สาธารณะ
ให้สิทธิ์แทรก อัปเดต ลบผู้เขียนถึง Mary, John, Tom
ให้สิทธิ์เลือกบน Plan_Data ให้กับการบัญชีด้วยตัวเลือกการให้สิทธิ์
ให้สิทธิ์เลือก Plan_Data ให้กับ Jack AS Accounting
แจ็คไม่ใช่สมาชิกของบทบาทการบัญชี แต่มีคนในบทบาทนั้นสามารถให้สิทธิ์ได้
ปฏิเสธ(ทั้งหมด |< вид действия >,…}
( บน (<имя таблицы или представления>} [(<имя столбца>,…)]
| บน (<имя хранимой процедуры>}
| บน (<имя пользовательской функции>}
ถึง ( สาธารณะ |<имя объекта системы безопасности>,…}
– การปฏิเสธการเข้าถึงผู้ใช้ไปยังวัตถุฐานข้อมูล CASCADE เพิกถอนสิทธิ์ไม่เพียงแต่จากผู้ใช้รายนี้เท่านั้น แต่ยังรวมถึงทุกคนที่เขาให้สิทธิ์ด้วย
ตัวอย่าง (บน คำสั่งห้าม TSQL):
ปฏิเสธการสร้างตารางให้กับ Jack CASCADE
ทีม ถอนใช้เพื่อปฏิเสธการเข้าถึงวัตถุฐานข้อมูลโดยปริยาย ไวยากรณ์เหมือนกับคำสั่ง DENY การปฏิเสธโดยนัยนั้นคล้ายคลึงกับการปฏิเสธการเข้าถึง ยกเว้นว่าจะใช้เฉพาะในระดับที่กำหนดไว้เท่านั้น ตัวอย่าง: ผู้ใช้ Jack ซึ่งเป็นสมาชิกของบทบาท GoodUsers ได้รับสิทธิ์ในการเข้าถึงตาราง XFiles หาก REVOKE ถูกปฏิเสธสำหรับบทบาท GoodUsers ในการเข้าถึงตารางนี้ Jack ยังคงสามารถเข้าถึงตารางนี้ได้เนื่องจากสิทธิ์สำหรับเขาถูกกำหนดไว้อย่างชัดเจน หากคุณใช้ REVOKE เป็นการส่วนตัว เขาจะเสียสิทธิ์ในการเข้าถึง XFiles
สิทธิ์ที่มอบให้กับบทบาทนั้นสืบทอดมาจากสมาชิก หากผู้ใช้ได้รับอนุญาตให้เข้าถึงออบเจ็กต์ผ่านการเป็นสมาชิกในบทบาทหนึ่ง แต่ถูกปฏิเสธในอีกบทบาทหนึ่ง ข้อขัดแย้งในการเข้าถึงจะได้รับการแก้ไขด้วยการปฏิเสธเสมอ
เทคโนโลยีการปกป้องข้อมูลใน MS SQL Server
1.กลไก จุดตรวจ- จุดตรวจสอบที่สร้างขึ้นหลังจาก ~ 60 วินาทีเพื่อเขียนหน้าที่อัพเดตลงดิสก์ (จุดตรวจสอบสามารถบังคับได้ด้วยคำสั่ง CHECKPOINT)
2. กลไกในตัวและภายนอกสำหรับการตรวจสอบความสมบูรณ์ของฐานข้อมูล (เปิดใช้งานโดยอัตโนมัติหรือเช่นยูทิลิตี้ DBCC - ตัวตรวจสอบความสอดคล้องของฐานข้อมูล - ด้วยตนเอง)
3. การทำสำเนาทางกายภาพ (หากอนุญาต) ของไฟล์ฐานข้อมูลโดยใช้ระบบปฏิบัติการ (รวมถึงกลไกของฮาร์ดไดรฟ์แบบมิเรอร์)
4. การสำรองฐานข้อมูลและบันทึกธุรกรรม - โดยการเขียนดัมพ์ฐานข้อมูลไปยังอุปกรณ์สำรองข้อมูล (เทปแม่เหล็กหรือฮาร์ดไดรฟ์)
5. การจำลองแบบ – ความสามารถในการทำซ้ำข้อมูลเป็นระยะ (ในบางกรณี พร้อมกัน) โดยถ่ายโอนข้อมูลจากเซิร์ฟเวอร์ SQL หนึ่งไปยังอีกเซิร์ฟเวอร์หนึ่ง
6. การเข้ารหัสการรับส่งข้อมูลระหว่างไคลเอนต์และเซิร์ฟเวอร์ รวมถึงการเข้ารหัสรหัสที่ใช้ในการทำงานกับออบเจ็กต์ฐานข้อมูล (ขั้นตอนที่เก็บไว้ ทริกเกอร์ ฯลฯ)
เคอร์เซอร์คืออ็อบเจ็กต์ที่ช่วยให้คุณสามารถประมวลผลแถวจากชุดผลลัพธ์ที่ส่งคืนโดยคำสั่ง SELECT แยกกัน ต่อไป เราจะดูที่เคอร์เซอร์ที่รองรับในภาษา Transact-SQL เหล่านี้เป็นเคอร์เซอร์ฝั่งเซิร์ฟเวอร์ที่มีอยู่เป็นวัตถุบนฝั่งเซิร์ฟเวอร์ฐานข้อมูล นอกจากนี้ยังมีเคอร์เซอร์ไคลเอนต์ที่ใช้ในการสร้างแอปพลิเคชันฐานข้อมูลไคลเอนต์
วรรณกรรมตั้งข้อสังเกตว่าการประมวลผลชุดข้อมูลแบบแถวต่อแถวโดยใช้เคอร์เซอร์ในกรณีส่วนใหญ่นั้นช้ากว่าการดำเนินการที่คล้ายกันซึ่งดำเนินการโดยเครื่องมือ SQL สำหรับการประมวลผลหลายแถวอย่างมาก ดังนั้นจึงแนะนำให้ใช้เคอร์เซอร์เฉพาะในกรณีที่การอธิบายการดำเนินการที่จำเป็นผ่านการดำเนินการด้วยชุดแถวนั้นไม่ได้ผลอย่างชัดเจนหรือเป็นไปไม่ได้เลยด้วยซ้ำ
การทำงานกับเคอร์เซอร์มักเกี่ยวข้องกับขั้นตอนต่อไปนี้:
- การประกาศเคอร์เซอร์
- เปิดเคอร์เซอร์;
- การอ่านค่าแอตทริบิวต์จากบันทึกเคอร์เซอร์แรกเป็นตัวแปร
- การเคลื่อนที่ไปรอบๆ เคอร์เซอร์ (โดยปกติจะอยู่ในวงวน) และประมวลผลรายการเคอร์เซอร์
- เคอร์เซอร์ปิด;
- การเพิ่มหน่วยความจำที่จัดสรรให้กับเคอร์เซอร์
เคอร์เซอร์ถูกประกาศโดยใช้คำสั่ง DECLARE ซึ่งมีรูปแบบดังแสดงด้านล่าง ควรสังเกตว่าใน SQL Server ตัวดำเนินการนี้รองรับทั้งไวยากรณ์ของมาตรฐาน ISO SQL (ไม่ได้ระบุเวอร์ชันของมาตรฐานในเอกสารประกอบ) และไวยากรณ์โดยใช้ชุดส่วนขยายภาษา Transact-SQL CURSOR
สำหรับ select_statement
ไวยากรณ์ Transact-SQL แบบขยาย:
ประกาศเคอร์เซอร์_ชื่อเคอร์เซอร์
สำหรับ select_statement
]][;]
การระบุคีย์เวิร์ด GLOBAL หมายความว่าเคอร์เซอร์ที่ประกาศจะพร้อมใช้งานในชุดงาน ทริกเกอร์ หรือขั้นตอนการจัดเก็บที่ทำงานภายในการเชื่อมต่อปัจจุบันไปยังเซิร์ฟเวอร์ เคอร์เซอร์จะถูกปล่อยโดยปริยายก็ต่อเมื่อการเชื่อมต่อขาดหาย
เคอร์เซอร์ "ท้องถิ่น" ไม่ว่าจะสร้างขึ้นโดยค่าเริ่มต้นหรือโดยการระบุ LOCAL อย่างชัดเจน จะมีอยู่ในชุดงาน ขั้นตอนการจัดเก็บ หรือทริกเกอร์ที่สร้างขึ้นเท่านั้น เคอร์เซอร์นี้จะถูกปล่อยออกมาโดยปริยายเมื่อแบตช์ กระบวนงานที่เก็บไว้ หรือทริกเกอร์ดำเนินการเสร็จสิ้น ข้อยกเว้นคือเมื่อเคอร์เซอร์ถูกส่งผ่านพารามิเตอร์ OUTPUT ของกระบวนงานที่เก็บไว้ จากนั้นเคอร์เซอร์จะว่างเมื่อตัวแปรทั้งหมดที่อ้างอิงถึงเคอร์เซอร์นั้นว่างหรือเมื่ออยู่นอกขอบเขต
FORWARD_ONLY หมายความว่าคุณสามารถ "เลื่อน" ไปตามเคอร์เซอร์ไปข้างหน้าเท่านั้น (เฉพาะคำสั่ง FETCH NEXT เท่านั้นที่ใช้งานได้ ดูด้านล่าง) เช่น แต่ละรายการในเคอร์เซอร์สามารถประมวลผลได้มากที่สุดหนึ่งครั้ง หากระบุ FORWARD ONLY โดยไม่มีคีย์เวิร์ด STATIC, KEYSET หรือ DYNAMIC เคอร์เซอร์จะทำงานเป็นเคอร์เซอร์ DYNAMIC (ดูด้านล่าง) หากไม่ได้ระบุ FORWARD_ONLY หรือ SCROLL และหากไม่มีการระบุคำหลัก STATIC, KEYSET หรือ DYNAMIC ค่าเริ่มต้นจะเป็น FORWARD_ONLY
SCROLL หมายความว่าคุณสามารถ “เลื่อน” ไปรอบๆ เคอร์เซอร์ในทิศทางใดก็ได้ (FIRST, LAST, PRIOR, NEXT, RELATIVE, ABSOLUTE มีอยู่ในคำสั่ง FETCH) ไม่สามารถระบุตัวเลือก SCROLL ด้วยตัวเลือก FAST_FORWARD เคอร์เซอร์แบบคงที่, คีย์เซ็ต และไดนามิกมีค่าเริ่มต้นเป็น SCROLL
STATIC หมายถึงเคอร์เซอร์ไม่สามารถอัปเดตได้ ชุดข้อมูลผลลัพธ์ของเคอร์เซอร์ดังกล่าวจะถูกดึงมาจากฐานข้อมูลและจัดเก็บไว้ในฐานข้อมูลสำหรับวัตถุชั่วคราว tempdb การเปลี่ยนแปลงตารางที่ทำหน้าที่เป็นพื้นฐานสำหรับเคอร์เซอร์จะไม่ปรากฏในเคอร์เซอร์หลังจากนี้
KEYSET – สำหรับเคอร์เซอร์ประเภทนี้ ชุดของค่าคีย์ที่ระบุบันทึกที่เลือกจะถูกเก็บไว้ในตารางชั่วคราว เมื่อคุณเลื่อนเคอร์เซอร์ ค่าของแอตทริบิวต์ที่ไม่ใช่คีย์จะถูกดึงมาจากตารางที่เกี่ยวข้อง ดังนั้นการเปลี่ยนแปลงในคอลัมน์ที่ไม่ใช่คีย์จะมองเห็นได้เมื่อคุณเลื่อนเคอร์เซอร์ หากแถวที่อยู่ในเคอร์เซอร์ถูกลบออกจากตารางแล้วตามเวลาที่ดึงข้อมูลโดยตัวดำเนินการ FETCH ตัวแปรบริการ @@ FETCH_STATUS จะส่งกลับค่า -2 แถวที่เพิ่มลงในตารางหลังจากเปิดเคอร์เซอร์จะไม่ปรากฏในเคอร์เซอร์ หากแบบสอบถามที่สร้างเคอร์เซอร์เกี่ยวข้องกับตารางอย่างน้อยหนึ่งตารางที่ไม่มีดัชนีเฉพาะ เคอร์เซอร์ประเภท KEYSET จะถูกแปลงเป็นประเภท STATIC
DYNAMIC เป็นประเภทเคอร์เซอร์ที่ใช้ทรัพยากรมากที่สุด ซึ่งจะแสดงการเปลี่ยนแปลงข้อมูลทั้งหมดที่เกิดขึ้นในแถวของชุดผลลัพธ์ รวมถึงแถวที่แทรกใหม่ด้วย ค่าข้อมูล ลำดับ และสมาชิกแถวในการเลือกแต่ละรายการอาจแตกต่างกันไป FETCH ABSOLUTE ไม่สามารถใช้กับเคอร์เซอร์แบบไดนามิกได้
FAST_FORWARD เป็นประเภทเคอร์เซอร์ที่เร็วที่สุด ซึ่งช่วยให้คุณสามารถย้ายจากบรรทัดหนึ่งไปยังอีกบรรทัดหนึ่งได้เพียง "ไปข้างหน้า" เท่านั้น นี่คือประเภทเคอร์เซอร์เริ่มต้น (เมื่อละเว้นคำหลักเพิ่มเติม) มันเทียบเท่ากับเคอร์เซอร์ที่ประกาศด้วยพารามิเตอร์ FORWARD_ONLY และ READ_ONLY
READ_ONLY - กำหนดเคอร์เซอร์ "อ่านอย่างเดียว": การเปลี่ยนแปลงฐานข้อมูลไม่สามารถทำได้ผ่านเคอร์เซอร์ดังกล่าว
SCROLL_LOCKS หมายความว่า SQL Server จะล็อกแถวขณะที่อ่านลงในเคอร์เซอร์ เพื่อให้แน่ใจว่าสามารถอัปเดตหรือลบแถวเหล่านั้นได้ผ่านเคอร์เซอร์ประเภทนั้น
เคอร์เซอร์ที่ประกาศด้วยคีย์เวิร์ด OPTIMISTIC ไม่จำเป็นต้องมีการล็อกแถว และอนุญาตให้แก้ไขข้อมูลได้ หากการเปลี่ยนแปลงในตารางฐานเกิดขึ้นหลังจากอ่านข้อมูลในเคอร์เซอร์แล้ว การพยายามแก้ไขข้อมูลนั้นผ่านเคอร์เซอร์จะส่งผลให้เกิดข้อผิดพลาด
TYPE_WARNING ระบุว่าเมื่อเคอร์เซอร์ถูกแปลงจากประเภทที่ร้องขอไปเป็นประเภทอื่นโดยปริยาย (เช่น การแปลงเคอร์เซอร์ KEYSET เป็น STATIC ที่อธิบายไว้ข้างต้นเมื่อไม่มีดัชนีที่ไม่ซ้ำกันในตาราง) คำเตือนจะถูกส่งไปยังไคลเอนต์
Select_statement – คำสั่ง SELECT ที่สร้างชุดผลลัพธ์ของเคอร์เซอร์
คำสั่ง FOR UPDATE ระบุคอลัมน์ในเคอร์เซอร์ที่ต้องการอัพเดต ถ้าของ column_name [, . . . n] ดังนั้นเฉพาะคอลัมน์ที่อยู่ในรายการเท่านั้นที่จะพร้อมสำหรับการเปลี่ยนแปลง หากไม่มีรายการคอลัมน์ การอัพเดตสามารถทำได้สำหรับคอลัมน์ทั้งหมด ยกเว้นในกรณีของการประกาศเคอร์เซอร์ด้วยพารามิเตอร์ READ_ONLY
หากต้องการเปิดและเติมเคอร์เซอร์ ให้ใช้คำสั่ง
เปิด (( cursor_name) ฉัน @cursor_variable)
เมื่อเปิด เคอร์เซอร์สามารถระบุด้วยชื่อ (cursor_name) หรือผ่านตัวแปรประเภท CURSOR (@cursor_variable) พารามิเตอร์ GLOBAL ระบุว่า cursor_name เป็นเคอร์เซอร์โกลบอล
หากต้องการเลื่อนผ่านชุดข้อมูลของเคอร์เซอร์และดึงข้อมูลเป็นค่าตัวแปร ให้ใช้คำสั่ง FETCH:
ดึงข้อมูล [
(( cursor_name] ฉัน @cursor_variable]
คำสั่งที่กำหนดทิศทางการเคลื่อนที่ตามเคอร์เซอร์มีอธิบายไว้ในตาราง 10.10. ตามที่ระบุไว้ก่อนหน้านี้ ขึ้นอยู่กับประเภทของเคอร์เซอร์ คำสั่งบางคำสั่งสำหรับเคอร์เซอร์บางตัวอาจไม่สามารถใช้ได้
สิ่งสำคัญคือต้องทราบว่าหากเคอร์เซอร์เพิ่งเปิดขึ้น การดำเนินการ FETCH NEXT ครั้งแรกจะส่งผลให้มีการข้ามไปยังบันทึกแรกของเคอร์เซอร์
ตารางที่ 10.10
การนำทางชุดข้อมูลเคอร์เซอร์
ตัวแปรโกลบอล @@FETCH_STATUS ช่วยให้คุณค้นหาผลลัพธ์ของการดำเนินการคำสั่ง FETCH ครั้งล่าสุด:
O – การดำเนินการเสร็จสมบูรณ์แล้ว
- -1 – การดำเนินการของผู้ปฏิบัติงานล้มเหลว หรือบรรทัดอยู่นอกชุดผลลัพธ์ (เคอร์เซอร์สิ้นสุด)
- -2 – แถวที่เลือกหายไป เช่น หากในขณะที่ทำงานกับเคอร์เซอร์ประเภท "ไวต่อการเปลี่ยนแปลง" บันทึกปัจจุบันจะถูกลบออกจากฐานข้อมูล
คำสั่ง CLOSE จะปิดเคอร์เซอร์ที่เปิดอยู่ ทำให้หน่วยความจำที่ใช้จัดเก็บชุดข้อมูลว่าง การดึงข้อมูลและการเคลื่อนที่ไปรอบๆ เคอร์เซอร์ที่ปิดอยู่นั้นเป็นไปไม่ได้ หากต้องการทำเช่นนี้ จะต้องเปิดใหม่อีกครั้ง
ปิด (( cursor_name)|@cursor_variable )
คำสั่ง DEALLOCATE จะลบการเชื่อมโยงระหว่างเคอร์เซอร์กับชื่อหรือตัวแปร หากนี่คือนามสกุลหรือตัวแปรที่อ้างอิงเคอร์เซอร์ ตัวเคอร์เซอร์เองจะถูกลบและทรัพยากรใดๆ ที่ใช้จะถูกปล่อย:
DEALLOCATE (( cursor_name] | @cursor_variable) ลองพิจารณาตัวอย่างง่ายๆ ของการใช้เคอร์เซอร์ ที่นี่ ผู้แต่งและชื่อหนังสือที่ตีพิมพ์ไม่เร็วกว่าปี 2000 จะถูกเลือกจากตาราง หลังจากนั้นข้อมูลจะถูกส่งออกในลูปไปยังคำสั่ง SELECT - แต่ละครั้งจะมีหนึ่งบันทึกที่มีชื่อของตัวเอง คำอธิบายเพิ่มเติมได้รับจากความคิดเห็นในโค้ด:
/*ประกาศตัวแปร*/
ประกาศ @auth varchar(50), @title varchar(50)
โดยที่ >= 2000
/*เปิดเคอร์เซอร์แล้ว “เรียกใช้” ผ่านเคอร์เซอร์ โดยแสดงผู้แต่งและชื่อเรื่องโดยใช้คำสั่ง SELECT แยกต่างหาก*/
ดึงข้อมูลถัดไปจากเคอร์เซอร์ไปที่ @auth, @title
ในขณะที่ SSFETCH_STATUS = 0
ดึงข้อมูลถัดไปจากเคอร์เซอร์ไปที่ @auth, Stitle
/*ปิดเคอร์เซอร์แล้วปล่อย*/
เคอร์เซอร์ DEALLOCATE
ตามที่ระบุไว้ข้างต้น ตัวแปรประเภท CURSOR สามารถใช้แทนชื่อเคอร์เซอร์ได้ ด้านล่างนี้เป็นโค้ดที่คล้ายกันโดยใช้ตัวแปรเหล่านี้:
ประกาศ varchar ใต้ (50), varchar สิทธิ์ (50)
/*ประกาศตัวแปรประเภทเคอร์เซอร์*/
ประกาศเคอร์เซอร์ Scurl
ประกาศเคอร์เซอร์ CURSOR FAST_FORWARD
เลือกผู้แต่ง ชื่อเรื่องจาก dbo.Bookl
โดยที่ >= 2000
/*กำหนดค่าให้กับตัวแปรประเภทเคอร์เซอร์*/
SET Scurl = เคอร์เซอร์
ในขณะที่ SSFETCH_STATUS = 0
ดึงข้อมูลถัดไปจาก Scurl Into South, Stitle