How to dynamically generate SQL query column names and values from arrays? - sql

I have about 20 columns in one row and not all columns are required to be filled in when row created also i dont want to cardcode name of every column in SQL query and on http.post request on frontend. All values are from form. My code:
var colNames, values []string
for k, v := range formData {
colNames = append(colNames, k)
values = append(values, v)
}
Now i have 2 arrays: one with column names and second with values to be inserted. I want to do something like this:
db.Query("insert into views (?,?,?,?,?,?) values (?,?,?,?,?,?)", colNames..., values...)
or like this:
db.Query("insert into views " + colNames + " values" + values)
Any suggestions?
Thanks!

I assume your code examples are just pseudo code but I'll state the obvious just in case.
db.Query("insert into views (?,?,?,?,?,?) values (?,?,?,?,?,?)", colNames..., values...)
This is invalid Go since you can only "unpack" the last argument to a function, and also invalid MySQL since you cannot use placeholders (?) for column names.
db.Query("insert into views " + colNames + " values" + values)
This is also invalid Go since you cannot concatenate strings with slices.
You could fromat the slices into strings that look like this:
colNamesString := "(col1, col2, col3)"
valuesString := "(val1, val2, val3)"
and now your second code example becomes valid Go and would compile but don't do this. If you do this your app becomes vulnerable to SQL injection and that's something you definitely don't want.
Instead do something like this:
// this can be a package level global and you'll need
// one for each table. Keep in mind that Go maps that
// are only read from are safe for concurrent use.
var validColNames = map[string]bool{
"col1": true,
"col2": true,
"col3": true,
// ...
}
// ...
var colNames, values []string
var phs string // placeholders for values
for k, v := range formData {
// check that column is valid
if !validColNames[k] {
return ErrBadColName
}
colNames = append(colNames, k)
values = append(values, v)
phs += "?,"
}
if len(phs) > 0 {
phs = phs[:len(phs)-1] // drop the last comma
}
phs = "(" + phs + ")"
colNamesString := "(" + strings.Join(colNames, ",") + ")"
query := "insert into views " + colNamesString + phs
db.Query(query, values...)

Related

How to change equal (=) to not equal to (<>) in postgres using Go

I have two sql querys that are very similar. The only difference is that in one of the WHERE clauses I am using equal to (=) instead of not equal to (<>). Is there a way to modify the query programmatically in an elegant way? I am looking for something like this:
func getEvents(name string, exclude bool) {
q := `SELECT * FROM events WHERE name`
if exclude {
q = q + " <> "
} else {
q = q + " = "
}
q = q + "$1"
result, err := DBQuery(q, name)
...
}
One elegant way would be to create a type and constants for the operators:
type Op string
const (
OpEqual Op = "="
OpNotEqual Op = "<>"
OpLessThan Op = "<"
OpGreaterThan Op = ">"
// ... any other ops you need
)
And then building the query string is a simple string concatenation:
q := "SELECT * FROM events WHERE name" + op + "$1"
Or you may use fmt.Sprintf():
q := fmt.Sprintf("SELECT * FROM events WHERE name %s $1", op)
Of course change signature of getEvents() to:
func getEvents(name string, op Op)
And calling getEvents() is now even nicer:
getEvents("error", OpEqual)
getEvents("error", OpNotEqual)
Try the examples on the Go Playground.

Using List inside a postgres Query

i have a dynamic list.
list=['a','b','c','d' ..... ] so length may change
i want to compare these list values in a query
select * from student where name in(all the list values);
i want to pass list values into this query
how i can do this.. ??? please help
Thank you
In Postgres, you can use arrays. The syntax in the where clause looks like:
where name = any (array[1, 2, 3])
or
where name = any (list_parameter)
You can write a function that gets a list as a parameter and return a string like "'one', 'two','three'".
// need a string like this 'one', 'two'
private String arrayToSqlInChecker(List<String> loc_list) {
StringBuilder value = new StringBuilder("");
for (int i = 0; i < loc_list.size(); i++) {
value.append("'" + loc_list.get(i) + "'");
if (i != loc_list.size() - 1) {
value.append(",");
}
}
return value.toString();
}
And then you have to append this string into you PostgreSQL IN query
"id IN (" + this.arrayToSqlInChecker(loc_list) + ")"
You can also handle null or empty values in the function

Checking if GroovyRowResult field is empty string

