Send integer to database with parameterized query - javascript

I'm having trouble sending data other than strings(varchar) to a stored procedure, through an ado parameterized query.
Let's assume I am just trying to send a single int parameter, and there is a stored procedure that accepts a single int. When I run the code I get type mismatch.
If I change the int to a String and the stored procedure to accept varchar it works. Also, the stored procedure works with other code written in VBScript and PHP. So I'm guessing it isn't the stored procedure.
I'm assuming it has something to do with .createParameter line - type or length
or maybe the attributes line a few lines below that. I've tried many different combinations, but to no avail.
Let me know if I need to clean the code up more. Thanks.
command.ActiveConnection = myconn;
command.CommandText = this.procedure;
command.CommandType = 4;
prmCount = 0;
//Build Parmeters
for(var p in this.params){ //<--- I set these params earlier -- they behave as expected
if(this.params.hasOwnProperty(p)){
command.Parameters.Append(
command.CreateParameter(
"",
this.params[p].type, // <-- i've tried pretty much every value for the type - 3, 19, 14, 5, ...
this.params[p].direction, // <-- this is the same for all inputs
this.params[p].length // <-- i send null for this value
)
);
//A Fix for Null String values
if(this.params[p].value === null || this.params[p].value === ""){
command.Parameters(prmCount).Attributes = 64;
}
//Some examples show adding a `.Attributes` value for some integers
//However this didn't help
//
//if(this.params[p].type === 3){
//command.Parameters(prmCount).Attributes = 128;
//}
command.Parameters(prmCount).Value = this.params[p].value; //<-- Set the value here
prmCount+=1;
}
}
record = command.Execute();

Related

Getting JSON data from snowflake stored procedure parameter and inserting it in target table

