UPDATE statement in Oracle - sql

We are building a client program where parameters for storage in a web server with Oracle backend are set in the .Net client program and uploaded as a dataset via webservice.
In the webservice code, data is read from the dataset and added to UPDATE statements on the web server (Oracle backend).
Because the server will run on the customer's LAN behind a firewall and because of the dynamic nature of the parameters involved, no sprocs are being used - SQL strings are built in the logic.
Here is an example string:
UPDATE WorkOrders
SET TravelTimeHours = :TravelTimeHours,
TravelTimeMinutes = :TravelTimeMinutes,
WorkTimeHours = :WorkTimeHours,
WorkTimeMinutes = :WorkTimeMinutes,
CompletedPersonID = :CompletedPersonID,
CompletedPersonName = :CompletedPersonName,
CompleteDate = :CompleteDate
WHERE WorkOrderNumber = :WorkOrderNumber
When debugging code in VS 2010 and stepping into the server code, we receive the following error:
ORA-01036: illegal variable name/number
when executing the SQL command on destination oracle machine, we were prompted to enter the bind
variables for the above statement, and as long as we used the correct date format, the UPDATE statement
worked correctly.
QUESTIONS:
1) is it possible that oracle threw the ORA-01036 error when the month format was wrong?
2) why don't we have to convert the date format from the ASP.net website running on the Oracle machine?
does Oracle have a default conversion routine that excludes the bind variable entry screen?
3) if the date format was not the problem, what precisely does ORA-1036 mean and how do I discover
WHICH variable had an illegal name/number?
This is a snippet of a function that takes the type of the dataset (WOName) and returns the appropriate SQL string.
Many Cases exist but have been removed for readability.
Private Function GetMainSQLString(ByVal WOName As String) As String
Dim Result As String = ""
Select Case WOName
Case "Monthly Site Inspection"
Dim sb As New StringBuilder
sb.Append("UPDATE WorkOrders SET ")
sb.Append("CompletedPersonID = :CompletedPersonID, CompletedPersonName = :CompletedPersonName, CompleteDate = :CompleteDate, ")
sb.Append("SupervisorID = :SupervisorID, SupervisorName = :SupervisorName ")
sb.Append("WHERE WorkOrderNumber = :WorkOrderNumber")
Result = sb.ToString
End Select
Return Result
End Function
This is a snippet of a function that takes the Oracle command object byRef and adds the required parameters to it,
depending upon which of the possible 15 types of dataset(WOName) is received from the client program.
Many Cases exist but have been removed for readability.
The updated Cmd object is then returned to the main program logic, where ExecuteNonQuery() is called.
The test values of params below are as follows:
dr.Item("CompletedPersonID") 21
dr.Item("CompletedPersonName") Pers Name
dr.Item("CompleteDate") #8/16/2010#
dr.Item("SupervisorID") 24
dr.Item("SupervisorName") Sup Name
dr.Item("WorkOrderNumber") 100816101830
Private Function addMainCmdParams(ByVal WOName As String, ByRef cmd As OracleCommand, ByVal dr As DataRow) As OracleCommand
Select Case WOName
Case "Monthly Site Inspection"
cmd.Parameters.Add(":CompletedPersonID", Oracle.DataAccess.Client.OracleDbType.Int32).Value = dr.Item("CompletedPersonID")
cmd.Parameters.Add(":CompletedPersonName", Oracle.DataAccess.Client.OracleDbType.Varchar2).Value = dr.Item("CompletedPersonName")
cmd.Parameters.Add(":CompleteDate", Oracle.DataAccess.Client.OracleDbType.Date).Value = dr.Item("CompleteDate")
cmd.Parameters.Add(":SupervisorID", Oracle.DataAccess.Client.OracleDbType.Int32).Value = dr.Item("SupervisorID")
cmd.Parameters.Add(":SupervisorName", Oracle.DataAccess.Client.OracleDbType.Varchar2).Value = dr.Item("SupervisorName")
cmd.Parameters.Add(":WorkOrderNumber", Oracle.DataAccess.Client.OracleDbType.Varchar2).Value = dr.Item("WorkOrderNumber")
End Select
Return cmd
End Function
While running this today, this precise code WAS successful; but another similar case was not. I still distrust any implicit typecasting performed by Oracle (if any) - and I'm especially suspicious of how Oracle handles any of these parameters that are passed with a dbNull.value - and I know it's going to happen. so if that's the problem I'll have to work around it. There are too many optional parameters and columns that don't always get values passed in for this system to break on nulls.