I am using sql.firstRow to check if a row exists in the postgres database based on some criteria.
def cur = sql.firstRow(r, '''
SELECT "some_thing"
FROM "my_table"
WHERE "customer_name" = :customer_name
AND "sad_date" = :sad_date
AND "forgiver" = :forgiver
''')
I find that this works:
if (cur){
log.debug("Found Some thing " + cur["some_thing"])
log.debug("Cur: " + cur.keySet())
}
however this lets in any rows that don't have some_field inside it.
ISSUE
To avoid this, when we try and check for the existance of a non empty value for some_field on the result row like this:
if (cur && "${cur.some_thing}" ){
log.debug("Found Some thing " + cur["some_thing"])
}
ERROR
I get an error suggesting that:
No signature of `String.positive` for argument types for the given type.
I have read this question and changed from cur.some_thing and cur['some_thing'] to "${cur.some_thing}" but the error does not go away
I have also tried this post and tried to use cur.getProperty("some_thing") and it still throws the same error.

DataContext.ExecuteQuery<object> returns object {}

I'm trying to write function for selecting optional columns in linq(columns that may not exist). The problem is in linq like this:
using (DataDataContext db = new DataDataContext()){
var collection = from t in table
select new
{
Nonoptional = t.A;
Optional = IsInDB("table","B") ? t.B : -1; //this is optional column
}}
Unfortunately, this won't work because the fragment near Optional will be translated to case statement and error arises that column not exists.
So i decided to "cover" it with function:
using (DataDataContext db = new DataDataContext()){
var collection = from t in table
select new
{
Nonoptional = t.A;
Optional = IsInDB("table","B") ? OptionalColumnValue<int>("table","B","id_table",t.id_table) : -1; //this is optional column
}}
I want this function to be universal. It should work like that" If there is no value or column is nullable and value is null then return default value for type.
I came up with something like this:
//table,column - obvious,id_column - PK column of table, id - id of currently processing record
public static T OptionalColumnValue<T>(string table,string column,string id_columm,int id) T t = default(T);
DataDataContext db = new DataDataContext();
IEnumerable<object> value = db.ExecuteQuery<object>("select " + column + " from " + table + " where " + id_columm + " = " + id.ToString());
List<object> valueList = value.ToList();
if (valueList.Count == 1)//here is the problem
t = (T)valueList.First();
return t;
}
When there is null value db.ExecuteQuery return something like object{}. I'm assuming this is "empty" object,with nothing really in there. I was thinking about checking for "emptiness" of this object( BTW this is not DBull).
When i realised that this is no way either with concrete value in this column(it cannot cast it to return correct type), then I tried db.ExecuteQuery<T>. Then concrete value - OK, null - Exception.
I thought, maybe Nullable<T> as return value. Nop, because string also can be T.
I don't know what to do next. Maybe there's another solution to this problem.

Get value from Titanium Appcelerator db column

I've looked around everywhere, but I can't seem to find exactly what I'm trying to do. It should be fairly simple...
I have a db table set up like this:
var db = Ti.Database.open('playerInfo.db');
db.execute('CREATE TABLE IF NOT EXISTS playersTable (id INTEGER PRIMARY KEY, name TEXT NOT NULL, "50" INTEGER, "25" INTEGER )');
I have two buttons with an assigned value of 25, and 50, respectively. Each button has a "value" key, where I assign their values. I am trying to accomplish three things:
When a button is pressed, find the column of corresponding value.
increase the value of this column by 1.
Retrieve the new value and console log it.
This is what my code looks like when a button is pressed:
var rows = db.execute("SELECT '" + button.value + "' FROM playersTable WHERE name= '" + owner + "'");
var imagesString = rows.fieldByName(button.value);
Ti.API.debug(imagesString)
This is all in a click event listener where the variable "owner" is passed in as a string.
This is the error I get:
message = "Attempted to access unknown result column 25";
I don't have too much experience with sql, so I'm not sure what I'm doing right and what I'm doing wrong. Any help is appreciated!
Thanks.
I'm not sure quite exactly what the problem is, but the following works for me. Note that the "?" variable substitution syntax makes sure that the values are quoted properly for MySQL:
button = e.source;
db = Titanium.Database.open('test');
var rows = db.execute("SELECT * FROM playersTable WHERE name= ?", "foo");
// Theoretically this should be returning a single row. For other results,
// we would loop through the result set using result.next, but here just check if
// we got a valid row.
if (rows.isValidRow()) {
var imagesString = rows.fieldByName(button.value);
var id = rows.fieldByName('id');
imagesString = imagesString + 1;
Ti.API.info("id = " + id + " val = " + imagesString);
// The ? substitution syntax doesn't work for column names, so we
// still need to stick the button value into the query string.
db.execute('UPDATE playersTable set "' + button.value +'"= ? where id = ?', imagesString, id);
}
else
{
Ti.API.info("Row not found.");
}
db.close();
If you get the row not found error, it's possible your data isn't getting inserted properly in the first place. Here's how I inserted my test row for player "foo":
db.execute('insert into playersTable (name, "50", "25") values (?,?,?)', 'foo', 0, 0);
I think this should solve your problem. Let me know if this doesn't work for you.