I have a table named cpns with fields as C_Bk_No (Coupon book number), St_No (starting coupon number) and End_No (number of the last coupon) all integers.
I have initiated the table with first record as 1, 1, 25.
I am trying to get the system insert new rows with cpn number + 1, start_No + 25 and End_No + 25 as the new record on clicking a button (Command13) on the form.
Thus, the expected second record should have cpn_bk_no = 2, St_No = 26, End_No = 50.
I am not sure why the following SQL is not working:
Private Sub Command13_Click()
Dim Sql As String
Dim CbkNo As Long
Dim StNo As Long
Dim EndNo As Long
CbkNo = Me![C_bk_No].Value + 1
StNo = Me![St_No].Value + 25
EndNo = Me![End_No].Value + 25
Sql = "Insert Into cpns ([C_bk_No], [St_No], [End_No]); Values (CBkNo, StNo, EndNo))"
CurrentDb.Execute Sql
End Sub
Every time I click the button, it says "Run time error 3061, Too few parameters: Expected 3." and the line "CurrentDb.Execute Sql" is highlighted in yellow.
Please can anyone help?
It's difficult to know where to begin with this, but the basic problem you're facing is that you've typed the names of your variables into the string. Unlike some other languages, vb doesn't look at your string contents and think "oh, he's typed a variable name into that string, I'll jut swap it for the value currently in the variable"
There are other problems with this code too, but not quite so fundamental as that. I'd genuinely recommend you throw all that code away and follow this tutorial instead, about how to access a database in one of the ways Microsoft recommends:
https://msdn.microsoft.com/en-us/library/ms171884.aspx
Even if you're not making a win forms app the concepts there inside can be applied to all kinds of app
This semicolon is wrong. Semicolons are use to separate sql statements from each other.
Insert Into cpns ([C_bk_No], [St_No], [End_No]) **;** Values (CBkNo, StNo, EndNo)
remove it and put it at the and it will work:
Insert Into cpns ([C_bk_No], [St_No], [End_No]) Values (CBkNo, StNo, EndNo);
Related
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.
Need help! I cannot retrieve data in SQ L, because the value is 100-200
and it says that it needs to be converted it to Integer, but it is Var char data type.
So i think the problem is about the "-", then the error in the code is in
the data table code, so what to do?
Private Sub RetriveData(ByVal ID As String)
If Sql.HasConnection() = True Then
Dim DT As DataTable = Sql.ExecuteDataTable("SELECT (EmpID As varchar), FirstName,MidName,LastName,Gender,BirthDate,CivilStat,Address,ContactNum FROM EmployeeTable Where EmpID=" & ID)
For Each Data As DataRow In DT.Rows
EmpID_Txt.Text = Data(0)
FirstName_Txt.Text = Data(1)
MiddleName_Txt.Text = Data(2)
LastName_Txt.Text = Data(3)
Gender_CB.Text = Data(4)
BirthDate_DTP.Value = Data(5)
CivilStat_CB.Text = Data(6)
Address_Txt.Text = Data(7)
Contact_Txt.Text = Data(8)
Next
Else
MsgBox("System Database Cannot be Connected", MsgBoxStyle.Information)
End If
End Sub
Pass the ID as SQL Parameter to your query:
"SELECT (EmpID As varchar), FirstName,MidName,LastName,Gender,BirthDate,CivilStat,Address,ContactNum FROM EmployeeTable Where Empid=#empid"
Actually, assuming those 100-200 values are in a table somewhere, I would say the error was in the cranium of the developer who decided to use a varchar to store a numeric value :-)
Either that, or the developer who decided to take an otherwise free-form text field and assume it was always going to be a single numeric value.
As to how to fix it, one of those paragraphs simply needs to be worked around. If you want a single numeric value in the table, you should change it to a numeric type field, along the way going through all 100-200 style values and cleaning them up (into, for example, 150).
If you want to be able to store ranges, then your code that processes them is going to have to be more intelligent, such as extracting the lower and upper value from the column and running a loop to process all values within the range, or changing a query from (pseudo-codish query):
where EmpId = ?value?
into:
where EmpId >= ?low? and EmpId <= ?high?
We can't really tell you which is the best solution based on the information provided, this is likely to be a business decision rather than a technical one.
If my assumption is wrong and it's the ID argument (being used in the where clause) to the function RetriveData (sic), then you either need to stop users entering that, or make the code that processes the argument more intelligent (similar to the looping/changed-query suggestion above).
I've got a tablea such as below, I know its bad design having multifield value column but I'm really looking for a hack right now.
Student | Age | Classes
--------|------|----------
foo | 23 | classone, classtwo, classthree, classfour
bar | 24 | classtwo, classfive, classeight
When I run a simple select query as below, I want the results such a way that even occurrence of classtwo is displayed as class2
select student, classes from tablea;
I tried the replace() function but it doesnt work on multivalued fields >_<
You are in a tough situation and I can't think of a SQL solution for you. I think your best option would be to write a VB function that will take the string of data, parse it out (replacing) the returning you the updated string that you can update your data with.
I can cook up quite a few ways to solve this.
You can explode the mv by using Classes.Value in your query. This will cause one row to appear for each value in the query and thus you now can use replace on that. However, this will result in one separate row for each class.
So use this:
Select student, classes.Value from tablea
Or, for this example:
Select student, replace(classes.Value,"classtwo","class2") as myclass
from tablea
If you want one line, AND ALSO the multi value classes are NOT from another table (else they will be returning ID not text), then then you can use the following trick
Select student, dlookup("Classes","tablea","id = " & [id]) as sclasses
from tablea
The above will return the classes separated by a space as a string if you use dlookup(). So just add replace to the above SQL. I suppose if you want, you could also do replace on the space back to a "," for display.
Last but not least, if this those classes are coming from another table, then the dlookup() idea above will not work. So just simply create a VBA function.
You query becomes:
Select student, strClass([id]) as sclasses from tablea
And in a standard code module you create a public function like this:
Public Function strClass(id As Variant) As String
Dim rst As DAO.Recordset
If IsNull(id) = False Then
Set rst = CurrentDb.OpenRecordset("select Classes.Value from tableA where id = " & id)
Do While rst.EOF = False
If strClass <> "" Then strClass = strClass & ","
strClass = strClass & Replace(rst(0), "classtwo", "class2")
rst.MoveNext
Loop
rst.Close
Set rst = Nothing
End If
End Function
Also, if you sending this out to a report, then you can DUMP ALL of the above ideas, and simply bind the above to a text box on the report and put the ONE replace command around that in the text box control. It is quite likely you going to send this out to a report, but you did ask how to do this in a query, and it might be the wrong question since you can "fix" this issue in the report writer and not modify the data at the query level. I also think the replace() command used in the report writer would likely perform the best. However, the above query can be exported, so it really depends on the final goal here.
So lots of easy ways to do this.
I have several records in a database that have Start and End Dates
09/15/2011 - 09/30/2011
10/15/2011 - 10/22/2011
11/01/2011 - 11/15/2011
When user stores a record, I need to make sure dates don't overlap.
My simple code checks date ranges within a specific record (e.g. user enters 9/16/2011 or 10/21/2011, I throw an exception.)
But, on the slim chance a user gets creative (e.g. 10/14/2011 - 10/23/2011 or even 10/14/2011 to 11/16/2011), now they have circumvented my check.
BTW, the user could enter 10/14/2011 to 10/23/2011 if they were editing the record that contained values 10/15/2011 - 10/22/2011.
So, I'm trying to solve this riddle with a linq query. However, what I have isn't working exactly right.
UPDATE Nevermind about code not working. While trying to provide an example to expand on Miika's repsonse, I found my answer. So, giving credit to Miika for pointing me in the right direction and posting my working code below:
Here's my code:
Private Sub CheckForOverlap(myMonth As Messages.MyMonth)
Dim am As New MyMonth()
Dim amCollection As Messages.MyMonthCollection
Dim overlappingMyMonthDate As Boolean = False
Dim sErrorMsg As String = ""
'...non-applicable code omitted
Dim query = From s In amCollection _
Let s1 As MyMonth = CType(s, MyMonth) _
Where s1.AttendanceMonthID <> attendanceMonth.AttendanceMonthID And _
(CDate(attendanceMonth.StartDate) < CDate(s1.StartDate) And CDate(attendanceMonth.EndDate) > CDate(s1.EndDate)) _
Select s1
If query.Count > 0 Then
sErrorMsg = "Dates entered surround another entry"
End If
If overlappingMyMonthDate Then
Throw New Exception(sErrorMsg)
End If
End Sub
End Class
It all came down a LINQ query.
Do you need to do it in code or would SQL be an option? If the data is in a database, you could use the following query to check for overlaps.
SELECT COUNT(*)
FROM Table1
WHERE Table1.StartDate < 'endCheckDate'
AND Table1.EndDate > 'startCheckDate'
This will return a count of the number of overlaps found. 'endCheckDate' and 'startCheckDate' are your new query values (in date format). If your data is in a object collection in memory, then you could use LINQ. If you need help with a LINQ statement, let me know.
I'm writing some code behind some spreadsheets and I need to loop through some code, like getting data from a database doing some formulas and moving the data to a new sheet. My code for getting the data from the database is getting all of the values in multiple columns where the data has not been reported and has the same file name ( the data is coming from a file ), I have a field which states whether or not it has be reported by a simple "Y" or "N", and a field which holds the filename it came from.
So I need a while that, "WHILE" there's data that hasn't been reported do the rest of my code ( this includes my first SQL statement I said as this get data that hasn't been reported from each individual filename ).
I've been trying to get this to work for days but just have not been able to figure out how, so any help would be very grateful.
Update:
Database has a entity called datareported, can either be "N" or "Y", and also datadatfile which is the name from which the data came from.
So,
WHILE datareported = 'N' THEN
"SELECT (the data rows I want)
FROM tbldata
WHERE datareported='N' and datadatfile =
(SELECT min(datadatfile)
FROM tbldata WHERE datareported='N')"
This means that I loop through the rest of my code WHILE there is data that hasn't been reported and only bring in data of the same name ( from datadatfile ) so that the code can be run on that data.
That's basically what I want to do and that's pretty much what I have tried. I have tried a few other things and normally get a return of type mis-match.
Cheers,
Sam
More information needed for a solution but this may be of some help to push you in the right direction...
'ws1 = worksheet object...
bC = true
cnt1 = 2 ' starting row
Do While bC
If ws1.cells(cnt1, 1) = "N" Then
'Run your SQL here
Else
'Already reported...
End If
cnt1 = cnt1 + 1
If 'you hit the last row on your worksheet set bC = False to exit loop
Loop
Missed the update...this wont help, sorry.