SQL सर्वर में अस्थायी तालिका और तालिका चर के बीच अंतर क्या है?


452

यह कुछ मिथकों और परस्पर विरोधी विचारों वाले क्षेत्र के रूप में प्रतीत होता है।

तो SQL सर्वर में एक टेबल चर और एक स्थानीय अस्थायी तालिका के बीच अंतर क्या है?


जवाबों:


668

अंतर्वस्तु

अंतर्वस्तु

चेतावनी

यह उत्तर SQL Server 2000 में पेश किए गए "क्लासिक" टेबल वेरिएबल्स पर चर्चा करता है। स्मृति OLTP में SQL Server 2014 स्मृति-अनुकूलित तालिका प्रकारों का परिचय देता है। उन लोगों के टेबल चर उदाहरण नीचे चर्चा की गई लोगों के लिए कई मायनों में अलग हैं! ( अधिक जानकारी )।

भंडारण स्थान

कोई फर्क नहीं। दोनों में जमा हो जाती है tempdb

मैंने देखा है कि यह सुझाव दिया गया है कि तालिका चर के लिए यह हमेशा ऐसा नहीं होता है लेकिन इसे नीचे से सत्यापित किया जा सकता है

DECLARE @T TABLE(X INT)

INSERT INTO @T VALUES(1),(2)

SELECT sys.fn_PhysLocFormatter(%%physloc%%) AS [File:Page:Slot]
FROM @T

उदाहरण परिणाम ( tempdb2 पंक्तियों में स्थान दर्शाया गया है)

File:Page:Slot
----------------
(1:148:0)
(1:148:1)

तार्किक स्थान

@table_variables#tempतालिकाओं की तुलना में अधिक व्यवहार करते हैं क्योंकि वे वर्तमान डेटाबेस का हिस्सा थे । तालिका चर (2005 के बाद से) के लिए, यदि स्पष्ट रूप से निर्दिष्ट नहीं किया गया है, तो स्तंभ डेटाबेस वर्तमान डेटाबेस का होगा जबकि #tempतालिकाओं के लिए यह tempdb( अधिक विवरण ) के डिफ़ॉल्ट टकराव का उपयोग करेगा । इसके अतिरिक्त उपयोगकर्ता-परिभाषित डेटा प्रकार और XML संग्रह टेम्प्लेट के लिए उपयोग करने के लिए tempdb में होने चाहिए, #tempलेकिन तालिका चर वर्तमान डेटाबेस ( स्रोत ) से उनका उपयोग कर सकते हैं ।

SQL सर्वर 2012 में निहित डेटाबेस का परिचय देता है। इन भिन्नताओं में अस्थायी तालिकाओं का व्यवहार (h / t Aaron)

एक समाहित डेटाबेस में अस्थायी तालिका डेटा समाहित डेटाबेस के समतुल्य है।

  • सभी मेटाडेटा अस्थायी तालिकाओं (उदाहरण के लिए, तालिका और स्तंभ के नाम, अनुक्रमित, और इसी तरह) से संबंधित हैं, जो कैटलॉग कोलाजेशन में होगा।
  • नामांकित बाधाओं का उपयोग अस्थायी तालिकाओं में नहीं किया जा सकता है।
  • अस्थायी तालिकाएँ उपयोगकर्ता-परिभाषित प्रकार, XML स्कीमा संग्रह या उपयोगकर्ता-परिभाषित कार्यों को संदर्भित नहीं कर सकती हैं।

विभिन्न स्कैप्स की दृश्यता

@table_variablesकेवल उस बैच और दायरे में पहुँचा जा सकता है जिसमें वे घोषित किए गए हैं। #temp_tablesबच्चे के बैच (नेस्टेड ट्रिगर्स, प्रक्रिया, execकॉल) के भीतर सुलभ हैं । #temp_tablesबाहरी दायरे में ( @@NESTLEVEL=0) बैचों को भी फैला सकते हैं क्योंकि वे सत्र समाप्त होने तक बने रहते हैं। चाइल्ड बैच में न तो किसी प्रकार की वस्तु बनाई जा सकती है और न ही कॉलिंग स्कोप में एक्सेस किया जा सकता है, जैसा कि आगे चर्चा की गई है (ग्लोबल ##tempटेबल हालांकि हो सकते हैं)।