One Oracle "gotcha" that can cause this error is the fact that, by default, Oracle maps parameters to parameter symbols in the query by sequence, not by name. If the number/type of parameters does not match, you get an error like this one.
The solution is to tell Oracle to bind by name:
cmd.BindByName = true
Without diving into the details of your code, this may or may not be the answer to your specific problem, but this setting should be the default, and should be part of any command setup that uses parameters. It's rather amazing to watch this one statement fix some obscure problems.
EDIT: This assumes that you're using Oracle's data access provider. In .NET, you should be using this, not Microsoft's Oracle provider.

The error has nothing to do with date formats, it means that a variable in the statement was not bound.
Could be as simple as a spelling mistake (would be nice if Oracle included the variable name in the error message).
Can you update your question with the surrounding code that creates, binds, and executes the statement?

This is a snippet of a function that takes the type of the dataset (WOName) and returns the appropriate SQL string.
Many Cases exist but have been removed for readability.
Private Function GetMainSQLString(ByVal WOName As String) As String
Dim Result As String = ""
Select Case WOName
Case "Monthly Site Inspection"
Dim sb As New StringBuilder
sb.Append("UPDATE WorkOrders SET ")
sb.Append("CompletedPersonID = :CompletedPersonID, CompletedPersonName = :CompletedPersonName, CompleteDate = :CompleteDate, ")
sb.Append("SupervisorID = :SupervisorID, SupervisorName = :SupervisorName ")
sb.Append("WHERE WorkOrderNumber = :WorkOrderNumber")
Result = sb.ToString
End Select
Return Result
End Function
This is a snippet of a function that takes the Oracle command object byRef and adds the required parameters to it,
depending upon which of the possible 15 types of dataset(WOName) is received from the client program.
Many Cases exist but have been removed for readability.
The updated Cmd object is then returned to the main program logic, where ExecuteNonQuery() is called.
The test values of params below are as follows:
dr.Item("CompletedPersonID") 21
dr.Item("CompletedPersonName") Pers Name
dr.Item("CompleteDate") #8/16/2010#
dr.Item("SupervisorID") 24
dr.Item("SupervisorName") Sup Name
dr.Item("WorkOrderNumber") 100816101830
Private Function addMainCmdParams(ByVal WOName As String, ByRef cmd As OracleCommand, ByVal dr As DataRow) As OracleCommand
Select Case WOName
Case "Monthly Site Inspection"
cmd.Parameters.Add(":CompletedPersonID", Oracle.DataAccess.Client.OracleDbType.Int32).Value = dr.Item("CompletedPersonID")
cmd.Parameters.Add(":CompletedPersonName", Oracle.DataAccess.Client.OracleDbType.Varchar2).Value = dr.Item("CompletedPersonName")
cmd.Parameters.Add(":CompleteDate", Oracle.DataAccess.Client.OracleDbType.Date).Value = dr.Item("CompleteDate")
cmd.Parameters.Add(":SupervisorID", Oracle.DataAccess.Client.OracleDbType.Int32).Value = dr.Item("SupervisorID")
cmd.Parameters.Add(":SupervisorName", Oracle.DataAccess.Client.OracleDbType.Varchar2).Value = dr.Item("SupervisorName")
cmd.Parameters.Add(":WorkOrderNumber", Oracle.DataAccess.Client.OracleDbType.Varchar2).Value = dr.Item("WorkOrderNumber")
End Select
Return cmd
End Function
While running this today, this precise code WAS successful; but another similar case was not. I still distrust any implicit typecasting performed by Oracle (if any) - and I'm especially suspicious of how Oracle handles any of these parameters that are passed with a dbNull.value - and I know it's going to happen. so if that's the problem I'll have to work around it. There are too many optional parameters and columns that don't always get values passed in for this system to break on nulls.

Related

vb.net sql Check if value exists in table and translate result into a variable

