Database
 sql >> डेटाबेस >  >> RDS >> Database

यार, उस #temp टेबल का मालिक कौन है?

आप शायद ऐसे परिदृश्य में रहे हैं जहाँ आप उत्सुक थे कि किसने #temp तालिका की एक विशिष्ट प्रति बनाई। 2007 के जून में वापस, मैंने #temp तालिकाओं को सत्रों में मैप करने के लिए DMV के लिए कहा, लेकिन इसे 2008 की रिलीज़ के लिए अस्वीकार कर दिया गया था (और कुछ साल पहले Connect सेवानिवृत्ति के साथ बह गया था)।

SQL सर्वर 2005, 2008 और 2008 R2 में, आपको इस जानकारी को डिफ़ॉल्ट ट्रेस से खींचने में सक्षम होना चाहिए:

DECLARE @filename VARCHAR(MAX);
 
SELECT @filename = SUBSTRING([path], 0,
 LEN([path])-CHARINDEX('\', REVERSE([path]))+1) + '\Log.trc'  
FROM sys.traces   
WHERE is_default = 1;  
 
SELECT   
     o.name,   
     o.[object_id],  
     o.create_date, 
     gt.SPID,  
     NTUserName = gt.NTDomainName + '\' + gt.NTUserName,
     SQLLogin = gt.LoginName,  
     gt.HostName,  
     gt.ApplicationName,
     gt.TextData -- don't bother, always NULL 
  FROM sys.fn_trace_gettable(@filename, DEFAULT) AS gt  
  INNER JOIN tempdb.sys.objects AS o   
    ON gt.ObjectID = o.[object_id] 
  WHERE gt.DatabaseID = 2 
    AND gt.EventClass = 46 -- (Object:Created Event from sys.trace_events)  
    AND gt.EventSubClass = 1 -- Commit
    AND o.name LIKE N'#%'
    AND o.create_date >= DATEADD(MILLISECOND, -100, gt.StartTime)   
    AND o.create_date <= DATEADD(MILLISECOND,  100, gt.StartTime);

(जोनाथन केहैयस के कोड के आधार पर।)

अंतरिक्ष उपयोग का निर्धारण करने के लिए आप इसे और बढ़ा सकते हैं ताकि डीएमवी से डेटा में शामिल हो सकें जैसे sys.dm_db_partition_stats - उदाहरण के लिए:

DECLARE @filename VARCHAR(MAX);
 
SELECT @filename = SUBSTRING([path], 0,
   LEN([path])-CHARINDEX('\', REVERSE([path]))+1) + '\Log.trc'  
FROM sys.traces   
WHERE is_default = 1;  
 
SELECT   
     o.name,   
     o.[object_id],  
     o.create_date, 
     gt.SPID,  
     NTUserName = gt.NTDomainName + '\' + gt.NTUserName,
     SQLLogin = gt.LoginName,  
     gt.HostName,  
     gt.ApplicationName,
     row_count = x.rc,
     reserved_page_count = x.rpc
  FROM sys.fn_trace_gettable(@filename, DEFAULT) AS gt  
  INNER JOIN tempdb.sys.objects AS o   
    ON gt.ObjectID = o.[object_id]
  INNER JOIN
  (
    SELECT 
      [object_id],
      rc  = SUM(CASE WHEN index_id IN (0,1) THEN row_count END), 
      rpc = SUM(reserved_page_count) 
    FROM tempdb.sys.dm_db_partition_stats
    GROUP BY [object_id]
  ) AS x 
    ON x.[object_id] = o.[object_id]
  WHERE gt.DatabaseID = 2 
    AND gt.EventClass = 46 -- (Object:Created Event from sys.trace_events)  
	AND gt.EventSubClass = 1 -- Commit
	AND gt.IndexID IN (0,1)
    AND o.name LIKE N'#%'
    AND o.create_date >= DATEADD(MILLISECOND, -100, gt.StartTime)   
    AND o.create_date <= DATEADD(MILLISECOND,  100, gt.StartTime);

SQL सर्वर 2012 में शुरू होने पर, यदि #temp तालिका एक ढेर थी, तो इसने काम करना बंद कर दिया। बॉब वार्ड (@bobwardms) ने इस बात का विस्तृत विवरण दिया कि ऐसा क्यों हुआ; संक्षिप्त उत्तर यह है कि डिफ़ॉल्ट ट्रेस से #temp तालिका निर्माण को फ़िल्टर करने का प्रयास करने के लिए उनके तर्क में एक बग था, और इस बग को SQL सर्वर 2012 के बेहतर संरेखण ट्रेस और विस्तारित घटनाओं के काम के दौरान आंशिक रूप से ठीक किया गया था। ध्यान दें कि SQL Server 2012+ अभी भी #temp तालिका निर्माण को इनलाइन बाधाओं जैसे प्राथमिक कुंजी के साथ कैप्चर करेगा, बस ढेर नहीं।

[बॉब की पूरी व्याख्या दिखाने/छिपाने के लिए यहां क्लिक करें।]

वस्तु:निर्मित घटना में वास्तव में 3 उप-घटनाएँ होती हैं:आरंभ, प्रतिबद्ध और रोलबैक। तो यदि आप सफलतापूर्वक ऑब्जेक्ट बनाते हैं तो आपको 2 ईवेंट मिलते हैं:1 शुरुआत के लिए और 1 कमिट के लिए। EventSubClass को देखकर आप जानते हैं कि कौन सा है।


एसक्यूएल सर्वर 2012 से पहले, केवल ऑब्जेक्ट:उपवर्ग के साथ बनाया गया =शुरुआत में ऑब्जेक्टनाम पॉप्युलेट होता है। तो सबक्लास =कमिट में ऑब्जेक्टनाम पॉप्युलेट नहीं था। यह इस सोच को दोहराने से बचने के लिए डिज़ाइन द्वारा था कि आप शुरुआत घटना में नाम देख सकते हैं।


जैसा कि मैंने कहा है कि डिफ़ॉल्ट ट्रेस किसी भी ट्रेस ईवेंट को छोड़ने के लिए डिज़ाइन किया गया था जहां dbid =2 और ऑब्जेक्ट का नाम "#" से शुरू हुआ था। तो डिफ़ॉल्ट ट्रेस में क्या दिखाई दे सकता है ऑब्जेक्ट:निर्मित उपवर्ग =कमिट इवेंट (यही कारण है कि ऑब्जेक्ट का नाम खाली है)।


भले ही हमने tempdb ऑब्जेक्ट्स को ट्रेस न करने के अपने "इरादे" का दस्तावेजीकरण नहीं किया, लेकिन व्यवहार स्पष्ट रूप से इरादा के अनुसार काम नहीं कर रहा था।


अब SQL सर्वर 2012 के निर्माण के लिए आगे बढ़ें। हम SQLTrace से XEvent में ईवेंट पोर्ट करने की प्रक्रिया की ओर बढ़ते हैं। हमने इस समय सीमा के दौरान इस XEvent कार्य के भाग के रूप में निर्णय लिया कि उपवर्ग =कमिट या रोलबैक को ऑब्जेक्टनाम पॉप्युलेट की आवश्यकता है। जिस कोड में हम ऐसा करते हैं वह वही कोड होता है जहां हम SQLTrace ईवेंट उत्पन्न करते हैं, इसलिए अब SQLTrace ईवेंट में सबक्लास =कमिट के लिए ऑब्जेक्टनाम है।


और चूंकि डिफ़ॉल्ट ट्रेस के लिए हमारा फ़िल्टरिंग तर्क नहीं बदला है, अब आप या तो शुरुआत या कमिट इवेंट नहीं देखते हैं।

आज आपको यह कैसे करना चाहिए

SQL सर्वर 2012 और उसके बाद के संस्करण में, विस्तारित ईवेंट आपको object_created को मैन्युअल रूप से कैप्चर करने की अनुमति देगा ईवेंट, और केवल # . से शुरू होने वाले नामों की परवाह करने के लिए फ़िल्टर जोड़ना आसान है . निम्नलिखित सत्र परिभाषा सभी #temp तालिका निर्माण, ढेर या नहीं पर कब्जा करेगी, और इसमें सभी उपयोगी जानकारी शामिल होगी जो सामान्य रूप से डिफ़ॉल्ट ट्रेस से पुनर्प्राप्त की जाएगी। इसके अलावा, यह तालिका निर्माण के लिए जिम्मेदार SQL बैच को कैप्चर करता है (यदि आप इसे चाहते हैं), जानकारी डिफ़ॉल्ट ट्रेस में उपलब्ध नहीं है (TextData हमेशा NULL होता है )।

CREATE EVENT SESSION [TempTableCreation] ON SERVER 
ADD EVENT sqlserver.object_created
(
  ACTION 
  (
    -- you may not need all of these columns
    sqlserver.session_nt_username,
    sqlserver.server_principal_name,
    sqlserver.session_id,
    sqlserver.client_app_name,
    sqlserver.client_hostname,
    sqlserver.sql_text
  )
  WHERE 
  (
    sqlserver.like_i_sql_unicode_string([object_name], N'#%')
    AND ddl_phase = 1   -- just capture COMMIT, not BEGIN
  )
)
ADD TARGET package0.asynchronous_file_target
(
  SET FILENAME = 'c:\temp\TempTableCreation.xel',
  -- you may want to set different limits depending on
  -- temp table creation rate and available disk space
      MAX_FILE_SIZE = 32768,
      MAX_ROLLOVER_FILES = 10
)
WITH 
(
  -- if temp table creation rate is high, consider
  -- ALLOW_SINGLE/MULTIPLE_EVENT_LOSS instead
  EVENT_RETENTION_MODE = NO_EVENT_LOSS
);
GO
ALTER EVENT SESSION [TempTableCreation] ON SERVER STATE = START;

आप 2008 और 2008 R2 में कुछ ऐसा ही करने में सक्षम हो सकते हैं, लेकिन मुझे पता है कि जो उपलब्ध है, उसमें कुछ सूक्ष्म अंतर हैं, और इस त्रुटि को बल्ले से प्राप्त करने के बाद मैंने इसका परीक्षण नहीं किया:

संदेश 25623, स्तर 16, राज्य 1, पंक्ति 1
ईवेंट का नाम, "sqlserver.object_created", अमान्य है, या वस्तु नहीं मिली

डेटा का विश्लेषण करना

फ़ाइल लक्ष्य से जानकारी खींचना डिफ़ॉल्ट ट्रेस की तुलना में थोड़ा अधिक बोझिल है, अधिकतर क्योंकि यह सभी एक्सएमएल के रूप में संग्रहीत है (ठीक है, पैडेंटिक होने के लिए, यह एक्सएमएल को NVARCHAR के रूप में प्रस्तुत किया जाता है)। यहां एक क्वेरी है जिसे मैंने डिफ़ॉल्ट ट्रेस के विरुद्ध उपरोक्त दूसरी क्वेरी के समान जानकारी वापस करने के लिए चाबुक किया था। ध्यान देने योग्य एक महत्वपूर्ण बात यह है कि विस्तारित ईवेंट अपना डेटा UTC में संग्रहीत करता है, इसलिए यदि आपका सर्वर किसी अन्य समय क्षेत्र पर सेट है, तो आपको समायोजित करने की आवश्यकता होगी ताकि create_date sys.objects . में इसकी तुलना यूटीसी के रूप में की जाती है। (टाइमस्टैम्प मेल खाने के लिए सेट हैं क्योंकि object_id मूल्यों का पुनर्चक्रण किया जा सकता है। मैं यहां मानता हूं कि किसी भी पुनर्नवीनीकरण मूल्यों को फ़िल्टर करने के लिए दो सेकंड की विंडो पर्याप्त है।)

DECLARE @delta INT = DATEDIFF(MINUTE, SYSUTCDATETIME(), SYSDATETIME());
 
;WITH xe AS
(
  SELECT 
    [obj_name]  = xe.d.value(N'(event/data[@name="object_name"]/value)[1]',N'sysname'),
    [object_id] = xe.d.value(N'(event/data[@name="object_id"]/value)[1]',N'int'),
    [timestamp] = DATEADD(MINUTE, @delta, xe.d.value(N'(event/@timestamp)[1]',N'datetime2')),
    SPID        = xe.d.value(N'(event/action[@name="session_id"]/value)[1]',N'int'),
    NTUserName  = xe.d.value(N'(event/action[@name="session_nt_username"]/value)[1]',N'sysname'),
    SQLLogin    = xe.d.value(N'(event/action[@name="server_principal_name"]/value)[1]',N'sysname'),
    HostName    = xe.d.value(N'(event/action[@name="client_hostname"]/value)[1]',N'sysname'),
    AppName     = xe.d.value(N'(event/action[@name="client_app_name"]/value)[1]',N'nvarchar(max)'),
    SQLBatch    = xe.d.value(N'(event/action[@name="sql_text"]/value)[1]',N'nvarchar(max)')
 FROM 
    sys.fn_xe_file_target_read_file(N'C:\temp\TempTableCreation*.xel',NULL,NULL,NULL) AS ft
    CROSS APPLY (SELECT CONVERT(XML, ft.event_data)) AS xe(d)
) 
SELECT 
  DefinedName         = xe.obj_name,
  GeneratedName       = o.name,
  o.[object_id],
  xe.[timestamp],
  o.create_date,
  xe.SPID,
  xe.NTUserName,
  xe.SQLLogin, 
  xe.HostName,
  ApplicationName     = xe.AppName,
  TextData            = xe.SQLBatch,
  row_count           = x.rc,
  reserved_page_count = x.rpc
FROM xe
INNER JOIN tempdb.sys.objects AS o
ON o.[object_id] = xe.[object_id]
AND o.create_date >= DATEADD(SECOND, -2, xe.[timestamp])
AND o.create_date <= DATEADD(SECOND,  2, xe.[timestamp])
INNER JOIN
(
  SELECT 
    [object_id],
    rc  = SUM(CASE WHEN index_id IN (0,1) THEN row_count END), 
    rpc = SUM(reserved_page_count)
  FROM tempdb.sys.dm_db_partition_stats
  GROUP BY [object_id]
) AS x
ON o.[object_id] = x.[object_id];

बेशक यह केवल #temp तालिकाओं के लिए स्थान और अन्य जानकारी लौटाएगा जो अभी भी मौजूद हैं। यदि आप सभी #temp तालिका निर्माण अभी भी फ़ाइल लक्ष्य में उपलब्ध देखना चाहते हैं, भले ही वे अभी मौजूद नहीं हैं, तो बस INNER JOIN के दोनों उदाहरणों को बदलें। करने के लिए LEFT OUTER JOIN .


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. NoSQL में DBA की भूमिका

  2. उबंटू 20.04 पर ज़ैबिक्स को कैसे स्थापित और कॉन्फ़िगर करें

  3. डायनेमिक्स सीआरएम में मल्टी-स्टेटमेंट टीवीएफ

  4. समूहीकृत माध्यिका के लिए सर्वोत्तम दृष्टिकोण

  5. SQL ग्रुप बाय स्टेटमेंट का उपयोग क्या है?