Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

Anh bạn, ai sở hữu cái bàn #temp đó?

Có thể bạn đã ở trong một tình huống mà bạn tò mò về việc ai đã tạo ra một bản sao cụ thể của bảng #temp. Vào tháng 6 năm 2007, tôi đã yêu cầu một DMV ánh xạ các bảng #temp tới các phiên, nhưng điều này đã bị từ chối cho bản phát hành năm 2008 (và đã bị loại bỏ khi Connect ngừng hoạt động vài năm trước).

Trong SQL Server 2005, 2008 và 2008 R2, bạn có thể lấy thông tin này từ dấu vết mặc định:

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

(Dựa trên mã của Jonathan Kehayias.)

Để xác định mức sử dụng dung lượng, bạn có thể nâng cao hơn nữa điều này để tham gia dữ liệu từ các DMV như sys.dm_db_partition_stats - ví dụ:

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

Tuy nhiên, bắt đầu từ SQL Server 2012, điều này đã ngừng hoạt động nếu bảng #temp là một đống. Bob Ward (@bobwardms) cung cấp một lời giải thích cặn kẽ về lý do tại sao điều này xảy ra; câu trả lời ngắn gọn là có một lỗi trong logic của họ để cố gắng lọc ra #temp tạo bảng khỏi dấu vết mặc định và lỗi này đã được sửa một phần trong quá trình SQL Server 2012 làm việc để căn chỉnh tốt hơn dấu vết và các sự kiện mở rộng. Lưu ý rằng SQL Server 2012+ sẽ vẫn nắm bắt được việc tạo bảng #temp với các ràng buộc nội tuyến chẳng hạn như khóa chính, không phải heap.

[Nhấp vào đây để hiển thị / ẩn lời giải thích đầy đủ của Bob.]

Đối tượng:Sự kiện đã tạo thực sự có 3 sự kiện phụ:Bắt đầu, Cam kết và Khôi phục. Vì vậy, nếu bạn tạo thành công một đối tượng, bạn sẽ nhận được 2 sự kiện:1 cho Bắt đầu và 1 cho Cam kết. Bạn biết cái nào bằng cách xem EventSubClass.


Trước SQL Server 2012, chỉ có Object:Created with subclass =Begin mới có ObjectName. Vì vậy, lớp con =commit không chứa ObjectName được phổ biến. Đây là do thiết kế để tránh lặp lại suy nghĩ này, bạn có thể tra cứu tên trong sự kiện Bắt đầu.


Như tôi đã nói dấu vết mặc định được thiết kế để bỏ qua bất kỳ sự kiện dấu vết nào trong đó dbid =2 và tên đối tượng bắt đầu bằng "#". Vì vậy, những gì có thể hiển thị trong dấu vết mặc định là các sự kiện Object:Created subclass =commit (đó là lý do tại sao Tên đối tượng bị trống).


Mặc dù chúng tôi không ghi lại "ý định" của mình để không theo dõi các đối tượng tempdb, nhưng hành vi này rõ ràng không hoạt động như dự định.


Bây giờ chuyển sang quá trình xây dựng SQL Server 2012. Chúng ta chuyển sang quá trình chuyển sự kiện từ SQLTrace sang XEvent. Chúng tôi đã quyết định trong khung thời gian này như một phần của công việc XEvent này rằng lớp con =commit hoặc Rollback cần ObjectName được điền vào. Mã nơi chúng tôi thực hiện điều này giống với mã mà chúng tôi tạo ra sự kiện SQLTrace, vì vậy bây giờ sự kiện SQLTrace có ObjectName trong đó cho lớp con =Cam kết.


Và vì logic lọc của chúng tôi cho theo dõi mặc định không thay đổi, nên bây giờ bạn không thấy các sự kiện Bắt đầu hoặc Cam kết.

Bạn nên làm như thế nào hôm nay

Trong SQL Server 2012 trở lên, Sự kiện mở rộng sẽ cho phép bạn nắm bắt thủ công object_created sự kiện và dễ dàng thêm bộ lọc để chỉ quan tâm đến những tên bắt đầu bằng # . Định nghĩa phiên sau sẽ nắm bắt tất cả việc tạo bảng #temp, heap hay không, và sẽ bao gồm tất cả thông tin hữu ích thường được truy xuất từ ​​theo dõi mặc định. Ngoài ra, nó nắm bắt lô SQL chịu trách nhiệm tạo bảng (nếu bạn muốn), thông tin không có sẵn trong dấu vết mặc định (TextData luôn là 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;

Bạn có thể làm điều gì đó tương tự trong 2008 và 2008 R2, nhưng tôi biết có một số khác biệt nhỏ đối với những gì có sẵn và tôi đã không kiểm tra nó sau khi gặp lỗi này ngay lập tức:

Msg 25623, Mức 16, Trạng thái 1, Dòng 1
Tên sự kiện, "sqlserver.object_create", không hợp lệ hoặc không thể tìm thấy đối tượng

Phân tích dữ liệu

Việc kéo thông tin từ đích tệp hơi cồng kềnh hơn so với theo dõi mặc định, chủ yếu là vì tất cả đều được lưu trữ dưới dạng XML (tốt, về mặt ngôn ngữ, nó là XML được trình bày dưới dạng NVARCHAR). Đây là một truy vấn tôi đã đánh dấu để trả về thông tin tương tự như truy vấn thứ hai ở trên đối với dấu vết mặc định. Một điều quan trọng cần lưu ý là Sự kiện mở rộng lưu trữ dữ liệu của nó ở UTC, vì vậy nếu máy chủ của bạn được đặt thành múi giờ khác, bạn sẽ cần phải điều chỉnh để create_date trong sys.objects được so sánh như thể nó là UTC. (Dấu thời gian được đặt để khớp vì object_id các giá trị có thể được tái chế. Tôi giả sử ở đây rằng một cửa sổ hai giây là đủ để lọc ra bất kỳ giá trị tái chế nào.)

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

Tất nhiên, điều này sẽ chỉ trả về không gian và thông tin khác cho các bảng #temp vẫn tồn tại. Nếu bạn muốn xem tất cả các tác phẩm tạo bảng #temp vẫn có sẵn trong mục tiêu tệp, ngay cả khi chúng không tồn tại ngay bây giờ, chỉ cần thay đổi cả hai phiên bản của INNER JOIN thành 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. Một số biến đổi tổng hợp BẤT KỲ đều bị hỏng

  2. So sánh các lớp trừu tượng cơ sở dữ liệu PHP và các plugin CRUD

  3. Xử lý các giá trị NULL một cách hiệu quả với hàm SQL COALESCE cho người mới bắt đầu

  4. GROUP BY so với ORDER BY

  5. Cách trở thành nhà thiết kế cơ sở dữ liệu