กำหนดค่าสำหรับคอลัมน์ที่คำนวณได้เมื่อใด
- เมื่อค่าถูกดึง?
- เมื่อมีการเปลี่ยนแปลงค่าหรือไม่
- บางเวลาอื่น ๆ
ฉันเดาว่านี่เป็นคำถามสามเณรเพราะฉันไม่พบสิ่งใดในการค้นหาของฉัน
กำหนดค่าสำหรับคอลัมน์ที่คำนวณได้เมื่อใด
ฉันเดาว่านี่เป็นคำถามสามเณรเพราะฉันไม่พบสิ่งใดในการค้นหาของฉัน
คำตอบ:
ขึ้นอยู่กับวิธีที่คุณกำหนดคอลัมน์ที่คำนวณ PERSISTED
คอลัมน์คำนวณจะถูกคำนวณแล้วเก็บไว้เป็นข้อมูลภายในตาราง หากคุณไม่ได้กำหนดคอลัมน์เป็นPERSISTED
มันจะถูกคำนวณเมื่อเรียกใช้แบบสอบถามของคุณ
โปรดดูคำตอบของแอรอนสำหรับคำอธิบายและการพิสูจน์ที่ยอดเยี่ยม
Pinal Daveยังอธิบายในรายละเอียดและแสดงหลักฐานการจัดเก็บในชุดของเขา:
มันง่ายมากที่จะพิสูจน์ด้วยตัวคุณเอง เราสามารถสร้างตารางที่มีคอลัมน์ที่คำนวณได้ซึ่งใช้ฟังก์ชันที่ผู้ใช้กำหนดด้วยสเกลาร์จากนั้นตรวจสอบแผนและสถิติฟังก์ชันก่อนและหลังทั้งการอัพเดทและเลือกและดูว่าจะมีการบันทึกการดำเนินการเมื่อใด
สมมติว่าเรามีฟังก์ชั่นนี้:
CREATE FUNCTION dbo.mask(@x varchar(32))
RETURNS varchar(32) WITH SCHEMABINDING
AS
BEGIN
RETURN (SELECT 'XX' + SUBSTRING(@x, 3, LEN(@x)-4) + 'XXXX');
END
GO
และตารางนี้:
CREATE TABLE dbo.Floobs
(
FloobID int IDENTITY(1,1),
Name varchar(32),
MaskedName AS CONVERT(varchar(32), dbo.mask(Name)),
CONSTRAINT pk_Floobs PRIMARY KEY(FloobID),
CONSTRAINT ck_Name CHECK (LEN(Name)>=8)
);
GO
ตรวจสอบsys.dm_exec_function_stats
(ใหม่ใน SQL Server 2016 และ Azure SQL Database) ก่อนและหลังการแทรกจากนั้นหลังจากเลือก:
SELECT o.name, s.execution_count
FROM sys.dm_exec_function_stats AS s
INNER JOIN sys.objects AS o
ON o.[object_id] = s.[object_id]
WHERE s.database_id = DB_ID();
INSERT dbo.Floobs(Name) VALUES('FrankieC');
SELECT o.name, s.execution_count
FROM sys.dm_exec_function_stats AS s
INNER JOIN sys.objects AS o
ON o.[object_id] = s.[object_id]
WHERE s.database_id = DB_ID();
SELECT * FROM dbo.Floobs;
SELECT o.name, s.execution_count
FROM sys.dm_exec_function_stats AS s
INNER JOIN sys.objects AS o
ON o.[object_id] = s.[object_id]
WHERE s.database_id = DB_ID();
ฉันไม่เห็นฟังก์ชั่นการโทรในการแทรกเฉพาะที่เลือก
ตอนนี้ปล่อยตารางและทำอีกครั้งคราวนี้เปลี่ยนคอลัมน์เป็นPERSISTED
:
DROP TABLE dbo.Floobs;
GO
DROP FUNCTION dbo.mask;
GO
...
MaskedName AS CONVERT(varchar(32), dbo.mask(Name)) PERSISTED,
...
และฉันเห็นสิ่งที่เกิดขึ้นในทางตรงกันข้าม: ฉันได้รับการดำเนินการบันทึกในส่วนแทรก แต่ไม่ใช่ในส่วนที่เลือก
ไม่มี SQL Server รุ่นทันสมัยพอที่จะใช้sys.dm_exec_function_stats
ใช่ไหม ไม่ต้องกังวลนี่เป็นแผนการในการดำเนินการเช่นกัน
สำหรับเวอร์ชั่นที่ไม่มีการคงอยู่เราจะเห็นฟังก์ชั่นอ้างอิงในตัวเลือกเท่านั้น:
ในขณะที่เวอร์ชันที่ยืนยันจะแสดงเฉพาะการคำนวณที่เกิดขึ้นกับส่วนแทรก:
ตอนนี้มาร์ตินได้นำเสนอประเด็นสำคัญในการแสดงความคิดเห็น : มันจะไม่เป็นจริงเสมอไป ลองสร้างดัชนีที่ไม่ครอบคลุมคอลัมน์ที่คำนวณแล้วและเรียกใช้คิวรีที่ใช้ดัชนีนั้นและดูว่าการค้นหารับข้อมูลจากข้อมูลที่มีอยู่เดิมหรือคำนวณข้อมูล ณ รันไทม์ (ฟังก์ชั่นการปล่อยและสร้างใหม่อีกครั้ง และโต๊ะที่นี่):
CREATE INDEX x ON dbo.Floobs(Name);
GO
INSERT dbo.Floobs(name)
SELECT LEFT(name, 32)
FROM sys.all_columns
WHERE LEN(name) >= 8;
ตอนนี้เราจะเรียกใช้คิวรีที่ใช้ดัชนี (อันที่จริงแล้วมันใช้ดัชนีตามค่าเริ่มต้นในกรณีเฉพาะนี้ต่อไปแม้ว่าจะไม่มีส่วนคำสั่งที่ไหน):
SELECT * FROM dbo.Floobs WITH (INDEX(x))
WHERE Name LIKE 'S%';
ฉันเห็นการประมวลผลเพิ่มเติมในสถิติฟังก์ชันและแผนไม่ได้โกหก:
ดังนั้นคำตอบก็คือมันขึ้นอยู่กับ ในกรณีนี้ SQL Server คิดว่ามันจะถูกกว่าในการคำนวณค่าใหม่กว่าที่จะทำการค้นหา สิ่งนี้อาจเปลี่ยนแปลงได้เนื่องจากปัจจัยหลายประการดังนั้นอย่าพึ่งพามัน และสิ่งนี้สามารถเกิดขึ้นได้ในทิศทางใด ๆ ไม่ว่าจะใช้ฟังก์ชั่นที่ผู้ใช้กำหนดเองหรือไม่ก็ตาม ฉันใช้ที่นี่เพียงเพราะมันทำให้ง่ายต่อการอธิบายมากขึ้น
คำตอบสำหรับคำถามนี้คือ "ขึ้นอยู่กับ" ฉันเพิ่งเจอตัวอย่างที่ SQL Server ใช้ดัชนีในคอลัมน์ที่คำนวณแล้ว แต่มันก็ยังคงทำหน้าที่ของฟังก์ชั่นราวกับว่าค่าเหล่านั้นไม่เคยยืนยันว่าจะเริ่มต้นด้วย มันอาจจะเกี่ยวข้องกับชนิดข้อมูลของคอลัมน์ ( nvarchar(37)
) หรืออาจเป็นขนาดของตาราง (ประมาณ 7 ล้านแถว) แต่ SQL Server ตัดสินใจที่จะเพิกเฉยต่อpersisted
คำค้นหามันปรากฏขึ้นในกรณีนี้โดยเฉพาะ
ในกรณีนี้คีย์หลักในตารางคือ TransactionID ซึ่งเป็นคอลัมน์ที่คำนวณและยืนยัน แผนการดำเนินการกำลังสร้างการสแกนดัชนีและในตารางที่มีเพียง 7 ล้านแถวแบบสอบถามแบบง่ายนี้ใช้เวลามากกว่า 2-3 นาทีในการเรียกใช้เนื่องจากฟังก์ชันจะทำงานอีกครั้งในทุกแถวและค่าจะไม่ปรากฏขึ้น ดัชนี