I am trying to query some information from the document db, it used to work earlier when we have id field , I recently replaced with Guid field and when we run the following query it throws the error as follows
Code:
queryString = string.Format(
"SELECT f.keys.Timestamp,f.keys.selectedFieldId FROM {0} f WHERE f.logevent = '{1}' AND f._ts > {2} AND f._ts <= {3}",
DocumentDbRepository.DatabaseId, Enums.Events.ApplicationAuthenticationSuccess, refUnixTime,
currentUnixTime);
ERROR:
"message":"Syntax error, invalid numeric value token '4d5f'."
any help would be appreciated!
If this is the replacement value of {0} in the FROM clause, then it has to conform to a valid identifier (i.e. starts with an alphabetic character or underscore).
Anyways, you really don't need to specify the database Id here, any identifier will do since the collection is always the one you're connected to. In other words, you could write the query as 'SELECT ... FROM f WHERE ...)
I also faced similar issue while using Cosmos DB Java API. It got resolved using SqlQuerySpec APIs
Use DocumentClient.queryDocuments(String collectionLink, SqlQuerySpec querySpec,FeedOptions options)
#param1 and #param2 are placeholder in query string and will be replaced by id and user when we set to SqlParameter. When SqlParameterCollection and query are passed to SQLQuerySpec , it replace placeholder values from query string with corresponding value againt key userd in SqlParameterCollection.
//Create SQLQuerySpec instance as below
String query = "SELECT * FROM mycollections m WHERE w.id =#param1 and w.user
=#param2";
SqlParameterCollection col = new SqlParameterCollection();
SqlParameter sqlParameter1 = new SqlParameter("#param1", id);
SqlParameter sqlParameter2 = new SqlParameter("#param1", user);
col.add(sqlParameter1);
col.add(sqlParameter2);
SQLQuerySpec sqlspec = new SQLQuerySpec(query,col);
FeedOptions options = new FeedOptions()
options.setEnableCrossPartitionKey(true);
List<Documents> results = documentClient.queryDocuments(collectionLink,
sqlSpec,options).getQueryIterable().toList()
Related
I want to insert into a table an array of values if they don't exist, the array is small and would not exceed 10 items, so it is safe to pass in an insert.
How can I execute this code with Dapper? I tried the following but I am getting an error:
const string sqlSymbolsInsert =
#"INSERT INTO Country (Name)
SELECT NewNames.Name FROM (VALUES(#Names)) AS NewNames (Name)
WHERE NOT EXISTS (SELECT 1 FROM Country AS C WHERE C.Name = NewNames.Name);";
await using var cn = new SqlConnection(CONNECTION_STRING);
await cn.ExecuteAsync(sqlSymbolsInsert, new { Names = countries.Select(x => x.Name) });
The error is:
Core Microsoft SqlClient Data Provider: Incorrect syntax near ','.
There is a similar problem on SO, but it is for the IN clause:
dapper "IN" clause not working with multiple values
Is there another way in Dapper to pass an array in my case?
What you are trying to do isn't possible. But instead of the array, you can just use your countries collection. It has a Name property and Dapper will run the query for each item in a suitable collection:
const string sqlSymbolsInsert =
#"INSERT INTO Country (Name)
SELECT #Name WHERE NOT EXISTS
(
SELECT 1
FROM Country
WHERE Name = #Name
)";
await using var cn = new SqlConnection(CONNECTION_STRING);
await cn.ExecuteAsync(sqlSymbolsInsert, countries);
In attempting to do a SQL query (which returns one string and one uniqueidentifier to columns 0 and 1 respectively) I get "Conversion failed when converting from a character string to uniqueidentifier" in my exceptions log. How can I avoid this? I'm assuming the issue is, the datatables columns are not defined, so it's expecting a string and SQL is trying to convert it. The exception is logged. Surprisingly to me the GUID is stored successfully to da[1]. So my program technically works, however I want to clear this exception and to do that I need to understand why it's happening and how to go about fixing it.
da = new DataTable();
da.Clear();
...
string invoiceStatusSQL = #"select status,invoice_id from invoices where acct_id='" + accountid + "'";
command = new SqlCommand(invoiceStatusSQL, cnn);
da.Load(command.ExecuteReader());
You should always parameterise your SQL queries to help prevent SQL injection and avoid problems like you're facing now. See Why do we always prefer using parameters in SQL statements?.
Use SqlParameter to add the parameters to the SqlCommand.
string invoiceStatusSQL = #"select status, invoice_id from invoices where acct_id = #accountId";
command = new SqlCommand(invoiceStatusSQL, cnn);
SqlParameter idParam = new SqlParameter("#accountId", accountid);
command.Parameters.Add(idParam);
da.Load(command.ExecuteReader());
You can also specify the actual database type when creating the parameter, which will reduce any issues you might have with the framework inferring the type incorrectly (although I don't think that would happen in your case for a Guid/UniqueIdentifier). One way to specify the type is shown below.
var p = new SqlParameter
{
ParameterName = "#accountId",
SqlDbType = SqlDbType.UniqueIdentifier,
Value = accountid
};
When trying to execute a SQL request with named parameters on EntityFrameworkCore (2.2) I get the following exception :
InvalidOperationException: No mapping to a relational type can be found for the CLR type 'SqlParameter'.
What I've tried and works but unsatisfactory:
Raw SQL request:
Works but unsafe
Unamed parameters:
Also works but less explicit imo
await context.Database.ExecuteSqlCommandAsync(
"UPDATE dbo.Table SET ActivationDate = #p0, SubscriptionEndTime = #p1 WHERE SerialNumber IN (#p2) AND UserId = #p3;",
DateTime.UtcNow,
DateTime.UtcNow.AddYears(1),
string.Join("','", eqs.Select(e => e.SerialNumber)),
user.Id);
What does not work:
Passing an array of SqlParameters or objects
This is my code:
await context.Database.ExecuteSqlCommandAsync(
"UPDATE dbo.Table SET ActivationDate = #actDate, SubscriptionEndTime = #endDate WHERE SerialNumber IN (#serials) AND UserId = #userId;",
new SqlParameter("#actDate", DateTime.UtcNow),
new SqlParameter("#endDate", DateTime.UtcNow.AddYears(1)),
new SqlParameter("#serials", string.Join("','", eqs.Select(e => e.SerialNumber))),
new SqlParameter("#userId", user.Id));
I don't understand why it isn't working because according to the documentation (link):
This allows you to use named parameters in the SQL query string. context.Database.ExecuteSqlCommandAsync("UPDATE dbo.Posts SET Rating = 5 WHERE Author = #author", new SqlParameter("#author", userSuppliedAuthor));
So how can I use named parameters in my request ?
EDIT :
I don't think the problem comes from the request itself.
The following one results in the same exact Exception:
await context.Database.ExecuteSqlCommandAsync(
"UPDATE dbo.Table SET Name = #name WHERE SerialNumber = 'XXXX'",
new SqlParameter("#name", "testing"));
Found my solution...
The imported SqlParameter was from namespace:
Microsoft.Azure.Documents.SqlParameter
The correct one is:
System.Data.SqlClient.SqlParameter
The method takes a string for the query, and an array of Object [] for the parameters, presumably to avoid SQL Injection.
However nowhere on earth is it documented what you should put into the object array.
There is another question on SO that asks this exact same thing, but the accepted answer doesn't work: When using DbSet<T>.SqlQuery(), how to use named parameters?
I've tried all forms of parameter replacement I can think of and all of them throw an exception. Any ideas?
Would that it were as simple as:
SqlQuery("SELECT * FROM #table", "Users")
Edit: Here are some things I've tried (Exception is an SqlException):
var result = context.Users.SqlQuery<T>("SELECT * FROM #p0 WHERE #p1 = '#p2'",
new SqlParameter("p0", tableName),
new SqlParameter("p1", propertyName),
new SqlParameter("p2", searchQuery));
This gives Must declare the table variable "#p0".
var result = context.Users.SqlQuery<T>("SELECT * FROM {0} WHERE {1} = '{2}'", tableName, propertyName, searchQuery);
This gives Must declare the table variable "#p0".
There is nothing wrong with your query syntax or how do you created and passed in the SqlParameter objects.
Your problem is that you try to use a variable as the table name, what you cannot do (see: Must declare the table variable #table), so you need to manually "template" the table name in your query:
Something like.
var result = context.Users.SqlQuery<T>(
"SELECT * FROM " + tableName + " WHERE #p0 = '#p1'",
new SqlParameter("p0", propertyName),
new SqlParameter("p1", searchQuery));
So I have my SqlDataSource with a SelectQuery defined as follows:
SELECT * FROM table
WHERE UserName IN (#EmployeesIn);
With #EmployeesIn coming from a session variable Session["EmployeesIn"]. During Page_Load I'm taking an ArrayList members and putting the results into a string and setting the session variable:
string employeesIn = "";
foreach (string s in members)
{
employeesIn = employeesIn + "'" + s + "',";
}
employeesIn = employeesIn.TrimEnd(',');
Session["EmployeesIn"] = employeesIn;
Writing the output to the console I can see the value of the parameter #EmployeesIn
#EmployeesIn = 'bob', 'joe'
However, I'm getting zero results back ... and after monitoring from the database level I see the parameter is coming in as:
'''bob'',''joe'''
Then again if I just pass in one employee, I get results back from the SQL as expected and the parameter is passed correctly as just 'bob'. I suppose this is some safety that .NET provides against SQL injection attacks, however what's the safe way around this?
You should absolutely use parameters for this, instead of including the values within the SQL itself. You can just generate the names for the parameters, so if you had three entries you'd generate SQL of:
SELECT * FROM table WHERE UserName IN (#p0, #p1, #p2)
and then fill in those three parameters from the three values.
// Or create the command earlier, of course
List<SqlParameter> parameters = new List<SqlParameter>();
StringBuilder inClause = new StringBuilder("(");
for (int i = 0; i < members.Count; i++)
{
string parameterName = "#p" + i;
inClause.Append(parameterName);
if (i != members.Count - 1)
{
inClause.Append(", ");
}
// Adjust data type as per schema
SqlParameter parameter = new SqlParameter(parameterName, SqlDbType.NVarChar);
parameter.Value = members[i];
parameters.Add(parameter);
}
inClause.Append(")");
// Now use inClause in the SQL, and parameters in the command parameters
I think you have three options:
Comma separated values - you can pass single parameter value as CSVs and split them out in the stored procedure. I don't like this idea ... too many limitations.
XML - you can pass an XML string into the stored procedure and open it as a table using OPENXML. This will give you a table that you can use to do joins, inserts, etc., onto other tables.
Table-Valued Parameters
The better way would be to user your members array to build the query using a parameter list:
SELECT * FROM table
WHERE UserName IN (#EmployeesIn1, #EmployeesIn2, #EmployeesIn3, ... n);
Then loop through your member list, adding the parameters as necessary.
The first thing I noticed was how you're making you're comma demilited list. Try this out:
string employeesIn = string.Join(",", members.Select(x => string.format("'{0}'", x)).ToArray());
As for the parameter, you need to rethink your approach. Have you looked at table value parameters?
SQL Parameters can only represent a single value, you can however pass in multiple parameters such as:
var emps = members.Select( (e,i) => "#EMPLOYEE" + i).ToArray();
string sqlQuery = "SELECT * FROM table WHERE ";
sqlQuery+=string.Format("UserName IN ({0})", string.Join(",", emps));
//add SQL parameters used in query
for (int i = 0; i < members.Count; ++i)
{
parameters.Parameters.Add(new SqlParameter("#EMPLOYEE" + i, members[i]));
}