2

I am working on a project that will upload some records to SQL Server from a node.js program. Right now, this is my approach (inside an async function):

con = await sql.connect(`mssql://${SQL.user}:${SQL.password}@${SQL.server}/${SQL.database}?encrypt=true`);
for (r of RECORDS) {
        columns = `([column1], [column2], [column3])`;
        values = `(@col1, @col2, @col3)`;
        await con
            .request()
            .input("col1", sql.Int, r.col1)
            .input("col2", sql.VarChar, r.col2)
            .input("col3", sql.VarChar, r.col3)
            .query(`INSERT INTO [dbo].[table1] ${columns} VALUES ${values}`);
}

Where records is an array of objects in the form:

RECORDS = [
    { col1: 1, col2: "asd", col3: "A" },
    { col1: 2, col2: "qwerty", col3: "B" },
    // ...
];

This code works, nevertheless, I have the feeling that it is not efficient at all. I have an upload of around 4k records and it takes roughly 10 minutes, it does not look good.

I believe if I can create a single query - instead of wrapping single inserts inside a for loop - with all the record values it will be faster, and I know there is a syntax for reaching that in SQL:

INSERT INTO table1 (column1, column2, column3) VALUES (1, "asd", "A"), (2, "qwerty", "B"), (...);

However I cannot find any documentation from mssql module for node on how to prepare the parameterized inputs to do everything in a single transaction.

Can anyone guide me into the right direction?

Thanks in advance.

3
  • 1
    use bull insert. stackoverflow.com/questions/43663017/… Commented Jul 20, 2020 at 12:56
  • Ok I've tried following the link you sent me but I am getting some errors. There is one complexity with the VarChar(max) fields which I think is sorted out with {length: infinity} and yet I have some column type error. Let me give it more time and get back to you Commented Jul 20, 2020 at 14:45
  • Nevermind, it was my bad, this one did the trick, thank you Joaquin Commented Jul 20, 2020 at 15:11

2 Answers 2

3

Also, very similar to the bulk insert, you can use a table valued parameter.

sql.connect("mssql://${SQL.user}:${SQL.password}@${SQL.server}/${SQL.database}?encrypt=true")
  .then(() => {
    const table = new sql.Table();
    table.columns.add('col1', sql.Int);
    table.columns.add('col2', sql.VarChar(20));
    table.columns.add('col3', sql.VarChar(20));

    // add data
    table.rows.add(1, 'asd', 'A');
    table.rows.add(2, 'qwerty', 'B');

    const request = new sql.Request();
    request.input('table1', table);  

    request.execute('procMyProcedure', function (err, recordsets, returnValue) {  
       console.dir(JSON.stringify(recordsets[0][0]));  
       res.end(JSON.stringify(recordsets[0][0]));  
    });  
  });

And then for the SQL side, create a user defined table type

CREATE TYPE typeMyType AS TABLE
(
   Col1 int,
   Col2 varchar(20),
   Col3 varchar(20)
)

And then use this in the stored procedure

CREATE PROCEDURE procMyProcedure
   @table1 typeMyType READONLY
AS
BEGIN
   INSERT INTO table1 (Col1, Col2, Col3)
   SELECT Col1, Col2, Col3
   FROM @MyRecords
END

This gives you more control over the data and lets you do more with the data in sql before you actually insert.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the reply, I went for Joaquin's approach but I upvoted your answer! Thanks!
This seems like the better approach if you're wanting to have the recordsets returned.
2

As pointed out by @JoaquinAlvarez, bulk insert should be used as replied here: Bulk inserting with Node mssql package

For my case, the code was like:

return await sql.connect(`mssql://${SQL.user}:${SQL.password}@${SQL.server}/${SQL.database}?encrypt=true`).then(() => {
    table = new sql.Table("table1");
    table.create = true;
    table.columns.add("column1", sql.Int, { nullable: false });
    table.columns.add("column2", sql.VarChar, { length: Infinity, nullable: true });
    table.columns.add("column3", sql.VarChar(250), { nullable: true });

    // add here rows to insert into the table
    for (r of RECORDS) {
        table.rows.add(r.col1, r.col2, r.col3);
    }

    return new sql.Request().bulk(table);
});

The SQL data types have to match (obviously) the column type of the existing table table1. Note the case of column2, which is a column defined in SQL as varchar(max).

Thanks Joaquin! I went down on the time significantly from 10 minutes to a few seconds

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.