i have a problem with doing parameterized upsert commands over odbc.
Thats the upsert command
Dim upsert As New OdbcCommand
upsert.Connection = connection
upsert.CommandText = "
INSERT INTO products_replacement
(products_model, products_replacement)
VALUES
(#products_model, #products_replacement)
ON DUPLICATE KEY UPDATE products_replacement = #products_replacement;
"
upsert.Parameters.Add("#products_replacement", OdbcType.VarChar)
upsert.Parameters.Add("#products_model", OdbcType.VarChar)
For Each Product In ListOfProducts
upsert.Parameters.Item("#products_replacement").Value = Product.Value
upsert.Parameters.Item("#products_model").Value = Product.Key
upsert.ExecuteNonQuery()
NEXT
Error message: "ERROR [HY000] [MySQL][ODBC 5.1 Driver][mysqld-5.7.30]Column 'products_model' cannot be null"
In the Debugger the values of the parameters are correctly set.
Something like that works
upsert.Commandtext = upsert.Commandtext.Replace("#products_replacement", $"'{Product.Value}'").Replace("#products_model", $"'{Product.Key}'")
upsert.ExecuteNonQuery()
ListOfProducts is a Dictionary(Of String, String)
Error handling and other stuff is stripped from my above example code.
Parameterized querys are prefered and i had no problems doing the same with MS SQL...
What am I missing?
Help is appreciated.
ODBC doesn't use named parameters
You can give them names in the SQL, but you should then imagine that they all get transformed into ? and are treated positionally by the driver; the name is meaningless
This means you need to add as many parameters to your VB Command.Parameters collection as your statement contains, even if it means repeating values - you cannot reuse VB parameters them by repeating the name in the SQL. The name is still semi useful in VB for indexing purposes:
Dim upsert As New OdbcCommand
upsert.Connection = connection
upsert.CommandText = "
INSERT INTO products_replacement
(products_model, products_replacement)
VALUES
(?, ?)
ON DUPLICATE KEY UPDATE products_replacement = ?;
"
upsert.Parameters.Add("#pmod", OdbcType.VarChar)
upsert.Parameters.Add("#prep1", OdbcType.VarChar)
upsert.Parameters.Add("#prep2", OdbcType.VarChar)
For Each Product In ListOfProducts
upsert.Parameters.Item("#pmod").Value = Product.Value
upsert.Parameters.Item("#prep1").Value = Product.Key
upsert.Parameters.Item("#prep2").Value = Product.Key
upsert.ExecuteNonQuery()
NEXT
Related
I am building a MySql query in vb.net:
cmd.CommandText = "Select id INTO #idDep from dBase.tableA where guid in (#strdepToDelete, #strOtherToDelete) and IsDependent = '1'; " & _
"Select id INTO #idOther from dBase.tableA where guid in (#strdepToDelete, #strOtherToDelete) and IsDependent = '0'; " & _
"delete from dBase.tableA where id in(#idDep, #idOther);"
cmd.Parameters.Add("#strdepToDelete", MySql.Data.MySqlClient.MySqlDbType.String)
cmd.Parameters("#strdepToDelete").Value = strdepToDelete
cmd.Parameters.Add("#strOtherToDelete", MySql.Data.MySqlClient.MySqlDbType.String)
cmd.Parameters("#strdepToDelete").Value = strOtherToDelete
cmd.Parameters.Add("#IdDep", MySql.Data.MySqlClient.MySqlDbType.Int24)
cmd.Parameters("#IdDep").Value = Nothing
cmd.Parameters.Add("#IdOther", MySql.Data.MySqlClient.MySqlDbType.Int24)
cmd.Parameters("#IdOther").Value = Nothing
Try
cmd.ExecuteNonQuery()
success = True
Catch ex As Exception
End Try
Return success
Error is caught which indicates that Null cannnot be #IdDep value. I have tried "" for value of that variable; I have tried ? for value of that variable. When I run the command text gained from hovering over cmd in MySql it works as it should. My question is how to paramaterize queries with nothing value.
I don't think your problem is the datatype, the command object is pretty good at inferring DBNull from Nothing. I think your SQL statement is the problem...
If you have an IN statement, FldNm IN(1,2,NULL) then the SQL engine will parse it as FldNm=1 OR FldNm=2 OR FldNm=NULL. That last item isn't valid (for SQL in general, and also for MySQL in particular ... just tried it to make verify).
You can ask for records where FldNm IS NULL, but not where FldNm=NULL.
So - when you construct that SQL statement, you'll need to skip the values in your IN clause if they are null. OR - use some non-existent value if the value is null as a "work around."
Hope that's helpful!
INSERT INTO voucher (voucher_no, account, party_name, rece_amt, particulars, voucher_date, voucher_type, cuid, cdt)
SELECT voucher_rec_no, #account, #party_name, #rece_amt, #particulars, #voucher_date, #voucher_type, #cuid, #cdt
FROM auto_number
WHERE (auto_no = 1)
Error:
A parameter is not allowed in this location. Ensure that the '#' sign is in a valid location or that parameters are valid at all in this SQL statement.
I've just stumbled upon this whilst trying to fix the same issue. I know it's late but, assuming that you're getting this error when attempting to execute the query via .net, ensure that you are setting the SqlCeParameter.DbType - if this is not specified, you get the exception you listed above.
Example (assume cmd is a SqlCeCommand - all the stuff is in the System.Data.SqlServerCe namespace):
SqlCeParameter param = new SqlCeParameter();
param.ParameterName = "#SomeParameterName";
param.Direction = ParameterDirection.Input;
param.DbType = DbType.String; // this is the important bit to avoid the exception
param.Value = kvp.Value;
cmd.Parameters.Add(param);
Obviously, you'd want to set the DB type to match the type of your parameter.
I have a database similar to the one below:
Table1(AutoNumber, Text, Number, Memo) // this is field types
Table1(ID, Title, Price, Image)
I'm trying to update an existing element of the database in C# using:
const string connectionString = "provider=Microsoft.Jet.OLEDB.4.0;" + "data source=bd.mdb";
OleDbConnection m_dataBase = new OleDbConnection(connectionString);
OleDbConnection m_dataBase.Open();
SQL = "UPDATE Table1 SET Title='test', Price=35, Image='Test' WHERE ID=1";
OleDbCommand SQLQueryCommand = new OleDbCommand(SQL, m_dataBase);
int response = SQLQueryCommand.ExecuteNonQuery();
As a result I am getting this error. "Microsoft JET Database Engine Error syntax in UPDATE instruction".
What am I doing wrong?
PS: I can successfully do SELECT or INSERT, but not UPDATE.
Well, if your SQL command is the only problem, there are some visible issues.
Simply try to parametrize your Update clause using such as below which will prevent lots of little mistakes and also an SQL injection.
SQL = "UPDATE Table1 SET Title=?, Price=?, Image=? WHERE ID=?";
SQLQueryCommand.Parameter.Add("#MyTitle", OleDbType.VarChar).Value = "Test";
SQLQueryCommand.Parameter.Add("#MyPrice", OleDbType.Integer).Value = 35;
SQLQueryCommand.Parameter.Add("#MyImage", OleDbType.VarChar).Value = "TestAgain";
SQLQueryCommand.Parameter.Add("#MyID", OleDbType.VarChar).Value = 1;
To learn more about parametrization try having a look at the example in the bottom of this MSDN article.
OleDbCommand.Parameters Property
Also, it's a good practice to surround your connection inside a using statement.
using (var m_dataBase = new OleDbConnection(connectionString) { ... }
In FoxPro using native table, I usually do this when inserting new Data.
Sele Table
If Seek(lcIndex)
Update Record
Else
Insert New Record
EndIf
If I will use MYSQL as my DataBase, what is the best and fastest way to
do this in FoxPro code using SPT? I will be updating a large number of records.
Up to 80,000 transactions.
Thanks,
Herbert
I would only take what Jerry supplied one step further. When trying to deal with any insert, update, delete with SQL pass through, it can run into terrible debugging problems based on similar principles of SQL-injection.
What if your "myValue" field had a single quote, double quote, double hyphen (indicating comment)? You would be hosed.
Parameterize your statement such as using VFP variable references, then use "?" in your sql statement to qualify which "value" should be used. VFP properly passes. This also helps on data types, such as converting numbers into string when building the "myStatement".
Also, in VFP, you can use TEXT/ENDTEXT to simplify the readability of the commands
lcSomeStringVariable = "My Test Value"
lnANumericValue = 12.34
lnMyIDKey = 389
TEXT to lcSQLCmd NOSHOW PRETEXT 1+2+8
update [YourSchems].[YourTable]
set SomeTextField = ?lcSomeStringVariable,
SomeNumberField = ?lnANumericValue
where
YourPKColumn = ?lnMyIDKey
ENDTEXT
=sqlexec( yourHandle, lcSQLCmd, "localCursor" )
You can use SQL Pass through in your Visual Foxpro application. Take a look at the SQLCONNECT() or SQLSTRINGCONNECT() for connecting to your Database. Also look at SQLEXEC() for executing your SQL statement.
For Example:
myValue = 'Test'
myHandle = SQLCONNECT('sqlDBAddress','MyUserId','MyPassword')
myStatement = "UPDATE [MySchema].[Mytable] SET myField = '" + myValue + "' WHERE myPk = 1"
=SQLEXEC(myHandle, myStatement,"myCursor")
=SQLEXEC(myHandle, "SELECT * FROM [MySchema].[Mytable] WHERE myPk = 1","myCursor")
SELECT myCursor
BROWSE LAST NORMAL
This would be your statement string for SQLEXEC:
INSERT INTO SOMETABLE
SET KEYFIELD = ?M.KEYFIELD,
FIELD1 = ?M.FIELD1
...
FIELDN = ?M.FIELDN
ON DUPLICATE KEY UPDATE
FIELD1 = ?M.FIELD1
...
FIELDN = ?M.FIELDN
Notice that the ON DUPLICATE KEY UPDATE part does not contain the key field, otherwise it would normally be identical to the insert (or not, if you want to do something else when the record already exists)
I have the following JScript code:
var conn = new ActiveXObject ("ADODB.Connection");
conn.Open("Driver={MySQL ODBC 5.1 Driver};Server=localhost;Database=blah_blah_blah;User=foo;Password=bar;");
var cmd = new ActiveXObject("ADODB.Command");
cmd.ActiveConnection = conn;
var strSQL = "SELECT id FROM tbl_info WHERE title LIKE :search ORDER BY id";
var search = "test";
try{
cmd.CommandText = strSQL;
var param = cmd.CreateParameter(':search', 200, 1, 100, search);
cmd.Parameters.Append(param);
var rs = cmd.Execute();
}
catch (ex) {
Application.Alert("Error retrieving id information from database.");
}
I've verified (by printing them) that the Connection object is set to be the Command's ActiveConnection, the parameter object has the correct value and the Command object has the correct SQL query as its CommandText. I also inserted an alert statement after each line in the try block to see where the error was occuring - it's fine after cmd.Parameters.Append but the exception gets thrown upon running the Execute statement.
I've tried displaying the actual exception but it's just a generic 'Object error' message.
The query executes fine and returns the correct result set when I just execute the SQL query (without the parameter) straight through the Connection object, but seems to fail when I use a parameterised query with the Command object.
As far as I can see all settings and properties of the Command and Connection objects are correct but for whatever reason it's throwing an exception.
Any help with this would be much appreciated.
With ODBC and ADO, generally speaking, a question mark ? is used as the placeholder for parameters. Parameters are bound in the order they are appended to the Parameters collection to the placeholders in the command. In your example, replace strSQL with:
var strSQL = "SELECT id FROM tbl_info WHERE title LIKE ? ORDER BY id";
You can still name the parameter that you create, but the only purpose it would serve is to be able to reference it by name later (e.g., with cmd.Parameters.Item(":search")).