Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

Bung, siapa yang memiliki tabel #temp itu?

Anda mungkin pernah berada dalam skenario di mana Anda ingin tahu tentang siapa yang membuat salinan tabel #temp tertentu. Kembali pada bulan Juni 2007, saya meminta DMV untuk memetakan tabel #temp ke sesi, tetapi ini ditolak untuk rilis 2008 (dan tersapu dengan penghentian Connect beberapa tahun yang lalu).

Di SQL Server 2005, 2008 dan 2008 R2, Anda harus dapat menarik informasi ini dari jejak default:

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);

(Berdasarkan kode oleh Jonathan Kehayias.)

Untuk menentukan penggunaan ruang, Anda dapat lebih meningkatkan ini untuk menggabungkan data dari DMV seperti sys.dm_db_partition_stats – misalnya:

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);

Mulai di SQL Server 2012, namun, ini berhenti bekerja jika tabel #temp adalah tumpukan. Bob Ward (@bobwardms) memberikan penjelasan menyeluruh mengapa ini terjadi; jawaban singkatnya adalah ada bug dalam logika mereka untuk mencoba memfilter pembuatan tabel #temp dari jejak default, dan bug ini sebagian diperbaiki selama pekerjaan SQL Server 2012 untuk menyelaraskan jejak dan acara yang diperluas. Perhatikan bahwa SQL Server 2012+ masih akan merekam pembuatan tabel #temp dengan batasan sebaris seperti kunci utama, hanya saja bukan tumpukan.

[Klik di sini untuk menampilkan/menyembunyikan penjelasan lengkap Bob.]

Object:Created event sebenarnya memiliki 3 subevent:Begin, Commit, dan Rollback. Jadi jika Anda berhasil membuat objek, Anda mendapatkan 2 acara:1 untuk Mulai dan 1 untuk Komit. Anda tahu yang mana dengan melihat EventSubClass.


Sebelum SQL Server 2012, hanya Object:Created with subclass =Begin yang memiliki ObjectName yang terisi. Jadi subclass =Commit tidak berisi ObjectName yang terisi. Ini dirancang untuk menghindari pengulangan pemikiran ini, Anda dapat mencari nama di acara Begin.


Seperti yang telah saya katakan, pelacakan default dirancang untuk melewati semua peristiwa pelacakan di mana dbid =2 dan nama objek dimulai dengan "#". Jadi apa yang bisa muncul di jejak default adalah Object:Created subclass =Commit event (itulah sebabnya Nama Objek kosong).


Meskipun kami tidak mendokumentasikan "niat" kami untuk tidak melacak objek tempdb, perilaku tersebut jelas tidak berfungsi sebagaimana mestinya.


Sekarang lanjutkan ke pembuatan SQL Server 2012. Kita pindah ke proses porting event dari SQLTrace ke XEvent. Kami memutuskan selama jangka waktu ini sebagai bagian dari pekerjaan XEvent ini bahwa subclass=Commit atau Rollback membutuhkan ObjectName yang diisi. Kode di mana kita melakukan ini adalah kode yang sama di mana kita menghasilkan acara SQLTrace jadi sekarang acara SQLTrace memiliki ObjectName di dalamnya untuk subclass=Commit.


Dan karena logika pemfilteran kami untuk pelacakan default tidak berubah, sekarang Anda tidak melihat peristiwa Mulai atau Komit.

Bagaimana seharusnya Anda melakukannya hari ini

Di SQL Server 2012 dan yang lebih baru, Extended Events akan memungkinkan Anda menangkap object_created secara manual acara, dan mudah untuk menambahkan filter untuk hanya peduli dengan nama yang dimulai dengan # . Definisi sesi berikut akan menangkap semua pembuatan tabel #temp, heap atau tidak, dan akan menyertakan semua informasi berguna yang biasanya diambil dari pelacakan default. Selain itu, ia menangkap kumpulan SQL yang bertanggung jawab atas pembuatan tabel (jika Anda menginginkannya), informasi yang tidak tersedia dalam pelacakan default (TextData selalu 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;

Anda mungkin dapat melakukan sesuatu yang serupa pada 2008 dan 2008 R2, tetapi saya tahu ada beberapa perbedaan halus pada apa yang tersedia, dan saya tidak mengujinya setelah mendapatkan kesalahan ini langsung:

Msg 25623, Level 16, Status 1, Baris 1
Nama peristiwa, "sqlserver.object_created", tidak valid, atau objek tidak dapat ditemukan

Menganalisis data

Menarik informasi dari target file sedikit lebih rumit dibandingkan dengan pelacakan default, sebagian besar karena semuanya disimpan sebagai XML (well, untuk bertele-tele, XML disajikan sebagai NVARCHAR). Berikut adalah kueri yang saya buat untuk mengembalikan informasi yang mirip dengan kueri kedua di atas terhadap jejak default. Satu hal penting yang perlu diperhatikan adalah bahwa Extended Events menyimpan datanya dalam UTC, jadi jika server Anda disetel ke zona waktu lain, Anda perlu menyesuaikan agar create_date di sys.objects dibandingkan seolah-olah UTC. (Stempel waktu disetel agar cocok karena object_id nilai dapat didaur ulang. Saya berasumsi di sini bahwa jendela dua detik sudah cukup untuk menyaring nilai daur ulang.)

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];

Tentu saja ini hanya akan mengembalikan ruang dan informasi lain untuk tabel #temp yang masih ada. Jika Anda ingin melihat semua pembuatan tabel #temp masih tersedia di file target, meskipun sekarang tidak ada, cukup ubah kedua instance INNER JOIN ke 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. Bagaimana Menghubungkan Database ke Python

  2. Memodelkan Struktur Data Dasar untuk Mengelola Pengguna, Utas, dan Posting

  3. Cara Membuat Tabel dari SQL Query

  4. Membandingkan Kinerja Windows Azure VM, Bagian 1

  5. Eksplorasi Mendalam Keamanan Tingkat Baris