ฉันพยายามดูว่ามีวิธีการหลอกลวง SQL Server ให้ใช้แผนบางอย่างสำหรับแบบสอบถามหรือไม่
1. สภาพแวดล้อม
ลองนึกภาพคุณมีข้อมูลบางอย่างที่ใช้ร่วมกันระหว่างกระบวนการที่แตกต่างกัน สมมติว่าเรามีผลการทดสอบบางอย่างซึ่งใช้พื้นที่มาก จากนั้นสำหรับแต่ละขั้นตอนเรารู้ว่าต้องการใช้ปี / เดือนใด
if object_id('dbo.SharedData') is not null
drop table SharedData
create table dbo.SharedData (
experiment_year int,
experiment_month int,
rn int,
calculated_number int,
primary key (experiment_year, experiment_month, rn)
)
go
ตอนนี้สำหรับทุกกระบวนการเรามีพารามิเตอร์ที่บันทึกไว้ในตาราง
if object_id('dbo.Params') is not null
drop table dbo.Params
create table dbo.Params (
session_id int,
experiment_year int,
experiment_month int,
primary key (session_id)
)
go
2. ทดสอบข้อมูล
ลองเพิ่มข้อมูลทดสอบ:
insert into dbo.Params (session_id, experiment_year, experiment_month)
select 1, 2014, 3 union all
select 2, 2014, 4
go
insert into dbo.SharedData (experiment_year, experiment_month, rn, calculated_number)
select
2014, 3, row_number() over(order by v1.name), abs(Checksum(newid())) % 10
from master.dbo.spt_values as v1
cross join master.dbo.spt_values as v2
go
insert into dbo.SharedData (experiment_year, experiment_month, rn, calculated_number)
select
2014, 4, row_number() over(order by v1.name), abs(Checksum(newid())) % 10
from master.dbo.spt_values as v1
cross join master.dbo.spt_values as v2
go
3. การดึงผลลัพธ์
ตอนนี้การรับผลการทดสอบทำได้ง่ายมากโดย@experiment_year/@experiment_month
:
create or alter function dbo.f_GetSharedData(@experiment_year int, @experiment_month int)
returns table
as
return (
select
d.rn,
d.calculated_number
from dbo.SharedData as d
where
d.experiment_year = @experiment_year and
d.experiment_month = @experiment_month
)
go
แผนดีและขนานกัน:
select
calculated_number,
count(*)
from dbo.f_GetSharedData(2014, 4)
group by
calculated_number
แบบสอบถาม 0 แผน
4. ปัญหา
แต่จะทำให้การใช้งานของข้อมูลบิตทั่วไปมากขึ้นผมต้องการที่จะมีฟังก์ชั่นอื่น dbo.f_GetSharedDataBySession(@session_id int)
- ดังนั้นวิธีที่ตรงไปตรงมาก็คือการสร้างฟังก์ชันสเกลาร์, การแปล@session_id
-> @experiment_year/@experiment_month
:
create or alter function dbo.fn_GetExperimentYear(@session_id int)
returns int
as
begin
return (
select
p.experiment_year
from dbo.Params as p
where
p.session_id = @session_id
)
end
go
create or alter function dbo.fn_GetExperimentMonth(@session_id int)
returns int
as
begin
return (
select
p.experiment_month
from dbo.Params as p
where
p.session_id = @session_id
)
end
go
และตอนนี้เราสามารถสร้างฟังก์ชั่นของเรา:
create or alter function dbo.f_GetSharedDataBySession1(@session_id int)
returns table
as
return (
select
d.rn,
d.calculated_number
from dbo.f_GetSharedData(
dbo.fn_GetExperimentYear(@session_id),
dbo.fn_GetExperimentMonth(@session_id)
) as d
)
go
แบบสอบถาม 1 แผน
แผนคือเหมือนเดิมยกเว้นมันแน่นอนไม่ขนานเพราะฟังก์ชั่นสเกลาร์ที่มีประสิทธิภาพทำให้การเข้าถึงข้อมูลอนุกรมแผนทั้งหมด
ดังนั้นฉันจึงได้ลองวิธีที่แตกต่างหลายอย่างเช่นการใช้แบบสอบถามย่อยแทนฟังก์ชั่นสเกลาร์:
create or alter function dbo.f_GetSharedDataBySession2(@session_id int)
returns table
as
return (
select
d.rn,
d.calculated_number
from dbo.f_GetSharedData(
(select p.experiment_year from dbo.Params as p where p.session_id = @session_id),
(select p.experiment_month from dbo.Params as p where p.session_id = @session_id)
) as d
)
go
แบบสอบถาม 2 แผน
หรือการใช้ cross apply
create or alter function dbo.f_GetSharedDataBySession3(@session_id int)
returns table
as
return (
select
d.rn,
d.calculated_number
from dbo.Params as p
cross apply dbo.f_GetSharedData(
p.experiment_year,
p.experiment_month
) as d
where
p.session_id = @session_id
)
go
แบบสอบถาม 3 แผน
แต่ฉันไม่สามารถหาวิธีเขียนแบบสอบถามนี้ให้ดีเท่าที่ใช้ฟังก์ชันสเกลาร์ได้
คู่ของความคิด:
- โดยทั่วไปสิ่งที่ฉันต้องการคือการสามารถบอก SQL Server เพื่อคำนวณค่าบางอย่างล่วงหน้าแล้วส่งต่อไปเป็นค่าคงที่
- สิ่งที่อาจเป็นประโยชน์คือถ้าเรามีคำแนะนำที่เป็นรูปธรรมบางอย่าง ฉันได้ตรวจสอบตัวแปรสองตัว (หลายคำสั่ง TVF หรือ cte พร้อมด้านบน) แต่ไม่มีแผนใดดีเท่ากับรุ่นที่มีฟังก์ชันสเกลาร์จนถึงปัจจุบัน
- ฉันรู้เกี่ยวกับการปรับปรุงของ SQL Server 2017 - Froid: การเพิ่มประสิทธิภาพของโปรแกรมที่จำเป็นในฐานข้อมูลเชิงสัมพันธ์ฉันไม่แน่ใจว่ามันจะช่วยได้อย่างไร มันคงจะดีถ้าได้พิสูจน์ความผิดที่นี่
ข้อมูลเพิ่มเติม
ฉันกำลังใช้ฟังก์ชั่น (แทนที่จะเลือกข้อมูลโดยตรงจากตาราง) เพราะมันง่ายต่อการใช้งานในแบบสอบถามที่แตกต่างกันซึ่งมักจะมี@session_id
พารามิเตอร์
ฉันถูกขอให้เปรียบเทียบเวลาดำเนินการจริง ในกรณีนี้โดยเฉพาะ
- แบบสอบถาม 0 ทำงานประมาณ ~ 500ms
- แบบสอบถาม 1 ทำงานประมาณ ~ 1500ms
- แบบสอบถาม 2 ทำงานประมาณ ~ 1500ms
- แบบสอบถาม 3 ทำงานประมาณ ~ 2000ms
แผน # 2 มีการสแกนดัชนีแทนการค้นหาซึ่งจะถูกกรองโดยเพรดิเคตบนลูปซ้อนกัน แผน # 3 นั้นไม่เลว แต่ก็ยังทำงานได้มากขึ้นและทำงานช้าลงตามแผน # 0
สมมติว่าdbo.Params
มีการเปลี่ยนแปลงน้อยมากและมักจะมีประมาณ 1-200 แถวไม่เกินสมมติว่ามี 2,000 รายการที่คาดหวัง ตอนนี้มีประมาณ 10 คอลัมน์และฉันไม่คิดว่าจะเพิ่มคอลัมน์บ่อยเกินไป
จำนวนแถวใน Params ไม่ได้รับการแก้ไขดังนั้นสำหรับทุก@session_id
แถวจะมีแถว จำนวนคอลัมน์ที่ไม่มีการแก้ไขเป็นหนึ่งในเหตุผลที่ฉันไม่ต้องการโทรdbo.f_GetSharedData(@experiment_year int, @experiment_month int)
จากทุกที่ดังนั้นฉันสามารถเพิ่มคอลัมน์ใหม่ในแบบสอบถามนี้ภายใน ฉันยินดีที่จะรับฟังความคิดเห็น / คำแนะนำเกี่ยวกับสิ่งนี้แม้ว่าจะมีข้อ จำกัด