Access 2013 - Count Field Values While Adding Any New Fields - sql

I need some help with this one and I can't seem to find the right question to ask to get an answer. We've got a table which is basically 1 primary key and 130+ software titles as Yes/No fields. I need to count the values (count) by Field Name which we can do with a simple aggregate query, but it's my understanding that would only return results for the fields that were included in the query when it was created. And as additional SW Fields are added to the table, I need to include counts for those also. Is there an easy way to accomplish this. My brain is fried! Thanks in advance.
PK SW1 SW2 SW3 ...
1 1 0 1
2 1 0 0
3 0 1 1
So I would need to return:
SW1 SW2 SW3
2 1 2
And if SW4 was added to the table, include results for that field also.
Chris

Presuming you are using vb within access, you could get the field names and dynamically build the new query for any added fields.
The code below is from An example of how to get field names of a table. Once getting the field names, put them in an array and delete any that exist at the time the query was built. Then dynamically build a new query for any added fields. Probably more work than you were hoping for, but it will work.
Public Function fReturnFieldList(strTableName)
Dim rst As DAO.Recordset
Dim fld As Field
Dim strReturn As String
On Error GoTo ProcError
Set rst = CurrentDb.OpenRecordset(strTableName)
For Each fld In rst.Fields
strReturn = strReturn & ", " & fld.Name
Next fld
EXIT_Proc:
fReturnFieldList = Mid(strReturn, 3)
On Error GoTo 0
Exit Function
ProcError:
strReturn = ", Cannot process " & strTableName
Resume EXIT_Proc
End Function

Related

how to get the last non empty column in ms access

so i have this huge database for my school project it goes like this
id
team
game1
score1
game2
score2
game3
score3
1
barca
vs real
2-1
vs bvb
5-2
vs atletic
0-3
2
real madrid
vs barca
1-2
vs betis
3-0
3
man city
vs man united
1-2
and i want to make a query that will give me only the last game of each team
in excel its easy but i cant do it in ms access
result that i need is
id
team
last game
1
barca
vs atletic
2
real madrid
vs betis
3
man city
vs man united
One thing that has been commented on is that your database design needs to be fixed - you don't go across (as you would in Excel), but down as extra rows in tables. There is a limit of 255 fields in an Access table that you need to be aware of.
However, if you decide to stick with this, a different way of approaching it would be to create a small VBA function that loops the fields backwards to get the answer that you need. Something like:
Function fLastMatch(lngTeamID As Long) As String
On Error GoTo E_Handle
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim strSQL As String
Dim lngLoop1 As Long
Set db = DBEngine(0)(0)
strSQL = "SELECT * FROM tblFootball WHERE id=" & lngTeamID
Set rs = db.OpenRecordset(strSQL)
If Not (rs.BOF And rs.EOF) Then
For lngLoop1 = rs.Fields.Count - 2 To 2 Step -2
If Not IsNull(rs.Fields(lngLoop1)) Then
fLastMatch = rs.Fields(lngLoop1)
Exit For
End If
Next lngLoop1
End If
fExit:
On Error Resume Next
rs.Close
Set rs = Nothing
Set db = Nothing
Exit Function
E_Handle:
MsgBox Err.Description & vbCrLf & vbCrLf & "fLastMatch", vbOKOnly + vbCritical, "Error: " & Err.Number
Resume fExit
End Function
This works because the last (nth) field in the recordset is position n-1 as recordsets are 0-indexed (the first field is in position 0, the second field is position 1.....). This means that the last match field is in position n-2. So we start at this position in the fields and check if there is data. If there is, we have found the last match played and can exit the loop. If there is no data, then we go back two fields, to the previous match, and then repeat the checks.
You can then use this function in a query to get the answers that you require:
SELECT id, fLastMatch(id) AS LastMatch
FROM tblFootball;
This approach means that you don't need to worry about how many matches are included and add the correct number of Nzs in if this changes over time (which is a bad idea in a database).
Regards,
Assuming the missing values are null, you can use nz():
select id, team,
nz(game3, nz(game2, game1)) as last_game
from t;
Note that this would be simpler in any other database. The standard SQL function coalesce() takes multiple arguments:
select id, team,
coalesce(game3, game2, game1) as last_game
from t;

SQL statement that selects array values