I have a requirement to receive JSON data in a Stored Proc parameter and insert the same in the snowflake target table (user_json_feedback). JSON Data has three key elements(User, EntityID, and Entity Type), whereas the target table has five columns (User, ID, Entity Type, Region, and Date). The region will have a default value of "NA," and the date will be the current date.
If the inserts are successful, it returns true; otherwise, it returns false.
I am struggling with the syntax and parsing issues here, as I am very new to writing procedures.
Here is what I have been trying to do, which is giving me errors obviously but serves the algorithm of my intent.
CREATE OR REPLACE SP_UPDATE_JSON_DATA (JSON_DATA VARIANT)
RETURNS BOOLEAN
LANGUAGE JAVASCRIPT
EXECUTE AS OWNER
AS
$$
//Declare variables
var REGION = 'NA'
var V_DATE = `select current_date;`;
var DATE_STMT= snowflake.createStatement({sqlText: V_DATE });
var curr_date = DATE_STMT.execute();
var src_json = JSON.parse(JSON_DATA);
var sql_command =
`INSERT INTO user_json_feedback (user,id,etype,region ,date)//
select src_json:USER,src_json:ENTITY_ID,src_json:ENTITY_TYPE,REGION,curr_date;`;
try {
snowflake.execute (
{sqlText: sql_command}
);
return "Succeeded."; // Return a success/error indicator.
}
catch (err) {
return "Failed: " + err; // Return a success/error indicator.
}
$$;
The function call with parameters will be like
call SP_UPDATE_JSON_DATA ('[{"USER":"XYZ","ENTITY_ID":"BMT0001","ENTITY_TYPE":"BMT"},{"USER":"ABC","ENTITY_ID":"BMT0002","ENTITY_TYPE":"BMT"}]');
Thanks in advance for the help!
theres a few things here.
Firstly the step to get current date. curr_date is a result set object. to extract the value and use it later, you need to read the first row with .next() then GetColumnValue to read the column content. to pass it later as a well formatted string you'll wanna convert with .toISOString().
Secondly the parsed json returns an array in this case so you'll need to iterate over the array to insert the individual records. As it's not known ahead of time if the variant will contain an array you're best checking if the parsed json is an array and handle it accordingly
Last tweak was altering the return type so you get the verbose feedback you're expecting from your return calls.
Updated code:
CREATE OR REPLACE TEMPORARY TABLE user_json_feedback
(
user VARCHAR(100)
,id VARCHAR(100)
,etype VARCHAR(100)
,region VARCHAR(100)
,date TIMESTAMP_NTZ
);
CREATE OR REPLACE TEMPORARY PROCEDURE SP_UPDATE_JSON_DATA(JSON_DATA VARIANT)
RETURNS STRING
LANGUAGE JAVASCRIPT
EXECUTE AS OWNER
AS
$$
//Declare variables
var REGION = 'NA'
var V_DATE = `select current_date;`;
var DATE_STMT= snowflake.createStatement({sqlText: V_DATE });
var DATE_STMT_RES = DATE_STMT.execute();
DATE_STMT_RES.next()
var curr_date = DATE_STMT_RES.getColumnValue(1).toISOString();
var src_json = JSON.parse(JSON_DATA);
try {
if (Array.isArray(src_json)){
for (key in src_json){
var sql_command =
`INSERT INTO user_json_feedback (user,id,etype,region,date)//
VALUES(:1,:2,:3,:4,:5)`;
snowflake.execute (
{
sqlText: sql_command,
binds: [src_json[key].USER,src_json[key].ENTITY_ID,src_json[key].ENTITY_TYPE,REGION,curr_date]
}
);
}
}
else {
var sql_command =
`INSERT INTO user_json_feedback (user,id,etype,region,date)//
VALUES(:1,:2,:3,:4,:5)`;
snowflake.execute (
{
sqlText: sql_command,
binds: [src_json.USER,src_json.ENTITY_ID,src_json.ENTITY_TYPE,REGION,curr_date]
}
);
}
return "Succeeded."; // Return a success/error indicator.
}
catch (err) {
return "Failed: " + err; // Return a success/error indicator.
}
$$;
--Need to cast variable string as variant.
--ARRAY example
call SP_UPDATE_JSON_DATA ('[{"USER":"XYZ","ENTITY_ID":"BMT0001","ENTITY_TYPE":"BMT"},{"USER":"ABC","ENTITY_ID":"BMT0002","ENTITY_TYPE":"BMT"}]'::VARIANT);
--Single object example
call SP_UPDATE_JSON_DATA ('{"USER":"JST","ENTITY_ID":"BMT0003","ENTITY_TYPE":"BMT"}'::VARIANT);
SELECT *
FROM user_json_feedback;
Result set:
While all this works, you may well be better served just inserting the whole variant into a table and relying on snowflake's significant semi-structured data querying capabilities. Certainly for large payloads you'll find much better performance from bulk loading to a variant column in a table then parsing in a view.

Attempting to compare a standalone variable to each value in an array

I am writing a script to use on a Google Sheet where I am accepting user input and comparing it to the values already present on the spreadsheet.
In the function below I have retrieved the users desired number with the variable voltReqed.
I am attempting to compare it (detect less than or greater than) to each value in the array generated with the variable voltCompareData.
I am quite new to scripts, please excuse my ignorance.
function getVoltage() {
var voltReqed = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2").getRange("A1").getValue();
Logger.log(voltReqed);
var voltCompareData = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SpecsList").getSheetValues(3, 2, 1, 3);
Logger.log(voltCompareData);
var voltSample1 = voltCompareData.getValue(1,1);
Logger.log(voltSample1);
}
The variable voltSample1 was me attempting to pull the first number out of the array generated by the voltCompareData variable for comparison operations.
Please see the image for the number and array retrieved and logged by the above script.
Comparing data to variable
function getVoltage() {
var voltReqed = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2").getRange("A1").getValue();
var vs = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SpecsList").getSheetValues(3, 2, 1, 3).flat();
vs.forEach(e => {
if(e == v) {
//do someting
}
});
}

get a range of numbers from sql column of type varchar

