Updating the google spreedsheet from MS SQL using Google Scripts - sql

I'm trying to connect the MS SQL to a google spreadsheet using google app scripts. here is my app script code
function SQLdb() {
// Replace the variables in this block with real values.
// Read up to 1000 rows of data from the table and log them.
function readFromTable() {
var user = '{usename}';
var userPwd = '{password}';
var database = '{databasename}'
var connectionString = 'jdbc:sqlserver://server.database.windows.net:1433;databaseName='+database;
var conn = Jdbc.getConnection(connectionString , user, userPwd);
var start = new Date();
var stmt = conn.createStatement();
stmt.setMaxRows(1000);
var results = stmt.executeQuery('SELECT TOP 1000 * FROM dbo.dbo');
var numCols = results.getMetaData().getColumnCount();
while (results.next()) {
var rowString = '';
for (var col = 0; col < numCols; col++) {
rowString += results.getString(col + 1) + '\t';
}
Logger.log(rowString)
}
results.close();
stmt.close();
var end = new Date();
Logger.log('Time elapsed: %sms', end - start);
}
readFromTable();
}
Now when I look the log in Script Editor, I can see that this connection to the SQL database is working and the script is able to read all the table cell. But I couldn't able to get that data into the spreedsheet. I'm new to app scripts. So is there something that I'm missing here ?
Any help would be much appricated!

Here's how I do it. Change all the capitalised bits to the appropriate URL, database name, Google Spreadsheet ID, etc.
To this function pass the SQL query you want to execute on the MSSQL database and the name of the sheet within the Spreadsheet that you want to put the data into. This basically fills that named sheet with the query results including column names.
function getData(query, sheetName) {
//jdbc:sqlserver://localhost;user=MyUserName;password=*****;
var conn = Jdbc.getConnection("jdbc:sqlserver://URL;user=USERNAME;password=PASSWORD;databaseName=DBNAME");
var stmt = conn.createStatement();
stmt.setMaxRows(MAXROWS);
var rs = stmt.executeQuery(query);
Logger.log(rs);
var doc = SpreadsheetApp.openById("SPREADSHEETID");
var sheet = doc.getSheetByName(sheetName);
var results = [];
var cell = doc.getRange('a1');
var row = 0;
cols = rs.getMetaData();
colNames = [];
for (i = 1; i <= cols.getColumnCount(); i++ ) {
Logger.log(cols.getColumnName(i));
colNames.push(cols.getColumnName(i));
}
results.push(colNames);
var rowCount = 1;
while(rs.next()) {
curRow = rs.getMetaData();
rowData = [];
for (i = 1; i <= curRow.getColumnCount(); i++ ) {
rowData.push(rs.getString(i));
}
results.push(rowData);
rowCount++;
}
sheet.getRange(1, 1, MAXROWS, cols.getColumnCount()).clearContent();
sheet.getRange(1, 1, rowCount, cols.getColumnCount()).setValues(results);
Logger.log(results);
rs.close();
stmt.close();
conn.close();
}

Here is how i did it. I also made a JDBC connector tool for Mysql & MSSQL. You can adapt this tool from my GitHub Repo Here: Google Spreadsheet JDBC Connector
function readFromTable(queryType, queryDb, query, tab, startCell) {
// Replace the variables in this block with real values.
var address;
var user;
var userPwd ;
var dbUrl;
switch(queryType) {
case 'sqlserver':
address = '%YOUR SQL HOSTNAME%';
user = '%YOUR USE%';
userPwd = '%YOUR PW%';
dbUrl = 'jdbc:sqlserver://' + address + ':1433;databaseName=' + queryDb;
break;
case 'mysql':
address = '%YOUR MYSQL HOSTNAME%';
user = '%YOUR USER';
userPwd = '%YOUR PW%';
dbUrl = 'jdbc:mysql://'+address + '/' + queryDb;
break;
}
var conn = Jdbc.getConnection(dbUrl, user, userPwd);
var start = new Date();
var stmt = conn.createStatement();
var results = stmt.executeQuery(query);
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var sheetTab = sheet.getSheetByName(tab);
var cell = sheetTab.getRange(startCell);
var numCols = results.getMetaData().getColumnCount();
var numRows = sheetTab.getLastRow();
var headers ;
var row =0;
clearRange(tab,startCell,numRows, numCols);
for(var i = 1; i <= numCols; i++){
headers = results.getMetaData().getColumnName(i);
cell.offset(row, i-1).setValue(headers);
}
while (results.next()) {
var rowString = '';
for (var col = 0; col < numCols; col++) {
rowString += results.getString(col + 1) + '\t';
cell.offset(row +1, col).setValue(results.getString(col +1 ));
}
row++
Logger.log(rowString)
}
results.close();
stmt.close();
var end = new Date();
Logger.log('Time elapsed: %sms', end - start);
}