I am working on a visual basic project. I have a mdb database connected to my project. I want to add a SELECT query that finds the results which are in array that i give it on my program
I have tried to write a statement like that:
SELECT kodu, adi_soyadi, sectigi_ders_say
FROM ogrenciler
WHERE kodu IN ?
But it does not work. In my page codes I have an array and I want to find results from "ogrenciler" table where the "kodu" is in my array.
Well, you could send that array to a temp table in Access, but that would prevent more then one user using the software at the same time. (or you could add some user name to the temp table. However, if the array of choices is small, say about max 50, then you can create the sql string.
eg:
Dim MySQL As String = "SELECT * from tblHotels WHERE ID IN("
Dim IdList(5) As Integer
Dim i As Integer
For i = 1 To 5
IdList(i) = i
Next
Dim MyList As String = ""
For i = 1 To 5
If MyList <> "" Then MyList = MyList & ","
MyList = MyList & IdList(i)
Next
MySQL = MySQL & MyList & ")"
Using MyCon2 As New OleDbConnection(My.Settings.OLESQL)
Dim da As New OleDbDataAdapter(MySQL, MyCon2)
Dim rstDat As New DataTable()
da.Fill(rstDat)
For i = 0 To rstDat.Rows.Count - 1
Debug.Print(rstDat.Rows(i).Item("HotelName"))
Next ' etc etc. etc.
End Using
So you can use the SQL format of:
SELECT * FROM tblHotels where ID IN (1,2,3)
And thus build up the "list". The only downside to this approach is that the sql string is limited to 2000 characters. So, if your list is larger then say 50 or so items, then you have to adopt a different approach.

Need top3 records in MS Access

I have to create a text box in MS Access where users are able to see the top 3 records of a particular result set. So even if the query results in 5 records I only want it to display the top 3 records as three textboxes (sometimes the result may also be 1,2 or 0 records).
I took the easy way out and created a new subform which was connected to the parent form using master/child field. The textbox was placed in the details part of the subform and as a recordsource of the subfrom used the following query:
Select top 3 tbl1.column1, tbl1.column2
from tbl1
column1 is the control source for the textbox and column2 is the column I have used for master/child link.
Now the catch is that the query works fine when I use it without top 3. But when I use top 3 the textbox suddenly disappears and the subform is completely blank.
I am not able to identify the cause of the error. My guess is that it has something to do with type of the subform. Not sure.
Is there any other way I can have a text box whose number can vary on the basis of the results?(but limiting the resultset to 3)
Appreciate the help.
Textbox are not meant to hold more than 1 value.
You are trying to assign three results of 2 columns to one textbox(No can do).
Use listbox to populate as you are doing, assigning the query you just wrote in the rowsource of the list(no subforms needed). This way users will see the three records.
You could use a textbox in order to accomplish what you are trying to do. But will require some VBA coding to accomplish this.
Public function CombineValuesForTextBox() as string
Dim rst as dao.recordset
Dim strSQL as string
strSQL = "SELECT TOP 3 tbl1.Column1 as field1, tbl1.Column2 as field2 " & _
"FROM tbl1;"
set rst = currentdb.openrecordset(strsql)
if rst.recordcount = 0 then 'Checks if the recordset has records or not
CombineValuesForTextBox = "No records found"
goto EndCode 'Or replace with what actions to take if no records are found
else
rst.movelast 'Forces the recordset to fully load
rst.movefirst
do while not rst.eof
if CombineValuesForTextBox = "" or CombineValuesForTextBox = empty then
CombineValuesForTextBox = rst![field1] & " - " & rst![Field2]
else
CombineValuesForTextBox = CombineValuesForTextBox & vbcrlf & _
rst![field1] & " - " & rst![Field2]
end if
Loop
end if
rst.close
set rst = nothing
EndCode:
if not rst is nothing then
rst.close
set rst = nothing
end if
end function
Then on your form put in the code (be sure the textbox is unbound...)
me.textbox = CombineValuesForTextBox

Delete record based on text found in a field (

I normally do most of this work in Excel 2007, but I do not think excel is the right tool for managing the data that I need to process. So I am trying to convert an excel spreadsheet to an Access 2007 db which I can do with no problem, but before doing anything to the spreadsheet I go through the process of cleaning up the data in it in order to use the resulting information. In excel I use a macro such as the following
Sub deletedExceptions_row()
Dim i As Long
Dim ws As Worksheet
On Error GoTo whoa
Set ws = Sheets("data")
With ws
For i = .Cells.SpecialCells(xlCellTypeLastCell).Row To 1 Step -1
If .Cells(i, 3) = "" Or _
VBA.Left(.Cells(i, 3), 4) = "511-" Or _
VBA.Left(.Cells(i, 3), 5) = "CARL-" Then
.Rows(i).Delete
End If
Next i
End With
Exit Sub
whoa:
MsgBox "Value of i is " & i
End Sub
to remove unnecessary records in the spreadsheet how would I accomplish the same thing in Access 2007.
The macro is looking for particular parts or rather the first few characters of the record's 3rd field in order to determine if the whole record needs to be removed (ex. 511-QWTY-SVP or CARL-52589-00). In all there about 180 such character types that affect 1000's of rows that need to be removed from the spreadsheet, but I would like to replicate that same process in Access 2007, but do not know how.
Thank you all for your assistance with this problem
Within Access you can execute a DELETE statement to discard rows where the value in a field is an empty string ("") or matches one of your patterns.
DELETE FROM YourTable
WHERE
YourField = ""
OR YourField ALike "511-%"
OR YourField ALike "CARL-%";
With YourField indexed, that pattern matching in the WHERE clause offers a potentially large performance improvement over a query using the Left() function such as your spreadsheet macro used. IOW, the following query would require the db engine to run those Left() expressions on every row of YourTable. But with the query above and YourField indexed, the db engine could simply select the matching rows ... which can easily be an order of magnitude faster.
DELETE FROM YourTable
WHERE
YourField = ""
OR Left(YourField, 4) = "511-"
OR Left(YourField, 5) = "CARL-";
Sub DeleteRows(strVal as string)
strVal = Trim(strVal)
if strVal = "" then exit sub
dim dbs as Database
set dbs = CurrentDB
dbs.execute "Delete * FROM YOURTABLE where YOURFIELD Like '" & strVal & "*'"
set dbs = Nothing
End Sub
then call it for each item
DeleteRows("Carl-")
DeleteRows("511-")
Given that you have 180 possible problem rows, it may make sense to create a problem list table. For example:
ExcelImport
ID ARow
1 Carl-abdre
2 511-ferw2
3 wywr-carl
4 123-456
ProblemList
Problem
511-
Carl-
Query
DELETE
FROM ExcelImport
WHERE ExcelImport.ID In (
SELECT ID
FROM ExcelImport, ProblemList
WHERE ARow Like [Problem] & "*" Or ARow & ""="")
ExcelImport after query
ID ARow
3 wywr-carl
4 123-456
You manage database data using the SQL language. For Access, check:
http://msdn.microsoft.com/en-us/library/bb177896%28v=office.12%29.aspx
Sub DeleteX()
Dim dbs As Database, rst As Recordset
Set dbs = OpenDatabase("Northwind.mdb")
dbs.Execute "DELETE * FROM " _
& "Employees WHERE Title ALike 'FOOBAR-%';"
dbs.Close
End Sub

VBA - Access 03 - Iterating through a list box, with an if statement to evaluate

So I have a one list box with values like DeptA, DeptB, DeptC & DeptD. I have a method that causes these to automatically populate in this list box if they are applicable. So in other words, if they populate in this list box, I want the resulting logic to say they are "Yes" in a boolean field in the table.
So to accomplish this I am trying to use this example of iteration to cycle through the list box first of all, and it works great:
dim i as integer
dim myval as string
For i = o to me.lstResults.listcount - 1
myVal = lstResults.itemdata(i)
Next i
if i debug.print myval, i get the list of data items that i want from the list box. so now i am trying to evaluate that list so that I can have an UPDATE SQL statement to update the table as i need it to be done.
so, i know this is a mistake, but this is what i tried to do (giving it as an example so that you can see what i am trying to get to here)
dim sql as string
dim i as integer
dim myval as string
dim db as database
sql = "UPDATE tblMain SET "
for i = 0 to me.lstResults.listcount - 1
myval = lstResults.itemdata(i)
If MyVal = "DeptA" Then
sql = sql & "DeptA = Yes"
ElseIF myval = "DeptB" Then
sql = sql & "DeptB = Yes"
ElseIf MyVal = "DeptC" Then
sql = sql & "DeptC = Yes"
ElseIf MyVal = "DeptD" Then
sql = sql & "DeptD = Yes"
End If
Next i
debug.print (sql)
sql = sql & ";"
set db= currentdb
db.execute(sql)
msgbox "Good Luck!"
So you can see why this is going to cause problems because the listbox that these values (DeptA, DeptB, etc) automatically populate in are dynamic....there is rarely one value in the listbox, and the list of values changes per OrderID (what the form I am using this on populates information for in the first place; unique instance).
I am looking for something that will evaluate this list one at a time (i.e. iterate through the list of values, and look for "DeptA", and if it is found add yes to the SQL string, and if it not add no to the SQL string, then march on to the next iteration). Even though the listbox populates values dynamically, they are set values, meaning i know what could end up in it.
Thanks for any help,
Justin
I don't understand what you're trying to accomplish. However, I suspect your UPDATE statement needs a WHERE clause. ('WHERE OrderID = X', with X replaced by the OrderID of the row you're editing)
I suppose you could create a dictionary object with values initially set to False.
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
dict.Add "DeptA", False
dict.Add "DeptB", False
' .. etc. '
Then go through the items in your listbox, changing the dict value to True.
dict(myval) = True
Finally, build your UPDATE statement based on the dictionary values.
But that all seems like too much work to me. So now I'm wondering about your table structure. Is tblMain set up similar to this?:
OrderID DeptA DeptB DeptC DeptD
------- ----- ----- ----- -----
127 True False False True
If so, consider a related table for the Dept information.
OrderID Which_Department
------- ----------------
127 DeptA
127 DeptD
The rule of thumb governing this is "columns are expensive; rows are cheap".
Edit: Seems to me you have two sets of items: SetA is all possible items; SetB is a subset of SetA. You want to produce a True for each item in SetB and a False for each SetA item which is not in SetB. Is that correct when you substitute dict (the dictionary object) for SetA and lstResults for SetB?
What I was trying to suggest is load dict with all the possible "DeptX" keys and assign them as False. Then iterate your lstResults and change each of those (in dict) to True. Afterward, build your SQL statement from dict.
Dim varKeys As Variant
Dim i As Integer
Dim strFragment As String
varKeys = dict.keys()
For i = LBound(varKeys) To UBound(varKeys)
strFragment = strFragment & ", " & varKeys(i) & " = " & dict(varKeys(i))
Next i
strFragment = Mid(strFragment, 3)
sql = sql & strFragment & "WHERECLAUSE"