I have a postgres server running with one column (say marks) of type VARCHAR(255), but is supposed to have numbers, like if i do a select *.. query , i will get ['100','50','21','14'...] etc.
i would like to run a range query on it, like user passes [10,30] and gets ['21','14'] as result. I think this would require casting at the time of running the BETWEEN query, but i cannot get it to work properly.
I am using sequalize.js which is generating the following query:
SELECT "id"
FROM "token_attributes" AS "token_attributes"
WHERE "token_attributes"."attributesDirectoryId" = 3
AND CAST('token_attributes.attributeValue' AS INTEGER) BETWEEN 10 AND 30;
on server also this query seems to fail. the sequalize query that is being created is :
{
where: {
attributesDirectoryId: 3,
attributeValue: Where { attribute: [Cast], comparator: '=', logic: [Object] }
},
attributes: [ 'id' ]
}
i have used the following code to create the where condition (cast and where were imported from sequelize):
let whereFilter ={}
let value = where(cast(`${tableName}.attributeValue`, 'integer'), {[Op.between]: rangeAsInt})
whereFilter['attributeValue'] = value
so this is basically calling table.findAll({where:whereFilter}) I am not sure how to either make sequelize create a correct sql api or what the actual correct SQL api would be. can anyone help?
found the issue, i missed the sequilize.col function :
let whereFilter ={}
let value = where(cast(col(`${tableName}.attributeValue`), 'integer'), {[Op.between]: rangeAsInt})
whereFilter['attributeValue'] = value
and the query would be :
SELECT "id"
FROM "token_attributes" AS "token_attributes"
WHERE "token_attributes"."attributesDirectoryId" = 3
AND CAST("token_attributes"."attributeValue" AS INTEGER) BETWEEN 10 AND 30;

Populating table with textbox value from previous HTML page