If you don't want to create your own solution, check out SeekWell. It allows you to connect to databases and write SQL queries directly in Sheets from a sidebar add-on. You can also schedule queries to automatically run daily, hourly or every five minutes.
Disclaimer: I made this.

Ok, I finally managed to make it work.
Some few tips: Script is executed at Google's server, so connection must be done over inetrnet, i.e. connect string should be something like "jdbc:sqlserver://172.217.29.206:7000;databaseName=XXXX" and make sure that ip/port mentioned at connect string, can reach you database from outside world. Open port at firewall, make IP forwarding at router and use dyndns or similar services if you do not have a valid domain etc. Sheet ID is the large id string that appeals at your google document's url.

Related

Automatically update my Gsheet with an SQL data base

I need to update a file automatically that already has data in it.
The document is filled with an SQL data base thanks to the code below
However, I want it to update itself everyday without deleting any data that are already in the document and only adding new ones (don't want any duplicates).
function readData(db, queryString) {
//connect to the database
var server = 'your-servername-OR-serverPublicIpAddress';
var username = 'your-sql-username';
var password = 'your-password';
var dbUrl = 'jdbc:sqlserver://' + server + ':1433;databaseName=' + db;
var conn = Jdbc.getConnection(dbUrl, username, password );
//query the data
var stmt = conn.createStatement();
var exec_query = stmt.executeQuery(queryString);
var metaData = exec_query.getMetaData();
var numCols = metaData.getColumnCount();
//save query data to an array
var result=[]; //initiate a blank array
//save the column header
header = []; //initiate the header row
for (var col = 0; col < numCols; col++) {
header.push(metaData.getColumnName(col + 1)); //add the name of each column to the header row
};
result.push(header);//after the header row is formed, put it to the result array
//save the data of each row
while (exec_query.next()) {
row_data = [];
for (var col = 0; col < numCols; col++) {
row_data.push(exec_query.getString(col + 1));//add data of each column to the row data
//Logger.log(row_data);
};
result.push(row_data); // add row data to result
//Logger.log(result);
};
exec_query.close();
return result
};
function pushDataToGoogleSheet(data, SheetName) {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.setActiveSheet(spreadsheet.getSheetByName(SheetName));
var lastRow = sheet.getLastRow();
sheet.getRange(lastRow+1, 1, data.length, data[0].length).setValues(data);
sheet.getDataRange().removeDuplicates();
};
function main() {
db = 'YOUR_DATABASE_NAME'
SQLquery = 'YOUR_SQL_QUERY'
raw_statistics = readData(db, SQLquery); //get raw statistics
pushDataToGoogleSheet(raw_statistics, 'YOUR_SHEET_NAME'); //push to google sheet
};
function main() {
db = 'YOUR_DATABASE_NAME'
SQLquery = 'YOUR_SQL_QUERY'
raw_statistics = readData(db, SQLquery); //get raw statistics
pushDataToGoogleSheet(raw_statistics, 'YOUR_SHEET_NAME'); //push to google sheet
};
However, in the pushDataToGoogleSheet it says that it can't define the length. So, I don't know if I put the right thing for data or not or if there is an issue in my code...
Do you have an idea ?
Thank you for your help !

Cannot add row without complete selection of batch/serial numbers

ERROR: (-4014) Cannot add row without complete selection of batch/serial numbers.
The default function of DI API SaveDraftToDocument() is working fine on MS SQL Database but not SAP HANA.
I am posting the Delivery document with Serial Numbers.
SAPbobsCOM.Documents oDrafts;
oDrafts = (SAPbobsCOM.Documents)oCompany.GetBusinessObject(SAPbobsCOM.BoObjectTypes.oDrafts);
oDrafts.GetByKey(Convert.ToInt32(EditText27.Value));
var count = oDrafts.Lines.Count;
var linenum = oDrafts.Lines.LineNum;
//Validation
#region
var RsRecordCount = (SAPbobsCOM.Recordset)oCompany.GetBusinessObject(SAPbobsCOM.BoObjectTypes.BoRecordset);
var sQryRecordCount = String.Format("Select * from \"SANTEXDBADDON\".\"#TEMPITEMDETAILS\" where \"U_DraftNo\" = '{0}'", EditText27.Value);
RsRecordCount.DoQuery(sQryRecordCount);
#endregion
if (count == RsRecordCount.RecordCount)
{
//LINES
string ItemCode = "", WhsCode = ""; double Quantity = 0; int index = 0;
for (int i = 0; i < oDrafts.Lines.Count; i++)
{
oDrafts.Lines.SetCurrentLine(index);
ItemCode = oDrafts.Lines.ItemCode;
//SERIAL NUMBERS
var RsSerial = (SAPbobsCOM.Recordset)oCompany.GetBusinessObject(SAPbobsCOM.BoObjectTypes.BoRecordset);
string table = "\"#TEMPSERIALS\"";
var sQrySerial = String.Format(
"Select \"U_ItemCode\" , \"U_DistNumber\" from \"SANTEXDBADDON\".\"#TEMPSERIALS\" where " +
"\"U_DraftNo\" = '{0}' and \"U_ItemCode\" = '{1}'", EditText27.Value, ItemCode);
RsSerial.DoQuery(sQrySerial);
int serialindex = 1, lineindex = 0;
#region
if (RsSerial.RecordCount > 0)
{
while (!RsSerial.EoF)
{
//OSRN SERIALS
var RsSerialOSRN = (SAPbobsCOM.Recordset)oCompany.GetBusinessObject(SAPbobsCOM.BoObjectTypes.BoRecordset);
var sQrySerialOSRN = String.Format(
"Select * from OSRN where \"DistNumber\" = '{0}' and \"ItemCode\" = '{1}'"
, RsSerial.Fields.Item("U_DistNumber").Value.ToString(), ItemCode);
RsSerialOSRN.DoQuery(sQrySerialOSRN);
oDrafts.Lines.SerialNumbers.SetCurrentLine(0);
oDrafts.Lines.SerialNumbers.BaseLineNumber = oDrafts.Lines.LineNum;
oDrafts.Lines.SerialNumbers.SystemSerialNumber =
Convert.ToInt32(RsSerialOSRN.Fields.Item("SysNumber").Value.ToString());
oDrafts.Lines.SerialNumbers.ManufacturerSerialNumber =
RsSerialOSRN.Fields.Item("DistNumber").Value.ToString();
oDrafts.Lines.SerialNumbers.InternalSerialNumber =
RsSerialOSRN.Fields.Item("DistNumber").Value.ToString();
oDrafts.Lines.SerialNumbers.Quantity = 1;
if (RsSerial.RecordCount != serialindex)
{
Application.SBO_Application.StatusBar.SetText("INTERNAL NO " + oDrafts.Lines.SerialNumbers.InternalSerialNumber, SAPbouiCOM.BoMessageTime.bmt_Long, SAPbouiCOM.BoStatusBarMessageType.smt_Success);
oDrafts.Lines.SerialNumbers.Add();
serialindex++;
lineindex++;
}
RsSerial.MoveNext();
}
}
#endregion
index++;
}
var status = oDrafts.SaveDraftToDocument();
if (status == 0)
{
oDrafts.Remove();
Application.SBO_Application.StatusBar.SetText("Delivery Posted Successfully !", SAPbouiCOM.BoMessageTime.bmt_Long, SAPbouiCOM.BoStatusBarMessageType.smt_Success);
}
else
{
int code = 0; string message = "";
oCompany.GetLastError(out code, out message);
Application.SBO_Application.SetStatusBarMessage(message, SAPbouiCOM.BoMessageTime.bmt_Medium, true);
}
}`
The error you have posted explains the problem. You are trying to deliver products but have not included all of the serial/batch numbers.
I don't think there's enough information to be sure where this problem happens, but here are some pointers:
You are reading the serial numbers from a custom table. Are the values you are reading valid? For example, could another user have added them to a different order? Could the values be for a different product?
Are you specifying the correct quantity of serial numbers? Is it possible that the item quantity on the line is more than the number of serial numbers you are adding?
Believe the error message until you can prove it's wrong. It doesn't seem like this is a HANA issue (we use HANA extensively) it's a logical problem with the data you are providing.
You may want to capture more debugging information to help you if you can't easily identify where the problem is.

How to escape _ wildcard within Google app script sql?

The function to run a standard sql query within the app script throws up an error when _is used within the sql. It is used within the condition filter to look for all names with _x_. Backslashes break the app script when used.
Within Google Apps Script: var sql1 = 'sql string';
Within sql: WHERE lower(name) like "%\_x_\%"
Update: I managed to find a workaround using REGEXP_CONTAINS(LOWER(name), r"(_x_)" but am still interested to know if it works with the regular LIKE clause.
I reproduced your case using a modified sample code from the documentation.
I queried against a sample dataset using where like "%_". Then, I write the results in a Google spreadsheet.The table I am querying in BigQuery is:
Row id
1 _id_1212
2 id1212
The code I am using is below:
/**
* Runs a BigQuery query and logs the results in a spreadsheet.
*/
function runQuery() {
// Replace this value with the project ID listed in the Google
// Cloud Platform project.
var projectId = 'project_id';
//modified query
var request = {
query: 'SELECT * from `project_id.dataset.table` where id LIKE "%_id_%";'//it will also work for where like "%\_id\_%",
//configuring the query to use StandardSQL
useLegacySql: false
};
var queryResults = BigQuery.Jobs.query(request, projectId);
var jobId = queryResults.jobReference.jobId;
// Check on status of the Query Job.
var sleepTimeMs = 500;
while (!queryResults.jobComplete) {
Utilities.sleep(sleepTimeMs);
sleepTimeMs *= 2;
queryResults = BigQuery.Jobs.getQueryResults(projectId, jobId);
}
// Get all the rows of results.
var rows = queryResults.rows;
while (queryResults.pageToken) {
queryResults = BigQuery.Jobs.getQueryResults(projectId, jobId, {
pageToken: queryResults.pageToken
});
rows = rows.concat(queryResults.rows);
}
if (rows) {
var spreadsheet = SpreadsheetApp.create('BiqQuery Results');
var sheet = spreadsheet.getActiveSheet();
// Append the headers.
var headers = queryResults.schema.fields.map(function(field) {
return field.name;
});
sheet.appendRow(headers);
// Append the results.
var data = new Array(rows.length);
for (var i = 0; i < rows.length; i++) {
var cols = rows[i].f;
data[i] = new Array(cols.length);
for (var j = 0; j < cols.length; j++) {
data[i][j] = cols[j].v;
}
}
sheet.getRange(2, 1, rows.length, headers.length).setValues(data);
Logger.log('Results spreadsheet created: %s',
spreadsheet.getUrl());
} else {
Logger.log('No rows returned.');
}
}
The output,
id
_id_1212
Both where id LIKE "%_id_%" and where id LIKE "%\_id\_%" work when I set the query to use StandardSQL (useLegacySql: false).
In addition, the error GoogleJsonResponseException: API call to bigquery.jobs.query failed with error: Syntax error: Illegal escape sequence: \_ will be thrown when trying to escape the underscore using a double backslash such as where id LIKE "%\\_id\\_%".

how do I change this google app script to hide sheets columns instead of rows based on column dates

so I'm a long way form a competent programmer, but I do dabble in google sheets scripts.
I previously had a script running on a timer trigger to hide rows based on dates found in column A.
function min() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName("OLD");
var v = s.getRange("A:A").getValues();
var today = new Date().getTime() - 86400000‬
for (var i = s.getLastRow(); i > 1; i--) {
var t = v[i - 1];
if (t != "") {
var u = new Date(t);
if (u < today) {
s.hideRows(i);
}
}
}
}
function max() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName("OLD");
var v = s.getRange("A:A").getValues();
var today = new Date().getTime() + 86400000
for (var i = s.getLastRow(); i > 1; i--) {
var t = v[i - 1];
if (t != "") {
var u = new Date(t);
if (u > today) {
s.hideRows(i);
}
}
}
}
function SHOWROWS() {
// set up spreadsheet and sheet
var ss = SpreadsheetApp.getActiveSpreadsheet(), sheets = ss.getSheets();
for(var i = 0, iLen = sheets.length; i < iLen; i++) {
// get sheet
var sh = sheets[i];
// unhide rows
var rRows = sh.getRange("A:A");
sh.unhideRow(rRows);
}
}
this would be set to run at midnight every night so as to hide all rows not +-1 day from the current date.
I have now switched to using this document primarily through the android app I need to swap the input layout Moving dates from rows and values in columns to the inverse, values are now in rows and the dates are in columns, inputing vertical values would be much quicker....but honestly its more about understanding what im doing wrong thats really motivating my search for a answer.
can anyone help me modify my old script to work on this changed sheet.
my attempts fall flat..eg:
function min() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName("OLD");
var v = s.getRange("1:1").getValues();
var today = new Date().getTime() - 86400000‬
for (var i = s.getLastColumn(); i > 1; i--) {
var t = v[i - 1];
if (t != "") {
var u = new Date(t);
if (u < today) {
s.hidecolumns(i);
}
}
}
}
example spreadsheet:
https://docs.google.com/spreadsheets/d/1EjRIeDPrG_qp1S9QhInl9r3G0cZx_16OvnTXORgA3n4/edit?usp=sharing
there is two sheets one with the old version of my layout called "OLD" and my new desired layout called "NEW"
thanks in advance to anyone able to help
This function hides a column whose top row date is less yesterday.
function hideColumnWhenTopRowDateIsBeforeYesterday() {
var ss=SpreadsheetApp.getActiveSpreadsheet();
var sh=ss.getSheetByName("Sheet1");
var rg=sh.getRange(1,1,1,sh.getLastColumn());
var vA=rg.getValues()[0];
var yesterday=new Date(new Date().getFullYear(),new Date().getMonth(),new Date().getDate()-1).valueOf();
vA.forEach(function(e,i){
if(new Date(e).valueOf()<yesterday) {
sh.hideColumns(i+1);
}
});
}
My Spreadsheet before hiding columns:
My Spreadsheet after hiding columns:

Set resolution when exporting artboards to PNG using script

Sorry that this is so similar to a recent post but I can't find the solution anywhere. I have created a simple script that loops through each artboard in an open illustrator document and exports it as a separate PNG file. All is working well except that I want to set the resolution to 150 dpi and not the default 72 dpi, for production reasons. This is an option that you can set when exporting manually to PNG but I don't seem to be able to set it in the PNG options in the code, although the script runs without errors it ignores the resolution setting. Could someone let me know how to do this, many thanks. Code as follows:
var doc = app.activeDocument;;//Gets the active document
var fileName = doc.name.slice(0, 9);//Gets the G Number
var numArtboards = doc.artboards.length;//returns the number of artboards in the document
var filePath = (app.activeDocument.fullName.parent.fsName).toString().replace(/\\/g, '/');
var options = new ExportOptionsPNG24();
for (var i = 0; i < numArtboards; i++ ) {
doc.artboards.setActiveArtboardIndex( i );
options.artBoardClipping = true;
options.matte = false;
options.horizontalScale = 100;
options.verticalScale = 100;
options.transparency = true;
var artboardName = doc.artboards[i].name;
//$.writeln("artboardName= ", artboardName);
var destFile = new File(filePath + "/" + fileName + " " + artboardName + ".png");
//$.writeln("destFile= ",destFile);
doc.exportFile(destFile,ExportType.PNG24,options);
}
After doing some digging I've found that if you use imageCapture you can set the resolutuion. So new script below. thanks to CarlosCanto for providing this link via the Adobe Forum https://forums.adobe.com/message/9075307#9075307
var doc = app.activeDocument;;//Gets the active document
var fileName = doc.name.slice(0, 9);//Gets the G Number
var numArtboards = doc.artboards.length;//returns the number of artboards in the document
var filePath = (app.activeDocument.fullName.parent.fsName).toString().replace(/\\/g, '/');
var options = new ImageCaptureOptions();
for (var i = 0; i < numArtboards; i++) {
doc.artboards.setActiveArtboardIndex(i);
var activeAB = doc.artboards[doc.artboards.getActiveArtboardIndex()];
options.artBoardClipping = true;
options.resolution = 150;
options.antiAliasing = true;
options.matte = false;
options.horizontalScale = 100;
options.verticalScale = 100;
options.transparency = true;
var artboardName = doc.artboards[i].name;
var destFile = new File(filePath + "/" + fileName + " " + artboardName + ".png");
doc.imageCapture(destFile, activeAB.artboardRect, options);
}