When I create an OdbcCommand and add parameters, parameters that are of VarChar 4000 are translated into text type in generated sql. My sample code is:
OdbcConnection dbConnection = new OdbcConnection("DSN=database;UID=uid;PWD=pwd;");
try
{
dbConnection.Open();
OdbcCommand odbcCommand = new OdbcCommand();
odbcCommand.CommandText = "UPDATE Table SET Notes = ? WHERE Notes = ?";
odbcCommand.Connection = dbConnection;
odbcCommand.Parameters.Add("", OdbcType.VarChar, 3999, "");
odbcCommand.Parameters.Add("", OdbcType.VarChar, 4000, "");
odbcCommand.Parameters[0].Value = "0";
odbcCommand.Parameters[1].Value = "1";
odbcCommand.ExecuteNonQuery();
}
finally
{
dbConnection.Close();
}
This is the generated SQL, that I got from SQL Server Profiler
exec sp_executesql N'UPDATE Table SET Notes = #P1 WHERE Notes = #P2',N'#P1 varchar(3999),#P2 text','0','1'
Even though MSDN does not say anything about maximum sizes for VarChar here. Does anyone know why this happens and if this is configurable?
I believe it's a OdbcCommand specific thing because the update SQL statement that Simple Data generates for other field that is stored as varchar(4000) in the SQL Server database is generated correctly.
VARCHAR with 4000 bytes will convert the stringsize to extented which will return text.
32767 bytes if MAX_STRING_SIZE = EXTENDED
4000 bytes if MAX_STRING_SIZE = STANDARD
I dont think you can change that.. so If you can post what you are trying to accomplish; that would be helpfull
Related
I try to UPDATE data in my SQL Server database and I get this error:
System.Data.SqlClient.SqlException
Incorrect syntax near 'de'
Unclosed quotation mark after the character string ')'
private void BtEnrMod_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection("Data Source=.\\BD4X4;Initial Catalog=BD4X4;Integrated Security=True");
con.Open();
SqlCommand cmd = new SqlCommand("UPDATE Service SET Type = " + TxBxService.Text + ", Prix = " + TxBxPrix.Text + "WHERE Code = " + LbCodeAff.Text + "')", con);
int i = cmd.ExecuteNonQuery();
if (i != 0)
{
MessageBox.Show("Service Modifié");
}
else
{
MessageBox.Show("Erreur");
}
this.Close();
con.Close();
}
Replace the one liner that declares your command with this code block:
SqlCommand cmd = new SqlCommand("UPDATE Service SET Type = #t, Prix = #p WHERE Code = #c", con);
cmd.Parameters.AddWithValue("#t", TxBxService.Text);
cmd.Parameters.AddWithValue("#p", TxBxPrix.Text);
cmd.Parameters.AddWithValue("#t", LbCodeAff.Text);
Always avoid writing an sql where you string concatenate in a value provided by the user in a text box; it's the number one security horror you can make with sql. Always use parameters to put values in, like you see here. For more info on this SQL injection hacking, see http://bobby-tables.com
If you ever fin yourself in a situation where you think you have to concatenate to make an sql, don't concatenate a value in; concatenate a parameter in and add the value into the parameters collection. Here's a hypothetical example:
var cmd = new SqlCommand("","connstr");
strSql = "SELECT * FROM table WHERE col IN (";
string[] vals = new[]{ "a", "b", "c" };
for(int x = 0; x<vals.Length; x++){
strSql += ("#p"+x+",");
cmd.Parameters.AddWithValue("#p"+x, vals[x]);
}
cmd.CommandText = strSql + ")";
This uses concatenation to make an sql of SELECT * FROM table WHERE col IN (#p0, #p1, #p2) and a nicely populated parameters collection
When you're done grokking that, read the link Larnu posted in the comments. There are good reasons to avoid using AddWithValue in various scenarios but it will always be preferable to concatenation of values. Never ditch the use of parameters "because I read a blog one time about how AddWithValue is bad" - form parameters using the new parameter constructor, or use AddWithValue shortcut, but never concat values
Or better still than all of this, use an ORM like Entity Framework, nHibernate or Dapper and leave most of this boring boilerplate low level SQL drudgery behind. These libraries do most of this wrangling for you; EF and nH even write th sql too, dapper you write it yourself but it takes care of everything else
Using a good ORM is like the difference between writing creating a UI manually line by line of position, font, anchor, event code for every button, label and text box versus using the windows forms designer; a world apart and there's no sense in taking hours to create manually what software can do more comprehensively, faster and safer for you in seconds
I have been trying to update data to database however i met this problem.I tried deleting the table and creating a new table yet the problem still persist.Below are the codes.Any help will be greatly appreciated
public int UpdateNOK(string wardClass, DateTime admissionDT, string nokFName, string nokLName, string nokNRIC, DateTime nokDOB, string nokGender, string nokNationality, string nokRelationship, int nokContactH, int nokContactHP, string nokEmail, string nokAddr1, string nokAddr2, string nokState, int nokZIP, string nokCountry, DateTime dischargeDT, string patientNRIC)
{
StringBuilder sqlStr = new StringBuilder();
int result = 0;
SqlCommand sqlCmd = new SqlCommand();
sqlStr.AppendLine("Update patientAdmission");
sqlStr.AppendLine("SET wardClass = #parawardClass,admissionDT = #paraadmissonDT, nokFName = #parapatientNokFname, nokLName = #parapatientNokLname,nokNRIC = #parapatientNokNRIC, nokDOB = #parapatientNOKDOB, nokGender = #parapatientNokGender, nokNationality = #parapatientNokNationality,");
sqlStr.AppendLine("nokRelationship = #parapatientNokRelationship,nokContactH = #parapatientNokContactH,nokContactHP = #parapatientNokContactHP, nokEmail = #parapatientNokEmail, nokAddr1 = #parapatientNokAddr1,nokAddr2 = #parapatientNokAddr2,nokState = #parapatientNokState, nokZIP = #parapatientNokZIP,");
sqlStr.AppendLine("nokCountry = #parapatientNokCountry, dischargeDT = #paradischargeDateTime");
sqlStr.AppendLine("WHERE patientNRIC = #parapatientNRIC");
try
{
SqlConnection myConn = new SqlConnection(DBConnect);
sqlCmd = new SqlCommand(sqlStr.ToString(), myConn);
sqlCmd.Parameters.AddWithValue("#parawardClass", wardClass);
sqlCmd.Parameters.AddWithValue("#paraadmissonDT", admissionDT);
sqlCmd.Parameters.AddWithValue("#parapatientNokFname", nokFName);
sqlCmd.Parameters.AddWithValue("#parapatientNokLname", nokLName);
sqlCmd.Parameters.AddWithValue("#parapatientNokNRIC", nokNRIC);
sqlCmd.Parameters.AddWithValue("#parapatientNOKDOB", nokDOB);
sqlCmd.Parameters.AddWithValue("#parapatientNokGender", nokGender);
sqlCmd.Parameters.AddWithValue("#parapatientNokNationality", nokNationality);
sqlCmd.Parameters.AddWithValue("#parapatientNokRelationship", nokRelationship);
sqlCmd.Parameters.AddWithValue("#parapatientNokContactH", nokContactH);
sqlCmd.Parameters.AddWithValue("#parapatientNokContactHP", nokContactHP);
sqlCmd.Parameters.AddWithValue("#parapatientNokEmail", nokEmail);
sqlCmd.Parameters.AddWithValue("#parapatientNokAddr1", nokAddr1);
sqlCmd.Parameters.AddWithValue("#parapatientNokAddr2", nokAddr2);
sqlCmd.Parameters.AddWithValue("#parapatientNokState", nokState);
sqlCmd.Parameters.AddWithValue("#parapatientNokZIP", nokZIP);
sqlCmd.Parameters.AddWithValue("#parapatientNokCountry", nokCountry);
sqlCmd.Parameters.AddWithValue("#paradischargeDateTime", dischargeDT);
sqlCmd.Parameters.AddWithValue("#parapatientNRIC", patientNRIC);
myConn.Open();
result = sqlCmd.ExecuteNonQuery();
myConn.Close();
Console.WriteLine(result);
}
catch (Exception ex)
{
logManager log = new logManager();
log.addLog("patientNOKDAO.UpdateNOK", sqlStr.ToString(), ex);
}
return result;
}
}
You should check table definition (sp_help) against your used columns in the table patientAdmission:
wardClass
admissionDT
nokFName
nokLName
nokNRIC
nokDOB
nokGender
nokNationality
nokRelationship
nokContactH
nokContactHP
nokEmail
nokAddr1
nokAddr2
nokState
nokZIP
nokCountry
dischargeDT
patientNRIC
If database default collation is a case-sensitive one, column names above must be exactly as defined in the table (case cannot be different).
One way to find the problem faster is to run SQL profiler and see the exact query against the database. Copy-paste it from there and run it into an Management Studio (SSMS) query file (use BEGIN TRAN .. ROLLBACK to ensure that nothing will actually be changed when you make it work). SSMS will try to indicate the exact column with the problem when clicking on the error.
I am trying to do create a where clause to pass as a parameter to an Oracle command and it's proving to be more difficult than I thought. What I want to do is create a big where query based off user input from our application. That where query is to be the single parameter for the statement and will have multiple AND, OR conditions in it. This code here works however isn't exactly what I require:
string conStr = "User Id=testschema;Password=pass12341;Data Source=orapdex01";
Console.WriteLine("About to connect to Database with Connection String: " + conStr);
OracleConnection con = new OracleConnection(conStr);
con.Open();
Console.WriteLine("Connected to the Database..." + Environment.NewLine + "Press enter to continue");
Console.ReadLine();
// Assume the connection is correct because it works already without the parameterization
String block = "SELECT * FROM TEMP_VIEW WHERE NAME = :1";
// set command to create anonymous PL/SQL block
OracleCommand cmd = new OracleCommand();
cmd.CommandText = block;
cmd.Connection = con;
// since execurting anonymous pl/sql blcok, setting the command type
// as text instead of stored procedure
cmd.CommandType = CommandType.Text;
// Setting Oracle Parameter
// Bind the parameter as OracleDBType.Varchar2
OracleParameter param = cmd.Parameters.Add("whereTxt", OracleDbType.Varchar2);
param.Direction = ParameterDirection.Input;
param.Value = "MY VALUE";
// Get returned values from select statement
OracleDataReader dr = cmd.ExecuteReader();
// Read the identifier for each result and display it
while (dr.Read())
{
Console.WriteLine(dr.GetValue(0));
}
Console.WriteLine("Selected successfully !");
Console.WriteLine("");
Console.WriteLine("***********************************************************");
Console.ReadKey();
If I change the lines below to be the type of result I want then I get an error "ORA-00933: SQL command not properly ended":
String block = "SELECT * FROM TEMP_VIEW :1";
...
...
param.Value = "WHERE NAME = 'MY VALUE' AND ID = 5929";
My question is how do I accomplish adding my big where query dynamically without causing this error?
Sadly there is no easy way to achieve this.
One thing you will need to understand with parameterised SQL in general is that bind parameters can only be used for values, such as strings, numbers or dates. You cannot put bits of SQL in them, such as column names or WHERE clauses.
Once the database has the SQL text, it will attempt to parse it and figure out whether it is valid, and it will do this without taking any look at the bind parameter values. It won't be able to execute the SQL without all of the values.
The SQL string SELECT * FROM TEMP_VIEW :1 can never be valid, as Oracle isn't expecting a value to immediately follow FROM TEMP_VIEW.
You will need to build up your SQL as a string and also build up the list of bind parameters at the same time. If you find that you need to add a condition on the column NAME, you add WHERE NAME = :1 to the SQL string and a parameter with name :1 and the value you wish to add. If you have a second condition to add, you append AND ID = :2 to the SQL string and a parameter with name :2.
Hopefully the following code should explain a little better:
// Initialise SQL string and parameter list.
String sql = "SELECT * FROM DUAL";
var oracleParams = new List<OracleParameter>();
// Build up SQL string and list of parameters.
// (There's only one in this somewhat simplistic example. If you have
// more than one parameter, it might be easier to start the query with
// "SELECT ... FROM some_table WHERE 1=1" and then append
// " AND some_column = :1" or similar. Don't forget to add spaces!)
sql += " WHERE DUMMY = :1";
oracleParams.Add(new OracleParameter(":1", OracleDbType.Varchar2, "X", ParameterDirection.Input));
using (var connection = new OracleConnection() { ConnectionString = "..."})
{
connection.Open();
// Create the command, setting the SQL text and the parameters.
var command = new OracleCommand(sql, connection);
command.Parameters.AddRange(oracleParams.ToArray());
using (OracleDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
// Do stuff with the data read...
}
}
}
I have a query
SELECT instance_guid FROM service_instances WHERE service_template_guid='E578F99360A86E4EE043C28DE50A1D84' AND service_family_name='TEST'
Directly executing this returns me
4FEFDE7671A760A8DC8FC63CFBFC8316
F2F9DF641D8E2CACC03175A7A628D51D
Now I am trying same code from JDBC.
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = executionContext.getConnection();
if (conn != null) {
ps = (PreparedStatement)conn.prepareStatement(query);
if (params == null) params = new Object[0];
for (int i=0;i<params.length;i++) {
if (params[i] instanceof Integer) {
ps.setInt(i+1, ((Integer)params[i]).intValue());
} else if (params[i] instanceof java.util.Date) {
((PreparedStatement)ps).setDATE(i+1, new oracle.sql.DATE((new java.sql.Timestamp(((Date)params[i]).getTime()))));
//ps.setObject(i+1, new oracle.sql.DATE(new Time(((Date)params[i]).getTime())));
} else {
if (params[i] == null) params[i] = "";
ps.setString(i+1, params[i].toString());
}
}
rs = ps.executeQuery();
I see params[0] =E578F99360A86E4EE043C28DE50A1D84 and params[1]=TEST
But the resultSet is empty and not getting the result.I debugged but not much help?
Can you please let me know Am i trying right?
In java its defined as below
final static private String INSTANCE_GUID_BY_TEMPLATE_GUID =
"SELECT instance_guid FROM service_instances WHERE service_template_guid=? AND service_family_name=? "
SERVICE_FAMILY_NAME NOT NULL VARCHAR2(256)
SERVICE_TEMPLATE_GUID NOT NULL RAW(16 BYTE)
First and foremost this breaks every sql mapping pattern I have ever seen.
String sql = "SELECT instance_guid FROM service_instances WHERE service_template_guid=? AND service_family_name=?";
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = executionContext.getConnection();
ps = conn.prepareStatement(sql);
ps.setString(1,guid);
ps.setString(2,family);
rs = ps.executeQuery();
while(rs.next(){...}
...
}
You should not be dynamically figuring out the data types as they come in, unless you are trying to write some code to port from database X to database Y.
UPDATE
I see you are using RAW as a datatype, from this post:
As described in the Oracle JDBC Developer's guide and reference 11g,
when using a RAW column, you can treat it as a BINARY or VARBINARY
JDBC type, which means you can use the JDBC standard methods
getBytes() and setBytes() which returns or accepts a byte[]. The other
options is to use the Oracle driver specific extensions getRAW() and
setRAW() which return or accept a oracle.sql.RAW. Using these two will
require you to unwrap and/or cast to the specific Oracle
implementation class.
Further from a code readability standpoint, your solution makes it painful for a new developer to take over. Far too often I see people making sql be "dynamic" when in reality 99% of the time you don't need this level of dynamic query building. It sounds good in most people's heads but it just causes pain and suffering in the SDLC.
There are several SQL servers having stored procedures, for example Microsoft SQL Server or PostgreSQL. There are also several client side objects implementing stored procedures calls (TADOStoredProc in Delphi, SqlCommand in .NET Framework etc.).
The question I always wanted to ask is:
are stored procedures executed always in special efficient way with binary representation of their parameters or are the super-advanced objects which represent stored procedures' parameters always converted to a plain text string and stored procedure is always executed by sending this plain text string to SQL server? (Let's take one technology for example - let it be SQL Server and ADO.NET).
I noticed that for ADO.NET procedure's parameter names do not have any meaning - only their creation order is important which makes me think about an idea with plain text string.
Update for #Alex K.
I've tested following code in .NET:
CREATE PROCEDURE paramtest
#par1 nvarchar(50),
#par2 nvarchar(50),
#par3 nvarchar(50)
AS
SELECT Res = '#par1 = ' + #par1 + '; #par2 = ' + #par2 + '; #par3 = ' + #par3
RETURN 555
using System;
using System.Data.SqlClient;
using System.Data;
namespace SqlParamTest
{
class Program
{
private static void addParam(SqlCommand cmd, string parameterName, ParameterDirection direction, SqlDbType dbType, int size, object value)
{
SqlParameter par = new SqlParameter(parameterName, dbType, size);
par.Direction = direction;
par.Value = value;
cmd.Parameters.Add(par);
}
static void Main(string[] args)
{
using (SqlConnection conn = new SqlConnection(#"Data Source=localhost\sqlexpress;Initial Catalog=test;Integrated Security=True"))
{
SqlCommand cmd = conn.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "paramtest";
addParam(cmd, "#par3", ParameterDirection.Input, SqlDbType.NVarChar, 50, "third");
addParam(cmd, "#par2", ParameterDirection.Input, SqlDbType.NVarChar, 50, "second");
addParam(cmd, "#par1", ParameterDirection.Input, SqlDbType.NVarChar, 50, "first");
addParam(cmd, "#Return", ParameterDirection.ReturnValue, SqlDbType.Int, 0, null);
conn.Open();
SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
if (rdr.Read()) Console.WriteLine((string)rdr["Res"]);
rdr.Close();
Console.WriteLine("Return value: {0}", cmd.Parameters["#Return"].Value);
}
Console.ReadKey();
}
}
}
and yes, it maintains parameters in a right way, but I think, it is .NET who adds additional checks to parameters, because following code in Delphi:
procedure TMyClass.Test(Conn: TADOConnection);
var SP:TADOStoredProc;
begin
SP := TADOStoredProc.Create(nil);
try
SP.Connection := Conn;
SP.ProcedureName := 'paramtest';
SP.Parameters.CreateParameter('#whatthehell', ftString, pdInput, 50, 'one');
SP.Parameters.CreateParameter('#AnotherCrap', ftString, pdInput, 50, 'two');
SP.Parameters.CreateParameter('?', ftString, pdInput, 50, 'three');
SP.ExecProc;
finally
SP.Free;
end;
end;
returns:
#par1 = one; #par2 = two; #par3 = three
and doesn't complain about missing parameters.
pdReturnValue works only if this parameter is created before any other parameters.
Not sure what answer your looking for, stored procedure command text & parameters are passed to the driver/provider or natively via ADO.NET which formats it as a TDS (tabular data stream) RPC (remote procedure call) Message which is then passed to the server over which ever network protocol is being used; pipes, tcp/ip et al. The data is sent in a binary stream.
The TDS spec is available from Microsoft if your interested.
SQLCommand Stored Procedure calls do need a parameter name, its OleDB/ODBC that only care about the order and use ? as the parameter placeholder rather than #NAME.
Regarding order
In your example the order is not relevant because you are providing the server with the correct names for the params, so this is what is sent to the server:
exec paramtest #par3=N'third',#par2=N'second',#par1=N'first'
This is enough information for the server to figure out the correct params/order.
If you changed to
addParam(cmd, "#xxpar3",
addParam(cmd, "#xxpar2",
addParam(cmd, "#xxpar1",
The server would detect that it does not have a param named xxxpar* and fail with a "missing #par1" error.
If you modified addParam so it did not set paramater names .net will create defaults:
exec paramtest #Parameter1=N'third',#Parameter2=N'second',#Parameter3=N'first'
Which would cause the above error.
If you modified addParam so it did not set paramater names and then overwrite the automatic ones;
cmd.Parameters.Add(par);
par.ParameterName = "";
This is what gets executed:
exec paramtest N'third',N'second',N'first'
resulting in
#par1 = third; #par2 = second; #par3 = first
I have no idea what Delphi does ... The full version of SQL Server ships with a tool called SQL Profiler which displays the textual data that gets sent to the server instance so you can see exactly whats going on.
What profiler to use with sql express?