The following sql (Sql Server 2016) generates an error:
INSERT INTO Requests (EntryDate,Status, AccountID)
VALUES (#EntryDate,#Status,#accountID)";
try
{
DatabaseConnection.Execute(sql,
new
{
DateTime.Now,
Status = (int)Status.New, '''an enum
accountID,
}, this.Transaction);
This generates the error:
Must declare scalar variable #EntryDate.
If I replace #EntryDate with getdate(), this runs OK. Even though it seems that getdate() is not used. Why this one value?
#EntryDate is a parameter of your command. You must supply it as such, even if the value is unused, otherwise SQL Server will look for a variable named #EntryDate (which likewise doesn't exist).
If you write new { DateTime.Now, ... } you are passing an anonymous object with a property named Now, which can't be mapped to the actual parameter. Change DateTime.Now to EntryDate = DateTime.Now so the name matches up. The question doesn't specify which object-relational mapper you are using (Dapper?) but they will all need to pass named parameters somehow.
Related
I have a simple SQL statement query that is executed as command from C# code. It is targetting DB2. I created variables for the server/schemas as follows. It throws error.
private const string DB2Query
= #"SELECT Name as Name FROM {Schema}.Application WHERE ID = ?";
I get this error.
ERROR [37000] [IBM][CLI Driver] CLI0118E Invalid SQL syntax. SQLSTATE=37000
However, I don't get that error when executing from SQL as follows:
SELECT Name as Name
FROM MyServer..FOR3.Application
WHERE ID = 'MOM'
To support this, I tried to also do something like below in code, still throws different error.
private const string DB2Query
= #"SELECT Name as Name FROM {ServerName}..{Schema}.Application WHERE ID = ?";
It throws error on this line of code:
DataApplicationBlockHelper<string>.Get(db, dbCommand, Obj);
UPDATE
I found the culprit. It's not replacing the {Schema} placeholder. When I actually removed that from query and placed the schema name, it worked like a charm. It's a .net thing I believe? Can someone please help how to replace {Schema} with a value fetched from web.config?
While I can't really speak to the syntax of DB2 queries themselves, so I'll rely on your assertion that the query itself should work...
What you have in C# is simply a string and nothing more:
private const string DB2Query = #"SELECT Name as Name FROM {Schema}.Application WHERE ID = ?";
Note that there's no need for the # operator in this string definition, so let's simplify:
private const string DB2Query = "SELECT Name as Name FROM {Schema}.Application WHERE ID = ?";
While this string appears intuitively to have a placeholder that can be replaced with a value, if there's no code which does that anywhere then it won't happen. For that you have a few options. For example, you can use a placeholder that string.Format() understands:
private const string DB2Query = "SELECT Name as Name FROM {0}.Application WHERE ID = ?";
And then later in a method somewhere, when you want to use that string, apply the format value to it:
var sql = string.Format(DB2Query, someVariable);
In this case someVariable (which doesn't even need to be a variable and could be a string literal) would be used to replace the placeholder in the string.
Or, if you want to keep the named placeholder, you can potentially replace it manually:
private const string DB2Query = "SELECT Name as Name FROM {Schema}.Application WHERE ID = ?";
and later in a method:
var sql = DB2Query.Replace("{Schema}", someVariable);
This would observably accomplish the same thing, perhaps with an extremely minor performance difference.
You could also take advantage of both approaches by using the more recent language feature of string interpolation. This would use the $ operator to apply format placeholders in place directly. I don't think you can use this in a const, it's more for a local variable. Something like this:
var sql = $"SELECT Name as Name FROM {someVariable}.Application WHERE ID = ?";
This would still perform the same replacement, putting someVariable where the placeholder is, it's just using a more concise syntax than a call to string.Format(). One thing to note about this syntax is that it makes it look more like this interpolation is happening directly in-place on the string. It's still a multi-step process behind the scenes, which is why it likely won't work on a const or on class members at all (and should I imagine produce a compiler error).
Remember that strings are immutable, so any operation you perform which modifies a string would be returning a new string rather than modifying the existing one in place.
In any case, you'll of course also need to apply your query parameter for the ? placeholder. Note that what C# considers to be a placeholder in a string formatting/interpolating operation and what DB2 considers to be a placeholder for a query parameter are two entirely different things which happen at different times in different environments. (One in the .NET runtime, one in the database server's query execution.) But again, I'm relying on your assertion that the database query itself works and the only problem we're focusing on here is the C# string placeholder.
I need to log every change of variable values in a SSIS, at package level.
I have a SQL Task Editor that receives, as parameters, System::VariableName and System::VariableValue. I use this parameter to log a line in a DB log table.
Everything is working fine BUT... so far i only have numeric variables so, in the "Parameter Mapping", i specificed that the variable with name "System::VariableValue" is a NUMERIC DataType.
What happens when I will have a Datetime variable or a varchar variable? How can I handle variables of different types in this case?
Thankx
Make the datatype sql_variant, which will accommodate the different datatypes.
More on that here: https://msdn.microsoft.com/en-us/library/ms173829.aspx
Also, SSIS does have a on variable changed event which maybe helpful for you, though it sounds like you have a good custom solution.
m
EDIT:
So there is some documentation on MSDN that seems to suggest nvarchar maps to sql_variant. This worked for strings, but left the value empty for numeric values. Looking at the type for Variable.Value in the variables collection in a script task, it notes the type is empty. So I tried setting the data type to null, which did not work at all.
Finally, I created a script task to do the job and this worked correctly. The script task must call a stored proc in order for it to handle all the data types. I tested it with string, int, double and bool, all of which worked correctly.
Here is the table I used:
create table logging (myval sql_variant);
Here is the proc definition:
Create proc [dbo].[insertLog]
#myVal sql_variant
AS
Begin
set nocount on;
insert into logging values(#myVal);
end;
In the OnVariableValueChanged event handler, I added a script task with the following code:
using System.Data.OleDb;
public void Main()
{
// TODO: Add your code here
object val = Dts.Variables["System::VariableValue"].Value;
OleDbConnection conn = new OleDbConnection(Dts.Connections[".\\sql2016.Test"].ConnectionString);
using (conn)
{
conn.Open();
OleDbCommand cmd = new OleDbCommand("insertLog", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#myVal", val);
cmd.ExecuteNonQuery();
}
Dts.TaskResult = (int)ScriptResults.Success;
}
".\sql2016.Test" is the name of the connection manager in the package.
Please see if this will work for you,
m
I'm using Entity Framework to send a query to the database using ExecuteStoreQuery
If (DBEntity.ExecuteStoreQuery(Of Integer)("SELECT COUNT(ReceiptUID) FROM qryRptSrc_Cust_GoodsReceipt_Issues WHERE ReceiptUID = #Recpt", ReceiptUID)(0) > 0) Then ....
Which gives me the error message that my scalar variable #Recpt hasn't been declared. I know what that error message means, but I'm wondering why it's being thrown in this case. ReceiptUID is a Guid with the correct value. The parameters that are passed don't have to be DbParameter objects, they can just be values and it should work fine. I've done it that was in the past before without issue, and even MSDN states
The parameters value can be an array of DbParameter objects or an array of parameter values. If only values are supplied, an array of DbParameter objects are created based on the order of the values in the array.
I could create a DbParameter object instead, but I'd like to know why this case isn't working.
Token answer:
Try #p0 instead of #Recpt
In one of our application the parameters passed to a stored procedure in this way
Dim parm As New SqlParameter("searchText", SqlDbType.VarChar)
parm.Direction = ParameterDirection.Input
parm.Size = 50
parm.Value="test"
cmd.Parameters.Add(parm)
and the procedure contains a parameter as #searchText
i.e. the parameter name passed from the code is searchText and that in the stored procedure is #searchText .
But it is working properly, I am always getting the required results.
So my question is like so there is no need to specify # before the parameter? Whether it will append #, can anyone please give an answer for this.
According to the documentation, the name must start with an #:
The ParameterName is specified in the form #paramname.
According to the source code (have a look at SqlCommand and SqlParameter.ParameterNameFixed in the reference source), an # is added automatically, if needed.
So yes, it works, but it's an undocumented feature. Best practice recommends that you do not rely on this and manually prefix your parameter name with an #.
Ref: SqlParameter.ParameterName Property and IDataParameter.ParameterName Property
The ParameterName is specified in the form #paramname. You must set ParameterName before executing a SqlCommand that relies on parameters. If you are using Sql Server as Database then you must specify # before
the parameter name.
your parameter name must be same as at backend eg. you have #searchText then in your parameter specification it must be SqlParameter("#searchText" ..
your code should be like this
Dim parm As New SqlParameter("#searchText", SqlDbType.VarChar)
parm.Direction = ParameterDirection.Input
parm.Size = 50
parm.Value="test"
cmd.Parameters.Add(parm)
Note: Oracle and SqLite use different use different character to specify parameter and there may be # symbol is not used specified by the specification of ado.net.
Edit: By comments
As you specified the link, it is also some sort of fix, but as per the msdn documentation, you must specify the positional parameter with '#' whether you are using any data provider oledb, sql, odbc. Ref
if (0 < parameterName.get_Length() && '#' != parameterName.get_Chars(0))
{
parameterName = "#" + parameterName;
}
Its not compulsory to specify the #. However, its a best practice.
Its similar in analogy to strings. There certainly is no harm in defining strings as such in .NET:
string s;
//Rest of the code follows;
But again, its a best practice to define them as :
string s = string.Empty;
You see, its a question of conventions and best practices!!!
I recommended you to use add "#" marker with your parameter name.
SqlParameter helps to add automatically, but others' parameter might not to.
Is the "#" symbol required? Yes and No. When you add a parameter using DbCommand, it's optional regardless of whether you're using SQL Server or not:
// Look Ma no # required!
DbCommand command = database.GetStoredProcCommand("StoredProctologistAndGambler");
database.AddInParameter(command, "Bet", DbType.Int32, fromLineNumber);
database.AddOutParameter(command, "Diagnosis", DbType.String, -1);
If you're going to reference the command later, however, the "#" prefix is required. Microsoft figured it was to hard to carry it over to the rest of the API.
var examResult = command.Parameters["#Diagnosis"]; // Ma! Microsoft lied! It requires the "#" prefix.
I would like to dynamically add fields (or a new columns) to the resulting output row in Kettle.
After spending hours reading through froum posts and he not so well done scripting documentation, I wondered if Stackoverflow would be of any help.
We can use the below steps to generate Dynamic column generation:
calculator
add constants.
Select required fields in table input and assign those values as a set variable and second transformtion level use get variables hop
How are your input values passed to the SQL query? if they are variables then just pass the table input step to a "get variables" step and get your new columns in that way.
Alternatively you can add columns using either calculator or add constants.
Or you could even use the "get system info" step to get commandline args and dates etc.
First, let me give you a code snippet of what I have in a User Defined Java Class step:
private int fieldToHashGeoIndex;
private int fieldToHashHeadIndex;
public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException
{
Object[] r=getRow();
if (r==null)
{
setOutputDone();
return false;
}
if (first) {
fieldToHashGeoIndex = getInputRowMeta().indexOfValue(getParameter("FIELD_TO_HASH_GEO"));
if (fieldToHashGeoIndex<0) {
throw new KettleException("Field to hash not found in the input row, check parameter 'FIELD_TO_HASH_GEO'!");
}
fieldToHashHeadIndex = getInputRowMeta().indexOfValue(getParameter("FIELD_TO_HASH_HEAD"));
if (fieldToHashHeadIndex<0) {
throw new KettleException("Field to hash not found in the input row, check parameter 'FIELD_TO_HASH_HEAD'!");
}
first=false;
}
Object[] outputRowData = RowDataUtil.resizeArray(r, data.outputRowMeta.size());
int outputIndex = getInputRowMeta().size();
String fieldToHashGeo = getInputRowMeta().getString(r, fieldToHashGeoIndex);
String fieldToHashHead = getInputRowMeta().getString(r, fieldToHashHeadIndex);
outputRowData[outputIndex++] = MurmurHash.hash64(fieldToHashGeo);
outputRowData[outputIndex++] = MurmurHash.hash64(fieldToHashHead);
putRow(data.outputRowMeta, outputRowData);
return true;
}
Now, normally you configure outputRowMeta from the step's config, but maybe you can modify it in the code. This should allow you to specify additional fields in the code.
As an alternative, you could latch on variable fields by defining fixed output fields on to the step like 'field1', 'field2', etc. and tracking the names of the fields elsewhere. You'd probably have to make all the fields of type String and then do your own type adjustments later.
Now that I think of it, though, variable output fields may lead to trouble: you have to be very careful with what you do in later steps to avoid having errors due to type mismatches or missing fields.