insert null into date column in database - sql

I've got a gridview in vb.net and am trying to insert a null value into a database column - unfortunately, i keep seeing 1900-01-01 being inserted. below is my code and i need help.
Protected Sub gvPOItems_RowCommand(sender As Object, e As System.Web.UI.WebControls.GridViewCommandEventArgs) Handles gvPOItems.RowCommand
If (e.CommandName = "save") Then
For i As Integer = 0 To gvPOItems.Rows.Count - 1
Dim row As GridViewRow = gvPOItems.Rows(i)
Dim value3 As String = DirectCast(row.Cells(9).FindControl("txtbxExpireDate"), TextBox).Text.Replace("'", "''")
If String.IsNullOrEmpty(value3) Or value3.ToString() Is Nothing Then
value3 = DBNull.Value.ToString()
End If
Next
End Sub

It works when the value isn't null, because you're injecting the value in that string:
INSERT INTO table (datecolumn) VALUES ('2014-08-04')
However, DBNull.Value.ToString() resolves to an empty string. When you then try and insert said empty string into your database via a SQL-injection-prone approach, you're really running a query something like:
INSERT INTO table (datecolumn) VALUES ('')
And that puts in the default value of 1900-01-01. What you need is:
INSERT INTO table (datecolumn) VALUES (NULL)
You need to parameterize your queries. Then you can pass Nothing directly to your SQL command, and it will work. Aside from that, you have to shift some abstraction so that you only add ' characters if the argument is not Nothing, and if it is, then you need to pass the string NULL.
As for the code you do have, there are some bugs here, too.
Dim value3 As String = DirectCast(row.Cells(9).FindControl("txtbxExpireDate"), TextBox).Text.Replace("'", "''")
If String.IsNullOrEmpty(value3) Or value3.ToString() Is Nothing Then
value3 = DBNull.Value.ToString()
End If
value3 is a string, and as per documentation, string.ToString() returns an unconverted instance of the original value. In other words, String.IsNullOrEmpty(value3) already does what you're checking for in your latter condition, except if the latter condition were ever true it would throw a NullReferenceException (or whatever the equivalent of that is in VB, I don't know whether it uses Nothing because I've literally never written anything in VB before).
Next, you should be aware that value3 will never be Nothing. It comes directly from a TextBox.Text property, and will consequently be at most an empty string. That's fine, because you check for that in IsNullOrEmpty, but since you added on the Is Nothing I thought I'd better point that out.
So basically, long story short, parameterize your queries. For all our sakes. But if you absolutely aren't going to, this should fix this particular problem:
Dim value3 As String = DirectCast(row.Cells(9).FindControl("txtbxExpireDate"), TextBox).Text
If String.IsNullOrEmpty(value3)
value3 = "NULL"
Else
value3 = "'" + value3.Replace("'", "''") + "'"
End If
You'll then have to adjust your later code to not add in those quotes, itself.

Related

Inserting empty values as string to sql table

