OLEDB query to SQL Server fails - asp.net

I have two SQL queries:
A.
SELECT (upper(rtrim(ltrim(lastname))) + upper(rtrim(ltrim(firstname))) +
upper(rtrim(ltrim(middlename))) + rtrim(ltrim(v)) ) AS userCompareStr
FROM atable ;
and
B.
SELECT (upper(rtrim(ltrim(lastname))) + upper(rtrim(ltrim(firstname))) +
upper(rtrim(ltrim(middlename))) + rtrim(ltrim(v)) ) AS userCompareStr
FROM atable WHERE userCompareStr='GAPYLE1111' ;
I have the following code:
Dim sql As String
Dim conn As OleDbConnection
Dim cmd As OleDbDataAdapter
Dim ds As DataSet
Dim tbl As DataTable
conn = " something here "
cmd = New OleDbDataAdapter(sql, conn)
ds = New DataSet
cmd.Fill(ds)
tbl = New DataTable
tbl = ds.Tables(0)
Near as I can tell it seems to work when sql is set to string A, but not when it's set to string B.
This leads me to suspect that there is something wrong with the clause
WHERE userCompareStr='GAPYLE1111'
Can I not use the alias userCompareStr in this way? I can't find any examples of this kind of use, but I do find analogous use when alias is used for table name -- and I don't see anything against that kind of us.

You have three options.
1) repeat what you did in the select in the where
SELECT (upper(rtrim(ltrim(lastname))) + upper(rtrim(ltrim(firstname))) +
upper(rtrim(ltrim(middlename))) + rtrim(ltrim(v)) ) AS userCompareStr
FROM atable
WHERE (upper(rtrim(ltrim(lastname))) + upper(rtrim(ltrim(firstname))) +
upper(rtrim(ltrim(middlename))) + rtrim(ltrim(v)) ) ='GAPYLE1111' ;
2) Use a common table expression
with CTE AS
(SELECT (upper(rtrim(ltrim(lastname))) + upper(rtrim(ltrim(firstname))) +
upper(rtrim(ltrim(middlename))) + rtrim(ltrim(v)) ) AS userCompareStr
FROM atable )
SELECT userCompareStr FROM CTE where userCompareStr = 'GAPYLE1111';
3) Inline query see Maziar Taheri's answer
As an aside I hope 'GAPYLE1111' doesn't come from user input, otherwise you're exposing yourself to SQL Injection attacks. Use parameterized queries instead

No, you cannot use an aliased column in the WHERE clause.
See Using an Alias column in the where clause in ms-sql 2000
(the article is about SQL 2000, but it still applies today)

you cannot use an alias you have set in the select clause, inside the where clause.
try this:
SELECT * FROM
(
SELECT (upper(rtrim(ltrim(lastname))) + upper(rtrim(ltrim(firstname))) + upper(rtrim(ltrim(middlename))) + rtrim(ltrim(v)) ) AS userCompareStr FROM atable)
as nested
WHERE userCompareStr='GAPYLE1111' ;

