How to add a value to Sql CommandText - sql

Nowadays I am programming a cricket scoring software and all the details are saved in a database. I want to know how to add +1 to the field "W" in the database when click "Wicket".
cmd.CommandText = "UPDATE database.table SET W=W+1 WHERE bowler='" & frmmain.lblbowler.Text & "' "
In this code frmmain.lblbowler.text contains the bowler name.
Is this code correct? What changes must do? Please be kind enough to answer.

Don’t ever build a query this way! The input frmmain.lblbowler.Text is typically retrieved from a TextBox control on either a Windows form or a Web Page. Anything placed into that TextBox control will be put into frmmain.lblbowler.Text and added to your SQL string. This situation invites a hacker to replace that string with something malicious. In the worst case, you could give full control of your computer away.
Instead of dynamically building a string, as shown in your code, use parameters.
Anything placed into a parameter will be treated as field data, not part of the SQL statement, which makes your application much more secure.
Try the following
cmd.CommandText = "UPDATE database.table SET W=W+1 WHERE bowler = #bowler"
command.Parameters.Add("#bowler", SqlDbType.NVarChar)
command.Parameters("#bowler").Value = frmmain.lblbowler.Text

Related

MS Access Form Bind to ADO Recordset

Driving me bonkers...
The problem is one of speed.
I have a working SQL Server linked to a client accessible website, which I am wanting to add an Access front end to enable us office bound staff to better support both client and field staff.
The current form I have constructed is a main form with five sub forms contained within it, giving us all the relevant client information in one view. This works however is taking 24 seconds to load a single clients complete records.
I have looked at the SQL Server and found the absence of indexes, fixed this and got the time down to 24 seconds with consequent loads closer to 18 seconds depending on the client (some have a lot more records). This might be okay, as whilst this is a relative eternity in computing time in real world time its okay...but not great. I would like to see if I can get a better load by changing the way I connect and how the form is bound to the records etc.
In looking at the various ideas and reading a lot I found:
https://learn.microsoft.com/en-us/office/vba/access/concepts/activex-data-objects/bind-a-form-to-an-ado-recordset?source=docs
Which appealed to me as I am more inclined to use ADO, seldom if ever to I use DAO. ADO I understood originally was intended to use with SQL and so on, and it seems like a sensible idea.
Again as I understand it if I can get this to work it will act as a pass through query returning only one record over the net and should consequently speed my form up considerably. However it wont work.
My code is:
Private Sub cssSetForm(lngID As Long)
Dim cnn As New ADODB.Connection
Dim Rs1 As New ADODB.Recordset
Dim strSQL As String
Dim strR As String
cnn = "Provider=MSOLEDBSQL;Server=Server;Database=DatabaseName;UID=UserName; PWD=Password;"
cnn.Open
strSQL = "SELECT Clients.Clientid, Clients.AccountContact, Clients.AccountEmail, Clients.Address, Clients.Name, Clients.OfficePhone, Clients.PostCode, " & _
"Clients.ShentonAcc, Clients.Suburb FROM Clients WHERE (((Clients.Clientid)=" & lngID & "));"
With Rs1
Set .ActiveConnection = cnn
.Source = strSQL
.LockType = adLockPessimistic
.CursorType = adOpenKeyset
.Open
End With
Debug.Print Rs1.RecordCount
Me.Recordset = Rs1
End sub
Now I am getting no errors until Me.Recordset=rs1 which is generating an error 3251 Operation is not supported for this type of object which is very nice for someone that understands why this is not supported when it is no different than I can see to the example I was copying from.
I don't understand why the form I am working on doesn't support recordsets according to the error message? Is there an error in my code? Is the error in my understanding of the destructions from the linked site? Is the error something else?
Thanks for the help
Well, loading up 5 sub forms is a lot of data pulling. converting to ado reocdsets is NOT going to speed this up.
What you want to do here is NOT load up the sub forms until such time the user say clicks on the appropriate tab to load the one given sub form.
As long as the form in question is opened with a were clause, then the one main form will ONLY pull the one main record from sql server. So doing all kinds of fancy reocrdsets etc. will gain you next to nothing. So, always - but always launch your main form to the one record. If that main form is bound to a table of 5,000 rows, or 1 million rows, it will load instant despite the fact that the form is bound directly to the linked table with 1 million rows.
With this one main form, you edit or do whatever, and then close it. You are then of course right back to the search form/prompt you have to ask the user what reocrd to work on. So, how a accouting package works, or even google? You search, display the search resutlts and pick ONE thing to view. This approach should get your main form load down to about 1 second. Again, just use the "where" clause when you open that form:
eg:
dim strInv as string
strInv = InputBox("Enter invoice number to view")
docmd.OpenForm "frmInvoice",,,"InvoiceNum = " & strInv
Of course the above is air code, and you will likely build some search form say like this:
So in above, the user types in a bit of the name. We then fill the form with a simple where clause, or
me.MySubForm.RecordSource = "select * from tourCust where LastName like '" & sTextbox & "*'"
When a user clicks on the glasses icon to edit + view the ONE row, we use this:
docmd.OpenForm "frmDetails",,,"id = " & me!id
Again, all bound forms, and dispite the tables having 500,000+ rows, the loading of the forms is instant - even when the back end is SQL server.
So, adopt a prompt + search results + edit/view design pattern. EVERY single software system has this loop or design pattern. Not only is it user friendly, it also performs well since access DOES NOT pull the whole table, but ONLY the reocrds you tell the form to load by using the where clause as per above.
Now, for the child forms (sub forms).
As noted, don't load them until the user actually clicks on the given tab.
So, in the on-change event of the tab, you can go:
If Me.TabMainpage.Pages(Me.TabMainpage).Name = Me.pgeDocs.Name Then
'' the tab been changed to show past tours
dim strSQL as string
strSQL = "select * from tblPastTours where tour_ID = " & me!ID
me.
' dynamic load the sub form
If Me.frmSubPastTours .SourceObject = "" Then
Me.frmSubPastTours.SourceObject = "Forms.frmPastTours"
End If
' now load sql into form
me.frmSubPastTours.Form.RecordSource = strSQL
The above is mostly air code, but the idea here is:
don't load the sub form until you need to.
OR YOU can have the sub form load, but leave its RecordSource blank and STUFF in the sql you need for display WHEN you click on the tab.
It is possible that you need all sub forms to display. You could thus leave all sub form RecordSource blank, and then it the main form on-load event, simply stuff in the sql to each sub form. This might improve speed a bit, and with your slower connection this would be the least amount of work.
You "fail" to mention how you launch + load the main form, but as noted, it should be opend to the ONE reocrd. The link master/child pulling of data can be bit slow, and I can't say JUST using the above sql stuff into those forms will help a lot. I would try that first as it is the least amount of work. If load time is still too slow, then placing te sub forms behind a tab control and ONLY loading the sub form, and then setting the datasource will be the fastest.
Attempting to build all those recordsets, and then bind them to a form? It not speed things up, and just shoving a sql string into the form (or sub form) recordSource amounts to really the SAME thing and same performance anyway. So, you save tons of work and code, and quite much the above idea not only applies to sql server back ends, but we all VERY often dynamic load sub-forms and don't load them until such time you view the sub form say behind a tab control.

