ดูเหมือนว่าจะเป็นพื้นที่ที่มีตำนานและมุมมองที่ขัดแย้งกันอยู่บ้าง
ดังนั้นความแตกต่างระหว่างตัวแปรตารางและตารางชั่วคราวใน SQL Server คืออะไร?
ดูเหมือนว่าจะเป็นพื้นที่ที่มีตำนานและมุมมองที่ขัดแย้งกันอยู่บ้าง
ดังนั้นความแตกต่างระหว่างตัวแปรตารางและตารางชั่วคราวใน SQL Server คืออะไร?
คำตอบ:
สารบัญ
ข้อแม้
คำตอบนี้กล่าวถึงตัวแปรตาราง "คลาสสิค" ที่แนะนำใน SQL Server 2000 SQL Server 2014 ในหน่วยความจำ OLTP แนะนำประเภทตารางที่ปรับให้เหมาะสมหน่วยความจำ อินสแตนซ์ตัวแปรของตารางเหล่านั้นแตกต่างกันไปตามที่กล่าวไว้ด้านล่าง! ( รายละเอียดเพิ่มเติม )
ที่เก็บสินค้า
ไม่แตกต่าง. tempdb
ทั้งสองจะถูกเก็บไว้ใน
ฉันเคยเห็นมันบอกว่าสำหรับตัวแปรตารางนี้ไม่ได้เป็นกรณีเสมอไป แต่สามารถตรวจสอบได้จากด้านล่าง
DECLARE @T TABLE(X INT)
INSERT INTO @T VALUES(1),(2)
SELECT sys.fn_PhysLocFormatter(%%physloc%%) AS [File:Page:Slot]
FROM @T
ตัวอย่างผลลัพธ์ ( tempdb
จัดเก็บตำแหน่งที่แสดงใน2 แถว)
File:Page:Slot
----------------
(1:148:0)
(1:148:1)
ตำแหน่งโลจิคัล
@table_variables
ทำตัวเหมือนเป็นส่วนหนึ่งของฐานข้อมูลปัจจุบันมากกว่า#temp
ตาราง สำหรับตัวแปรตาราง (ตั้งแต่ปี 2005) การจัดเรียงคอลัมน์หากไม่ได้ระบุไว้อย่างชัดเจนจะเป็นฐานข้อมูลปัจจุบันส่วน#temp
ตารางจะใช้การจัดเรียงเริ่มต้นของtempdb
( รายละเอียดเพิ่มเติม ) นอกจากนี้ชนิดข้อมูลที่ผู้ใช้กำหนดเองและคอลเลกชัน XML จะต้องอยู่ใน tempdb เพื่อใช้สำหรับ#temp
ตาราง แต่ตัวแปรตารางสามารถใช้พวกเขาจากฐานข้อมูลปัจจุบัน ( ที่มา )
SQL Server 2012 แนะนำฐานข้อมูลที่มีอยู่ พฤติกรรมของตารางชั่วคราวในสิ่งเหล่านี้แตกต่าง (h / t แอรอน)
ในฐานข้อมูลที่มีอยู่ชั่วคราวตารางข้อมูลจะถูกเรียงในการเปรียบเทียบของฐานข้อมูลที่มีอยู่
- ข้อมูลเมตาทั้งหมดที่เกี่ยวข้องกับตารางชั่วคราว (ตัวอย่างเช่นชื่อตารางและคอลัมน์ดัชนีและอื่น ๆ ) จะอยู่ในการจัดเรียงแคตาล็อก
- ข้อ จำกัด ที่มีชื่อไม่สามารถใช้ในตารางชั่วคราว
- ตารางชั่วคราวอาจไม่อ้างถึงประเภทที่ผู้ใช้กำหนดคอลเลกชันสกีมา XML หรือฟังก์ชั่นที่ผู้ใช้กำหนด
การเปิดเผยขอบเขตที่แตกต่าง
@table_variables
สามารถเข้าถึงได้ภายในชุดและขอบเขตที่มีการประกาศเท่านั้น #temp_tables
สามารถเข้าถึงได้ภายในชุดลูก (ทริกเกอร์ซ้อนขั้นตอนการexec
โทร) #temp_tables
สร้างที่ขอบเขตด้านนอก ( @@NESTLEVEL=0
) สามารถขยายแบตช์ได้เช่นกันจนกว่าจะสิ้นสุดเซสชัน ไม่สามารถสร้างวัตถุประเภทใดก็ได้ในชุดลูกและเข้าถึงได้ในขอบเขตการโทรอย่างไรก็ตามตามที่กล่าวไว้ถัดไป ( ##temp
ตารางทั่วโลกสามารถเป็นได้)
ตลอดชีวิต
@table_variables
จะถูกสร้างขึ้นโดยปริยายเมื่อแบทช์ที่มีDECLARE @.. TABLE
คำสั่งถูกเรียกใช้งาน (ก่อนที่รหัสผู้ใช้ในแบตช์นั้นจะทำงาน) และจะถูกทิ้งโดยปริยายในตอนท้าย
แม้ว่า parser จะไม่อนุญาตให้คุณลองและใช้ตัวแปร table ก่อนที่DECLARE
คำแถลงการสร้างโดยนัยสามารถเห็นได้ด้านล่าง
IF (1 = 0)
BEGIN
DECLARE @T TABLE(X INT)
END
--Works fine
SELECT *
FROM @T
#temp_tables
จะถูกสร้างขึ้นอย่างชัดเจนเมื่อCREATE TABLE
พบคำสั่งTSQL และสามารถถูกดร็อปอย่างชัดเจนด้วยDROP TABLE
หรือจะถูกทิ้งโดยปริยายเมื่อแบทช์สิ้นสุดลง (หากสร้างในแบทช์ย่อยด้วย@@NESTLEVEL > 0
) หรือเมื่อเซสชันสิ้นสุดลงมิฉะนั้น
หมายเหตุ: ภายในรูทีนที่เก็บไว้ทั้งสองประเภทของวัตถุสามารถแคชได้แทนที่จะสร้างและวางตารางใหม่ซ้ำ ๆ มีข้อ จำกัด ว่าเมื่อใดการแคชนี้อาจเกิดขึ้นได้อย่างไรก็ตามมีความเป็นไปได้ที่จะละเมิด#temp_tables
แต่มีข้อ จำกัด ในการ@table_variables
ป้องกันอยู่ดี ค่าใช้จ่ายในการบำรุงรักษาที่แคช#temp
ตารางเป็นเล็กน้อยมากกว่าสำหรับตัวแปรตารางที่แสดงที่นี่
ข้อมูลเมตาของวัตถุ
สิ่งนี้สำคัญเหมือนกันสำหรับวัตถุทั้งสองประเภท tempdb
จะถูกเก็บไว้ในตารางฐานของระบบใน มันตรงไปตรงมามากกว่าที่จะมองหา#temp
ตารางอย่างไรก็ตาม OBJECT_ID('tempdb..#T')
สามารถใช้ในการคีย์ลงในตารางระบบและชื่อที่สร้างขึ้นภายในมีความสัมพันธ์อย่างใกล้ชิดกับชื่อที่กำหนดในCREATE TABLE
คำสั่ง สำหรับตัวแปรตารางobject_id
ฟังก์ชันไม่ทำงานและชื่อภายในเป็นระบบทั้งหมดที่สร้างขึ้นโดยไม่มีความสัมพันธ์กับชื่อตัวแปร ด้านล่างนี้แสดงให้เห็นว่าข้อมูลเมตายังคงอยู่ที่นั่นอย่างไรก็ตามโดยป้อนชื่อคอลัมน์ (หวังว่าจะไม่ซ้ำกัน) สำหรับตารางที่ไม่มีชื่อคอลัมน์ที่ไม่ซ้ำกัน object_id สามารถพิจารณาได้โดยใช้DBCC PAGE
ตราบเท่าที่ยังไม่ว่างเปล่า
/*Declare a table variable with some unusual options.*/
DECLARE @T TABLE
(
[dba.se] INT IDENTITY PRIMARY KEY NONCLUSTERED,
A INT CHECK (A > 0),
B INT DEFAULT 1,
InRowFiller char(1000) DEFAULT REPLICATE('A',1000),
OffRowFiller varchar(8000) DEFAULT REPLICATE('B',8000),
LOBFiller varchar(max) DEFAULT REPLICATE(cast('C' as varchar(max)),10000),
UNIQUE CLUSTERED (A,B)
WITH (FILLFACTOR = 80,
IGNORE_DUP_KEY = ON,
DATA_COMPRESSION = PAGE,
ALLOW_ROW_LOCKS=ON,
ALLOW_PAGE_LOCKS=ON)
)
INSERT INTO @T (A)
VALUES (1),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13)
SELECT t.object_id,
t.name,
p.rows,
a.type_desc,
a.total_pages,
a.used_pages,
a.data_pages,
p.data_compression_desc
FROM tempdb.sys.partitions AS p
INNER JOIN tempdb.sys.system_internals_allocation_units AS a
ON p.hobt_id = a.container_id
INNER JOIN tempdb.sys.tables AS t
ON t.object_id = p.object_id
INNER JOIN tempdb.sys.columns AS c
ON c.object_id = p.object_id
WHERE c.name = 'dba.se'
เอาท์พุต
Duplicate key was ignored.
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
| object_id | name | rows | type_desc | total_pages | used_pages | data_pages | data_compression_desc |
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
| 574625090 | #22401542 | 13 | IN_ROW_DATA | 2 | 2 | 1 | PAGE |
| 574625090 | #22401542 | 13 | LOB_DATA | 24 | 19 | 0 | PAGE |
| 574625090 | #22401542 | 13 | ROW_OVERFLOW_DATA | 16 | 14 | 0 | PAGE |
| 574625090 | #22401542 | 13 | IN_ROW_DATA | 2 | 2 | 1 | NONE |
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
การทำธุรกรรม
การดำเนินงาน@table_variables
จะดำเนินการในฐานะระบบธุรกรรมเป็นอิสระจากการทำธุรกรรมของผู้ใช้ภายนอกใด ๆ ในขณะที่การ#temp
ดำเนินงานตารางเทียบเท่าจะดำเนินการเป็นส่วนหนึ่งของการทำธุรกรรมของผู้ใช้เอง ด้วยเหตุนี้ROLLBACK
คำสั่งจะส่งผลกระทบต่อ#temp
ตาราง แต่ปล่อยให้@table_variable
ไม่มีใครแตะต้อง
DECLARE @T TABLE(X INT)
CREATE TABLE #T(X INT)
BEGIN TRAN
INSERT #T
OUTPUT INSERTED.X INTO @T
VALUES(1),(2),(3)
/*Both have 3 rows*/
SELECT * FROM #T
SELECT * FROM @T
ROLLBACK
/*Only table variable now has rows*/
SELECT * FROM #T
SELECT * FROM @T
DROP TABLE #T
เข้าสู่ระบบ
ทั้งสองสร้างบันทึกการบันทึกไปยังบันทึกการtempdb
ทำธุรกรรม ความเข้าใจผิดที่พบบ่อยคือนี่ไม่ใช่กรณีของตัวแปรตารางดังนั้นสคริปต์ที่แสดงสิ่งนี้อยู่ด้านล่างมันประกาศตัวแปรตารางเพิ่มสองแถวแล้วอัปเดตและลบออก
เนื่องจากตัวแปรตารางถูกสร้างและลดลงโดยปริยายในตอนเริ่มต้นและตอนท้ายของแบทช์จึงจำเป็นต้องใช้แบตช์หลายชุดเพื่อดูการบันทึกแบบเต็ม
USE tempdb;
/*
Don't run this on a busy server.
Ideally should be no concurrent activity at all
*/
CHECKPOINT;
GO
/*
The 2nd column is binary to allow easier correlation with log output shown later*/
DECLARE @T TABLE ([C71ACF0B-47E9-4CAD-9A1E-0C687A8F9CF3] INT, B BINARY(10))
INSERT INTO @T
VALUES (1, 0x41414141414141414141),
(2, 0x41414141414141414141)
UPDATE @T
SET B = 0x42424242424242424242
DELETE FROM @T
/*Put allocation_unit_id into CONTEXT_INFO to access in next batch*/
DECLARE @allocId BIGINT, @Context_Info VARBINARY(128)
SELECT @Context_Info = allocation_unit_id,
@allocId = a.allocation_unit_id
FROM sys.system_internals_allocation_units a
INNER JOIN sys.partitions p
ON p.hobt_id = a.container_id
INNER JOIN sys.columns c
ON c.object_id = p.object_id
WHERE ( c.name = 'C71ACF0B-47E9-4CAD-9A1E-0C687A8F9CF3' )
SET CONTEXT_INFO @Context_Info
/*Check log for records related to modifications of table variable itself*/
SELECT Operation,
Context,
AllocUnitName,
[RowLog Contents 0],
[Log Record Length]
FROM fn_dblog(NULL, NULL)
WHERE AllocUnitId = @allocId
GO
/*Check total log usage including updates against system tables*/
DECLARE @allocId BIGINT = CAST(CONTEXT_INFO() AS BINARY(8));
WITH T
AS (SELECT Operation,
Context,
CASE
WHEN AllocUnitId = @allocId THEN 'Table Variable'
WHEN AllocUnitName LIKE 'sys.%' THEN 'System Base Table'
ELSE AllocUnitName
END AS AllocUnitName,
[Log Record Length]
FROM fn_dblog(NULL, NULL) AS D)
SELECT Operation = CASE
WHEN GROUPING(Operation) = 1 THEN 'Total'
ELSE Operation
END,
Context,
AllocUnitName,
[Size in Bytes] = COALESCE(SUM([Log Record Length]), 0),
Cnt = COUNT(*)
FROM T
GROUP BY GROUPING SETS( ( Operation, Context, AllocUnitName ), ( ) )
ORDER BY GROUPING(Operation),
AllocUnitName
ผลตอบแทน
เท่าที่ฉันสามารถแยกแยะการดำเนินงานของทั้งสองสร้างการเข้าสู่ระบบจำนวนเท่า ๆ กัน
ขณะที่ปริมาณของการเข้าสู่ระบบจะคล้ายกันมากหนึ่งความแตกต่างที่สำคัญคือบันทึกเข้าสู่ระบบที่เกี่ยวข้องกับ#temp
ตารางไม่สามารถล้างออกไปจนถึงการทำธุรกรรมของผู้ใช้ใด ๆ ที่มีเสร็จสิ้นเพื่อให้การทำธุรกรรมที่ทำงานมานานแล้วว่าในบางจุดเขียนไปยัง#temp
ตารางจะป้องกันไม่ให้เข้าสู่ระบบการตัดในtempdb
ขณะที่การทำธุรกรรมในกำกับของรัฐ วางไข่สำหรับตัวแปรตารางไม่ได้
ตัวแปรของตารางไม่รองรับTRUNCATE
ดังนั้นจึงอาจเป็นข้อเสียของการบันทึกเมื่อข้อกำหนดคือการลบแถวทั้งหมดออกจากตาราง (แม้ว่าตารางขนาดเล็กมากDELETE
สามารถทำงานได้ดีกว่าอยู่ดี )
cardinality
แผนการดำเนินการจำนวนมากที่เกี่ยวข้องกับตัวแปรตารางจะแสดงแถวเดียวโดยประมาณเป็นผลลัพธ์จากพวกเขา การตรวจสอบคุณสมบัติของตัวแปรตารางแสดงว่า SQL Server เชื่อว่าตัวแปรของตารางมีแถวเป็นศูนย์ (เพราะเหตุใดค่าประมาณ 1 แถวจะถูกปล่อยออกมาจากตารางแถวศูนย์จะอธิบายโดย @Paul White ที่นี่ )
อย่างไรก็ตามผลที่แสดงในส่วนก่อนหน้านี้แสดงให้เห็นความถูกต้องนับrows
sys.partitions
ปัญหาคือในกรณีส่วนใหญ่งบตัวแปรอ้างอิงตารางจะรวบรวมในขณะที่ตารางว่างเปล่า หากคำสั่งนั้นถูกคอมไพล์@table_variable
แล้วจะถูกนำมาใช้กับ cardinality ของตารางแทน (สิ่งนี้อาจเกิดขึ้นเนื่องจากความชัดเจนrecompile
หรืออาจเป็นเพราะคำสั่งนั้นอ้างอิงวัตถุอื่นที่ทำให้เกิดการคอมไพล์รอการตัดบัญชีหรือคอมไพล์ใหม่)
DECLARE @T TABLE(I INT);
INSERT INTO @T VALUES(1),(2),(3),(4),(5)
CREATE TABLE #T(I INT)
/*Reference to #T means this statement is subject to deferred compile*/
SELECT * FROM @T WHERE NOT EXISTS(SELECT * FROM #T)
DROP TABLE #T
Plan แสดงจำนวนแถวที่ถูกต้องโดยประมาณหลังจากคอมไพล์รอการตัดบัญชี
ใน SQL Server 2012 SP2 แนะนำการตั้งค่าสถานะการสืบค้นกลับ 2453 รายละเอียดเพิ่มเติมที่อยู่ภายใต้ "สัมพันธ์เครื่องยนต์" ที่นี่
เมื่อเปิดใช้งานการตั้งค่าสถานะการสืบค้นกลับนี้อาจทำให้คอมไพล์ซ้ำโดยอัตโนมัติพิจารณาบัญชีของ cardinality ที่มีการเปลี่ยนแปลงตามที่กล่าวไว้ในไม่ช้า
หมายเหตุ: เมื่อวันที่ Azure ในระดับความเข้ากัน 150 รวบรวมคำสั่งที่จะรอการตัดบัญชีวันนี้ถึงการดำเนินการครั้งแรก ซึ่งหมายความว่าจะไม่มีปัญหาการประมาณการแถวศูนย์อีกต่อไป
ไม่มีสถิติคอลัมน์
การมีความสำคัญเชิงตารางที่แม่นยำยิ่งขึ้นนั้นไม่ได้หมายความว่าการนับแถวโดยประมาณจะมีความแม่นยำมากขึ้น (เว้นแต่จะทำการดำเนินการกับแถวทั้งหมดในตาราง) SQL Server จะไม่เก็บสถิติคอลัมน์สำหรับตัวแปรตารางเลยดังนั้นจะลดการเดาตามการเปรียบเทียบ (เช่น 10% ของตารางจะถูกส่งกลับสำหรับ=
คอลัมน์ที่ไม่ซ้ำกันหรือ 30% สำหรับการ>
เปรียบเทียบ) ในทางตรงกันข้ามคอลัมน์สถิติถูกเก็บรักษาไว้สำหรับ#temp
ตาราง
SQL Server รักษาจำนวนการแก้ไขที่ทำกับแต่ละคอลัมน์ หากจำนวนการแก้ไขตั้งแต่แผนถูกคอมไพล์เกินขีด จำกัด การคอมไพล์ใหม่ (RT) ดังนั้นแผนจะถูกคอมไพล์ใหม่และอัปเดตสถิติ RT ขึ้นอยู่กับประเภทและขนาดของตาราง
จากการแคชแผนใน SQL Server 2008
RT คำนวณดังนี้ (n หมายถึง cardinality ของตารางเมื่อรวบรวมแผนแบบสอบถาม)
ตารางถาวร
- ถ้า n <= 500, RT = 500
- ถ้า n> 500, RT = 500 + 0.20 * nตารางชั่วคราว
- ถ้า n <6, RT = 6.
- ถ้า 6 <= n <= 500, RT = 500
- ถ้า n> 500, RT = 500 + 0.20 * n
ตัวแปรตาราง
- RT ไม่มีอยู่ ดังนั้นการคอมไพล์ใหม่จะไม่เกิดขึ้นเนื่องจากการเปลี่ยนแปลงความสำคัญของตัวแปรตาราง (แต่ดูหมายเหตุเกี่ยวกับ TF 2453 ด้านล่าง)
KEEP PLAN
คำใบ้สามารถใช้ในการตั้งค่า RT สำหรับ#temp
ตารางเช่นเดียวกับตารางถาวร
ผลกระทบสุทธิของสิ่งนี้คือบ่อยครั้งที่แผนการดำเนินการที่สร้างขึ้นสำหรับ#temp
ตารางเป็นลำดับของขนาดที่ดีกว่า@table_variables
เมื่อมีหลายแถวที่เกี่ยวข้องเนื่องจาก SQL Server มีข้อมูลที่ดีกว่าในการทำงาน
NB1: ตัวแปรตารางไม่มีสถิติ แต่ยังคงสามารถเกิดเหตุการณ์คอมไพล์ "Statistics Changed" ภายใต้แฟล็กการติดตาม 2453 (ไม่ได้ใช้สำหรับแผน "เล็กน้อย") สิ่งนี้ดูเหมือนจะเกิดขึ้นภายใต้เกณฑ์การคอมไพล์ซ้ำตามที่แสดงสำหรับตารางอุณหภูมิด้านบน N=0 -> RT = 1
อีกหนึ่งว่าถ้า เช่นคำสั่งทั้งหมดที่รวบรวมเมื่อตัวแปรตารางว่างเปล่าจะได้รับการคอมไพล์ใหม่และแก้ไขTableCardinality
ในครั้งแรกที่ถูกเรียกใช้เมื่อไม่ว่างเปล่า cardinality ตารางเวลาการคอมไพล์จะถูกเก็บไว้ในแผนและหากคำสั่งถูกดำเนินการอีกครั้งด้วย cardinality เดียวกัน (เนื่องจากการไหลของคำสั่งการควบคุมหรือการใช้ซ้ำของแผนแคช) ไม่มีการคอมไพล์ซ้ำเกิดขึ้น
NB2: สำหรับตารางชั่วคราวที่แคชไว้ในขั้นตอนการจัดเก็บเรื่องราวการคอมไพล์ใหม่นั้นซับซ้อนกว่าที่อธิบายไว้ข้างต้น ดูตารางชั่วคราวในขั้นตอนการจัดเก็บสำหรับรายละเอียดเต็มไปด้วยเลือดทั้งหมด
recompiles
เช่นเดียวกับการปรับเปลี่ยนตาม recompiles อธิบายไว้ข้างต้น#temp
ตารางยังสามารถเชื่อมโยงกับคอมไพล์เพิ่มเติมเพียงเพราะพวกเขาช่วยให้การดำเนินงานที่เป็นสิ่งต้องห้ามสำหรับตัวแปรตารางที่เรียกคอมไพล์ (เช่นการเปลี่ยนแปลง DDL CREATE INDEX
, ALTER TABLE
)
ล็อค
มันได้รับการระบุว่าตัวแปรตารางไม่ได้มีส่วนร่วมในการล็อค กรณีนี้ไม่ได้. การรันเอาต์พุตด้านล่างไปยังแท็บข้อความ SSMS รายละเอียดของการล็อกที่ถูกใช้และรีลีสสำหรับคำสั่งแทรก
DECLARE @tv_target TABLE (c11 int, c22 char(100))
DBCC TRACEON(1200,-1,3604)
INSERT INTO @tv_target (c11, c22)
VALUES (1, REPLICATE('A',100)), (2, REPLICATE('A',100))
DBCC TRACEOFF(1200,-1,3604)
สำหรับการค้นหาที่SELECT
มาจากตัวแปรตาราง Paul White ชี้ให้เห็นในความคิดเห็นว่าสิ่งเหล่านี้มาพร้อมกับNOLOCK
คำใบ้โดยนัย ดังแสดงด้านล่าง
DECLARE @T TABLE(X INT);
SELECT X
FROM @T
OPTION (RECOMPILE, QUERYTRACEON 3604, QUERYTRACEON 8607)
*** Output Tree: (trivial plan) ***
PhyOp_TableScan TBL: @T Bmk ( Bmk1000) IsRow: COL: IsBaseRow1002 Hints( NOLOCK )
ผลกระทบของสิ่งนี้ในการล็อคอาจจะค่อนข้างน้อย
SET NOCOUNT ON;
CREATE TABLE #T( [ID] [int] IDENTITY NOT NULL,
[Filler] [char](8000) NULL,
PRIMARY KEY CLUSTERED ([ID] DESC))
DECLARE @T TABLE ( [ID] [int] IDENTITY NOT NULL,
[Filler] [char](8000) NULL,
PRIMARY KEY CLUSTERED ([ID] DESC))
DECLARE @I INT = 0
WHILE (@I < 10000)
BEGIN
INSERT INTO #T DEFAULT VALUES
INSERT INTO @T DEFAULT VALUES
SET @I += 1
END
/*Run once so compilation output doesn't appear in lock output*/
EXEC('SELECT *, sys.fn_PhysLocFormatter(%%physloc%%) FROM #T')
DBCC TRACEON(1200,3604,-1)
SELECT *, sys.fn_PhysLocFormatter(%%physloc%%)
FROM @T
PRINT '--*--'
EXEC('SELECT *, sys.fn_PhysLocFormatter(%%physloc%%) FROM #T')
DBCC TRACEOFF(1200,3604,-1)
DROP TABLE #T
ไม่ส่งคืนผลลัพธ์เหล่านี้ในลำดับของคีย์ดัชนีซึ่งบ่งชี้ว่า SQL Server ใช้การสแกนตามคำสั่งที่จัดสรรสำหรับทั้งคู่
ฉันรันสคริปต์ด้านบนสองครั้งและผลลัพธ์สำหรับการวิ่งครั้งที่สองต่ำกว่า
Process 58 acquiring Sch-S lock on OBJECT: 2:-1325894110:0 (class bit0 ref1) result: OK
--*--
Process 58 acquiring IS lock on OBJECT: 2:-1293893996:0 (class bit0 ref1) result: OK
Process 58 acquiring S lock on OBJECT: 2:-1293893996:0 (class bit0 ref1) result: OK
Process 58 releasing lock on OBJECT: 2:-1293893996:0
ผลลัพธ์การล็อกสำหรับตัวแปรตารางนั้นน้อยมากเนื่องจาก SQL Server เพิ่งได้รับการล็อคความมั่นคงของสคีมาบนวัตถุ แต่สำหรับ#temp
ตารางมันเกือบจะเบาเหมือนที่ใช้S
ล็อคระดับวัตถุ สามารถระบุNOLOCK
คำใบ้หรือREAD UNCOMMITTED
ระดับการแยกได้อย่างชัดเจนเมื่อทำงานกับ#temp
ตารางเช่นกัน
ในทำนองเดียวกันกับปัญหาที่เกิดขึ้นกับการบันทึกการทำธุรกรรมของผู้ใช้โดยรอบอาจหมายถึงการล็อคจะถูกเก็บไว้นานกว่าสำหรับ#temp
ตาราง ด้วยสคริปต์ด้านล่าง
--BEGIN TRAN;
CREATE TABLE #T (X INT,Y CHAR(4000) NULL);
INSERT INTO #T (X) VALUES(1)
SELECT CASE resource_type
WHEN 'OBJECT' THEN OBJECT_NAME(resource_associated_entity_id, 2)
WHEN 'ALLOCATION_UNIT' THEN (SELECT OBJECT_NAME(object_id, 2)
FROM tempdb.sys.allocation_units a
JOIN tempdb.sys.partitions p ON a.container_id = p.hobt_id
WHERE a.allocation_unit_id = resource_associated_entity_id)
WHEN 'DATABASE' THEN DB_NAME(resource_database_id)
ELSE (SELECT OBJECT_NAME(object_id, 2)
FROM tempdb.sys.partitions
WHERE partition_id = resource_associated_entity_id)
END AS object_name,
*
FROM sys.dm_tran_locks
WHERE request_session_id = @@SPID
DROP TABLE #T
-- ROLLBACK
เมื่อทำงานด้านนอกของการทำธุรกรรมของผู้ใช้ที่ชัดเจนสำหรับทั้งสองกรณีล็อคเพิ่งกลับมาเมื่อตรวจสอบเป็นล็อคที่ใช้ร่วมกันบนsys.dm_tran_locks
DATABASE
ในการยกเลิกการใส่เครื่องหมายความคิดเห็นBEGIN TRAN ... ROLLBACK
26 แถวจะถูกส่งกลับแสดงให้เห็นว่าล็อคจะถูกเก็บไว้ทั้งในตัววัตถุและในแถวตารางระบบเพื่อให้สามารถย้อนกลับและป้องกันการทำธุรกรรมอื่น ๆ จากการอ่านข้อมูลปราศจากข้อผูกมัด การดำเนินการตัวแปรตารางที่เทียบเท่านั้นไม่ได้ขึ้นอยู่กับการย้อนกลับของธุรกรรมผู้ใช้และไม่จำเป็นต้องหยุดการล็อกเหล่านี้เพื่อให้เราตรวจสอบในคำสั่งถัดไป แต่การติดตามการล็อคที่ได้รับและเผยแพร่ใน Profiler หรือ เกิดขึ้น
ดัชนี
สำหรับรุ่นก่อนหน้าดัชนี SQL Server 2014 สามารถสร้างได้โดยปริยายบนตัวแปรตารางซึ่งเป็นผลข้างเคียงของการเพิ่มข้อ จำกัด ที่ไม่ซ้ำกันหรือคีย์หลัก แน่นอนว่านี่หมายความว่าสนับสนุนเฉพาะดัชนีเฉพาะเท่านั้น ดัชนีที่ไม่ใช่คลัสเตอร์ที่ไม่ซ้ำกันในตารางที่มีดัชนีคลัสเตอร์ที่ไม่ซ้ำกันสามารถจำลองได้โดยเพียงแค่ประกาศUNIQUE NONCLUSTERED
และเพิ่มคีย์ CI ไปยังจุดสิ้นสุดของคีย์ NCI ที่ต้องการ (SQL Server จะทำสิ่งนี้อยู่เบื้องหลังแม้ว่าจะไม่ซ้ำกันก็ตาม สามารถระบุ NCI ได้)
ดังที่แสดงไว้ก่อนหน้านี้index_option
สามารถระบุ s ต่าง ๆในการประกาศข้อ จำกัด ซึ่งรวมถึงDATA_COMPRESSION
, IGNORE_DUP_KEY
และ, FILLFACTOR
(แม้ว่าจะไม่มีจุดในการตั้งค่าที่มันจะสร้างความแตกต่างในการสร้างดัชนีและคุณไม่สามารถสร้างดัชนีบนตัวแปรตาราง!)
นอกจากนี้ตัวแปรตารางไม่สนับสนุนINCLUDE
คอลัมน์ d, ดัชนีที่กรอง (จนถึง 2016) หรือการแบ่งพาร์ติชัน, #temp
ตารางทำ (ต้องสร้างชุดรูปแบบพาร์ติชันtempdb
)
ดัชนีใน SQL Server 2014
ดัชนีที่ไม่ซ้ำกันสามารถประกาศแบบอินไลน์ในนิยามตัวแปรตารางใน SQL Server 2014 ตัวอย่างไวยากรณ์สำหรับสิ่งนี้อยู่ด้านล่าง
DECLARE @T TABLE (
C1 INT INDEX IX1 CLUSTERED, /*Single column indexes can be declared next to the column*/
C2 INT INDEX IX2 NONCLUSTERED,
INDEX IX3 NONCLUSTERED(C1,C2) /*Example composite index*/
);
ดัชนีใน SQL Server 2016
จาก CTP 3.1 เป็นไปได้ที่จะประกาศดัชนีตัวกรองสำหรับตัวแปรตาราง โดย RTM อาจเป็นกรณีที่อนุญาตให้รวมคอลัมน์ได้แม้ว่าพวกเขาจะไม่ทำให้เป็น SQL16 เนื่องจากข้อ จำกัด ของทรัพยากร
DECLARE @T TABLE
(
c1 INT NULL INDEX ix UNIQUE WHERE c1 IS NOT NULL /*Unique ignoring nulls*/
)
ความเท่าเทียม
คำค้นหาที่แทรกลงใน (หรือแก้ไขอย่างอื่น) @table_variables
ไม่สามารถมีแผนขนาน#temp_tables
ไม่ได้ถูก จำกัด ในลักษณะนี้
มีวิธีแก้ปัญหาที่ชัดเจนในการเขียนใหม่ดังต่อไปนี้จะช่วยให้SELECT
ส่วนที่จะเกิดขึ้นในแบบคู่ขนาน แต่ที่จบลงด้วยการใช้ตารางชั่วคราวที่ซ่อนอยู่(เบื้องหลัง)
INSERT INTO @DATA ( ... )
EXEC('SELECT .. FROM ...')
ไม่มีข้อ จำกัด ดังกล่าวในแบบสอบถามที่เลือกจากตัวแปรตารางตามที่แสดงในคำตอบของฉันที่นี่
ความแตกต่างการทำงานอื่น ๆ
#temp_tables
ไม่สามารถใช้ภายในฟังก์ชั่น @table_variables
สามารถใช้ได้ภายใน UDFs แบบสเกลาร์หรือแบบหลายคำสั่ง@table_variables
ไม่สามารถมีข้อ จำกัด ในการตั้งชื่อ@table_variables
ไม่สามารถSELECT
-ed INTO
, ALTER
-ed, TRUNCATE
d หรือเป็นเป้าหมายของDBCC
คำสั่งเช่นDBCC CHECKIDENT
หรือจากSET IDENTITY INSERT
และไม่สนับสนุนคำใบ้ตารางเช่นWITH (FORCESCAN)
CHECK
ข้อ จำกัด ของตัวแปรตารางไม่ได้รับการพิจารณาโดยเครื่องมือเพิ่มประสิทธิภาพสำหรับการทำให้เข้าใจง่ายสรุปโดยนัยหรือการตรวจจับที่ขัดแย้งPAGELATCH_EX
รอคอยที่มากขึ้น ( ตัวอย่าง )หน่วยความจำเท่านั้น?
tempdb
ตามที่ระบุไว้ในตอนต้นทั้งสองได้รับการจัดเก็บไว้บนหน้าเว็บใน อย่างไรก็ตามฉันไม่ได้ระบุว่ามีพฤติกรรมแตกต่างกันหรือไม่เมื่อพูดถึงการเขียนหน้าเหล่านี้ลงดิสก์
ฉันได้ทำการทดสอบเล็กน้อยในตอนนี้และจนถึงขณะนี้ไม่เห็นความแตกต่างดังกล่าว ในการทดสอบเฉพาะที่ฉันทำในอินสแตนซ์ของ SQL Server 250 หน้าของฉันดูเหมือนว่าจะเป็นจุดตัดก่อนที่จะเขียนไฟล์ข้อมูล
หมายเหตุ: พฤติกรรมด้านล่างไม่เกิดขึ้นอีกต่อไปใน SQL Server 2014 หรือSQL Server 2012 SP1 / CU10 หรือ SP2 / CU1นักเขียนที่กระตือรือร้นไม่ได้กระตือรือร้นที่จะเขียนหน้าลงดิสก์ รายละเอียดเพิ่มเติมเกี่ยวกับการเปลี่ยนแปลงว่าในSQL Server 2014: การซ่อน tempdb อัญมณีผลการดำเนินงาน
เรียกใช้สคริปต์ด้านล่าง
CREATE TABLE #T(X INT, Filler char(8000) NULL)
INSERT INTO #T(X)
SELECT TOP 250 ROW_NUMBER() OVER (ORDER BY @@SPID)
FROM master..spt_values
DROP TABLE #T
และการตรวจสอบเขียนไปยังtempdb
ไฟล์ข้อมูลด้วยการตรวจสอบกระบวนการฉันไม่เห็น (ยกเว้นบางครั้งไปยังหน้าบูตฐานข้อมูลที่ชดเชย 73,728) หลังจากเปลี่ยน250
เป็น251
ฉันเริ่มเห็นการเขียนดังต่อไปนี้
ภาพหน้าจอด้านบนแสดงการเขียน 5 * 32 หน้าและการเขียนหน้าเดียวหนึ่งครั้งระบุว่า 161 หน้าถูกเขียนลงดิสก์ ฉันได้จุดตัด 250 หน้าเหมือนกันเมื่อทดสอบกับตัวแปรตารางด้วย สคริปต์ด้านล่างแสดงวิธีที่แตกต่างโดยดูที่sys.dm_os_buffer_descriptors
DECLARE @T TABLE (
X INT,
[dba.se] CHAR(8000) NULL)
INSERT INTO @T
(X)
SELECT TOP 251 Row_number() OVER (ORDER BY (SELECT 0))
FROM master..spt_values
SELECT is_modified,
Count(*) AS page_count
FROM sys.dm_os_buffer_descriptors
WHERE database_id = 2
AND allocation_unit_id = (SELECT a.allocation_unit_id
FROM tempdb.sys.partitions AS p
INNER JOIN tempdb.sys.system_internals_allocation_units AS a
ON p.hobt_id = a.container_id
INNER JOIN tempdb.sys.columns AS c
ON c.object_id = p.object_id
WHERE c.name = 'dba.se')
GROUP BY is_modified
is_modified page_count
----------- -----------
0 192
1 61
แสดงว่ามีการเขียนหน้า 192 แผ่นลงบนดิสก์และล้างค่าสถานะสกปรก นอกจากนี้ยังแสดงให้เห็นว่าการเขียนลงดิสก์ไม่ได้หมายความว่าหน้าจะถูกขับออกจากบัฟเฟอร์พูลทันที แบบสอบถามที่ตรงกับตัวแปรในตารางนี้ยังคงสามารถตอบสนองได้อย่างสมบูรณ์จากหน่วยความจำ
บนเซิร์ฟเวอร์ที่ไม่ได้ใช้งานที่มีการmax server memory
ตั้งค่า2000 MB
และDBCC MEMORYSTATUS
การรายงานหน้าบัฟเฟอร์พูลจัดสรรเป็นประมาณ 1,843,000 KB (c. 23,000 หน้า) ฉันแทรกลงในตารางข้างต้นด้วยแบทช์ 1,000 แถว / หน้าและสำหรับการบันทึกซ้ำแต่ละครั้ง
SELECT Count(*)
FROM sys.dm_os_buffer_descriptors
WHERE database_id = 2
AND allocation_unit_id = @allocId
AND page_type = 'DATA_PAGE'
ทั้งตัวแปรตารางและ#temp
ตารางให้กราฟเกือบเหมือนกันและจัดการให้พูลบัฟเฟอร์ออกมาสูงสุดก่อนที่จะถึงจุดที่พวกเขาไม่ได้ถูกเก็บไว้ในหน่วยความจำทั้งหมดดังนั้นดูเหมือนจะไม่มีข้อ จำกัด ใด ๆ เกี่ยวกับจำนวนหน่วยความจำ สามารถบริโภคได้
มีบางสิ่งที่ฉันต้องการชี้ให้เห็นจากประสบการณ์ที่เฉพาะเจาะจงมากกว่าการศึกษา ในฐานะ DBA ฉันใหม่มากดังนั้นโปรดแก้ไขให้ถูกต้องถ้าจำเป็น