I stepped away from the problem for a while, worked on something else, and came back to it.
I have solved the primary problem by switching from using oledb to "something else."
I'm not sure what the new (to me) method is called - except maybe "native sqlserver?"
Important points:
Cannot use field name alias in the WHERE clause. (as per maziar and matt)
Conrad's fix #1 worked on OLEDB, but I don't like that method because it's verbose (and the real command is a lot more complicated than the scaled down example I provide here) and there is a LOT of different invocations. Error-prone and hard to read (but works in a pinch).
To get either the WITH or the nested select work I had to switch from OLEDB to "native sqlserver" (or whatever it's called). The WITH (as suggested by Conrad) is my preferred solution - much easier to read. The nested select (suggested by Maziar) also works when I switch from OLEDB to native.
I need to switch to "parameterized queries" to avoid sql injection attacks as noted by Conrad.
Anyway, suggestions above work when I switched to that method.
Instead of using
Provider=SQLOLEDB
I used:
providerName="System.Data.SqlClient"
I now make no reference to oledb (such as oledbadapter), but instead make references to sqlDataAdapter. I ignore the upper, ltrim, and trim functions (because it turns out they weren't the issue) and focus on the WITH which is what oledb had been choking on. Here's what I got to work:
Dim conn As New SqlConnection("server=localhost;database=DB;Integrated Security=SSPI;")
Dim sql As String
Dim da As SqlDataAdapter
Dim ds As DataSet = New DataSet()
Dim tbl As DataTable = New DataTable
conn = New SqlConnection()
conn.ConnectionString = ConfigurationManager.ConnectionStrings("DB").ConnectionString
Sql = " WITH cte AS "
sql = sql & "(lastname + firstname + middlename"
Sql = Sql & " + v) as userCompareStr FROM atable ) "
sql = sql & "SELECT userCompareStr FROM cte WHERE userCompareStr = '" & "GAPYLE1111" & "' ;"
da = New SqlDataAdapter(sql, conn)
da.Fill(ds)
tbl = ds.Tables(0)
TextBox2.Text = sql
If tbl.Rows.Count < 1 Then
TextBox1.Text = "no items"
Else
TextBox1.Text = tbl.Rows.Count & " items selected"
End If
conn.Dispose()
Also, in web.config, I added:
I have not added the SQL Injection fix stuff yet, but I am looking into it and I'm sure now that this is something I need to do. I found some information on that here:
http://msdn.microsoft.com/en-us/library/ff648339.aspx
and here:
http://weblogs.asp.net/cibrax/archive/2006/09/28/Parameterized-Queries-_2800_Oracle_2C00_-SQLServer_2C00_-OleDb_2900_.aspx
but I need some time to experiment with it. Thanks for the help and the pointers.

Related

insert multiple rows with one query using ms access db

I am trying to get multiple rows into a table hence my attempt to get the row number and put it into a for loop, the countC is exactly the same number of rows as the select statement, so the issue is not there
I'm using an oledb connection as my code is in vb asp.net but my database is in ms access 2003
For c As Integer = 1 To countC
Dim cmdstring As String
cmdstring = " INSERT INTO [KN - ProductionMachineAllocation] (BatchNo, ComponentID)
SELECT POH.BatchNo, SSCDD.ComponentID
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY BatchNo ASC) AS rownumber
([KN - ProductionOrderHeader] AS POH
INNER JOIN [FG - End Product Codes] AS EPC
ON POH.ProductID = EPC.ProductID)
INNER JOIN ([KN - ProductionOrderDetails] AS POD
INNER JOIN [FG - Style Size Comp Def Details] AS SSCDD
ON POD.SizeID = SSCDD.SizeID)
ON (POH.BatchNo = POD.BatchNo)
AND (EPC.StyleID = SSCDD.StyleID)
WHERE POH.BatchNo = '" & BatchNo & "'
) AS temptablename
WHERE rownumber IN (" & c & ");"
Dim con As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\Shantara Production IT.mdb")
Dim cmd As New OleDbCommand(cmdstring)
cmd.CommandType = CommandType.Text
cmd.Connection = con
cmd.Connection.Open()
cmd.ExecuteNonQuery()
cmd.Connection.Close()
Next
I found out that ms access doesn't support ROW_NUMBER() so I need to find another going through each row since ms access doesn't support multi row insert by insert into select statement such as mine, any suggestions around my problem?
Most databases are able to do all this work much more efficiently entirely in the database. Certainly in SQL Server I could get entire thing down to a single query. Access is a little different, since vbscript is its procedural language, rather than something more like t-sql. There's still probably a way to do it, but since what you have works, we can at least focus on making that better.
GridViews are visual constructs that will use up extra memory and resources. If Access won't do a real INSERT/SELECT, you can at least read direct from the previous result set into your insert. You can also improve on this significantly by using parameters and re-using a single open connection for all the inserts:
Dim cnString As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\Shantara Production IT.mdb"
Dim SQLDown As String = _
"SELECT DISTINCT POH.BatchNo, SSCDD.ComponentID
FROM ([KN - ProductionOrderHeader] AS POH
INNER Join [FG - End Product Codes] AS EPC
On POH.ProductID = EPC.ProductID)
INNER Join([KN - ProductionOrderDetails] AS POD
INNER Join [FG - Style Size Comp Def Details] AS SSCDD
On POD.SizeID = SSCDD.SizeID)
On (POH.BatchNo = POD.BatchNo)
And (EPC.StyleID = SSCDD.StyleID)
WHERE POH.BatchNo = ? "
Dim SQLUp As String = _
" INSERT INTO [KN - ProductionMachineAllocation]
(BatchNo, ComponentID)
VALUES( ?, ? )"
Dim dt As New DataTable
Using con As New OleDbConnection(cnString), _
cmd As New OleDbCommand(SQLDown, con)
'Guessing at parameter type/length here.
'Use the actual column type and size from your DB
cmd.Parameters.Add("#BatchNo", OleDbType.VarWChar, 10).Value = BatchNo
con.Open()
dt.Load(cmd.ExecuteReader())
End Using
Using con As New OleDbConnection(cnString), _
cmd As New OleDbCommand(SqlUp, con)
'Guessing at parameter types/lengths again
cmd.Parameters.Add("#BatchNo", OleDbType.VarWChar, 10)
cmd.Parameters.Add("#ComponentID", OleDbType.Integer)
'Connection is managed *outside of the loop*. Only one object created, only one negotiation with DB
con.Open()
For Each row As DataRow In dt.Rows
cmd.Parameters(0).Value = row(0)
cmd.Parameters(1).Value = row(1)
cmd.ExecuteNonQuery()
Next
End Using
Normally, with any ADO.Net provider you do not re-use your connection or command objects. You want a new connection object for every query sent to the DB to allow connection pooling to work correctly. Using the connection in a tight loop like this for the same query is one of the few exceptions.
I might be able to improve further by sticking with the active DataReader, rather than first loading it into a DataTable. That would allow us to avoid loading the entire result set into memory. You would only ever need one record in memory at a time. Certainly this would work for Sql Server. However, Access was designed mainly as a single-user database. It doesn't really like multiple active connections at once, and I'm not sure how it would respond.
It would also be nice to be able to do all of this work in a transactional way, where there's never any risk of it failing part way through the loop and getting stuck with half the updates. Sql Server would handle this via a single INSERT/SELECT query or with an explicit transaction. But, again, this isn't the kind of the Access is designed for. It probably does have a way to do this, but I'm not familiar with it.
OK SO I finally found a way around it, it's a bit of a long process but basically I loaded the SELECT statement(with multiple rows) into a gridview table and the used a for loop to insert it into my insert into statement. bellow is my code:
Displaying into a table
Dim Adapter As New OleDbDataAdapter
Dim Data As New DataTable
Dim SQL As String
Dim con As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\Shantara Production IT.mdb")
Dim cmd As New OleDbCommand()
grdvmachincomp.Visible = false
SQL = "SELECT DISTINCT POH.BatchNo, SSCDD.ComponentID
FROM ([KN - ProductionOrderHeader] AS POH
INNER Join [FG - End Product Codes] AS EPC
On POH.ProductID = EPC.ProductID)
INNER Join([KN - ProductionOrderDetails] AS POD
INNER Join [FG - Style Size Comp Def Details] AS SSCDD
On POD.SizeID = SSCDD.SizeID)
On (POH.BatchNo = POD.BatchNo)
And (EPC.StyleID = SSCDD.StyleID)
WHERE POH.BatchNo = '" & BatchNo & "'"
con.Open()
cmd.Connection = con
cmd.CommandText = SQL
Adapter.SelectCommand = cmd
Adapter.Fill(Data)
grdvmachincomp.DataSource = Data
grdvmachincomp.DataBind()
cmd.Connection.Close()
Insert into through for loop
For c As Integer = 0 To grdvmachincomp.Rows.Count - 1
Dim cmdstring As String
cmdstring = " INSERT INTO [KN - ProductionMachineAllocation] (BatchNo, ComponentID) VALUES('" & grdvmachincomp.Rows(c).Cells(0).Text & "', " & grdvmachincomp.Rows(c).Cells(1).Text & ");"
Dim con As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\Shantara Production IT.mdb")
Dim cmd As New OleDbCommand(cmdstring)
cmd.CommandType = CommandType.Text
cmd.Connection = con
cmd.Connection.Open()
cmd.ExecuteNonQuery()
cmd.Connection.Close()
Next

