เมื่อใดที่คำนวณคอลัมน์


29

กำหนดค่าสำหรับคอลัมน์ที่คำนวณได้เมื่อใด

  • เมื่อค่าถูกดึง?
  • เมื่อมีการเปลี่ยนแปลงค่าหรือไม่
  • บางเวลาอื่น ๆ

ฉันเดาว่านี่เป็นคำถามสามเณรเพราะฉันไม่พบสิ่งใดในการค้นหาของฉัน

คำตอบ:


19

ขึ้นอยู่กับวิธีที่คุณกำหนดคอลัมน์ที่คำนวณ PERSISTEDคอลัมน์คำนวณจะถูกคำนวณแล้วเก็บไว้เป็นข้อมูลภายในตาราง หากคุณไม่ได้กำหนดคอลัมน์เป็นPERSISTEDมันจะถูกคำนวณเมื่อเรียกใช้แบบสอบถามของคุณ

โปรดดูคำตอบของแอรอนสำหรับคำอธิบายและการพิสูจน์ที่ยอดเยี่ยม

Pinal Daveยังอธิบายในรายละเอียดและแสดงหลักฐานการจัดเก็บในชุดของเขา:

SQL Server - คอลัมน์ที่คำนวณ - PERSISTED และที่เก็บข้อมูล


6
ถ้าพวกเขายังคงอยู่ แต่แผนแบบสอบถามใช้ดัชนีที่ไม่ครอบคลุมคอลัมน์นั้น ฉันไม่แน่ใจว่าคุณจะได้รับการค้นหาหรือถ้ามันจะคำนวณได้ทันทีและไม่สามารถทดสอบได้ในขณะนี้
Martin Smith

1
@ มาร์ตินคุณพูดถูกในการทดสอบของฉัน SQL Server เลือกคำนวณอีกครั้งผ่านการค้นหา
Aaron Bertrand

34

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

สมมติว่าเรามีฟังก์ชั่นนี้:

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 คิดว่ามันจะถูกกว่าในการคำนวณค่าใหม่กว่าที่จะทำการค้นหา สิ่งนี้อาจเปลี่ยนแปลงได้เนื่องจากปัจจัยหลายประการดังนั้นอย่าพึ่งพามัน และสิ่งนี้สามารถเกิดขึ้นได้ในทิศทางใด ๆ ไม่ว่าจะใช้ฟังก์ชั่นที่ผู้ใช้กำหนดเองหรือไม่ก็ตาม ฉันใช้ที่นี่เพียงเพราะมันทำให้ง่ายต่อการอธิบายมากขึ้น


ชื่นชมมากฉันไม่เคยถามพฤติกรรมของเครื่องยนต์ในการคำนวณผลลัพธ์
Arthur D

8
@ArthurD มันเป็นการตัดสินใจของเครื่องมือเพิ่มประสิทธิภาพ (โดยส่วนใหญ่) ตามค่าใช้จ่ายโดยประมาณของแต่ละทางเลือกดูคำตอบของฉันสำหรับคำถามอื่นที่นี่
พอลไวท์พูดว่า GoFundMonica

-1

คำตอบสำหรับคำถามนี้คือ "ขึ้นอยู่กับ" ฉันเพิ่งเจอตัวอย่างที่ SQL Server ใช้ดัชนีในคอลัมน์ที่คำนวณแล้ว แต่มันก็ยังคงทำหน้าที่ของฟังก์ชั่นราวกับว่าค่าเหล่านั้นไม่เคยยืนยันว่าจะเริ่มต้นด้วย มันอาจจะเกี่ยวข้องกับชนิดข้อมูลของคอลัมน์ ( nvarchar(37)) หรืออาจเป็นขนาดของตาราง (ประมาณ 7 ล้านแถว) แต่ SQL Server ตัดสินใจที่จะเพิกเฉยต่อpersistedคำค้นหามันปรากฏขึ้นในกรณีนี้โดยเฉพาะ

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

สร้างตารางด้วยคอลัมน์ที่คงอยู่ แผนการดำเนินการแสดงฟังก์ชันกำลังดำเนินการอยู่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.