Join query VB.net datareader - vb.net

My query on two different tables user table and zone table is creating a problem:
cmd.CommandText = "SELECT zone_name, zone_difference FROM user_master INNER JOIN zones on user_master.zone_id = zones.ID WHERE user_master.uname LIKE " & """" & usr_gl & """"
Dim reader_q As OleDbDataReader
reader_q = cmd.ExecuteReader()
Here, zone name and difference are from zones table and zone_id (from customer) and ID (zones) are in relation, Also user name (uname) is coming from outside as usr_gl variable for e.g. "admin"
It is saying No value given for one or two parameters. I checked all the table columns and data. The same query is running independently from Access database.
Is there anything wrong i am executing here?

Yes, you are trying to concatenate strings and this is a NO-NO in code
cmd.CommandText = "SELECT zone_name, zone_difference FROM " & _
"user_master INNER JOIN zones on user_master.zone_id = zones.ID " & _
"WHERE user_master.uname LIKE ?"
cmd.Parameters.AddWithValue("#p1", usr_gl)
Dim reader_q As OleDbDataReader
reader_q = cmd.ExecuteReader()
String concatenation is considered a bad practice because many problems could arise with that
Correct string formatting (with quotes, decimals, dates) is the first problem but the Sql Injection is the worst of all. Using Parametrized queries should avoid all of those problems

Related

How can I combine these two SQL queries (Access/VBA)