Multiple Values in DropdownList.DataTextField Not Working

I have the below code which is populating a dropdownlist in ASP.NET. When I use a single value, everything works like a charm, but I want the DataTextField to use two fields coming from the database, not one. Any assistance would be greatly appreciated. I have tried several ways, but nothing seems to work :(
Dim connstr As String = Session("ConnStrEP")
Using con As New SqlConnection(connstr)
Using cmd As New SqlCommand()
cmd.CommandType = CommandType.StoredProcedure
cmd.CommandText = "GetWaivers"
cmd.Connection = con
con.Open()
Dim dr As SqlDataReader = cmd.ExecuteReader()
dr.Read()
Code.DataSource = dr
Code.DataTextField = String.Format("{0}, {1}", Code.SelectedValue("tblWVCode").ToString(), Code.SelectedValue("tblWVDesc").ToString())
Code.DataValueField = "tblWVDesc"
Code.DataBind()
dr.Close()
con.Close()
End Using
End Using
UPDATE:
I generated the below SQL, but I am receiving an error when I execute the SQL Server 2008 Stored Procedure. "Invalid operator for data type. Operator equals add, type equals ntext.
"
SELECT TblWvCode, TblWvDesc, (TblWvCode + ' - ' + TblWvDesc) As FullList FROM EP.dbo.WaiverVarianceTbl
Modify the stored proc to concatenate the tblWVCode and tblWVDesc values and return them in a new field, you can then use that field for the DataTextField
You can't do that, you'll need to change your SQL query to return the field on the format you need. DataTextField must be a field or property name.
Your query should looks like this
SELECT
TblWvCode,
TblWvDesc,
(
CAST(TblWvCode as nvarchar(max)) + ', ' + CAST(tblWVDesc as nvarchar(max))
) as FullList
FROM EP.dbo.WaiverVarianceTbl
and then your VB code would be something like this
Code.DataTextField = "FullList"
Code.DataValueField = "tblWVDesc"

How to make value of a column name appear with single apostrophe in sql statement of sql helper inside asp. net

SQLHelper sqhlpr = new SQLHelper();
sqhlpr.SqlText = "Select StudentName from tblStudentInfo where class=" + NurseryButton.Text;
DataTable dt = sqhlpr.getDataTable(false);
This is my code.Now the result of sqhlpr.sqlText is
select StudentName from tblStudentInfo where class= **Nursery**
(i.e.NurseryButton.Text=Nursery) but the result that i want is select StudentName from tblStudentInfo where class= 'Nursery'.How can this be done??? This looks simple but I can't just figure it out...
"Select StudentName from tblStudentInfo where class='" + NurseryButton.Text + "'";
But you definitively should not use it that way! (SQL Injection)
Here is a good answer: Sql inline query with parameters. Parameter is not read when the query is executed
Your query is a string. You do:
result = "somestring" + someVariable;
Now you want to enclose someVariable in sinlge quotes, which is done like this:
result = "somestring" + "'" + someVariable + "'";
Or shorter:
result = "somestring'" + someVariable + "'";
However is is worth noting that manually building queries is quite "not done". You should look at tools like parameterized queries or even O/R mappers like Entity Framework.
The following code will do what you want:
SQLHelper sqhlpr = new SQLHelper();
sqhlpr.SqlText = "Select StudentName from tblStudentInfo where class = '" + NurseryButton.Text + "'";
DataTable dt = sqhlpr.getDataTable(false);
You need to think about two more things though:
What happens if someone puts an apostrophe in the NurseryButton.Text somehow
Will SQLHelper protect you from this sort of thing, or do you need to do it yourself
You should consider parametrized querying or stored procedures in some way to make sure that your input to the database is done safely.

Not getting the correct results of an SQL query to be displayed in a grid view

I have a query which only retrieves the records of the Description Field of Table 1 that do not match or contain the keywords available in the Keywords Field in Table 2. So the ones that match should not appear in the GridView, rather only the ones that do not match. I am trying to display the results of the SQL Query in a GridView, however I am getting the ones that match, which is not my requirement.
I tried running the SQL Query in SQL Server and it works really fine. Even in my web app it works fine, but only if I specify explicitly the keyword after the Like Statement. But rather I want it to span all the Keywords in Table2. Here is my code.
conn.Open()
For Each row As GridViewRow In Me.GridView1.Rows
For i As Integer = 0 To GridView2.Rows.Count - 1
Dim Records1 As String = GridView2.Rows(i).Cells(0).Text
Dim cmd = New SqlCommand("Select DISTINCT Description From DB.dbo.Table1 " +
"WHERE NOT EXISTS (Select * From [DB].dbo.Table2 WHERE " +
"Table1.Description LIKE '%' +TABLE2.Keywords + '%')", conn)
DA.SelectCommand = cmd
DA.Fill(dt)
GridView3.DataSource = dt
GridView3.DataBind()
cmd.ExecuteNonQuery()
cmd.Dispose()
DA.Dispose()
dt.Clear()
dt.Dispose()
Next
Next
GridView1 has the data from Table1 (The Description)
GridView2 has the data from Tabel2 (The Keywords)
GridView3 has the results of the query
However, as said before I am still getting the ones that match. Is there something wrong with the format of the query, something missing or written in an incorrect format? Any suggestions or thoughts would really be appreciated.
shouldn't that be:
WHERE Table1.Description LIKE #Grid)"
cmd.Parameters.AddWithValue("#Grid", "%" + GridView2.Rows(i).Cells(0).Text.ToString() + "%")
Better solution would be to do this from within a stored procedure and pass the #Grid parameter. IMHO
But, to answer your question specifically, this should work
For i As Integer = 0 To GridView2.Rows.Count - 1
Dim Records1 As String = GridView2.Rows(i).Cells(0).Text
Dim cmd = New SqlCommand("Select DISTINCT Description From DB.dbo.Table1 " +
"WHERE NOT EXISTS (Select * From [DB].dbo.Table2 WHERE " +
"Table1.Description LIKE "
"'%" + GridView2.Rows(i).Cells(0).Text.ToString() + "%')", conn)
DA.SelectCommand = cmd
DA.Fill(dt)
GridView3.DataSource = dt
GridView3.DataBind()
cmd.ExecuteNonQuery()
cmd.Dispose()
DA.Dispose()
dt.Clear()
dt.Dispose()
Next

why this code enter two entries into the database

i have a code that retrieve some content and enter it the database :
MySqlConnection conn = new MySqlConnection(#"connection string");
MySqlCommand cmd = new MySqlCommand("INSERT INTO copy (id) VALUES ('" + Page.User.Identity.Name + "')", conn);
MySqlCommand cmd2 = new MySqlCommand("INSERT INTO copy (cv) VALUES ('" + mainEditor.Content.Replace("'", "''") + "')",conn);
conn.Open();
cmd.ExecuteNonQuery();
cmd2.ExecuteNonQuery();
conn.Close();
it connects and enters the data fine but it enters the data in two not one (it creates two rows instead of one)
i am using asp.net 3.5 and mysql 5.0
what am i doing wrong, thanks.
It's inserting two rows because you're executing two INSERT statements. Each time you run an INSERT it does just that: inserts a row.
I'm guessing you wanted to create a single row with both the id and cv fields populated. The SQL syntax for that is INSERT INTO copy (id, cv) VALUES ('x', 'y');
So:
MySqlCommand cmd = new MySqlCommand("INSERT INTO copy (id) VALUES ('" + Page.User.Identity.Name + "', '" + mainEditor.Content.Replace("'", "''") + "')",conn);
It's because two separate inserts are running. You can insert more than one value, try this:
MySqlCommand cmd = new MySqlCommand("INSERT INTO copy (id, cv) VALUES ('" + Page.User.Identity.Name + "', '" + mainEditor.Content.Replace("'", "''") + "')", conn);
You can comma separate the fields, and the values so it inserts into one record. Executing 2 insert commands will always create 2 records.
You didn't say which driver you're using so I'll use the documentation I found for dotConnect. I would try to use something along these lines (explanation of code below)
using( var conn = new MySqlConnection(#"connection string"))
using( cmd = new MySqlCommand("", conn) ){
cmd.CommandText = #"
INSERT INTO copy (id, cv)
VALUES (:name, :userContent)";
cmd.Parameters.Add("name", MySqlType.[correct type]]).Value = Page.User.Identity.Name;
cmd.Parameters.Add("userContent", MySqlType.[correct type], [column size]).Value = mainEditor.Content;
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
}
The use of the using construct is because MySqlConnection and MySqlCommand classes both implement the IDisposable interface so they need to be disposed of when you're done using them to avoid possible resource leaks.
The :name and :userContent is what I found in documentation for creating parametrized queries. This will allow the database driver to take care of escaping all of the special characters out of user input to avoid SQL injection attacks. This part is actually really important, there are some REALLY sophisticated SQL injection attacks out there, so there's a good chance simply escaping ' (as you were doing) isn't enough.

Resources