जीवन काल

@table_variablesजब एक DECLARE @.. TABLEबयान युक्त एक बैच निष्पादित किया जाता है (उस बैच में किसी भी उपयोगकर्ता कोड से पहले) चलाया जाता है और अंत में अनुमानित रूप से गिरा दिया जाता है।

हालाँकि पार्सर आपको टेबल वेरिएबल का उपयोग करने की कोशिश नहीं करने देगा और DECLAREकथन से पहले निहित सृजन को नीचे देखा जा सकता है।

IF (1 = 0)
BEGIN
DECLARE @T TABLE(X INT)
END

--Works fine
SELECT *
FROM @T

#temp_tablesTSQL CREATE TABLEस्टेटमेंट के सामने आने पर स्पष्ट रूप से बनाए जाते हैं और DROP TABLEबैच के समाप्त होने पर (या यदि बच्चा बैच में बनाया जाता है @@NESTLEVEL > 0) या तब जब सत्र समाप्त हो जाता है, तब स्पष्ट रूप से ड्रॉप किया जा सकता है या इसके साथ ही इसे हटा दिया जा सकता है ।

NB: संग्रहीत दिनचर्या के भीतर दोनों प्रकार की वस्तु को बार-बार बनाने और नए टेबल को छोड़ने के बजाय कैश किया जा सकता है । जब यह कैशिंग तब भी हो सकती है जब इस पर प्रतिबंध लगाया जा सकता है, #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 वैसे भी बेहतर काम कर सकते हैं )

प्रमुखता

तालिका चर में शामिल निष्पादन योजनाओं में से कई एक एकल पंक्ति दिखाएंगे जो उनसे आउटपुट के रूप में अनुमानित हैं। तालिका चर गुणों का निरीक्षण करने से पता चलता है कि SQL सर्वर का मानना ​​है कि तालिका चर में शून्य पंक्तियाँ हैं (यह अनुमान लगाता है कि क्यों 1 पंक्ति शून्य पंक्ति तालिका से उत्सर्जित की जाएगी @Paul व्हाइट द्वारा यहाँ समझाया गया है )।