I'm creating a simple relational database system, to ensure no repeated data is saved in. I have created a chunk of code to check if the value of Album is already present in the selected table:
If Album IsNot Nothing Then 'checks if ALBUM exists
ALBUM_CHECK = New SqlCommand("SELECT ALBUM_ID FROM ALBUM_DB WHERE NAME=(#NAME)", SQLcon)
ALBUM_CHECK.Parameters.AddWithValue("#NAME", Album)
If ALBUM_CHECK.ExecuteScalar IsNot Nothing Then
album_Exist = True
Else
album_Exist = False
End If
End If
However this returns the error:
System.Data.SqlClient.SqlException: 'The data types text and nvarchar are incompatible in the equal to operator.'
Any ideas on how to get round this?
I believe it's not allowing me to read if the value returned is null. All help appreciated!
From the documentation of SQL Server 2017 :
IMPORTANT! ntext, text, and image data types will be removed in a future version of SQL Server. Avoid using these data types in new development work, and plan to modify applications that currently use them. Use nvarchar(max), varchar(max), and varbinary(max) instead.
I would suggest to change the datatype of this column to varchar(max), which has the same capacity in terms of storage and is properly supported by this RDBMS.
Text is a very poor choice for an Album. Comments and explanations in line.
Private Sub OPCode()
Dim Album As String = ""
Dim albumExist As Boolean
If String.IsNullOrEmpty(Album) Then
Return
End If
'Using blocks ensure that your database objects are not only closed but disposed.
'The disposal is important because they can contain unmanaged objects.
Using SQLcon As New SqlConnection("Your connection string")
'If Exists is a good method for large tables because it stops as soon
'as it finds a match.
Using ALBUM_CHECK As New SqlCommand("If Exists (SELECT 1 FROM ALBUM_DB WHERE NAME = #NAME) Select 1 Else Select 0;", SQLcon)
'Using .Add instead of .AddWithValue tells the database what kind of
'data we are sending. Without this we have no control over what ADO
'decides to sent to the database.
ALBUM_CHECK.Parameters.Add("#NAME", SqlDbType.NVarChar).Value = Album
'The query will only return a 0 or a 1.
SQLcon.Open
albumExist = CBool(ALBUM_CHECK.ExecuteScalar())
End Using
End Using
End Sub

Firebird ADO.NET issue with loading a Dataset from the FbDataAdapter