I have some JS that stores the name and value of selected checkboxes on one page and then, on a button click, adds this data to a table on page 2.
This works, but now I am looking to do the same for a textbox containing a number. Specifically, I'm looking to take the value entered by the user and add this to a cell in the table. What would be the best way to approach this? Add to the existing function or create a separate on button click function specifically for the textbox value?
I have added a screenshot of the HTML table on page 2 along with where I would like the textbox value to go (highlighted with a red rectangle).
Here's what I have so far:
HTML for textbox (page 1):
<div class="selecttier">
<h1>5. Number of Clicks</h1>
<input id="numberofclickstextbox" name="numberofclicks" type="text" value="0" data-total="0" oninput="calculatetier()" />
</div>
JS on page 1:
$('#sales_order_form_button').click(function() {
let table_info = [];
$('input[type=checkbox]').each(
function(index, value) {
if($(this).is(':checked')) {
table_info.push(
{
name: $(this).attr('name'),
value: $(this).attr('value'),
}
);
}
});
let base64str=btoa(JSON.stringify(table_info));
window.location = "page2.html?table_data=" + base64str;
});
JS on page 2:
// Helper function
function getUrlParameter(name) {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
var results = regex.exec(location.href);
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
};
// actual code
let table_data = getUrlParameter('table_data');
let data_from_page_1 = JSON.parse(atob(table_data));
for(let i = 0; i < data_from_page_1.length; i++){
let row = $("<tr></tr>");
let recordName = $("<td></td>").text(data_from_page_1[i].name);
let recordValue = $("<td></td>").text(data_from_page_1[i].value);
row.append(recordName, recordValue);
$('#output_table').append(row);
}
// code to sum CPC column
var sum1 = 0;
$("#output_table tr > td:nth-child(2)").each(
(_,el) => sum1 += Number($(el).text()) || 0
);
$("#sum1").text(sum1);
//datetime stamp
var dt = new Date();
document.getElementById("datetime").innerHTML = dt.toLocaleString();
Output HTML table (page 2):
<table id="output_table">
<tr>
<th>Name</th>
<th>Value</th>
<th>Number of Clicks</th>
</tr>
<tfoot>
<tr>
<th id="total" colspan="1">Total CPC:</th>
<td id="sum1"></td>
</tr>
</tfoot>
</table>
As stated in the #Manu Varghese comment, the way to go would be using sessionStorage or localStorage.
First, let's differentiate both. According to the Stack Overflow question "HTML5 Local storage vs Session Storage", we have the following answer:
localStorage and sessionStorage both extend Storage. There is no difference between them except for the intended "non-persistence" of sessionStorage.
That is, the data stored in localStorage persists until explicitly deleted. Changes made are saved and available for all current and future visits to the site.
For sessionStorage, changes are only available per tab. Changes made are saved and available for the current page in that tab until it is closed. Once it is closed, the stored data is deleted.
Considering they are used the same way and you must to choose between what better fits your case, I will proceed using sessionStorage.
For that, in the first page you must use:
sessionStorage.setItem("key", "value")
You may set the item right when you perceives a change, like in the input 'blur' event.
and when you land in the second page (right when jQuery calls its start event), you will retrieve your data using:
sessionStorage.getItem("key")
Take in mind that localStorage/sessionStorage can support a limited amount of data. Even if that limit is way bigger than URL, most browsers will store only 2.5MB to 10MB per origin, according to the browser implementation (you may test by yourself in the link recommended in MDN (Mozilla Development Network), http://dev-test.nemikor.com/web-storage/support-test/).
Also, you may want to avoid storing sensitive data in the storages, due to some some discussions about security, which seems not to be a complaint here.
Implementation in the given case
Your code can be modified in three steps:
Change the way you save the data to use the storage
Creates a JSON of an object containing the array, instead the make the JSON using the array itself. Then you can add more fields.
Load the JSON object and its fields (the array and the number).
Step 1 - Changing to sessionStorage
Just now you have your Javascript on page 1 creating an array of data and stringifying that data to a JSON string.
If you want to use the storage rather than the URL for all the data, just change these lines of code from:
let base64str=btoa(JSON.stringify(table_info));
window.location = "page2.html?table_data=" + base64str;
to the code that will save the data into a (local/session)Storage:
let jsonStr=JSON.stringify(table_info); // converts to JSON string
sessionStorage.setItem("oldData", jsonStr); // save to storage
window.location = "page2.html"; // navigate to other page
Notice that the storage can receive any string, but only strings, then we can remove the btoa function, but we must keep the stringify.
Step 2 -- Adding more data to save
Now you have one JSON that is an array of items. But what do you want is to include one more field, parallel to this array. Of course, you can't include it in the array, as it is a different thing. So, what we must to do is to create a JSON object which has a number field AND the array field itself.
Your function to create the array is all ok, then we will use the same "table_data" as the array and include it to a new JSON object:
let table_data = []; // the array you have
$('input[type=checkbox]').each(
... rest of code ...
); // the function that creates the array (I abbreviated it here)
// Creates an object with an array and a number
let jsonObj = {
table_data: table_data,
number_of_clicks: theNumberYouHave/* your variable with the number here */
};
// This is the bit above with CHANGES into variable names
// Instead of "table_data", now we save "jsonObj"
let jsonStr=JSON.stringify(jsonObj); // converts the "jsonObj" to a JSON string
sessionStorage.setItem("oldData", jsonStr);
window.location = "page2.html";
Remember to change "theNumberYouHave" to whatever your variable with the number is called. The you will append the number as a field of the JSON object.
In other words, this simply will create an structure like that:
{
number_of_clicks: 5216,
table_data: [
{ name: "...", value: "..."},
{ name: "...", value: "..."},
{ name: "...", value: "..."},
...
]
}
See? Your table_data is still there, but with a new sibling (number_of_clicks) inside an object.
Step 3 -- Loading data from page 1
For now, you have these two lines of code in page 2 to read data from page 1:
let table_data = getUrlParameter('table_data');
let data_from_page_1 = JSON.parse(atob(table_data));
What do you need there, is to simply replace the getUrlParameter function to read from the storage, and remove the atob function to reflect the changes we made in page 1, this way:
let jsonObj = sessionStorage.getItem("oldData"); // reads the string
let data_from_page_1 = JSON.parse(jsonObj); // parse the JSON string
let table_data = data_from_page_1.table_data; // grab the table data
let number_of_clicks = data_from_page_1.number_of_clicks; // grab the number
Now you are free to use the variable "table_data" like you did, and to use the "number_of_clicks" in the way you want to use it. It is the number passed from page 1, then you may set it to your table cell.
You have it with the unique ID "sum1", the you may just:
$("#sum1").text(number_of_clicks);
And you are done!
I highly recommend localStorage and sessionStorage to be used, as per this and this
Page 1 code full source
$('#next_page_button').click(function(){
let table_info = [];
// Do for checkboxes
$('.campaignstrategy input[type=checkbox]').each(
function(index, value){
if($(this).is(':checked')){
table_info.push(
{
name: $(this).attr('name'),
value: $(this).attr('value'),
type: 'checkbox'
}
);
}
});
$('.campaignstrategy input[type=text]').each(
function(index, value){
table_info.push(
{
name: $(this).attr('name'),
value: $(this).attr('value'),
type: 'text'
}
);
});
let base64str=btoa(JSON.stringify(table_info));
window.location = "page2.html?table_data=" + base64str;
});
Page 2 Code full source
// Helper function
function getUrlParameter(name) {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
var results = regex.exec(location.href);
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
};
// actual code
let table_data = getUrlParameter('table_data');
let data_from_page_1 = JSON.parse(atob(table_data));
// clear table
$('#output_table').html("");
// generator checboxes
for(let i=0;i<data_from_page_1.length;i++){
if(data_from_page_1[i].type == "checkbox"){
let row = $("<tr></tr>");
let recordName = $("<td></td>").text(data_from_page_1[i].name);
let recordValue = $("<td></td>").text(data_from_page_1[i].value);
let recordCount = $("<td></td>").text("");
row.append(recordName, recordValue, recordCount); // not used but needed
$('#output_table').append(row);
}
}
// generate textboxes
for(let i=0;i<data_from_page_1.length;i++){
if(data_from_page_1[i].type == "text"){
let row = $("<tr></tr>");
let recordName = $("<td></td>").text("");
let recordValue = $("<td></td>").text("");
let recordCount = $("<td></td>").text(data_from_page_1[i].value);
row.append(recordName, recordValue, recordCount);
$('#output_table').append(row);
}
}
ANSWER:
What would be the best way to approach this?
window.localStorage - stores data with no expiration date
window.sessionStorage - stores data for one session

resolving a javascript and database table logic situation

When I query a database table, I get back values "yes" or "no" for records that represent whether an item is present or not (the item is the column name). I want to create a string that represents the products that are available by name (rather than what I am doing now "kitchen table =" + kitchenTable;
I am thinking this can be solved (poorly) by a series of if statements setting variables to either the product name or to "" and then include all variables in the string
var kt;
if (kitchenTable == yes) kt = "kitchen table";
else kt = "";
if (kitchenCabinet == yes) kc = "kitchen cabinet";
else ka = "";
output = kt + ', ' + kc;
There are about 50 items that can be presented to the user, is there a more efficient way of accomplishing this task?? One option is to change how values are entered into the datbase table such that instead of yes, its the item name but this seems like a poorer way to resolve the issue
Of course you don't give all the details about how do you make query so that is an imaginary mockup of a function simulating query
var available = [];
var result = query("kitchen table");
result === "yes" && ( available.push("kitchen table") );
......
var output = available.join();
What you want is actually built into javascript itself.
I would say using an object literal will really simply your life in this situation by organizing your code and turning it into a more readable format.
I would also recommend turning your server data into true and false as this is a standardized way to communicated a Boolean and allows for the method below to work as it does:
// From server response
var results = {
kitchenCabinet: true,
kitchenTable: true
}
// Use this for your storage of all related items
var kitchenProps = {
kitchenCabinet: 'kitchen cabinet',
kitchenTable: 'kitchen table'
}
// Reuse this function for each time your need a new category (masterBathroomProps...)
function getItemDataIfExists(results, hashTable){
'use strict';
var output = 'Your total is: ';
for (var item in results) {
if (!results.hasOwnProperty(item)) return;
if (results[item]) output += 'A '+hashTable[item]+' ';
}
return output;
}
getItemDataIfExists(results, kitchenProps);
Explanation:
You loop through a result set of an object containing keys names and true false values. In the loop, if the keyname's value is true, then use that keyname to access the properties (in this case a string of your choice. The "key" here is that the key names in each object must line up.
Here is a live demo:
http://codepen.io/nicholasabrams/pen/JXXbYz?editors=0010

Categories

Resources