This is an experimental chat participant that uses Copilot to query your Sql Server database. It is based on great work done by @robconery on https://github.com/microsoft/copilot-pg
- This is my first foray into Node so if you see anything agregious that's why, positive criticism is welcomed.
This extension looks for a .env file in your project root, which should have a DATABASE_URL setting, pointing toward the database you want to use. For example:
DATABASE_URL="Server=localhost;Database=chinook;User Id=<user>;Password=<password>;Encrypt=false;"
The entire dbo schema of your database will be loaded up to Copilot for each query. This is only for the prompt; we're not storing anything.
The query can be plain English, like so (using the Chinook database):
@ssab Show all albums in 'Metal' genre
The names of the tables and any literal values should be cased properly and accurately named, otherwise Copilot will hallucinate the returns to best fit what you need.
Using the prompt above, you should see a message explaining the query and some actual SQL:
SELECT a.AlbumId, a.Title, a.ArtistId
FROM Album a
JOIN Track t ON a.AlbumId = t.AlbumId
JOIN Genre g ON t.GenreId = g.GenreId
WHERE g.Name = 'Metal';This is an actual Copilot response, and it was able to be this accurate because we sent along the schema in the background.
Let's do another one, looking for albums by AC/DC:
@ssab Show all albums by 'Metallica'
Copilot's response:
SELECT Album.Title
FROM Album
JOIN Artist ON Album.ArtistId = Artist.ArtistId
WHERE Artist.Name = 'Metallica';@ssab Show the total sales amount for each genre, along with the genre's name.
Copilot's response:
SELECT
g.Name AS GenreName,
SUM(il.UnitPrice * il.Quantity) AS TotalSalesAmount
FROM
Genre g
JOIN
Track t ON g.GenreId = t.GenreId
JOIN
InvoiceLine il ON t.TrackId = il.TrackId
GROUP BY
g.Name
ORDER BY
TotalSalesAmount DESC;You can have copilot do all kinds of thing with your database schema. Need a repository for a given table (yeah, I know, just go with it):
@ssab create a repository for the albums table using Node. Do it in a single file for an example that's easy to copy and paste.
And you should see something like this:
const sql = require('mssql');
const config = {
user: 'your_username',
password: 'your_password',
server: 'your_server',
database: 'your_database',
options: {
encrypt: true, // Use encryption if required
enableArithAbort: true
}
};
class AlbumRepository {
async getAllAlbums() {
try {
let pool = await sql.connect(config);
let result = await pool.request().query('SELECT * FROM Album');
return result.recordset;
} catch (err) {
console.error('SQL error', err);
}
}
async getAlbumById(albumId) {
try {
let pool = await sql.connect(config);
let result = await pool.request()
.input('AlbumId', sql.Int, albumId)
.query('SELECT * FROM Album WHERE AlbumId = @AlbumId');
return result.recordset[0];
} catch (err) {
console.error('SQL error', err);
}
}
async createAlbum(title, artistId) {
try {
let pool = await sql.connect(config);
let result = await pool.request()
.input('Title', sql.NVarChar(160), title)
.input('ArtistId', sql.Int, artistId)
.query('INSERT INTO Album (Title, ArtistId) VALUES (@Title, @ArtistId)');
return result.rowsAffected;
} catch (err) {
console.error('SQL error', err);
}
}
async updateAlbum(albumId, title, artistId) {
try {
let pool = await sql.connect(config);
let result = await pool.request()
.input('AlbumId', sql.Int, albumId)
.input('Title', sql.NVarChar(160), title)
.input('ArtistId', sql.Int, artistId)
.query('UPDATE Album SET Title = @Title, ArtistId = @ArtistId WHERE AlbumId = @AlbumId');
return result.rowsAffected;
} catch (err) {
console.error('SQL error', err);
}
}
async deleteAlbum(albumId) {
try {
let pool = await sql.connect(config);
let result = await pool.request()
.input('AlbumId', sql.Int, albumId)
.query('DELETE FROM Album WHERE AlbumId = @AlbumId');
return result.rowsAffected;
} catch (err) {
console.error('SQL error', err);
}
}
}
module.exports = new AlbumRepository();Don't want inline SQL? Ask Copilot to build you a query builder:
@ssab I need a class that wraps the tables and helps build SQL queries using Node. Show examples for all tables.
Notice that I needed to provide explicit directions for language and also that it's for all the tables. You should see something like this:
const sql = require('mssql');
class Database {
constructor(config) {
this.config = config;
this.pool = new sql.ConnectionPool(config);
this.poolConnect = this.pool.connect();
}
async query(queryString) {
await this.poolConnect;
try {
const request = this.pool.request();
const result = await request.query(queryString);
return result.recordset;
} catch (err) {
console.error('SQL error', err);
}
}
async getAlbums() {
return this.query('SELECT * FROM Album');
}
async getArtists() {
return this.query('SELECT * FROM Artist');
}
async getCustomers() {
return this.query('SELECT * FROM Customer');
}
async getEmployees() {
return this.query('SELECT * FROM Employee');
}
async getGenres() {
return this.query('SELECT * FROM Genre');
}
async getInvoices() {
return this.query('SELECT * FROM Invoice');
}
async getInvoiceLines() {
return this.query('SELECT * FROM InvoiceLine');
}
async getMediaTypes() {
return this.query('SELECT * FROM MediaType');
}
async getPlaylists() {
return this.query('SELECT * FROM Playlist');
}
async getPlaylistTracks() {
return this.query('SELECT * FROM PlaylistTrack');
}
async getTracks() {
return this.query('SELECT * FROM Track');
}
}
// Example usage:
const config = {
user: 'your_username',
password: 'your_password',
server: 'your_server',
database: 'your_database',
options: {
encrypt: true, // Use encryption
enableArithAbort: true
}
};
const db = new Database(config);
db.getAlbums().then(albums => console.log(albums));
db.getArtists().then(artists => console.log(artists));You can save time and typing by using slash commands when asking @ssab for help. Here are a few:
/connwill prompt you for the new connection string./outwill set the format of your results tocsv,json, ortext(ascii table)/showshows a list of your tables in the chat window./show [table]will show the details of the table./schemahelps you build your database, either starting from scratch or extending your current schema./fixHave a SQL error you need help with? Try using/fix.
Have an issue or need to report a bug? Help is always appreciated!