Website user can enter search criteria to query orders. User, States, Status, OrderID, etc.
Website communicates with API. Query parameters are in the header, so I assume they come in as strings. API communicates with Access via Dapper.
For some criteria, they can send multiple values. So I want to use an "IN" clause.
where UserID in (150, 3303, 16547)
Dapper handles this nicely.
connection.Query<int>("select * from table where Id in #Ids", new { Ids = new int[] { 1, 2, 3 } });
This works in MS-Access
SELECT top 100 * from Orders where UserID in (150, 30330)
But that only works when the values are ints. String and Strings both give "Data type mismatch in criteria expression" in Access.
SELECT top 100 * from Orders where UserID in ("150", "30330") // two strings
SELECT top 100 * from Orders where UserID in ("150, 30330") // single string
It may be a coincidence, but all the examples I see are integers. Access throws an error on strings if you don't specify the size. Using DynamicParameters makes it easy to specify the size.
But when the field is an int, my dapper code gives the same error (Data type mismatch in criteria expression):
var paramlist = new DynamicParameters();
if ((!string.IsNullOrWhiteSpace(userId)) && userId != "0") {
paramlist.Add("userId", userId, DbType.String, ParameterDirection.Input, 50);
sbWhere.AppendFormat("AND CustFID in (?) ", paramIndex++);
}
So I assume the issue is that I'm telling it that the parameter is a string.
But if I make the parameter an int, then it won't take the string with multiple values. Conversely, if I include the () in the string, it complains about the parens being missing from the 'in' clause.
I tried splitting the string of numbers into an array and/or list.
if ((!string.IsNullOrWhiteSpace(userId)) && userId != "0") {
var userIds = userId.Split(','); //.ToList(); fails, can't map to native type
paramlist.Add("userId", userIds, DbType.String, ParameterDirection.Input, 1000);
if (userIds.Length > 1) {
sbWhere.AppendFormat("AND CustFID in #userId ", paramIndex++);
} else {
sbWhere.AppendFormat("AND CustFID = #userId ", paramIndex++);
}
}
and it gives ": No mapping exists from object type System.String[] to a known managed provider native type." whether I say the parameters are int32 or string.
UPDATE:
There may be multiple search criteria, so I'm using DynamicParameters.
Here is my attempt at implementing Palle Due's idea.
if ((!string.IsNullOrWhiteSpace(userId)) && userId != "0") {
// var userIds = userId.Split(',').Select(i => Int32.Parse(i)).ToList();// fails, can't map to native type
IEnumerable<int> userIds = userId.Split(',').Select<string, int>(int.Parse);
paramlist.Add("userId", userIds, DbType.Int32, ParameterDirection.Input);
if (userIds.Count() > 1) {
sbWhere.AppendFormat("AND CustFID in #userId ", paramIndex++);
} else {
sbWhere.AppendFormat("AND CustFID = #userId ", paramIndex++);
}
}
using (IDbConnection conn = Connection) {
string sQuery = string.Format("SELECT {0} FROM vwweb_Orders {1}", columns, where);
conn.Open();
var result = await conn.QueryAsync<Order>(sQuery, paramlist);
return result.ToList();
}
throws
Message: System.AggregateException : One or more errors occurred. (Failed to convert parameter value from a SelectArrayIterator`2 to a Int32.)
----> System.InvalidCastException : Failed to convert parameter value from a SelectArrayIterator`2 to a Int32.
----> System.InvalidCastException : Object must implement IConvertible.
The github page #Dai links to specifies that the Dapper list support only works with IEnumerable<int>.
But as I understand it your UserID is an int, so I don't get why you try to enter a string. You need to get the string the user has input and convert it to IEnumerable<int>. Do something like this:
IEnumerable<int> userIDs = (userId?? "").Split(',').Select<string, int>(int.Parse);
var result = connection.Query<int>("SELECT TOP 100 * FROM Orders WHERE UserID IN #Ids", new { Ids = userIDs });
You might want to apply some input checking to that, and you might also want to reconsider using Access as the "database" for a website. It's not what it was meant for.
I give up. Dapper should be able to handle this, but it's a newer feature, so...
I just built the IN clause myself.
if (userIds.Count() > 1) {
sbWhere.AppendFormat("AND CustFID in ( ");
int paramCnt = 0;
foreach (int id in userIds) {
sbWhere.AppendFormat("?, "); // Access doesn't mind a trailing ,
paramlist.Add("userId" + paramCnt.ToString(), id, DbType.Int32, ParameterDirection.Input);
paramCnt++;
}
sbWhere.AppendFormat(") ");
} else {
sbWhere.AppendFormat("AND CustFID = ? ");
paramlist.Add("userId", userIds.ToArray<int>()[0], DbType.Int32, ParameterDirection.Input);
}
Related
when i query a single table , i do not want all columns , i just want some column that i interest in.
For example, when i use where method to query a table, it will query all columns in a table like
public class SubjectSpecimenType extends Model {
}
SubjectSpecimenType.where("SUBJECT_ID = ? AND SITE_ID = ?", subjectId, siteId);
i don't know if there has a method named select that i can use to query some column like
SubjectSpecimenType.select("SUBJECT_NAME", "SITE_NAME").where("SUBJECT_ID = ? AND SITE_ID = ?", subjectId, siteId);
there are the source code in LazyList.java
/**
* Use to see what SQL will be sent to the database.
*
* #param showParameters true to see parameter values, false not to.
* #return SQL in a dialect for current connection which will be used if you start querying this
* list.
*/
public String toSql(boolean showParameters) {
String sql;
if(forPaginator){
sql = metaModel.getDialect().formSelect(null, null, fullQuery, orderBys, limit, offset);
}else{
sql = fullQuery != null ? fullQuery
: metaModel.getDialect().formSelect(metaModel.getTableName(), null, subQuery, orderBys, limit, offset);
}
if (showParameters) {
StringBuilder sb = new StringBuilder(sql).append(", with parameters: ");
join(sb, params, ", ");
sql = sb.toString();
}
return sql;
}
when call formSelect method, the second param columns always be null
is there a unfinish TODO ?
When operating on Models, ActiveJDBC always selects all columns, because if you load a model and it has partial attributes loaded, then you have a deficient model. The columns are specified in some edge cases, as in the RawPaginator: https://github.com/javalite/javalite/blob/e91ebdd1e4958bc0965d7ee99e6b7debc59a7b85/activejdbc/src/main/java/org/javalite/activejdbc/RawPaginator.java#L141
There is nothing to finish here, the behavior is intentional.
Is is possible to use updatewithparms() in vertx postgres client to insert data to the using an json array. I tried the following with out any success.
String INSERT_QUERY="INSERT INTO testDb (rid , aid , created_at, expiry_time, strings , strings) VALUES"
private JsonArray preparedParameters(){
//JsonArray params = new JsonArray();
DateTimeFormatter dateTimeFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return new JsonArray()
.add(device.getRecordId().toString())
.add( device.getAccountId())
.add(dateTimeFormat.format(device.getCreatedAt()))
.add(dateTimeFormat.format(device.getExpiresAt()))
.add( device.getEnrollmentId())
.add(device.getHashedEnrollmentId());
}
Then I call functions as follows.
try (SQLConnection connection = connectionResult.result()) {
connection.updateWithParams(INSERT_QUERY,preparedParameters() ,queryRes -> {
if (queryRes.succeeded()) {
booleanFuture.complete(true);
} else {
booleanFuture.complete(false);
}
});
} catch (Exception e) {
booleanFuture.complete(false);
}
I receive the following error.
The query contains 0 parameters but you gave it 6 ("sda","sda",2018-01-22 20:23:26,2018-02-21 20:23:26,"sda","sda")
com.github.mauricio.async.db.exceptions.InsufficientParametersException: The query contains 0 parameters but you gave it 6 ("sda","sda",2018-01-22 20:23:26,2018-02-21 20:23:26,"sda","sda")
Your query is not valid, it misses the parameters.
String INSERT_QUERY="INSERT INTO testDb (rid , aid , created_at, expiry_time, strings , strings) VALUES (?,?,?,?,?,?)"
See the Prepared Statement Updates section of the Vert.x SQL doc.
I am new to android programming, I am doing a simple SELECT with a rawquery and it is giving me an error...
Here's my code
public Cursor getSubCategory(int categoryID){
String select = "SELECT subcategory_name FROM subcategory WHERE id_category = " + categoryID;
return mDb.rawQuery(select, null);
}
As you can see the id_category is an Integer
If anyone has ideas it would be great
Your not using the API to its full advantage there you should use
String select = "SELECT subcategory_name FROM subcategory WHERE id_category = ?"
and then pass in the categoryID to the second argument like
...
String[] arguments = { categoryID.toString() }
return mDb.rawQuery(select, arguments);
...
This should remove SQL injection risks as you are using parameters (the "?").
Apart from that we will need more details about the error to help you further
Using Dapper-dot-net...
The following yields no results in the data object:
var data = conn.Query(#"
select top 25
Term as Label,
Type,
ID
from SearchTerms
WHERE Term like '%#T%'",
new { T = (string)term });
However, when I just use a regular String Format like:
string QueryString = String.Format("select top 25 Term as Label, Type, ID from SearchTerms WHERE Term like '%{0}%'", term);
var data = conn.Query(QueryString);
I get 25 rows back in the collection. Is Dapper not correctly parsing the end of the parameter #T?
Try:
term = "whateverterm";
var encodeForLike = term => term.Replace("[", "[[]").Replace("%", "[%]");
string term = "%" + encodeForLike(term) + "%";
var data = conn.Query(#"
select top 25
Term as Label,
Type,
ID
from SearchTerms
WHERE Term like #term",
new { term });
There is nothing special about like operators, you never want your params inside string literals, they will not work, instead they will be interpreted as a string.
note
The hard-coded example in your second snippet is strongly discouraged, besides being a huge problem with sql injection, it can cause dapper to leak.
caveat
Any like match that is leading with a wildcard is not SARGable, which means it is slow and will require an index scan.
Yes it does. This simple solution has worked for me everytime:
db.Query<Remitente>("SELECT *
FROM Remitentes
WHERE Nombre LIKE #n", new { n = "%" + nombre + "%" })
.ToList();
Best way to use this to add concat function in query as it save in sql injecting as well, but concat function is only support above than sql 2012
string query = "SELECT * from country WHERE Name LIKE CONCAT('%',#name,'%');"
var results = connection.query<country>(query, new {name});
The answer from Sam wasn't working for me so after some testing I came up with using the SQLite CONCAT equivalent which seems to work:
string sql = "SELECT * FROM myTable WHERE Name LIKE '%' || #NAME || '%'";
var data = IEnumerable data = conn.Query(sql, new { NAME = Name });
Just to digress on Sam's answer, here is how I created two helper methods to make searches a bit easier using the LIKE operator.
First, creating a method for generating a parameterized query, this method uses dynamic: , but creating a strongly typed generic method should be more desired in many cases where you want static typing instead of dynamic.
public static dynamic ParameterizedQuery(this IDbConnection connection, string sql, Dictionary<string, object> parametersDictionary)
{
if (string.IsNullOrEmpty(sql))
{
return null;
}
string missingParameters = string.Empty;
foreach (var item in parametersDictionary)
{
if (!sql.Contains(item.Key))
{
missingParameters += $"Missing parameter: {item.Key}";
}
}
if (!string.IsNullOrEmpty(missingParameters))
{
throw new ArgumentException($"Parameterized query failed. {missingParameters}");
}
var parameters = new DynamicParameters(parametersDictionary);
return connection.Query(sql, parameters);
}
Then adding a method to create a Like search term that will work with Dapper.
public static string Like(string searchTerm)
{
if (string.IsNullOrEmpty(searchTerm))
{
return null;
}
Func<string, string> encodeForLike = searchTerm => searchTerm.Replace("[", "[[]").Replace("%", "[%]");
return $"%{encodeForLike(searchTerm)}%";
}
Example usage:
var sql = $"select * from products where ProductName like #ProdName";
var herringsInNorthwindDb = connection.ParameterizedQuery(sql, new Dictionary<string, object> { { "#ProdName", Like("sild") } });
foreach (var herring in herringsInNorthwindDb)
{
Console.WriteLine($"{herring.ProductName}");
}
And we get our sample data from Northwind DB:
I like this approach, since we get helper extension methods to do repetitive work.
My solution simple to this problem :
parameter.Add("#nomeCliente", dfNomeCliPesquisa.Text.ToUpper());
query = "SELECT * FROM cadastrocliente WHERE upper(nome) LIKE " + "'%" + dfNomeCliPesquisa.Text.ToUpper() + "%'";
I'm trying to retrieve a list of orders based on parameters specified by a user (basic search functionality). The user will enter either an orderId or a bunch of other params, those will get wrapped up into a message, and eventually make their way to the method below. My question is, how do I only look at the parameters that actually have values? So if a user were to enter a received date range and a store number and all other fields were null, I want to return orders for stores received in the date range and ignore all the null parameters. At first I was thinking I could use a conjunction, but I can't see a way to ignore the null parameters. Then I started splitting things out into the if statements below the main expression, but I don't want to look at those criteria if the user provides an externalId. Is there a simple way to do this?
public IList<Core.Order> GetOrderByCriteria
(
string ExternalId,
int? Store,
int? Status,
DateTime? beforeTransmissionDate, DateTime? afterTransmissionDate,
DateTime? beforeAllocationProcessDate, DateTime? afterAllocationProcessDate,
DateTime? beforeReceivedDate, DateTime? afterReceivedDate
)
{
try
{
NHibernate.ICriteria criteria = NHibernateSession.CreateCriteria(typeof(Core.Order))
.Add(Expression.Or
(
Expression.Like("ExternalId", ExternalId),
Expression.Conjunction()
.Add(Expression.Between("ReceivedDate", beforeReceivedDate, afterReceivedDate))
.Add(Expression.Between("TransmissionDate", beforeTransmissionDate, afterTransmissionDate))
.Add(Expression.Between("AllocationProcessDate", beforeAllocationProcessDate, afterAllocationProcessDate))
)
);
if(Store.HasValue)
criteria.Add(Expression.Eq("Status", Status));
if(Status.HasValue)
criteria.Add(Expression.Eq("Store", Store));
return criteria.List<Core.Order>();
}
catch (NHibernate.HibernateException he)
{
DataAccessException dae = new DataAccessException("NHibernate Exception", he);
throw dae;
}
}
I wound up dropping the whole conjunction thing and replacing the code in the try block with the code below. I also used joins which reduced the number of db accesses and reduced the amount of code needed.
NHibernate.ICriteria criteria = NHibernateSession.CreateCriteria(typeof(Core.Order));
if (!String.IsNullOrEmpty(ExternalId))
{
criteria.Add(Expression.Like("ExternalId", ExternalId));
}
if (beforeReceivedDate != null && afterReceivedDate != null)
criteria.Add(Expression.Between("ReceivedDate", beforeReceivedDate, afterReceivedDate));
if (beforeTransmissionDate != null && afterTransmissionDate != null)
criteria.Add(Expression.Between("TransmissionDate", beforeTransmissionDate, afterTransmissionDate));
if (beforeAllocationProcessDate != null && afterAllocationProcessDate != null)
criteria.Add(Expression.Between("AllocationProcessDate", beforeAllocationProcessDate, afterAllocationProcessDate));
if (Store.HasValue)
criteria.CreateCriteria("Store", "Store").Add(Expression.Eq("Store.LocationNumber", Store.Value));
return criteria.List<Core.Order>();
I had to do something similar not long ago. I'm pretty sure you can modify this to fit your needs.
private ICriteria AddSearchCriteria(ICriteria criteria, string fieldName, string value)
{
if (string.IsNullOrEmpty(fieldName))
return criteria;
if(string.IsNullOrEmpty(value))
return criteria;
criteria.Add(Expression.Like(fieldName, "%" + value + "%"));
return criteria;
}
The code calling the method ended up looking like this:
var query = session.CreateCriteria(typeof (User));
AddSearchCriteria(query, "FirstName", form["FirstName"]);
AddSearchCriteria(query, "LastName", form["LastName"]);
var resultList = new List<User>();
query.List(resultList);
return resultList;
Leave it up to the function to determine if the input is valid and whether to return the unmodified ICriteria or to add another Expression before returning it.