ฉันสามารถพึ่งพาฟังก์ชั่นที่ถูกดำเนินการเป็นครั้งแรกใน SQL


9

โปรดพิจารณาสคริปต์ต่อไปนี้:

create or replace function f(p_limit in integer) return integer as
begin
  set_global_context ('limit', p_limit);
  return p_limit;
end;
/

create view v as 
select level as val from dual connect by level<=sys_context('global_context','limit');

select f(2), v.* from v;

/*
F(2)                   VAL                    
---------------------- ---------------------- 
2                      1                      
2                      2                      
*/

select f(4), v.* from v;

/*
F(4)                   VAL                    
---------------------- ---------------------- 
4                      1                      
4                      2                      
4                      3                      
4                      4                      
*/

ฉันสามารถพึ่งพาf(x)การดำเนินการก่อนที่บริบทจะถูกอ่านภายในมุมมองตามที่ได้รับในกรณีทดสอบนี้รันใน 10.2 หรือไม่


ไม่ได้ช่วย แต่สามารถคิดทริกเกอร์เข้าสู่ระบบอาจจะมีความเหมาะสมมากขึ้น (ถ้าระดับมักจะเป็นแบบเดียวกันที่มี)
Philᵀᴹ

@Phil นี่เป็นเพียงตัวอย่าง - ฉันใช้ sys_context เพื่อเพิ่มมุมมองและพารามิเตอร์จะแตกต่างกันในแต่ละครั้ง หากคุณรู้วิธีตั้งค่าบริบททั่วโลกจาก SQL โดยไม่ต้องยุ่งเกี่ยวกับเรื่องนี้ฉันก็อยากจะได้ยินเช่นกัน!
แจ็คบอกว่าลอง topanswers.xyz

1
@JackDouglas: การกำหนดมุมมองเป็นความคิดที่ไม่ "รู้สึก" ถูกต้องสำหรับฉัน ภายใต้ MSSQL สิ่งที่คุณพยายามทำสามารถทำได้โดยใช้ฟังก์ชั่นที่ผู้ใช้กำหนดซึ่งส่งคืนชุดผลลัพธ์ (แทนที่จะเป็นค่า) - คุณสามารถทำได้SELECT stuff FROM dbo.FuncReturningTable(param)หรือคล้ายกัน Oracle อาจมีฟังก์ชันการทำงานที่เทียบเท่า แม้ว่าการใช้สิ่งนี้กับชุดข้อมูลขนาดใหญ่ฉันควรระมัดระวังในการตรวจสอบประสิทธิภาพ: ฉันไม่แน่ใจว่าตัววางแผนคิวรีจะต้องมีความสว่างเพียงใดในการวางแผนอย่างมีประสิทธิภาพจากไวยากรณ์ดังกล่าว
David Spillett

พารามิเตอร์ @David การเพิ่มมุมมองโดยทั่วไปจะทำด้วย sys_context - โดยปกติคุณจะต้องตั้งค่าบริบทก่อนที่จะดำเนินการค้นหา (เช่นด้วยบิตของ PL / SQL) Oracle มีการตั้งค่าส่งคืนและ / หรือฟังก์ชั่นไปป์ไลน์ แต่มันไม่ใช่วิธีปกติในการบรรลุผลนี้ เพื่อความชัดเจนฉันคิดว่าคำตอบของคำถามในชื่อเรื่องคือ "ไม่" - ฉันแค่สงสัยว่ามีคนรู้ดีกว่า
แจ็คบอกว่าลอง topanswers.xyz

คำตอบ:


8

เลขที่

หากคุณเขียนมุมมองของคุณใหม่โดยใช้การกรองบริบทกับส่วนคำสั่ง where (แทนการเชื่อมต่อโดย) คุณจะได้รับค่าที่ตั้งไว้ก่อนหน้าสำหรับบริบท:

create table t as 
 select rownum r from dual connect by level <= 10;

create or replace view v as 
  select r val from t where r <=sys_context('global_context','limit');

select f(2), v.* from v;

F(2) VAL
---- ---
   2   1 
   2   2 

select f(4), v.* from v;

F(4) VAL
---- ---
   4   1 
   4   2 

select f(4), v.* from v;

F(4) VAL
---- ---
   4   1 
   4   2 
   4   3 
   4   4 

เนื่องจากข้อใดถูกประเมินก่อนที่จะเลือกคอลัมน์ค่าที่ส่งผ่านไปยังฟังก์ชันจะไม่ถูกตั้งค่าจนกว่าจะอ่านบริบทแล้ว ตำแหน่งของการโทร sys_context ในคิวรีของคุณ (เลือกที่จัดกลุ่มตาม ฯลฯ ) จะมีผลกับการตั้งค่านี้


+1 ที่ค่อนข้าง "คดีปิด" ในหนังสือของฉันขอบคุณ
แจ็คบอกว่าลอง topanswers.xyz

2

โดยทั่วไปแล้วคุณไม่สามารถคาดเดาได้อย่างปลอดภัยเกี่ยวกับลำดับที่ DBMS ของคุณจะทำสิ่งต่าง ๆ เมื่อประเมินคำสั่ง SQL เดียว นี่คือสาเหตุที่ DBMS จำนวนมากไม่อนุญาตให้ใช้ฟังก์ชั่นที่มีผลข้างเคียง (เช่น MSSQL จะไม่อนุญาตให้ฟังก์ชันตั้งค่าสถานะโกลบอล / การเชื่อมต่อซึ่งคุณกำลังทำอยู่หรือแก้ไขเนื้อหาตาราง) ชุดคำสั่งจะต้องดำเนินการในลักษณะที่เหมาะสมจากขั้นตอนหนึ่งไปยังอีกขั้น (นั่นคือพวกเขาจะทำงานแบบลำดับหรือในลักษณะที่คุณไม่สามารถบอกพวกเขาไม่ได้) แต่ภายในคำสั่งเดียวที่วางแผนการสืบค้น มีการครองราชย์ฟรีตราบใดที่มันไม่ได้มีความกำกวมซึ่งไม่ได้มีอยู่ (ในตัวอย่างของคุณมีความกำกวมอยู่แล้วเนื่องจากฟังก์ชันมีผลข้างเคียงที่มีผลต่อมุมมอง)