Passing a query a parameter [Access 2016]

To make a longer story shorter:
I'm an Access noob, doing a quick-and-dirty conversion of a massive Excel spreadsheet into an Access database. Part of the requirements are to mimic some of the functionality of Excel, specifically, pulling data from a certain table and doing some basic calculations on it (sums, averages, etc.).
I've written a chain of queries to pull the data, count/sum it, etc., and have been testing them by using a manually-entered Parameter (i.e., the kind where the input box pops up and asks you to type a response). Now that I'm ready to drop these queries into a (sub)form, though, I have no idea how to automatically pass that parameter from a box in the form into the subform into the query.
Every query I've written uses a manually-entered Parameter named "MATCHNAME," which holds the name of an individual. In manual testing, if I enter this parameter on one query, all the queries it calls also get that value. So, I think I just need to figure out how to tell the top query what MATCHNAME actually is, and that'll take care of it.
Problem is, I don't know how to do that in Access. If it was any other programming language, I'd do something like "queryXYZ(MATCHNAME);", but I don't think I can do that in Access. Plus, since the values queryXYZ returns are all calculated, I'm not sure how to add an extra MATCHNAME field, nor how to actually make sure that gets read by the queries, nor how to make sure it gets passed down the chain. I've even tried creating a Parameter in design view, then trying to set up Link Master Fields, but the Parameter doesn't appear in that window.
I'd also like to re-run these queries whenever a new record is pulled up, but I'm not sure how to do that either--i.e., the numbers should be current for whatever record I'm looking at.
And, before we go there--I feel like a Relationship is out of the question, as the data itself is auto-generated, and is in rough enough shape to where I can't guarantee that any given key is wholly unique, and large enough (20k+) that, outside of writing a magical script, I can't assign a numerical key. However, I don't know much about Relationships in Access, so please prove me wrong.
(Is this all making sense?)
Do you have any suggestions for me--for how to make a subform read a field on the main form to run its queries on? Alternately, is there an easier way to do this, i.e., to bed SQL calls inside a form?
Thanks very much for your help...
You can use SQL as the recordsource of the subform in the property tab and use the afterupdate event of your matchname field to change yourform.recordsource = "Select * from table where filteredfieldname = & me.matchname & ";" . You can also use sql as the control source of form fields. To pass criteria to filter the subform using the whole table as the recordsource, add an event procedure to your field's after update event like this
`In the declarataions at the top
Global mtchnmfltr as string
Private Sub MATCHNAME_AfterUpdate()
'use the same procedure for Private Sub yourmainform_Current()
mtchnmfltr = "[yourfilterfield] = " & Chr(34) & me.matchname & Chr(34)
'if matchname is not text then just = "[yourfilterfield] = " & me.matchname
with me.subformname.form
.filter = mtchnmfltr
.filteron = true
end with
'Build your sql as a string for your sum avg fields etc. using mtchnmfltr in the where clause
me.yoursumfield.controlsource = "Select...where " & mtchnmfltr & ";"
'etc.
end sub
Or you could throw Matchname into a sql recordsource of the subform and add the function fields to the subform on the same on current and after update events
if me.newrecord = true then
me.dirty = false
end if
me.subform.form.recordsource = "Select Table.Matchname, sum(yourfield) as sumalias, _
(etc.) from yourtable where table.matchname = " & chr(34) & me.matchname & _
chr(34) & Group By table.matchname"
If you are storing your sums etc in a table you need to do it a bit different, since your controls controlsource are bound to fields.
dim strsqlsumfld as string
dim rs as dao.recordset
strsqlsumfld= "Select SUM.....AS sumfldalias where " & mtchnmfltr & ";"
set rs = currentdb.openrecordset(strsqlsumfld)
me.yoursumfield = rs("sumfldalias")
rs.close

Access SQL to save value in unbound textbox cannot save more than 255 characters

I've read through a couple similar posts, but not found a solution for this issue:
I have a form with an unbound rich text, multiline textbox named tbxNote. When the textbox is exited, I use VBA code to create an SQL string which I subsequently execute to UPDATE a table field [Note] with the value in the unbound textbox. [Note] is a "Long Text" field (from my understanding, "Long Text" is equivalent to what used to be called a "Memo" field). The backend is an Access database.
Problem is: Only the first 250 characters of what is in tbxNote get stored in the target table field [Note] even though other "Long Text" fields in other tables are accepting values much longer than 250 characters. So, it does not seem to be an issue with the field type or characteristics in the backend table.
Furthermore, if I manually open the target table and paste 350 characters into the same [Note] field in the target table, all 350 characters get stored. But, if I load up that record into the form or put the same 350 characters into the form's tbxNote textbox, only 250 characters are pulled into tbxNote or saved out to [Note].
Is there a way to store more than 250 characters in an unbound textbox using an UPDATE SQL in code?
In case it matters, here's the SQL code that I used to prove only 250 of 350 characters gets saved to the table field [Note]:
dbs.Execute "UPDATE tblSupeGenNotes " & _
"SET [NoteDate] = #" & Me.tbxNoteDate & "#, " & _
"[SupeType] = " & Chr(34) & Me.cbxSupeType & Chr(34) & ", " & _
"[SupeAlerts] = " & alrt & ", " & _
"[Note] = " & Chr(34) & String(350, "a") & Chr(34) & " " & _
"WHERE [SupeGenNoteID] = " & Me.tbxSupeGenNoteID & ";"
Of course, normally I'd have Me.tbxNote instead of String(350, "a") but the String proves that only 250 of the 350 characters get stored in the [Note] field.
I must be missing something simple, but I cannot figure it out.
Unfortunately, you posted test code works, but you FAILED to post your actual update string that fails. A common (and known) problem is if you include a function (especially aggregates) in your SQL, then you are limited to 255 characters.
In fact this can apply if you have function(s) that surrounds the unbound text box and is used in the query.
So such an update should and can work, but introduction functions into this mix can cause problems with the query processor.
If you included the actual update, then the above issue(s) likely could have been determined.
So the workarounds are:
Don’t use any “functions” directly in the SQL update string, but build up the string.
So in place of say:
Dbs.Execute "update tblTest set Notes = string(350,’a’)"
Note how above the string function is INSIDE the sql.
You can thus place the function(s) OUTSIDE of the query and thus pre-build the string - the query processor is NOT executing nor will it even see such functions.
So we can change above to as PER YOUR EXAMPLE:
Eg:
Dbs.Execute "update tblTest set Notes = ‘" & string(350,’a’) & "’"
(this is how/why your posted example works, but likely why your actual code fails). So functions can (and should) be moved out of the actual query string.
Also make sure there is NO FORMAT in the formatting for the text box, as once again this will truncate the text box to 255.
And as noted here the other suggestion is to consider using a recordset update in place of the SQL update.
Using a recordset can often remove issues of delimiters and functions then become a non issue.
So such SQL updates can work beyond 255 characters, but functions need to be evaluated in your VBA code before the query processor gets its hands on the data as per above examples.
And as noted remove any “format” you have for the text box (property sheet, format tab).
#HansUp's suggested trying a DAO recordset to update the table. That did the trick! Thank you, HansUp. HansUp requested that I post the answer, so, here is the code that worked for anyone else who comes across this thread:
Dim dbs As DAO.Database
Dim rsTable As DAO.Recordset
Dim rsQuery As DAO.Recordset
Set dbs = CurrentDb
'Open a table-type Recordset
Set rsTable = dbs.OpenRecordset("tblSupeGenNotes", dbOpenDynaset)
'Open a dynaset-type Recordset using a saved query
Set rsQuery = dbs.OpenRecordset("qrySupeGenNotes", dbOpenDynaset)
'update the values vased on the contents of the form controls
rsQuery.Edit
rsQuery![NoteDate] = Me.tbxNoteDate
rsQuery![SupeType] = Me.cbxSupeType
rsQuery![SupeAlerts] = alrt
rsQuery![Note] = Me.tbxNote
rsQuery.Update
'clean up
rsQuery.Close
rsTable.Close
Set rsQuery = Nothing
Set rsTable = Nothing
AH! Another bit to the solution is that prior to using the DAO recordset, I was pulling values from the table into a listbox and from the listbox into the form controls (instead of directly into the form controls from the table). Part of the problem (I believe) was that I was then populating the form controls from the selected item in the listbox instead of directly from the table. I believe listboxes will only allow 255 characters (250 characters?) in any single column, so, everytime I pulled the value into the textbox from the listbox, the code was pulling only the first 255 characters into the textbox. Then, when the textbox was exited, the code was updating the table with the full textbox text, but when it was pulled back into the form through the listbox, we'd be back down to 255 characters. Of course, when I switched to the DAO approach, I also switched to reading the textbox value directly from the table instead of pulling it from the listbox.
Moral: Beware of pulling Long Text values through a listbox!
Thanks to everyone who helped me solve this. Sorry for such a newbie error seeming more complicated than it was.
I assume you are using the SqlClient library. In which case, I recommend trying SqlParameters rather than creating a SQL string the way you are. With the SqlParameter you can specify the size of each parameter. http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.parameters(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-2 . I am a C# dev so my apologies about doing the example code below in C#:
string param = "Hello World";
byte [] encodedStr = Encoding.UTF8.GetBytes(param);
SqlParameter sqlParam = new SqlParameter();
sqlParam.Size = encodedStr.Count; // uses byte count
you could condense it by calling Encoding.UTF8.GetBytes(param).Count. Anyways, this might fix your issue

Use toggle buttons to update a record in Access

Hopefully not too difficult question but I cannot figure this out and have been unable to find anything searching the forums.
I want to convert the output of my toggle boxes from 1,2,3,4,5 to the text each button displays.
Couldn't find any setting on the toggle boxes properties themselves so decided I would have to write a macro/vba to do it from the table but as it's quite new to me I am struggling on syntax.
I have tried doing this on the inbuilt data macro mainly, but also tried it via a query and vba and still couldn't figure it out.
[don't have any reputation yet so have not been allowed to post pics of my macro attmept]
please help! Any solution using vba, data macro or a query would be great
EDIT
To be specific rather than a message box I want to update field1 in my table "Major Equipment" based off the option group selection this is my latest attempt but still not sure how to reference the option group. Do I need to set grp equal to the option group and if so how? Is it something like forms!myform!optiongroup ?
Option Compare Database
Function MyFunc(grp As OptionGroup)
Dim rcrdset As Recordset
set grp =
Set rcrdset = Application.CurrentDb.OpenRecordset("Major Equipment", dbOpenDynaset)
Select Case grp.Value
Case 1
With rcrdset
.AddNew
![Field1] = "Not Reviewed"
Case 2
.AddNew
![Field1] = "Reviewed"
Case Else
MsgBox "Error"
End Select
End Function
Also just realised since these toggle buttons will be updated by the user and so I probably need an update rather than addnew?
http://i59.tinypic.com/2ym8wet.jpg
Your buttons are part of an Option Group. That is what you must reference. Below is a snippet from my net search.
From the AfterUpdate() event of your Option Group:
Call MyFunc(Me.MyGroup)
... which will use Select Case to evaluate:
Function MyFunc(grp As OptionGroup)
Select case grp.Value
Case 1
MsgBox "Option value is 1."
Case 2
MsgBox "Option value is 2."
Case Else
MsgBox "Huh?"
End Select
End Function
If you are entirely new to VBA, there are a half-dozen things to learn here, but they will serve you well. VBA provides a bit less-friendly-looking start than a macro, but I can tell you have more adventures ahead and I suggest you skip macros. For most needs, VBA will serve you better; and it's much easier to trouble shoot or provide details when you need advice.
To convert this to a useful function, you will fill a string variable rather than raising a message box. Then you can use the string variable to do something like run an update query. Your latest edit suggests you will go for something like:
strSQL = "UPDATE [Major Equipment] " _
& "SET Field1='" & strUserSelection & "' " _
& "WHERE MyID=" & lngThisRecord
DoCmd.RunSQL strSQL
Your last edit proposes using a DAO recordset. I think you might be fine with the humble DoCmd. Less to learn. You can hammer out a prototype of the query in good ol' Access; then switch to SQL View and paste the query into your VBA module. Insert variables as seen above, taking care with the quote marks. If it doesn't work, use Debug.Print to grab the value for strSQL and take that back to good ol' Access where you can poke at it into shape; use your findings to improve the VBA.

Using Access to store Usernames and Passwords and opening via Visual Basic

Basically i am trying to create a programme, which needs to have some authorisation.
When the application starts up, it starts straight into the login screen.
I want the Username and Password textboxes to read the database and if they match then progress to the next form but if they dont match then a message box will appear.
I also want to create groups of people so if a certain group of people log in they go to a certain form and if the another group of people log in i want them to go to a different form.
Also i want the password box to be * instead of visable text.
Can any one help, this is my code so far...
dbProvider = "PROVIDER=Microsoft.Jet.OLEDB.4.0;"
dbSource = "Data Source = D:/Users.mdb"
con.ConnectionString = dbProvider & dbSource
con.Open()
sql = "SELECT * FROM tblUsers"
da = New OleDb.OleDbDataAdapter(sql, con)
da.Fill(ds, "Users")
con.Close()
MaxRows = ds.Tables("Users").Rows.Count
inc = -1
If UserIDTextBox.Text = ds.Tables("tblUsers").Rows(0).Item("UserID") & User_PasswordTextBox.Text = ds.Tables("tblUsers").Rows(0).Item("Password") Then
MsgBox("This have worked correctly!")
Else
MsgBox("This has not worked, try again!")
End If
The first and most important thing you need to know is that it's almost always a bad idea to code your own authentication system. It's just so easy to do this in ways that seem to be right and even pass all your tests, but still have subtle, hard to spot errors that won't show up until a year later when you find out you were cracked six months ago. Always lean as much as possible on whatever authentication features are made available to you in your platform.
The next thing you need to know is that you do not store passwords in the database. Ever. The correct way to handle password matching is to use a salt to alter the original password in some simple way so that simple dictionary passwords will now no longer result in values that can be easily reversed by a google lookup, and then use use a cryptographic hashing algorithm like bcrypt, scrypt, or (if your really have to) sha1. Do not use md5. Examples of this are readily available on google, and will explain it far better than I can here. When someone wants to log in, you perform the same steps on their attempted password and now compare only the hashes. If you're not doing it this way, it's only a matter of time before your user's passwords become public knowledge.
Next up I noticed a problem with your database connection handling in your code. The code you have is not guaranteed to close the connection. con.Close() should always be in a Finally block, and for preference I like the Using block shorthand.
Finally, way near the bottom you try to use VB's string concatenation operator (&) as a logical AND. Oops. You want to use the AndAlso operator here instead.
Sam,
First of I second what Joel said, hash the passwords. Here's a link: What is the easiest way to encrypt a password when I save it to the registry?
As for the TextBox, just set the PasswordChar property to something like *, and it will mask the characters. You can also limit the length.
As for only allowing certain groups, if you're in a windows domain, you should use active directory groups. To determine is someone is a member of a group you can do something like:
def IsMemberOfAdGroup(grouName as string):
windID = System.Security.PrincipalWindowsIdentity.GetCurrent()
return System.Security.PrincipalWindowsPrincipal(windID).IsInRole(grouName)
This looks like a really good page, that covers what you're trying to accomplish.
ASP.NET 2.0 Forms Authentication Using Access Database