Sql select statement with two conditions in visual basic - sql-server

I am trying to retrieve Two Columns; App_ID & App_Slot from a table su_Appointments from an sql server database using vb, and I have two conditions the Date and Time; App_Date & App_Time for the query, now when I run the query it throws an error saying : Incorrect syntax near '2014'. The query is as follows
I am going to store App_ID into the variable AP_ID
CODE
Using Query As New SqlCommand("Select App_ID From su_Appointments Where (App_Date = ' and App_Time = ' )" & DT & TM, sqlcon)
sqlcon.Open()
Dim dr As SqlDataReader = Query.ExecuteReader()
While dr.Read()
AP_ID = dr(0)
End While
End Using
sqlcon.Close()

Well, your syntax is effectively wrong
A SELECT statement requires
SELECT <fieldA>, <FieldB>, ....
FROM <Table>
WHERE <FieldX> = <Condition1> AND <FieldZ> = <Condition2>
But, a part from this basic error, you need to start using a parameterized query approach
Using sqlcon = new SqlConnection(.....connectionstring...)
Dim cmdText = "Select App_ID From su_Appointments Where App_Date =#dt and App_Time = #tm"
Using Query = New SqlCommand(cmdText, sqlcon)
sqlcon.Open()
Query.Parameters.AddWithValue("#dt", DT)
Query.Parameters.AddWithValue("#tm", TM)
Using dr =Query.ExecuteReader()
While dr.Read()
AP_ID = dr(0)
End While
End Using
End Using
End Using
With a parameterized query, you get many benefits. There is no possibility of Sql Injection Attacks, the text of your command is more clear and understandable, the parameters are treated for correct quoting by the code itself so you don't need to check for single quotes inside your strings, or format correctly dates and decimal numbers.
Eventually, you could encounter a different problem. If your columns App_Date and App_Time are of type datetime then you need to pass parameters of the appropriate type, not simply strings. Instead if, these fields are of type nvarchar (or some other kind of text type) then you pass strings but you will have problems storing and querying correctly in these fields.

Related

How to pass a date time value to SQL without a conversion error