I have a project which has a Class which has some members as string which equal "". My teacher had shown examples with Class members declared as String = "NULL". I try to insert to SQL and I get this message:
Error insertion... Error converting data type varchar to numeric.
I know many will suggest to use parameterized values for inserting but I like to finish the way I started this.
I have tried to cast query string Object values with CDec, CInt, Cbit and still no clue how to do it right because I got Exceptions for trying to cast an "".
Also, I have changed to default values of members like this;
Dim NumberOfPackage As String = ""
instead of;
Dim NumberOfPackage As String = "NULL"
Some Columns in SQL table had ALLOW NULL checkBox set to NOT allowed, I changed the design of the Table to make it easier for insertion.
The field in database have been set to not null? I think that dbnull.value do what you want
I think I understand what is happening. Your variable NumberOfPackage contains numbers but since you want to insert NULL sometime, you made it a string.
NumberOfPackage = "23"
NumberOfPackage = "NULL"
"INSERT INTO table (column) VALUES (" & NumberOfPackage & ");"
But since you use a string, you might be trying to do
"INSERT INTO table (column) VALUES ('" & NumberOfPackage & "');"
Which would cause the error on NULL since your column is a number and it is trying to do
"INSERT INTO table (column) VALUES ('NULL');"
Stick with my first example (without the ') if you really want to concatenate strings but everything would be much easier if you used parameters. By using parameters, it would be easier to keep NumberOfPackage as a decimal or a decimal? and do proper math with it.
To insert a NULL value, it must be without the apostrophes like #RichBenner and #the_lotus state:
Dim myCommand As String
myCommand = "INSERT INTO dbo.Foo (Foo_Text) VALUES (NULL);"
'The query for an empty string would look like this
myCommand = "INSERT INTO dbo.Foo (Foo_Text) VALUES ('');"
col1 in my Builder database is datatype int and nulls are allowed. This is how it is done with Parameters. TextBox1 will hold the number of packages and TextBox2 the name. I used the object datatype so it could hold either DBNull.Value or an Integer.
Private Sub InsertRecord()
Dim myValue As Object
Dim myInt As Integer
If Integer.TryParse(TextBox1.Text, myInt) Then
myValue = myInt
Else
myValue = DBNull.Value
End If
Using cn As New SqlConnection(My.Settings.BuildersConnectio)
Using cmd As New SqlCommand("Insert Into Builders (BuilderName, col1) VALUES (#Name, #NoOfPackages);", cn)
cmd.Parameters.Add("#Name", SqlDbType.VarChar, 100).Value = TextBox2.Text
cmd.Parameters.Add("NoOfPackages", SqlDbType.Int).Value = myValue
cn.Open()
cmd.ExecuteNonQuery()
End Using
End Using
End Sub

Null Check a column value that can be Null without two DLookups?

How do I remove one DLookup from code similar to this example?
Dim sCustomerName as String
If IsNull(DLookup("sName", "tblCustomers", "iCustomerID=12345")) Then
MsgBox "Customer name is null!"
Else
sCustomerName = DLookup("sName", "tblCustomers", "iCustomerID=12345")
End If
In VBA, reading a column value that can be Null (or missing) into a variable, and then comparing it to Null gives an error. Is there a better way to do these checks?
Obviously, if there's no performance penalty, it's not bad code because of the performance hit. Is there?
Even if not, it has duplicated/copy-pasted code. How can it be rewritten to avoid this?
A Variant data type can store Null values. Try to get the value and check if Null.
Dim customerName As Variant
customerName = DLookup("sName", "tblCustomers", "iCustomerID=12345")
If Not IsNull(customerName) Then
'do stuff
End If
Another approach is the Nz() function:
You can use the Nz function to return zero, a zero-length string (" "), or another specified value when a Variant is Null.
Dim customerName As String
customerName = Nz(DLookup("sName", "tblCustomers", "iCustomerID=12345"), vbNullString)
If customerName <> vbNullString Then
'do stuff
End If

Retrieving the Query used for a OleDBCommand

I'm currently using the following VB code to make a query against an Access Database, I would like to know is it possible to obtain what the SELECT statement that is being run and send that output to the console.
Dim QuestionConnectionQuery = New OleDb.OleDbCommand("SELECT Questions.QuestionID FROM Questions WHERE Questions.QuestionDifficulty=[X] AND ( Questions.LastDateRevealed Is Null OR Questions.LastDateRevealed < DateAdd('d',-2,Date() ) AND Questions.LastUsedKey NOT LIKE ""[Y]"" );", QuestionConnection)
QuestionConnectionQuery.Parameters.AddWithValue("X", questionDifficulty.ToString)
QuestionConnectionQuery.Parameters.AddWithValue("Y", strDatabaseKey)
Right now when I try to use: Console.WriteLine("Query: " & QuestionConnectionQuery.ToString)
I only get this:
Loop Question #1
Query: System.Data.OleDb.OleDbCommand
The short version comes down to this:
QuestionConnectionQuery.ToString
The QuestionConnectionQuery object is much more than just the text of your command. It's also the parameters, execution type, a timeout, and a number of other things. If you want the command text, ask for it:
QuestionConnectionQuery.CommandText
But that's only the first issue here.
Right now, your parameters are not defined correctly, so this query will never succeed. OleDb uses ? as the parameter placeholder. Then the order in which you add the parameters to the collection has to match the order in which the placeholder shows in the query. The code in your question just has X and Y directly for parameter placeholders. You want to do this:
Dim QuestionConnectionQuery AS New OleDb.OleDbCommand("SELECT Questions.QuestionID FROM Questions WHERE Questions.QuestionDifficulty= ? AND ( Questions.LastDateRevealed Is Null OR Questions.LastDateRevealed < DateAdd('d',-2, Date() ) AND Questions.LastUsedKey NOT LIKE ? );", QuestionConnection)
QuestionConnectionQuery.Parameters.Add("?", OleDbType.Integer).Value = questionDifficulty
QuestionConnectionQuery.Parameters.Add("?", OleDbType.VarChar, 20).Value = strDatabaseKey
I had to guess at the type and lengths of your parameters. Adjust that to match the actual types and lengths of the columns in your database.
Once you have made these fixes, this next thing to understand is that the completed query never exists. The whole point of parameterized queries is parameter data is never substituted directly into the sql command text, not even by the database engine. This keeps user data separated from the command and prevents any possibility of sql injection attacks.
While I'm here, you may also want to examine the WHERE conditions in your query. The WHERE clause currently looks like this:
WHERE A AND ( B OR C AND D )
Whenever you see an AND next to an OR like that, within the same parenthetical section, I have to stop and ask if that's what is really intended, or whether you should instead close the parentheses before the final AND condition:
WHERE A AND (B OR C) AND D
This will fetch the command text and swap in the parameter values. It isnt necessarily valid SQL, the NET Provider objects haven't escaped things yet, but you can see what the values are and what the order is for debugging:
Function GetFullCommandSQL(cmd As Data.Common.DbCommand) As String
Dim sql = cmd.CommandText
For Each p As Data.Common.DbParameter In cmd.Parameters
If sql.Contains(p.ParameterName) AndAlso p.Value IsNot Nothing Then
If p.Value.GetType Is GetType(String) Then
sql = sql.Replace(p.ParameterName,
String.Format("'{0}'", p.Value.ToString))
Else
sql = sql.Replace(p.ParameterName, p.Value.ToString)
End If
End If
Next
Return sql
End Function
Given the following SQL:
Dim sql = "INSERT INTO Demo (`Name`, StartDate, HP, Active) VALUES (#name, #start, #hp, #act)"
After parameters are supplied, you can get back this:
INSERT INTO Demo (`Name`, StartDate, HP, Active) VALUES ('johnny', 2/11/2010 12:00:00 AM, 6, True)
It would need to be modified to work with OleDB '?' type parameter placeholders. But it will work if the DbCommand object was created by an OleDBCOmmandBuilder, since it uses "#pN" internally.
To get or set the text of the command that will be run, use the CommandText property.
To print the results, you need to actually execute the query. Call its ExecuteReader method to get an OleDbDataReader. You can use that to iterate over the rows.
Dim reader = QuestionConnectionQuery.ExecuteReader()
While reader.Read
Console.WriteLine(reader.GetValue(0))
End While
reader.Close()
If you know the data type of the column(s) ahead of time, you can use the type-specific methods like GetInt32. If you have multiple columns, change the 0 in this example to the zero-based index of the column you want.

Convert a two-table exists query from SQL to Linq using dynamic fields in the subquery

I'm trying to query old Access database tables and compare them with SQL Server tables.
They often don't have primary keys, or they have extra fields that had some purpose in the nineties, etc., or the new tables have new fields, etc.
I need to find records - based on a set of fields specified at runtime - that are in one table but not another.
So, I do this kind of query all the time in SQL, when I'm comparing data in different tables:
dim fields_i_care_about as string = "field1, field2, field3"
'This kind of thing gets set by a caller, can be any number of fields, depends on the
'table
dim s as string= ""
dim flds = fields_i_care_about.split(",")
for i as integer = 0 to ubound(flds)
if s > "" then s += " AND "
s += " dysfunctional_database_table." & flds(i) & "=current_database_table." & flds(i)
next
s = "SELECT * from dysfunctional_database_table where not exists (SELECT * from current_database_table WHERE " & s & ")"
====
I'm trying to do this using Linq because it seems like some of the datatype problems with two different database types become less of a headache,
but I'm new to Linq and totally stuck.
I got as far as this:
Put old and new tables into datatables as dt1 and dt2
Dim new_records = _
From new_recs In dt2.AsEnumerable
Where Not ( _
From old_recs In dt1.AsEnumerable Where old_recs(field1) = new_recs(field1) AndAlso old_recs(field2) = new_recs(field2)).Any
Select new_recs
But I can't figure out how to put this part in on the fly -
old_recs(field1) = new_recs(field1) AndAlso old_recs(field2) = new_recs(field2)
So far I've tried:
putting the fields I want to compare and making them a string and just putting that string in as a variable ( I thought I was probably cheating, and I guess I was)
dim str = old_recs(field1) = new_recs(field1) AndAlso old_recs(field2) = new_recs(field2)
From new_recs In dt2.AsEnumerable
Where Not ( _
From old_recs In dt1.AsEnumerable Where str).Any
Select new_recs
It tells me it can't convert a Boolean -
Is there any way to do this without Linq expressions? They seem far more complex than what I'm trying to do here, and they take a lot of code, and also I can't seem to find examples of Expressions where we're comparing two fields in a subquery.
Is there a simpler way? I know I could do the usual EXISTS query using JOIN or IN - in this case I don't need the query to be super fast or anything. And I don't need to use a DataTable or DataSet - I can put the data in some other kind of object.
So I found a lot of sample code that used MethodInfo and reflection and things like that, but I couldn't get any of it to work - these Datarows have a Field method but it requires that you add an (of object) argument before the field name argument and that's tricky to do.
So I'm not sure if this solution is the most efficient way, but at least it works. I'd be interested in finding out whether this way of doing it is efficient and why or why not. It seemed like most people used reflection to do this kind of thing, but I couldn't get that working properly and anyway what I'm trying to do is pretty simple while those methods were pretty complex. I suppose I'm doing Linq with a SQL mindset, but anyway it works.
Dim f As Func(Of DataRow, DataRow, String, Boolean) = Function(d1 As DataRow, d2 As DataRow, s As String)
Dim fields = Split(s, ",")
Dim results As Boolean = True
For k As Integer = 0 To UBound(fields)
Dim obj = DataRowExtensions.Field(Of Object)(d1, fields(k))
Dim obj2 = DataRowExtensions.Field(Of Object)(d2, fields(k))
If obj <> obj2 Then results = False : Exit For
Next
Return results
End Function
Dim new_records = _
From new_recs In dt2.AsEnumerable.AsQueryable()
Where Not ( _
From old_recs In dt1.AsEnumerable.AsQueryable Where f(old_recs, new_recs, id_key)).Any
Select new_recs
Try
Return new_records.CopyToDataTable
Catch ex As Exception
Stop
End Try

DataTable Select(String) Function Help VB .NET

I made a datatable with 2 columns a transactionTime column and a numberOfTransactions column. I made the table with the pre-defined transaction times and want to add the number of transactions from an XML file. I have gotten through the XML file and want to add the data to the correct row. Here is the function:
Function AddRow(ByVal timeOfTransaction As String, ByVal numberOfTransactions As String, ByRef dataTableOfTransactions As DataTable) As String
Dim row() As DataRow = dataTableOfTransactions.Select("transactionTime = timeOfTransaction")
If row(0) IsNot Nothing Then
row(0)("numberOfTransactions") = numberOfTransactions
End If
Return Nothing
End Function
When I run this it overwrites the first element in the table's numberOfTransactions coloumn. I know it has to do with the "transactionTime = timeOfTransaction" part but I can't seem to get it to read timeOfTransaction as a reference to a string instead of a literal. Any help would be much appreciated. Thanks!
You need to write something like this :
Dim row() As DataRow = dataTableOfTransactions.Select("transactionTime=#" & timeOfTransaction & "#")
But be careful with your date/month or month/date format, it depends of your regional settings.
row(0)("numberOfTransactions") = numberOfTransactions
Right there you are telling the program to overwrite that value with number of transactions.
If you want that value you need to set it to something, not set something to it.
Also, if you want your select to work properly try doing it like this
dataTableOfTransactions.Select("transactionTime = " + timeOfTransaction)