I have several freely available data access layers on my business site. I have always followed the Firebird database on their site and have now decided to build a new data access layer for this database engine.
I am using the ADO.NET provider recommended on the Firebird site (https://www.firebirdsql.org/en/net-provider/). The version being used is 5.5.
I have developed a simple stored procedure to process returning several columns of data, which has been tested successfully in my database manager. It has also been tested successfully with the data-reader within the test-client I am building for my Firebird data access layer.
However, when I attempt to test the stored-procedure against the return of a dataset, I consistently receive the following error...
Unable to cast object of type 'System.String' to type 'System.Byte[]'
Even a simple inline query with a parameter yields the same error.
Yet these very same tests work fine with my data-reader processes.
This error is always yielded when it comes to my code in the process that "fills" a dataset from the Firebird data-adapter.
The code I am testing is below
Public Overloads Function ExecuteDataSet(ByVal psConnectionString As String, _
ByVal psQuery As String, _
ByRef poParameterStructures As ArrayList) As DataSet
Dim loParameterStructures As New ArrayList()
Dim loDataSet As DataSet
Dim loFbSqlConnection As FbConnection = Nothing
Dim loFbSqlParameter As FbParameter
Dim loFbSqlCommand As FbCommand = Nothing
Dim loFbSqlDataAdapter As FbDataAdapter = Nothing
Try
'DataTable dt = new DataTable();
'FbDataAdapter da = new FbDataAdapter("select_mytable", ConfigurationManager.ConnectionStrings["mydb"].ConnectionString);
'da.SelectCommand.CommandType = CommandType.StoredProcedure;
'da.SelectCommand.Parameters.Add("#id", 123);
'da.Fill(dt);
loFbSqlConnection = Get_DBConnection(psConnectionString)
loFbSqlCommand = New FbCommand(psQuery, loFbSqlConnection)
loFbSqlCommand.CommandTimeout = ciTimeOut
' check for parameterized inline SQL or stored-procedure
If ((psQuery.IndexOf("#") > -1) Or _
(psQuery.ToUpper().IndexOf("SELECT ") > -1)) Then
loFbSqlCommand.CommandType = CommandType.Text
Else
loFbSqlCommand.CommandType = CommandType.StoredProcedure
End If
' create parameters for stored-procedure
If (Not poParameterStructures Is Nothing) Then
For Each loFbSqlHelperParameterStructure As FbSqlHelperParameterStructure In poParameterStructures
loFbSqlParameter = New FbParameter()
loFbSqlParameter.ParameterName = loFbSqlHelperParameterStructure.PARAMETER_NAME
loFbSqlParameter.DbType = loFbSqlHelperParameterStructure.PARAMETER_FBSQLDBTYPE
loFbSqlParameter.Direction = loFbSqlHelperParameterStructure.PARAMETER_DIRECTION
Select Case loFbSqlParameter.Direction
Case ParameterDirection.Input:
loFbSqlParameter.Value = loFbSqlHelperParameterStructure.PARAMETER_VALUE
loFbSqlParameter.Size = loFbSqlHelperParameterStructure.PARAMETER_SIZE
Case ParameterDirection.Output:
loFbSqlParameter.Size = loFbSqlHelperParameterStructure.PARAMETER_SIZE
End Select
loFbSqlCommand.Parameters.Add(loFbSqlParameter)
Next
End If
loFbSqlDataAdapter = New FbDataAdapter()
loFbSqlDataAdapter.SelectCommand = loFbSqlCommand
loDataSet = New DataSet()
loFbSqlDataAdapter.Fill(loDataSet)
Catch loFbSqlException As FbException
Throw loFbSqlException
Catch loException As Exception
Throw loException
Finally
loFbSqlCommand.Dispose()
loFbSqlConnection.Close()
loFbSqlConnection.Dispose()
End Try
poParameterStructures = loParameterStructures
Return (loDataSet)
End Function
The code above has also been successfully used with my other data access layers that support SQL Server, SQL Server CE, MySQL, and PostgreSQL.
However, in the case of the Firebird database, no matter what code-setup from the all of the documentation I have gone through on acquiring a data-set from the Firebird database yields exactly the same error noted above. Thus the code provided here is the original code that is used successfully in my other data access layers.
I finally found the problem with the code above...
If you look in my "Finally" statement you will see the following line of code... "loFbSqlCommand.Dispose()"
Though, this line of code has been used successfully in other data access layers I distribute, for the Firebird ADO.NET data provider it breaks a chain of reference pointers up into the returned dataset, which is a completely separate object from the FbDataAdapter.
Remove this line of code or comment it out and the code works fine.

Razor VB - Pass a Null value if a Request variable is empty

I've been trying so hard to find a solution for this, but no luck. I'm fairly new to VB and SQL, but this shouldn't be too hard. I've inherited a lot of code and there's not too much room to change db attribute types or anything.
I'm trying to run an UPDATE query using parameters in Razor, like so:
Dim updateCommand = "UPDATE [tblJobRating] SET [ProjectManagement] = #0, [ProjComments] = #1, [Schedule] = #2, [SchedComments] = #3 WHERE [JobRatingID] = #4"
All of the columns in question need INT values, but I have one exception where I need to pass it a null value (passing another number or zero won't do). Essentially a "N/A" value for the user.
I assign the variables from Post requests, like so:
Dim projMgmt = Request.Form('projMgmt')
' ...
Dim sched = Request.Form('sched')
I have the "N/A" value posting no value right now (or it can be a string and I can check for IsNumber if need be, I guess). But when I call the query execution, it enters the value as a 0.
db.Execute(updateCommand, projMgmt, projComments, sched, schedComments, ratingId)
It needs to be a NULL value for the backend to work properly. I've tried type checking and passing Nothing, System.Data.SqlTypes.SqlInt32.Null, etc., but it either gives conversion errors or sets to 0. How can I pass it properly?
Edit: I left out the first param in the db.Execute method, passing in the updateCommand. Edited for clarity.
The problem is in your vb variable definition. I assume you have an integer, it needs to be a nullable(of integer) all the way through to the SQL. This can also be written as integer?.

Doing a lot of input validation in VB.NET

I have a form set up where users can enter their booking for a room at my college. I want to validate the user input to avoid SQL injection (my program uses a MS Access database) and also stop numbers and synbols in their name, etc.
I can do the validation fine, but there is to be a lot of validation and then methods executed only if all validation tests come back as true. I did have something like this:
If txtName.Text = "" Then
frmBookErr.SetError(txtName, "Name field cannot be left blank.")
fail = 1
Else
frmBookErr.SetError(txtName, "")
fail = 0
End If
And then check the fail variable, but it obviously gets overridden later in the form if one of the validation tests come back as true.
Can anyone provide some input into this? Thanks.
If you want to avoid SQL injection, use parameterised SQL queries or stored procedures, and do not construct SQL by concatenation.
Set your fail variable at the start of the procedure to 0 then only set it to 1 if something fails...
fail = 0
If txtName.Text = "" Then
frmBookErr.SetError(txtName, "Name field cannot be left blank.")
fail = 1
End If
If txtSomethingElse.Text = String.Empty Then fail = 1
If fail = 0 Then frmBookErr.Clear()
To avoid SQL Injections you need to use something that doesn't directly allow for changes in the SQL Query.
Now this doesn't mean you cant provide values, it means that you strongly specifies what types you want to process to the server.
Example from CodeProject
string commandText = "SELECT * FROM Customers "+
"WHERE Country=#CountryName";
SqlCommand cmd = new SqlCommand(commandText, conn);
cmd.Parameters.Add("#CountryName",countryName);
Instead of providing the countrName as concated string, you actually tell your Sql Command that you provide it as a parameter, which wont allow for any changes in the query itself.

What does the "New ... With" syntax do in VB Linq?

What (if any) is the difference between the results of the following two versions of this VB Linq query?
' assume we have an XElement containing employee details defined somewhere else
Dim ee = From e In someXML.<Employee> _
Select New With {.Surname = e.<Surname>, .Forename = e.<Forename>}
and
Dim ee = From e In someXML.<Employee> _
Select Surname = .Surname = e.<Surname>, .Forename = e.<Forename>
ie what is the point of the New ... With syntax?
I suspect that this has a simple answer, but I can't find it - any links to suitable tutorials or Microsoft documentation would be appreciated.
The difference is that the 1st explicitly creates an anonymous type. The 2nd is a query expression, and may use an existing type rather than creating an anonymous type. From the documentation linked by Cameron MacFarland:
Query expressions do not always require the creation of anonymous types. When possible, they use an existing type to hold the column data. This occurs when the query returns either whole records from the data source, or only one field from each record.
My understanding is that there is no difference.
New With is aimed to out-of-query usage like
Dim X = New With { .Surname = "A", .Forename = "B" }
Specifically for Linq queries, you can skip New With, but it is still useful for other situations. I am not sure, however, since I do not know VB 9 :)
There is no functional difference between the two pieces of code you listed. Under the hood both pieces code will use an anonymous type to return the data from the query.
The first piece of code merely makes the use of an anonymous type explicit. The reason this syntax is allowed is that it's possible to return any type from a Select clause. But the type must be used explicitly.
Dim x = From it in SomeCollection Select New Student With { .Name = it.Name }
Joel is incorrect in his statement that the second query may use an existing type. Without an explicit type, a select clause which uses an explicit property name will always return an anonymous type.
They're called Anonymous Types.
The main reason for their use is to keep the data from a query in a single object, so the iterators can continue to iterate over a list of objects.
They tend to work as temporary types for storage in the middle of a large or multi-part LINQ query.
There is no difference. The compiler will infer the anonymous type.
You most likely want to return the Value of the elements as in e.<Surname>.Value, which returns a String instead of an XElement.
Your 2nd example could be simplified as
Dim ee = From e In someXML.<Employee> _
Select e.<Surname>.Value, e.<Forename>.Value
because the compiler will also infer the names of the members of the anonymous type.
However, if you have the following class
Class Employee
Private _surname As String
Public Property Surname() As String
Get
Return _surname
End Get
Set(ByVal value As String)
_surname = value
End Set
End Property
Private _forename As String
Public Property Forename() As String
Get
Return _forename
End Get
Set(ByVal value As String)
_forename = value
End Set
End Property
End Class
Then you could change the 1st query to produce an IQueryable(Of Employee) instead of the anonymous type by using New ... With like so:
Dim ee = From e In someXML.<Employee> _
Select New Employee With {.Surname = e.<Surname>.Value, _
.Forename = e.<Forename>.Value}
One difference is that Anonymous types aren't serializable.