tediousjs / node-mssql

Microsoft SQL Server client for Node.js
https://tediousjs.github.io/node-mssql
MIT License
2.23k stars 467 forks source link

Not able to fetch execution plan by setting `SET SHOWPLAN_XML ON` using mssql #1466

Closed charmi2011 closed 5 months ago

charmi2011 commented 1 year ago

I want to fetch the estimated execution plan of a query using the MSSQL package using Nodejs. As per the document https://learn.microsoft.com/en-us/sql/t-sql/statements/set-showplan-xml-transact-sql?view=sql-server-ver16 If I set the SHOWPLAN_XML to ON before the query execution it will return the execution plan for that query and all other consecutive queries until the SHOWPLAN_XML is set back to OFF for that particular session.

I tried to add the same thing, unfortunately I am not able to retrieve the XML execution plan for the query even after setting the SHOWPLAN_XML as ON.

Expected behaviour:

Running query:

Query 1: SET SHOWPLAN_XML ON Query 2: select top(1) UUID from users where UUID is not null; Query 3: SET SHOWPLAN_XML OFF

Should return the execution XML plan for the SELECT query, but instead returns the actual result for Select Query.

Configuration:

Here is the SAMPLE code file which will show multiple options I tried to make this work but wasn't successful. Can someone please check this and let me know what I am missing here.

const sql = require("mssql");

const config = {
    user: '<username>',
    password: '<password>',
    server: '<server>',
    database: '<database>',
    pool: {
        max: 100,
        min: 1,
        idleTimeoutMillis: 30000
    }
};

const pool = new sql.ConnectionPool(config, err => {    
    if (err) {
        console.error(err);
        return;
    }
});

let request;
async function runQuery(query) {
    try {
        return await request.query(query);
    } catch (err) {
        console.error(err);
    }
}

// Option 1 to run all 3 statements in single query with GO statement to allow the SET SHOWPLAN_XML to be executed in different instance as mentioned in the document.
// Desired OUTPUT is that the query will not be executed and the result will print the XML execution plan,
// but instead the query returns an error "Incorrect syntax near 'GO'."
// async function getData() {
//     const result = await runQuery(`SET SHOWPLAN_XML ON;
//         GO
//         select top(1) UUID from users where UUID is not null;
//         GO
//         SET SHOWPLAN_XML OFF;`);
//     console.log("πŸš€ ~ getData ~ result", result);
// }

// Option 2 to run all 3 statements in single query.
// Desired OUTPUT is that the query will not be executed and the result will print the XML execution plan,
// but instead the query returns an error "The SET SHOWPLAN statements must be the only statements in the batch".
// async function getData() {

//     const result = await runQuery(`SET SHOWPLAN_XML ON;
//         select top(1) UUID from users where UUID is not null;
//         SET SHOWPLAN_XML OFF;`);
//     console.log("πŸš€ ~ getData ~ result", result);

// }

// Option 3 to run all 3 queries individually back to back.
// First set the SHOWPLAN_XML as ON, then run the desired query for which the XML plan is required and then set the SHOWPLAN_XML as OFF.
// Desired OUTPUT is that the query will not be executed and the result will print the XML execution plan, but instead the query is executed and returns the result.
async function getData() {
    const sessionId = await runQuery(`select @@SPID`);
    console.log("πŸš€ ~ getData ~ sessionId", sessionId)

    await runQuery(`SET SHOWPLAN_XML ON`);
    const result = await runQuery(`select top(1) UUID from users where UUID is not null;`);
    console.log("πŸš€ ~ getData ~ result", result);

    await runQuery(`SET SHOWPLAN_XML OFF`);

    const sessionId1 = await runQuery(`select @@SPID`);
    console.log("πŸš€ ~ getData ~ sessionId1", sessionId1);
}

(async () => {
    await pool.connect();
    request = pool.request();

    getData();
})();

Software versions

dhensby commented 1 year ago

Option 1: Syntax error - can't help with that - invalid syntax will result in an error

Option 2: As per error message, you can't combine the queries in that way - can't help with that.

Option 3: The pool is not guaranteed to return the same connection one after another. Therefore running them as 3 consecutive queries is not guaranteed to give you the behaviour you expect.

You will need to run this type of query in a transaction or over a stored procedure so that you can hold onto a single connection and make these queries over a guaranteed connection. Alternatively you can set the pool max to 1; though this still isn't guaranteed to work as connections could be closed and recreated between requests.