I am using two SQL queries in VBA that i believe they could be done in one, but I cant get it to work. I Want to turn the VBA portion into a Query outside of VBA, the VBA keeps breaking my file due to the amount of data it processes. (By break i mean it gives a message that says "this file is not a valid database" rendering the file corrupted). I search for that error but all i found was not related to breaking because of VBA code.
Anyways, here are the two queries ran with VBA.
SELECT ET.VerintEID AS EID, Sum(ET.ExceptMin)/60 AS Exeptions
FROM Tbl_VExceptTime AS ET
INNER JOIN Tbl_VCodes ON ET.Exception = Tbl_VCodes.Exception
WHERE (ET.ExceptDate Between #" & sDate & "# And #" & eDate & "#)
GROUP BY ET.VerintEID, Tbl_VCodes.IsApd
HAVING Tbl_VCodes.IsApd = ""OFF"";
I loop these results to update a table.
Do While Not .EOF
SQL = "UPDATE Tbl_AttendanceByAgent SET EXC = " & recSet.Fields(1).Value & _
" WHERE VerintID = '" & recSet.Fields(0).Value & "'"
CurrentDb.Execute SQL
.MoveNext
Loop
I know that i can save the results from the first query into a table and without looping I can update the main table with another SQL query, but I believe it can be done on a single SQL. I have tried using an UPDATE with a SELECT of the first query but it just errors out on me with an invalid syntax.
Yes this could be achieved in one single query as shown below
UPDATE Tbl_AttendanceByAgent
SET Tbl_AttendanceByAgent.EXC = t2.Exeptions
from Tbl_AttendanceByAgent t1
inner join (
SELECT ET.VerintEID AS EID, Sum(ET.ExceptMin)/60 AS Exeptions
FROM Tbl_VExceptTime AS ET
INNER JOIN Tbl_VCodes as TV ON ET.Exception = TV.Exception
WHERE (ET.ExceptDate Between #" & sDate & "# And #" & eDate & "#)
GROUP BY ET.VerintEID, TV.IsApd
HAVING Tbl_VCodes.IsApd = 'OFF'
) AS t2 on t2.EID = t1.VerintID
Note: I suppose you will replace sDate, eDate with values within your code
This question is an answer to the described errors and the given code, although it technically does not answer the request for a single SQL statement. I started adding a comment, but that's just too tedious when this answer box allows everything to be expressed efficiently at once.
First of all, referring to CurrentDb is actually NOT a basic reference to a single object instance. Rather it is more like a function call that generates a new, unique "clone" of the underlying database object. Calling it over and over again is known to produce memory leaks, and at the least is very inefficient. See MS docs for details.
Although the given code is short, it's not sweet. Not only is it repeatedly creating new database objects, it is repeatedly executing an SQL statement to update what I assume is a single row each time. That also entails regenerating the SQL string each time.
Even if executing the SQL statement repeatedly was an efficient option, there are better ways to do that, like creating a temporary (in-memory) QueryDef object with parameters. Each loop iteration then just resets the parameters and executes the same prepared SQL statement.
But in this case, it may actually be more efficient to load the table being updated into a DAO.Recordset, then use the in-memory Recordset to search for a match, then use the recordset to update the row.
I suspect that addressing a couple of those issues would make your VBA code viable.
Dim db as Database
Set db = CurrentDb 'Get just a single instance and reuse
Dim qry as QueryDef
SQL = "PARAMETERS pEXC Text ( 255 ), pID Long; " & _
" UPDATE Tbl_AttendanceByAgent SET EXC = pEXC " & _
" WHERE VerintID = pID"
set qry = db.CreateQueryDef("", SQL)
'With recSet '???
Do While Not .EOF
qry.Parameters("pEXC") = recSet.Fields(1).Value
qry.Parameters("pID") = recSet.Fields(0).Value
qry.Execute
.MoveNext
Loop
'End With recSet '???
'OR an alternative
Dim recUpdate As DAO.Recordset2
Set recUpdate = db.OpenRecordset("Tbl_AttendanceByAgent", DB_OPEN_TABLE)
Do While Not .EOF
recUpdate.FindFirst "VerintID = " & recSet.Fields(0).Value
If Not recUpdate.NoMatch Then
recUpdate.Edit
recUpdate.Fields("EXC") = recSet.Fields(1).Value
recUpdate.Update
End If
.MoveNext
Loop
I realized in commenting on Gro's answer, that the original query's aggregate clauses will produce unique values on EID, but it then becomes obvious that there is no need to group on (and sum) values which do not have Tbl_VCodes.IsApd = 'OFF'. The query would be more efficient like
SELECT ET.VerintEID AS EID, Sum(ET.ExceptMin)/60 AS Exeptions
FROM Tbl_VExceptTime AS ET
INNER JOIN Tbl_VCodes ON ET.Exception = Tbl_VCodes.Exception
WHERE (ET.ExceptDate Between #" & sDate & "# And #" & eDate & "#)
AND Tbl_VCodes.IsApd = 'OFF'
GROUP BY ET.VerintEID;
BTW, you could consider implementing the same temporary QueryDef pattern as I showed above, then you'd change the first WHERE expression to something like
PARAMETERS PsDate DateTime, PeDate DateTime;
...
WHERE (ET.ExceptDate Between [PsDate] And [PeDate])
...

Vb 2015 query with parameters not working

I already used parametrized queries with no problem, but today I'm stuck on an error that I cannot debug.
This is my working query
cmd.CommandText = "select idcliente, ragsociale from Clienti where idcliente =" & strName & " or codiceamministrazione='" & strName & "' or piva='" & strName & "' or codfisc='" & strName & "'"
The same, but with parameter, not working
cmd.CommandText = "select idcliente, ragsociale from Clienti where idcliente = #Cliente or codiceamministrazione=#Cliente or piva=#Cliente or codfisc=#Cliente"
cmd.Parameters.AddWithValue("#Cliente", strName)
I use this in an autocomplete procedure that shows the name of a client based on internal id or on commercial license number (and other similar codes). On the db a client record can have all the code fields compiled or just 1.
With the non-parametrized query the autocomplete suggestion pop-up,with the parametrized one nothing shows. No errors either.
EDIT:
using this
cmd.Parameters.Add("#Cliente", SqlDbType.VarChar)
cmd.Parameters("#Cliente").Value = strName
now another query (omitted before for semplicity) in the same function works, but, strange enough, the one for what I did this question don't.
Working:
cmd.CommandText = "select idcliente, ragsociale from Clienti where ragsociale like '%'+#Cliente+'%' or codiceamministrazione=#Cliente"
Still not Working:
cmd.CommandText = "select idcliente, ragsociale from Clienti where idcliente = #Cliente or piva=#Cliente or codfisc=#Cliente"
In your original query, when testing against idcliente, you treat strName as a number (no quotes round it), but for all the other fields you treat it like a string. So you're implying it could potentially contain a number or a string. This is problematic, if you type a number, and the parameterised version of the query now treats it like a string in all cases, then it won't match the numeric value of idcliente in the DB and therefore you may get no results.
To clarify: if your input is a number, but your query thinks it's a string (because of the data type in the param), it will not match against any numeric field in the database. 12345 != "12345".
You need to define separate parameters for these scenarios. You can pass the same value into them, but in one case set the parameter's datatype to varchar and in the other case to int (you might need to check if the value can be parsed as a number before you do this, otherwise it will likely crash. In that case just set it null or 0 or something that won't make an accidental match).

How do I access multiple records from the same table using SQLDataAdapter?

This almost works. I get an error at the last line that looks like it's complaining about the C1 reference. Is there a simple way around this? There is nothing wrong with the query or connection.
Dim CmdString As String
Dim con As New SqlConnection
Try
con.ConnectionString = PubConn
CmdString = "select * from " & PubDB & ".dbo.Suppliers as S " & _
" join " & PubDB & ".dbo.Address as A" & _
" on S.Supplier_Address_Code = A.Address_IDX" & _
" join " & PubDB & ".dbo.Contacts as C1" & _
" on S.Supplier_Contact1 = C1.Contact_IDX" &
" join " & PubDB & ".dbo.Contacts as C2" & _
" on S.Supplier_Contact2 = C2.Contact_IDX" &
" WHERE S.Supplier_IDX = " & LookupIDX
Dim cmd As New SqlCommand(CmdString)
cmd.Connection = con
con.Open()
Dim DAdapt As New SqlClient.SqlDataAdapter(cmd)
Dim Dset As New DataSet
DAdapt.Fill(Dset)
con.Close()
With Dset.Tables(0).Rows(0)
txtAddress1.Text = .Item("Address1").ToString
txtAddress2.Text = .Item("Address2").ToString
txtSupplierName.Text = .Item("Address_Title").ToString
txtAttn.Text = .Item("Attn").ToString
txtBusinessPhone1.Text = .Item("C1.Contact_Business_Phone").ToString
You would not include the "C1" table alias as part of your column name. It will be returned from your query as Contact_Business_Phone.
For accessing multiple rows you could use the indexer as you are in the example above "Rows(0)" by placing your With block into a For loop and accessing the "Rows(i)" with your loop variable. However, this would not help much as your are assigning this to individual text boxes, so you'd only see the last value on your page/screen.
The alias C1 is used by SQL Server and is not persisted to the result set. Have you taken this query into SQL Management Studio to see the results?
Since you requested all columns (*) and joined to the Contacts table twice, you'll end up with duplicate column names in the result. For example, if the Contacts table has a LastName field, you'll end up with TWO LastName columns in your result.
I haven't tried to duplicate this in my local environment, but I can't imagine the data adapter is going to like having duplicate column names.
I recommend specifically including the columns you want to return instead of using the *. That's where you'll use the alias of C1, then you can rename the duplicate columns using the AS keyword:
SELECT C1.LastName AS [Supplier1_LastName],
C2.LastName AS [Supplier2_LastName],
...
This should solve your problem.
Good Luck!
You should only be pulling back the columns that you're in fact interested in, as opposed to *. It's sort of hard to tell exactly what data exists in which tables since you're pulling the full set, but at a quick guess, you'll want in your select statement to pull back A.Address1, A.Address2, A.AddressTitle, ?.Attn (not sure which table this actually derives from) and C1.Contact_Business_Phone. Unless you actually NEED the other fields, you're much better off specifying the individual fields in your query, besides having the possible duplicate field issue that you're running into here, it can also be a significant performance hit pulling everything in. After you clean up the query and only pull in the results you want, you can safely just reference them the way you are for the other fields, without needing a table alias (which as others have pointed out, isn't persisted to the result set anyways).

SQL Select Query via Excel VBA - Defining Parameters, query of a query

I need to place 3 Parameters into a Select query from Excel VBA to SQL - One of which can just be replaced using a variable. But this query is a query of another query, and the parameters are held within those two other queries. If running this query in Access I'm just prompted by all three to manually type them in - "Start", "End", and "AdvisorName".
Running the code will prompt the "No value given for one or more required parameters" - However, only 1 Parameter is in this query, the other 2 parameters are held within the other two queries inside this query - "Q_SoloFocus_Advisor_QuestionsYes" and "Q_SoloFocus_Advisor_QuestionsNo".
The three parameters are called "Start" (Start Date range), "End" (End Date range), and "AdvisorName" (held within this query).
Dim cnn As ADODB.Connection
Dim rst As ADODB.Recordset
Dim Command As New ADODB.Command
Dim strSQL As String
Set cnn = New ADODB.Connection
cnn.Open ConnectionString:=Cnct
Set rst = New ADODB.Recordset
strSQL = "SELECT Q_SoloFocus_Advisor_QuestionsAll.Advisor,
Q_SoloFocus_Advisor_QuestionsAll.KeyID,
Q_SoloFocus_Advisor_QuestionsAll.SubQ_Text,
Q_SoloFocus_Advisor_QuestionsAll.CountOfAnswer AS [All],
Q_SoloFocus_Advisor_QuestionsNo.CountOfAnswer AS [No],
Q_SoloFocus_Advisor_QuestionsYes.CountOfAnswer
AS Yes," & _"Format(Q_SoloFocus_Advisor_QuestionsYes.CountOfAnswer/
Q_SoloFocus_Advisor_QuestionsAll.CountOfAnswer,'0.0%')
AS Result" & _"
FROM (Q_SoloFocus_Advisor_QuestionsAll
LEFT JOIN Q_SoloFocus_Advisor_QuestionsNo
ON (Q_SoloFocus_Advisor_QuestionsAll.SubQ_Text =
Q_SoloFocus_Advisor_QuestionsNo.SubQ_Text)
AND (Q_SoloFocus_Advisor_QuestionsAll.KeyID =
Q_SoloFocus_Advisor_QuestionsNo.KeyID)
AND (Q_SoloFocus_Advisor_QuestionsAll.Advisor =
Q_SoloFocus_Advisor_QuestionsNo.Advisor))
LEFT JOIN Q_SoloFocus_Advisor_QuestionsYes
ON (Q_SoloFocus_Advisor_QuestionsAll.SubQ_Text =
Q_SoloFocus_Advisor_QuestionsYes.SubQ_Text)
AND (Q_SoloFocus_Advisor_QuestionsAll.Advisor =
Q_SoloFocus_Advisor_QuestionsYes.Advisor)
AND (Q_SoloFocus_Advisor_QuestionsAll.KeyID =
Q_SoloFocus_Advisor_QuestionsYes.KeyID)" & _
" WHERE (((Q_SoloFocus_Advisor_QuestionsAll.Advisor)=[AdvisorName]));"
'----------------------------------------------------------------------------------
'----------------------------------------------------------------------------------
rst.Open strSQL, cnn, adOpenStatic
rst.MoveFirst
The SQL for the other two queries are similar, one has a where "No" and the other a where not "No" where the parameters are required are:
SELECT tbl_Surveys.Advisor, tbl_QuestionRef.KeyID, tbl_QuestionRef.CallSkill,
tbl_QuestionRef.CallReason, tbl_QuestionRef.MainQ_Text
tbl_QuestionRef.SubQ_Text,
Count(tbl_SurveyAnswers.Answer) AS CountOfAnswer
FROM tbl_QuestionRef
INNER JOIN (tbl_SurveyAnswers
INNER JOIN tbl_Surveys
ON tbl_SurveyAnswers.SurveyLink = tbl_Surveys.ID)
ON tbl_QuestionRef.KeyID = tbl_SurveyAnswers.QuestionRef
WHERE (((tbl_SurveyAnswers.Answer)<>"No"
And (tbl_SurveyAnswers.Answer)<>"N/A"
And (tbl_SurveyAnswers.Answer)<>"0")
AND ((tbl_Surveys.CallDate)>=[Start]
And (tbl_Surveys.CallDate)<=[End]))
GROUP BY tbl_Surveys.Advisor, tbl_QuestionRef.KeyID, tbl_QuestionRef.CallSkill,
tbl_QuestionRef.CallReason,
tbl_QuestionRef.MainQ_Text, tbl_QuestionRef.SubQ_Text;
As you can see, the "Start" and "End" parameters are in this second SQL query... Any ideas how I can put these two parameters into the first SQL function? I can't put the Start and End into the "All" query, as it'll knock out the "Count" part...
Assuming the other two queries are saved and named, instead of calling named queries, incorporate the SQL from the two queries into the main query as derived tables. By doing so, the query will prompt for all parameters. See the JOIN clauses for some clarification.
strSQL = "SELECT Q_SoloFocus_Advisor_QuestionsAll.Advisor, "
Q_SoloFocus_Advisor_QuestionsAll.KeyID,
Q_SoloFocus_Advisor_QuestionsAll.SubQ_Text,
Q_SoloFocus_Advisor_QuestionsAll.CountOfAnswer AS [All],
Q_SoloFocus_Advisor_QuestionsNo.CountOfAnswer AS [No],
Q_SoloFocus_Advisor_QuestionsYes.CountOfAnswer
AS Yes," & _"Format(Q_SoloFocus_Advisor_QuestionsYes.CountOfAnswer/
Q_SoloFocus_Advisor_QuestionsAll.CountOfAnswer,'0.0%')
AS Result" & _"
FROM (Q_SoloFocus_Advisor_QuestionsAll
LEFT JOIN (YOUR FIRST SAVE QUERY SQL) AS Q_SoloFocus_Advisor_QuestionsNo
ON (Q_SoloFocus_Advisor_QuestionsAll.SubQ_Text =
Q_SoloFocus_Advisor_QuestionsNo.SubQ_Text)
AND (Q_SoloFocus_Advisor_QuestionsAll.KeyID =
Q_SoloFocus_Advisor_QuestionsNo.KeyID)
AND (Q_SoloFocus_Advisor_QuestionsAll.Advisor =
Q_SoloFocus_Advisor_QuestionsNo.Advisor))
LEFT JOIN (YOUR SECOND QUWERY SQL) AS Q_SoloFocus_Advisor_QuestionsYes
ON (Q_SoloFocus_Advisor_QuestionsAll.SubQ_Text =
Q_SoloFocus_Advisor_QuestionsYes.SubQ_Text)
AND (Q_SoloFocus_Advisor_QuestionsAll.Advisor =
Q_SoloFocus_Advisor_QuestionsYes.Advisor)
AND (Q_SoloFocus_Advisor_QuestionsAll.KeyID =
Q_SoloFocus_Advisor_QuestionsYes.KeyID)" & _
" WHERE (((Q_SoloFocus_Advisor_QuestionsAll.Advisor)=[AdvisorName]));"

parameterized query not working in VB

statement = "SELECT OrderID, (SELECT VendName FROM Vendors WHERE Vendors.VendorID = Orders.VendorID) " &
",OrderDt, RcvdDt, OrderTotal " &
"FROM Orders " &
"WHERE VendName=? " &
"ORDER BY OrderDt DESC"
Dim cmd As New OleDbCommand(statement, connection)
cmd.Parameters.AddWithValue("VendName", txtVendorFilter.Text)
Dim reader As OleDbDataReader = cmd.ExecuteReader(CommandBehavior.Default)
I was trying to do this before by simply concatenating the textbox value right into the SQL and I was getting a "No values given for required parameters", and read that that I should use parameterized queries instead. So I tried this and it doesn't give me any errors, but the reader never has anything in it. I've never used parameterized queries before, so I'm a bit lost as to why this isn't working.
Edit:
I've changed the above code to account for OLEDB from what I briefly read on how it should work, and it's giving me the "no values given for required parameters" again.
One problem is here:
"WHERE VendName='#x' " &
Drop the ' marks - the parameterization will take care of this for you:
"WHERE VendName= #x " &
Using the ' in the query means that '#x' is treated as a string type, not a parameter name.
Additionally, since you are using OleDb, names parameters are not supported. You need to use ? to signify a parameter in the query:
"WHERE VendName= ? " &