I'm doing a windows form using Visual Basic.NET, SQL Server 2016, Visual Studio 2017.
I have been trying to fix this problem and already tried set dateformat mdy in SQL Server Management Studio query, but the dates on the table I have are still in this format: 2022-07-17 00:00:00.000. Does this have anything to do with this error when trying to insert something while running the project?
Everyone says stuff along the line "datetime doesn't work with yy/mm/dd or dd/mm/yy, use mm/dd/yy instead". But nobody says how you actually change/fix it in the database or Visual Studio.
I never found this error while using MySQL when I was studying and doing stuff on other languages, so this datetime thing is really getting desperate. Any insight on how to actually fix this error is greatly appreciated.
Code:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim someid As Integer = TextCode.Text
Dim descri As String = TextDescription.Text
Dim somedate As DateTime = DateTimePickerinsert.Text
Dim value As String = TextValue.Text
Dim stock As String = TextStock.Text
Dim warehouse As String = ComboWarehouse.Text
con.Open()
Dim command As New SqlCommand("Insert into Item values('" & someid & "','" & descri & "','" & somedate & "','" & value & "','" & stock & "','" & warehouse & "')", con)
command.ExecuteNonQuery()
con.Close()
MessageBox.Show("Inserted succesfully")
LoadDataInGrid()
End Sub
I get
System.Data.SqlClient.SqlException: 'The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
The statement has been terminated.' error on the line: command.ExecuteNonQuery()
You should use proper parameterization, keeping your dates as actual DateTime values, rather than strings, likewise for numbers. Otherwise you will get SQL injection problems, this is not just a security issue but also about correctness.
The parameter values should be cast to the correct type before you send them, and the parameter objects should be declared with the correct SqlDbType and precision/length also.
You should also create and dispose your connection object, rather than keeping a global connection open, which is wasteful. Automatic connection pooling will ensure efficient usage of available connections.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim query As String = "
INSERT INTO Item (someid, descri, somedate, value, stock, warehouse)
VALUES (#id, #description, #date, #value, #stock, #warehouse)
"
Dim someid As Integer = Integer.Parse(TextCode.Text)
Dim somedate As DateTime = DateTimePickerinsert.Value
Dim value As Decimal = Decimal.Parse(TextValue.Text)
Dim stock As Integer = Integer.Parse(TextStock.Text)
Using con As new SqlConnection("YourConnectionString"),
command As New SqlCommand(query, con)
command.Parameters.Add("#id", SqlDbType.Int).Value = someid
command.Parameters.Add("#description", SqlDbType.VarChar, 100).Value = If(TextDescription.Text, DBNull.Value)
command.Parameters.Add("#date", SqlDbType.DateTime).Value = somedate
command.Parameters.Add("#value", SqlDbType.Decimal).Value = value
command.Parameters.Add("#stock", SqlDbType.Int).Value = stock
command.Parameters.Add("#warehouse", SqlDbType.VarChar, 50).Value = If(ComboWarehouse.Text, DBNull.Value)
con.Open()
command.ExecuteNonQuery()
End Using
MessageBox.Show("Inserted succesfully")
LoadDataInGrid()
End Sub
As far as viewing the results in SSMS: datetime values don't have an inherent format. SSMS will have a default way of displaying them, but you can show them any way you like by converting them using CONVERT, or in VB using ToString
You are seriously entertaining SQL Injection using that Code.
You don't directly insert data to SQL database from your Windows Controls.
Use SQL Connection Parameters to house the values.
That way, any text in the incoming data will not be evaluated as a SQL Script but a text literal.
Hackers could assign SQL Script to your TextDescription.Text
like "Exec 'Delete From XXX '"
and it will be executed.
Dim descri As String = TextDescription.Text
Use SQL Connection Parameters to house the values.
You may run into many issues while using strings as dates. If you are connecting to a stored procedure or just executing SQL via SqlClient or ODBC, one way to fix this error is to use Cast in your SQL string to convert the date string to something that the server will understand. ex:
Insert Into MyTable (MyID, MyDate) Values (#MyID, Cast(#MyDate as datetime));
or,
Insert Into MyTable (MyID, MyDate) Values (123, Cast('2022-03-14 14:12:00' as datetime));
It will be more forgiving on different formats that you might use.

ADO Parameterized Queries with Subqueries Error

I have a legacy classic ASP application running with SQL Server 2012 (also tested with 2016) that I am trying to switch over to using parameterized queries. All the site's queries run through a function which expects a sql statement as a string with parameters represented by question marks as well as an array of those parameters. The function currently filters the parameters to make them sql safe and puts them into the sql string before executing the statement.
Given this, I thought it would be pretty straightforward to switch this to parameterized queries. Initial testing looked good, and everything appeared to be working properly until I hit a sql statement with parameters in subqueries.
Here's a test sample of what works:
Const connectionString = "Provider=SQLNCLI11; DataTypeCompatibility=80; Server=********; Database=********; UID=*******; PWD=*******"
Dim sql, productId, parameters
sql = "SELECT SKU FROM Products WHERE ProductId = ?"
productId = 3
parameters = Array(productId)
Dim conn
Set conn = Server.CreateObject("ADODB.Connection")
conn.Open connectionString
Dim cmd
Set cmd = Server.CreateObject("ADODB.Command")
cmd.ActiveConnection = conn
cmd.CommandText = sql
cmd.Parameters.Refresh
Dim rs
Set rs = cmd.Execute(, parameters)
Response.Write("SKU: " & rs("SKU"))
No problem, this returns the SKU as expected. However, if I use a subquery:
Const connectionString = "Provider=SQLNCLI11; DataTypeCompatibility=80; Server=********; Database=********; UID=*******; PWD=*******"
Dim sql, productId, parameters
sql = "SELECT SKU FROM ( SELECT SKU FROM Products WHERE ProductId = ? ) AS P"
productId = 3
parameters = Array(productId)
Dim conn
Set conn = Server.CreateObject("ADODB.Connection")
conn.Open connectionString
Dim cmd
Set cmd = Server.CreateObject("ADODB.Command")
cmd.ActiveConnection = conn
cmd.CommandText = sql
cmd.Parameters.Refresh
Dim rs
Set rs = cmd.Execute(, parameters)
Response.Write("SKU: " & rs("SKU"))
It throws an error on the cmd.Parameters.Refresh line:
Microsoft VBScript runtime error '0x80004005'
Microsoft SQL Server Native Client 11.0
Syntax error, permission violation, or other nonspecific error
If I check cmd.Parameters.Count in the first sample, I correctly get 1. In the bad sample it throws the same error.
Is there any explanation as to why putting the parameter into a subquery causes problems with the parameter collection? I did try manually adding the parameter to the Parameters collection, and that works fine, but it means modifying hundreds of existing sql calls, so for the moment the cmd.Parameters.Refresh round-trip was worth the expense.
For anyone who might stumble across this, I finally figured out the issue thanks to a co-worker. It turns out there is nothing wrong with the code, but rather with the connection string. I somehow left it out of the sample code, but my connection strings included "DataTypeCompatability=80". If that is present, the code throws the error. However, if I remove it, the error no longer occurs and I get the results back as suspected.
My understanding from this KB article on using ADO with the native client is that DataTypeCompatability should be included to ensure newer data types work properly, but so far I have not found any issues with removing it.
You can give cmd.execute what you want, but I haven't used it in a long time.
cmd.execute("SELECT SKU FROM ( SELECT SKU FROM Products WHERE ProductId = ? ) AS P", Array(productId))

SQL command to using SUM() with the Where clause

Please I am typing from my phone as I am not with the laptop, don't decrease my reputation.
I have a column in my SQL server database named Total_Amount, I want to get the Sum(Total_Amount) for a specific day for a specific Cashier. I am able to get for specific cashier only but if I want to get using the WHERE clause for both cashier AND date, it returns nothing. The command works well in SQL server Management Studio but from the VB. nET, it does not.
The below is my code.
Dim conn As New SqlConnection("data.
source=PRECIOUSMUM\MSSQLSERVER_1; initial.
catalog=inventory; user id=mantics;
password=emudeji;")
Try
'Dim Total_Amountss As Double
conn.Open()
Dim cmd = New SqlCommand
With cmd
.Connection = conn
.CommandText = "SELECT SUM(Total_Amount)
AS Total_Amount FROM tblOrder WHERE.
(cashier=#cashier) AND (Order_date=#Order_date)"
.Parameters.AddWithValue("#cashier",
lbl_Cashier_Name.Text)
.Parameters.AddWithValue("#Order_date", Date.Today.ToString)
'.Parameters.AddWithValue("#enddate", dtpicker.Value.Date)
End With
Dim dr As SqlDataReader
dr = cmd.ExecuteReader
dr.Read()
If IsDBNull(dr("Total_Amount")) Then
lbl_cashier_Totalsales.Text = "N0.00"
Else
Dim str As Double
str = dr.Item("Total_Amount")
lbl_cashier_Totalsales.Text = FormatCurrency(dr.Item("Total_Amount"))
End If
The first thing that it is clearly wrong is the fact that you use a string to query a Date column. This is never correct because the string is something that you use to display the date to your end users. It is not how the database (or .NET) stores the date value. So, a Date column is not queried using a string but passing directly the C# DateTime value in the Add method and specifying the DataType of the parameter.
A second possible error is caused by the fact that if you have also stored the Time part then passing a Date like Today will never match any row but only the ones that have 00:00:00 as their time value.
You need to query for >= midnight of the starting date and < of the following day.
This considerations will give a query like this:
With cmd
.Connection = conn
.CommandText = "SELECT SUM(Total_Amount) AS Total_Amount
FROM tblOrder
WHERE (cashier=#cashier) AND
(Order_date >= #StartDate AND
Order_date < #EndDate)"
.Parameters.Add("#cashier", SqlDbType.NVarChar).Value = lbl_Cashier_Name.Text
.Parameters.Add("#StartDate", SqlDbType.DateTime).Value = Date.Today
.Parameters.Add("#EndDate", SqlDbType.DateTime).Value = Date.Today.AddDays(1)
End With

Updating ms access database [duplicate]

I am trying to create an SQL statement using user-supplied data. I use code similar to this in C#:
var sql = "INSERT INTO myTable (myField1, myField2) " +
"VALUES ('" + someVariable + "', '" + someTextBox.Text + "');";
var cmd = new SqlCommand(sql, myDbConnection);
cmd.ExecuteNonQuery();
and this in VB.NET:
Dim sql = "INSERT INTO myTable (myField1, myField2) " &
"VALUES ('" & someVariable & "', '" & someTextBox.Text & "');"
Dim cmd As New SqlCommand(sql, myDbConnection)
cmd.ExecuteNonQuery()
However,
this fails when the user input contains single quotes (e.g. O'Brien),
I cannot seem to get the format right when inserting DateTime values and
people keep telling me that I should not do this because of "SQL injection".
How do I do it "the right way"?
Use parameterized SQL.
Examples
(These examples are in C#, see below for the VB.NET version.)
Replace your string concatenations with #... placeholders and, afterwards, add the values to your SqlCommand. You can choose the name of the placeholders freely, just make sure that they start with the # sign. Your example would look like this:
var sql = "INSERT INTO myTable (myField1, myField2) " +
"VALUES (#someValue, #someOtherValue);";
using (var cmd = new SqlCommand(sql, myDbConnection))
{
cmd.Parameters.AddWithValue("#someValue", someVariable);
cmd.Parameters.AddWithValue("#someOtherValue", someTextBox.Text);
cmd.ExecuteNonQuery();
}
The same pattern is used for other kinds of SQL statements:
var sql = "UPDATE myTable SET myField1 = #newValue WHERE myField2 = #someValue;";
// see above, same as INSERT
or
var sql = "SELECT myField1, myField2 FROM myTable WHERE myField3 = #someValue;";
using (var cmd = new SqlCommand(sql, myDbConnection))
{
cmd.Parameters.AddWithValue("#someValue", someVariable);
using (var reader = cmd.ExecuteReader())
{
...
}
// Alternatively: object result = cmd.ExecuteScalar();
// if you are only interested in one value of one row.
}
A word of caution: AddWithValue is a good starting point and works fine in most cases. However, the value you pass in needs to exactly match the data type of the corresponding database field. Otherwise, you might end up in a situation where the conversion prevents your query from using an index. Note that some SQL Server data types, such as char/varchar (without preceding "n") or date do not have a corresponding .NET data type. In those cases, Add with the correct data type should be used instead.
Why should I do that?
It's more secure: It stops SQL injection. (Bobby Tables won't delete your student records.)
It's easier: No need to fiddle around with single and double quotes or to look up the correct string representation of date literals.
It's more stable: O'Brien won't crash your application just because he insists on keeping his strange name.
Other database access libraries
If you use an OleDbCommand instead of an SqlCommand (e.g., if you are using an MS Access database), use ? instead of #... as the placeholder in the SQL. In that case, the first parameter of AddWithValue is irrelevant; instead, you need to add the parameters in the correct order. The same is true for OdbcCommand.
Entity Framework also supports parameterized queries.
VB.NET Example Code
This is the example code for the wiki answer in vb.net, assuming Option Strict On and Option Infer On.
INSERT
Dim sql = "INSERT INTO myTable (myField1, myField2) " &
"VALUES (#someValue, #someOtherValue);"
Using cmd As New SqlCommand(sql, myDbConnection)
cmd.Parameters.AddWithValue("#someValue", someVariable)
cmd.Parameters.AddWithValue("#someOtherValue", someTextBox.Text)
cmd.ExecuteNonQuery()
End Using
UPDATE
Dim sql = "UPDATE myTable SET myField1 = #newValue WHERE myField2 = #someValue;"
' see above, same as INSERT
SELECT
Dim sql = "SELECT myField1, myField2 FROM myTable WHERE myField3 = #someValue;"
Using cmd As New SqlCommand(sql, myDbConnection)
cmd.Parameters.AddWithValue("#someValue", someVariable)
Using reader = cmd.ExecuteReader()
' ...
End Using
' Alternatively: Dim result = cmd.ExecuteScalar()
' if you are only interested in one value of one row.
End Using

SQL Server text column affects results returned to classic ASP

Using classic asp, I am trying to query a SQL Server database like so:
strSQL = "select column_1, column_2, column_3, column_4 from someview " &_
"where RecordNo=" & i
set rs=conn.Execute(strSQL)
if not rs.eof then
A = rs("column_1")
B = rs("column_2")
C = rs("column_3")
D = rs("column_4")
end if
Column_3 is an NText type, the other columns are varchar or int (sometimes there may be more than 4 columns returned) but the query only returns 1 record because of the where clause.
On the ASP page the results vary - sometimes A,B,D are populated, sometimes not - but all columns in the view contain data (when I query the SQL Server I see the expected results - all columns do contain data). If I remove column_3 which is NText from the strSQL everything works fine.
I've seen this behaviour on a couple other pages in the past. If I modify the ASP to get column_3 separately:
strSQL = "select column_3 from someview where RecordNo=" & i
The NText data is returned correctly.
Is there a maximum record length to a SQL Server recordset returned to classic ASP? Apart from splitting out the NTEXT into a separate query, is there anything else I can do?
EDIT: It just occured to me to try changing the connection string - inspired by this comment on a similar problem - the connection is via SQL Server ODBC Driver (Driver={SQL Server};).
I have had this problem. Microsoft acknowledge it somewhere on their website.
If you put the NText column last in the SELECT list, you will be able to access it ok.
However, your code cannot access any other columns after it has read the NText value. Once you move to the next row of the recordset you're OK again.
Best solution is to change your connection string though, and use something more modern. That solves the problem in the best way!
To avoid using the recordset, try this:
For 1 record returned:
arr = rs.Getrows
if IsArray(arr) then
A = arr(0)
B = arr(1)
C = arr(2)
D = arr(3)
end if
For more records:
aryList = rec.GetRows
iCount = Ubound(aryList,2)
For i = 0 to iCount
A = aryList(0,i)
B = aryList(1,i)
C = aryList(2,i)
D = aryList(3,i)
' Do something with A,B,C,D
Next
casting ntext to varchar will do the job.
You're mixing unicode data (the ntext column) with non-unicode (varchar). That may be the reason, since the ASP page has to decide which to use.
Try and use either one or the other (casting non-unicode data to unicode may be the better option).
One extra tip for those who are working with older code:
When a recordset's column value is blank using ADO/ASP and you have a single line of data, you can bypass this problem by using a parameterised Command statement and returning the string value into a variable:
Some hand-typed code hopefully explains what I mean:
' DB connection
Set objCon = Server.CreateObject("ADODB.Connection")
objCon.CursorLocation = adUseClient
objCon.Open pubDbConnString
' statement construction
Set cmd = Server.CreateObject("ADODB.Command")
Set cmd.ActiveConnection = objCon
cmd.CommandText = "SELECT ?=T.Column1, ?=T.Column2 From Table T WHERE ID=?"
cmd.CommandType = adCmdText
' add parameters
cmd.Parameters.Append cmd.CreateParameter("#column1Data", adVarChar, adParamOutput, 8000)
cmd.Parameters.Append cmd.CreateParameter("#column2Data", adTinyInt, adParamOutput)
cmd.Parameters.Append cmd.CreateParameter("#id", adBigInt, adParamInput)
cmd.Parameters("#id").value = 1
set objRS = cmd.Execute
#column1Data will contain the large string. objRS will actually not have any records in it, so be mindful of this.
In theory, this should also work with named parameters with the same results, but I have not tested this.

Resources