हालाँकि पिछले अनुभाग में दिखाए गए परिणाम सटीक rowsगिनती दिखाते हैं sys.partitions। मुद्दा यह है कि ज्यादातर मौकों पर टेबल के खाली होने के संदर्भ में टेबल वेरिएबल को संकलित किया जाता है। यदि कथन @table_variableपॉपुलेट होने के बाद संकलित (पुनः) है तो इसका उपयोग टेबल कार्डिनैलिटी के लिए किया जाएगा (यह स्पष्ट recompileया शायद इसलिए हो सकता है क्योंकि यह कथन एक अन्य ऑब्जेक्ट का संदर्भ भी देता है जो एक आस्थगित संकलन या एक 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

योजना स्थगित अनुमान के बाद सटीक अनुमानित पंक्ति गणना दिखाती है।

सटीक पंक्ति गणना दिखाता है

SQL Server 2012 SP2 में, ट्रेस ध्वज 2453 पेश किया गया है। अधिक विवरण यहां "रिलेशनल इंजन" के तहत हैं

जब इस ट्रेस ध्वज को सक्षम किया जाता है, तो यह स्वत: recompiles के कारण परिवर्तित कार्डिनैलिटी का कारण बन सकता है, जैसा कि बहुत जल्द ही चर्चा की गई है।

नायब: संगतता स्तर में Azure पर कथन का 150 संकलन अब पहले निष्पादन तक स्थगित कर दिया गया है । इसका मतलब है कि यह अब शून्य पंक्ति अनुमान समस्या के अधीन नहीं होगा।

कोई कॉलम आँकड़े नहीं

अधिक सटीक टेबल कार्डिनैलिटी होने का मतलब यह नहीं है कि अनुमानित पंक्ति गणना किसी भी अधिक सटीक होगी (जब तक कि तालिका में सभी पंक्तियों पर एक ऑपरेशन नहीं किया जाता है)। SQL सर्वर तालिका चर के लिए स्तंभ आँकड़े बनाए नहीं रखता है, इसलिए तुलना विधेय के आधार पर अनुमानों पर वापस गिर जाएगा (उदाहरण के लिए कि तालिका का 10% =एक गैर अद्वितीय स्तंभ या >तुलना के लिए 30% के लिए वापस कर दिया जाएगा )। इसके विपरीत कॉलम में तालिकाओं के लिए आँकड़े बनाए रखे जाते हैं#temp

SQL सर्वर प्रत्येक स्तंभ पर किए गए संशोधनों की संख्या की एक संख्या रखता है। यदि योजना को संकलित करने के बाद से संशोधनों की संख्या recompilation सीमा (RT) से अधिक है, तो योजना recompiled और आँकड़े अद्यतन किए जाएंगे। RT तालिका प्रकार और आकार पर निर्भर करता है।

SQL Server 2008 में प्लान कैशिंग से

आरटी की गणना निम्नानुसार की जाती है। (जब एक क्वेरी योजना संकलित की जाती है, तो तालिका की कार्डिनैलिटी को संदर्भित करता है।)

स्थायी तालिका
- यदि 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।
टेबल चर
- आरटी मौजूद नहीं है। इसलिए, तालिका चर की कार्डिनैलिटी में बदलाव के कारण पुनर्मिलन नहीं होता है। (लेकिन टीएफ 2453 के बारे में नीचे नोट देखें)

KEEP PLANसंकेत के लिए आर टी सेट करने के लिए इस्तेमाल किया जा सकता #tempटेबल स्थायी तालिकाओं के लिए के रूप में ही।

इस सब का शुद्ध प्रभाव यह है कि अक्सर #tempतालिकाओं के लिए बनाई गई निष्पादन योजनाएं @table_variablesकई पंक्तियों के शामिल होने से बेहतर परिमाण के आदेश हैं क्योंकि SQL सर्वर के पास काम करने के लिए बेहतर जानकारी है।

NB1: टेबल चर में आँकड़े नहीं होते हैं लेकिन फिर भी ट्रेस फ़्लैग 2453 के तहत "सांख्यिकी परिवर्तित" पुनरावृत्ति घटना को लागू नहीं कर सकते ("तुच्छ" योजनाओं के लिए लागू नहीं होता है) यह उसी पुनरावर्ती थ्रेशोल्ड के तहत प्रतीत होता है जैसा कि ऊपर टेम्प टेबल के साथ दिखाया गया है। अतिरिक्त एक कि अगर N=0 -> RT = 1। जब टेबल वैरिएबल के खाली होने पर सभी स्टेटमेंट संकलित हो TableCardinalityजाते हैं, तो वे खाली होने पर पहली बार रिजेक्ट होने पर ठीक हो जाते हैं। संकलित समय सारणी कार्डिनैलिटी योजना में संग्रहीत की जाती है और यदि स्टेटमेंट को फिर से उसी कार्डिनैलिटी (या तो नियंत्रण स्टेटमेंट के प्रवाह के कारण या कैश्ड प्लान के पुन: उपयोग के कारण) निष्पादित किया जाता है, तो कोई पुनरावृत्ति नहीं होती है।

NB2: संग्रहीत प्रक्रियाओं में कैश्ड अस्थायी तालिकाओं के लिए ऊपर वर्णित की तुलना में पुनर्नवीनीकरण कहानी बहुत अधिक जटिल है। देखें संग्रहित प्रक्रिया में अस्थायी टेबल्स सभी रक्तमय जानकारी के लिए।

recompiles

साथ ही संशोधन आधारित ऊपर वर्णित recompiles #tempटेबल भी साथ जुड़ा हो सकता अतिरिक्त compiles बस क्योंकि वे कार्य है कि तालिका चर कि एक संकलन को गति प्रदान करने के लिए मना कर रहे हैं अनुमति देते हैं (उदाहरण के लिए 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तालिका चर से होने वाले प्रश्नों के लिए पॉल व्हाइट टिप्पणियों में बताते हैं कि ये स्वचालित रूप से निहित 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 सर्वर ने दोनों के लिए आवंटन आदेश स्कैन का उपयोग किया है ।

मैंने उपरोक्त स्क्रिप्ट को दो बार चलाया और दूसरे रन के परिणाम नीचे हैं

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 सर्वर सिर्फ ऑब्जेक्ट पर स्कीमा स्थिरता लॉक प्राप्त करता है। लेकिन एक #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 ... ROLLBACK26 पंक्तियों को जोड़ने पर यह दिखाते हुए लौटा दिया जाता है कि रोलबैक के लिए अनुमति देने के लिए और सिस्टम टेबल की दोनों पंक्तियों पर ताले लगे रहते हैं और अन्य लेनदेन को बिना पढ़े हुए डेटा को पढ़ने से रोकने के लिए। समतुल्य तालिका चर संचालन उपयोगकर्ता लेन-देन के साथ रोलबैक के अधीन नहीं है और हमें अगले विवरण में जाँच करने के लिए इन तालों को रखने की कोई आवश्यकता नहीं है, लेकिन प्राप्त किए गए तालों का पता लगाया गया है और प्रोइलर में जारी किया गया है या ट्रेस ध्वज 1200 का उपयोग करते हुए बहुत से लॉकिंग घटनाओं का पता चलता है। पाए जाते हैं।

इंडेक्स

SQL सर्वर 2014 अनुक्रमित से पहले के संस्करणों के लिए केवल एक अद्वितीय बाधा या प्राथमिक कुंजी को जोड़ने के साइड इफेक्ट के रूप में तालिका चर पर स्पष्ट रूप से बनाया जा सकता है। यह निश्चित रूप से इसका मतलब है कि केवल अद्वितीय सूचकांक समर्थित हैं। यूनिक क्लस्टर्ड इंडेक्स वाली टेबल पर एक नॉन यूनिक नॉन क्लस्टर्ड इंडेक्स का अनुकरण किया जा सकता है, लेकिन इसे केवल UNIQUE NONCLUSTEREDएनसीआई कुंजी के अंत में सीआई कुंजी जोड़कर (एसक्यूएल सर्वर इस तरह से पर्दे के पीछे भी करेगा, भले ही एक नॉन यूनिक हो NCI निर्दिष्ट किया जा सकता है)

प्रदर्शन पहले विभिन्न रूप में index_optionरों बाधा घोषणा में निर्दिष्ट किया जा सकता सहित DATA_COMPRESSION, IGNORE_DUP_KEYहै, और FILLFACTOR(हालांकि स्थापित करने कि यह के रूप में एक ही सूचकांक पर कोई फर्क पुनर्निर्माण होगा और आप तालिका चर पर अनुक्रमित पुनर्निर्माण नहीं कर सकता का कोई मतलब नहीं है!)

इसके अतिरिक्त टेबल चर INCLUDEडी कॉलम, फ़िल्टर किए गए अनुक्रमित (2016 तक) या विभाजन का समर्थन नहीं करते हैं , #tempटेबल करते हैं (विभाजन योजना बनाई जानी चाहिए tempdb)।

SQL सर्वर 2014 में अनुक्रमणिका

SQL सर्वर 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 सर्वर 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स्केलर या मल्टी-स्टेटमेंट टेबल यूडीएफ के अंदर इस्तेमाल किया जा सकता है।
  • @table_variables बाधाओं का नाम नहीं दिया जा सकता है।
  • @table_variablesSELECT-ed INTO, ALTER-ed, TRUNCATEd या be DBCCजैसे कमांड्स का लक्ष्य नहीं हो सकता DBCC CHECKIDENTहै SET IDENTITY INSERTऔर जैसे टेबल संकेत का समर्थन नहीं करते हैंWITH (FORCESCAN)
  • CHECK टेबल चर पर बाधाओं को सरलीकरण, निहित विधेय या विरोधाभास का पता लगाने के लिए अनुकूलक द्वारा विचार नहीं किया जाता है।
  • तालिका चर, पंक्तियों को साझा करने के अनुकूलन के अर्थ के लिए अर्हता प्राप्त नहीं करते हैं, जिसका अर्थ है कि इन के विरुद्ध योजनाओं को हटाना और अपडेट करना अधिक उपरि और PAGELATCH_EXप्रतीक्षा कर सकता है। ( उदाहरण )

केवल मेमोरी?

जैसा कि शुरुआत में कहा गया है कि दोनों पन्नों में संग्रहीत हैं tempdb। हालाँकि, मैंने यह पता नहीं लगाया कि क्या व्यवहार में कोई अंतर था जब यह इन पृष्ठों को डिस्क में लिखने की बात आती है।

मैंने अभी इस पर थोड़ी मात्रा में परीक्षण किया है और अब तक ऐसा कोई अंतर नहीं देखा है। SQL सर्वर 250 पृष्ठों के अपने उदाहरण पर मैंने जो विशिष्ट परीक्षण किया, उसमें डेटा फ़ाइल के लिखे जाने से पहले का कट ऑफ़ पॉइंट प्रतीत होता है।

NB: SQL सर्वर 2014 या SQL Server 2012 SP1 / CU10 या SP2 / CU1 में नीचे दिया गया व्यवहार उत्सुक लेखक अब डिस्क पर पेज लिखने के लिए उत्सुक नहीं है। SQL सर्वर 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मैं नीचे के रूप में लेखन को देखने के लिए शुरू कर दिया।

ProcMon

ऊपर दिया गया स्क्रीनशॉट 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 (सी। 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ने लगभग समान ग्राफ दिए और इस बिंदु पर पहुंचने से पहले बफर पूल को अधिकतम रूप से प्रबंधित करने में कामयाब रहे कि वे पूरी तरह से स्मृति में नहीं थे इसलिए स्मृति पर कोई विशेष सीमा नहीं लगती है या तो उपभोग कर सकते हैं।

बफ़र पूल में पेज


मैंने पाया है कि SQL सर्वर एक समान तालिका चर की तुलना में अस्थायी टेबल (कैशिंग के साथ भी) बनाते समय काफी अधिक लेचेस प्राप्त करता है। आप latch_acquired डीबग XE का उपयोग करके और लगभग 35 स्तंभों के साथ एक तालिका बनाकर परीक्षण कर सकते हैं। मुझे 4 चरखी लेने के लिए टेबल चर मिला और लगभग 70 कुंडी कहीं ले जाने के लिए अस्थायी तालिका।
जो ओबिश

40

कुछ चीजें हैं जो मैं अध्ययन के बजाय विशेष अनुभवों के आधार पर इंगित करना चाहता हूं। एक डीबीए के रूप में, मैं बहुत नया हूं इसलिए कृपया मुझे जहां आवश्यक हो, सही करें।

  1. डिफ़ॉल्ट रूप से #temp टेबल SQL सर्वर आवृत्ति के डिफ़ॉल्ट टकराने का उपयोग करें। इसलिए जब तक अन्यथा निर्दिष्ट नहीं किया जाता है, आप #temp तालिकाओं और डेटाबेस तालिकाओं के बीच मूल्यों की तुलना या अद्यतन करने की समस्याओं में भाग सकते हैं, अगर मास्टरडब डेटाबेस से अलग टकराव है। देखें: http://www.mssqltips.com/sqlservertip/2440/create-sql-server-temporary-tables-with-the-correct-collation/
  2. पूरी तरह से व्यक्तिगत अनुभव के आधार पर, उपलब्ध स्मृति पर एक प्रभाव पड़ता है जिस पर बेहतर प्रदर्शन होता है। MSDN छोटे परिणाम सेटों को संग्रहीत करने के लिए तालिका चर का उपयोग करने की सलाह देता है, लेकिन अधिकांश समय अंतर ध्यान देने योग्य नहीं होता है। हालांकि बड़े सेटों में, यह कुछ मामलों में स्पष्ट हो जाता है कि टेबल चर अधिक स्मृति गहन हैं और क्वेरी को क्रॉल तक धीमा कर सकते हैं।

6
यह भी ध्यान दें कि यदि आप SQL Server 2012 का उपयोग कर रहे हैं और डेटाबेस सम्‍मिलित है तो #temp टेबलों पर टकराने से कॉलिंग डेटाबेस के कॉलेशन को इनहेरिट किया जा सकता है।
हारून बर्ट्रेंड

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.