SQL Server

SQL Server Management Studio Github Copilot activity

Cl

Claus Munch

Apr 14, 2026 ยท 1 min read

2 views

As AI is making it's entry into the workplaces, we started looking into it, to enable the developers to use it directly in SQL Server Management Studio (SSMS). A common complaint I keep bumping into both in our company and when meeting other database professionals around, is the fact that it is not forthcomming in regards to what it actually does/run on the instance.

We needed a way to find out what it does, and this was our solution:

Setting up the Xevent session:

ALTER EVENT SESSION [CaptureSSMSCopilot] ON DATABASE STATE = STOP;
DROP EVENT SESSION  [CaptureSSMSCopilot] ON DATABASE;

CREATE EVENT SESSION [CaptureSSMSCopilot] ON DATABASE
ADD EVENT sqlserver.sql_statement_completed (
    ACTION (
        sqlserver.client_app_name,
        sqlserver.client_hostname,
        sqlserver.sql_text,
        sqlserver.username,
        sqlserver.query_hash
    )
    WHERE sqlserver.client_app_name = N'Microsoft SQL Server Management Studio - GitHub Copilot'
       OR sqlserver.client_app_name = N'Microsoft SQL Server Management Studio - Copilot Completions'
)
ADD TARGET package0.ring_buffer (
    SET max_memory = 51200
)
WITH (
    MAX_DISPATCH_LATENCY = 5 SECONDS
);

ALTER EVENT SESSION [CaptureSSMSCopilot] ON DATABASE STATE = START;

Setup the table and procedure, to capture the data:

DROP TABLE IF EXISTS dbo.CopilotQueryLog;

CREATE TABLE dbo.CopilotQueryLog (
    id                  INT IDENTITY(1,1) NOT NULL,
    sql_hash            BINARY(32)        NOT NULL,  -- SHA2_256 of full sql_text
    sql_text            NVARCHAR(MAX)     NOT NULL,  -- full batch sent by Copilot
    sample_statement    NVARCHAR(MAX)     NULL,       -- one example sub-statement
    client_app          NVARCHAR(256)     NULL,
    username            NVARCHAR(256)     NULL,
    host                NVARCHAR(256)     NULL,
    first_seen          DATETIME2         NOT NULL DEFAULT SYSUTCDATETIME(),
    last_seen           DATETIME2         NOT NULL DEFAULT SYSUTCDATETIME(),
    execution_count     INT               NOT NULL DEFAULT 1,
    total_duration_us   BIGINT            NOT NULL DEFAULT 0,
    total_cpu_us        BIGINT            NOT NULL DEFAULT 0,
    total_logical_reads BIGINT            NOT NULL DEFAULT 0,
    CONSTRAINT PK_CopilotQueryLog      PRIMARY KEY (id),
    CONSTRAINT UQ_CopilotQueryLog_Hash UNIQUE (sql_hash)
);
GO

CREATE OR ALTER PROCEDURE dbo.usp_DrainCopilotXE
AS
BEGIN
    SET NOCOUNT ON;

    SELECT
        HASHBYTES('SHA2_256', LTRIM(RTRIM(
            event_data.value('(action[@name="sql_text"]/value)[1]', 'nvarchar(max)')
        )))                                                                              AS sql_hash,
        LTRIM(RTRIM(
            event_data.value('(action[@name="sql_text"]/value)[1]', 'nvarchar(max)')
        ))                                                                               AS sql_text,
        LTRIM(RTRIM(
            event_data.value('(data[@name="statement"]/value)[1]',  'nvarchar(max)')
        ))                                                                               AS sample_statement,
        event_data.value('(action[@name="client_app_name"]/value)[1]', 'nvarchar(256)') AS client_app,
        event_data.value('(action[@name="username"]/value)[1]',        'nvarchar(256)') AS username,
        event_data.value('(action[@name="client_hostname"]/value)[1]', 'nvarchar(256)') AS host,
        event_data.value('(@timestamp)[1]',                            'datetime2')     AS ts,
        event_data.value('(data[@name="duration"]/value)[1]',          'bigint')        AS duration_us,
        event_data.value('(data[@name="cpu_time"]/value)[1]',          'bigint')        AS cpu_us,
        event_data.value('(data[@name="logical_reads"]/value)[1]',     'bigint')        AS logical_reads
    INTO #xe_raw
    FROM (
        SELECT CAST(target_data AS XML) AS target_xml
        FROM sys.dm_xe_database_session_targets t
        JOIN sys.dm_xe_database_sessions        s ON s.address = t.event_session_address
        WHERE s.name        = 'CaptureSSMSCopilot'
          AND t.target_name = 'ring_buffer'
    ) AS ring
    CROSS APPLY target_xml.nodes('//RingBufferTarget/event') AS x(event_data)
    WHERE event_data.value('(action[@name="sql_text"]/value)[1]', 'nvarchar(max)') IS NOT NULL;

    WITH aggregated AS (
        SELECT
            sql_hash,
            MAX(sql_text)           AS sql_text,
            MAX(sample_statement)   AS sample_statement,
            MAX(client_app)         AS client_app,
            MAX(username)           AS username,
            MAX(host)               AS host,
            MIN(ts)                 AS first_seen,
            MAX(ts)                 AS last_seen,
            COUNT(*)                AS execution_count,
            SUM(duration_us)        AS total_duration_us,
            SUM(cpu_us)             AS total_cpu_us,
            SUM(logical_reads)      AS total_logical_reads
        FROM #xe_raw
        GROUP BY sql_hash
    )
    MERGE dbo.CopilotQueryLog AS tgt
    USING aggregated           AS src ON tgt.sql_hash = src.sql_hash
    WHEN MATCHED THEN UPDATE SET
        tgt.last_seen           = src.last_seen,
        tgt.execution_count     = tgt.execution_count     + src.execution_count,
        tgt.total_duration_us   = tgt.total_duration_us   + src.total_duration_us,
        tgt.total_cpu_us        = tgt.total_cpu_us        + src.total_cpu_us,
        tgt.total_logical_reads = tgt.total_logical_reads + src.total_logical_reads
    WHEN NOT MATCHED BY TARGET THEN INSERT (
        sql_hash, sql_text, sample_statement, client_app, username, host,
        first_seen, last_seen, execution_count,
        total_duration_us, total_cpu_us, total_logical_reads
    ) VALUES (
        src.sql_hash, src.sql_text, src.sample_statement, src.client_app, src.username, src.host,
        src.first_seen, src.last_seen, src.execution_count,
        src.total_duration_us, src.total_cpu_us, src.total_logical_reads
    );

    DROP TABLE #xe_raw;
END;

If you need to capture the data on a regular interval, you should just schedule the newly created procedure.

EXEC dbo.usp_DrainCopilotXE;

Querying our collected data:

SELECT
    LEFT(sql_text, 100)     AS sql_preview,
    sample_statement,
    client_app,
    username,
    execution_count,
    first_seen,
    last_seen,
    total_logical_reads
FROM dbo.CopilotQueryLog
ORDER BY last_seen DESC;
Share this article:

Related Articles