หากตัววางแผนคิวรีมีความสว่างเพียงพอที่จะตรวจพบว่ามุมมองได้รับผลกระทบจากผลข้างเคียงของฟังก์ชั่นมันจะทำอย่างไรถ้าคุณเข้าร่วมในมุมมองอื่นที่เรียกว่าฟังก์ชันนั้นอาจมีค่าอินพุตต่างกัน มันอาจจะค่อนข้างขนดกอย่างรวดเร็ว - สิ่งนี้เป็นเหตุผลว่าทำไมโดยทั่วไปในบริบทของการเขียนโปรแกรมฟังก์ชั่นไม่ควรมีเอฟเฟกต์เกินกว่าที่จะเอาท์พุทของตัวเอง

ในตัวอย่างเฉพาะนี้ฉันจะบอกว่ามันไม่น่าเป็นไปได้ที่จะเรียก f (x) เป็นอันดับแรกเนื่องจากเป็นส่วน "แสดง" ของคำแถลง int: ผลลัพธ์ที่ตั้งจากมุมมองน่าจะถูกเรียกคืนก่อนหน้าที่ใด ๆ ภายใน รายการของคอลัมน์ที่จะส่งคืนจะถูกประเมิน แน่นอนว่าสิ่งนี้จะแตกต่างกันไปขึ้นอยู่กับ DBMS ที่ใช้: ฉันไม่มีผู้เชี่ยวชาญของ Oracle และผลการทดสอบของคุณแสดงให้เห็นว่าฟังก์ชั่นนั้นถูกเรียกมาเป็นอันดับแรกในกรณีเหล่านี้ แต่ฉันจะระวังการพึ่งพาคำสั่งการดำเนินการภายในคำสั่ง SQL ใด ๆ ทั้งหมดเหมือนกัน - แม้ว่ามันจะทำงานได้อย่างที่คุณคาดหวังในขณะนี้มันอาจไม่ทำเช่นนั้นในการแก้ไขในอนาคต (เว้นแต่จะมีการบันทึกไว้อย่างเป็นทางการ ทางนี้)


2
คำตอบที่ดี แต่ฉันรู้สึกว่าแจ็คกำลังมองหาคำตอบทางเทคนิคที่ชัดเจนของ Oracle
Philᵀᴹ

1

เอกสารประกอบสัญญาเท่านั้นว่า "เครื่องมือเพิ่มประสิทธิภาพจะประเมินนิพจน์และเงื่อนไขที่มีค่าคงที่ให้ครบถ้วนที่สุด" ( 10.2 , 11.2 ) คุณไม่ได้รับประกันว่ามันจะประเมินการแสดงออกใด ๆ ก่อนหรือจะไม่เปลี่ยนลำดับนั้นเป็นครั้งคราว (แพทช์ใหม่ที่อยู่ในรีลีสเดียวกัน?)


+1 ยอดเยี่ยมขอบคุณ (แม้ว่าฉันจะอ่านเอกสารเหล่านั้นไม่ค่อยตรงกับคำตอบของคริส )
แจ็คบอกว่าลอง topanswers.xyz

1
ความแตกต่างคือไม่ว่าจะเป็นฟังก์ชั่นที่เรียกว่าในที่ไหนเลือกหรือประโยคอื่น ๆ ฟังก์ชั่นในส่วนที่เลือกจะไม่ส่งผลต่อการตัดสินใจของเครื่องมือเพิ่มประสิทธิภาพ (ยกเว้นว่าเป็นคำถามย่อย) ดังนั้นจึงไม่จำเป็นต้องประเมินสิ่งเหล่านี้จนกว่าจะได้ผลลัพธ์ ฟังก์ชั่นในส่วนคำสั่ง where จะส่งผลกระทบต่อวิธีการรวมที่ใช้ดังนั้นจึงต้องมีการประเมินให้เร็วที่สุดเท่าที่จะทำได้
Chris Saxon

@Chris เป็นประสบการณ์ที่พูดหรือคุณได้รับจากเอกสารที่ไหนสักแห่ง?
แจ็คบอกว่าลอง topanswers.xyz

ฉันไม่พบเอกสารอ้างอิง จากประสบการณ์ของฉันหากมีการเรียกในส่วนคำสั่งที่จะกรองตารางเดียวมันจะเข้าถึงได้ทุกแถว (สมมติว่าเป็น FTS) แต่เฉพาะแถวที่ส่งคืนหากอยู่ในรายการที่เลือก เนื่องจากแผนการดำเนินการถูกตั้งค่าในระหว่างการวิเคราะห์คำสิ่งนี้หมายความว่าฟังก์ชันในตัวเลือกไม่สามารถส่งผลกระทบต่อมันได้ กรณีทดสอบเพื่อตรวจสอบสิ่งนี้สามารถทำได้โดยการสร้างฟังก์ชั่นการตั้งค่าตัวนับ (ในแพ็คเกจหรือตาราง) และเปรียบเทียบผลลัพธ์ตามตำแหน่งของแบบสอบถาม
Chris Saxon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.