Suffix Field Values based on number of occurrences in Table - sql

I have a table in MS Access that has a field layout_desc. I need to create a query that changes each value in this field by adding how many times the value is repeated in the table.
For example:
Thank you.

Without a primary key:
Assuming that your table does not contain a primary key field by which to sort records using a domain aggregate function, one possible method is using Static variables in VBA.
Open the VBA IDE using Alt+F11
Insert a new Public Module Alt+I,M
Copy the following basic code into the new Module:
Function Occurrence(Optional strVal As String) As Long
Static lngTmp As Long
Static strTmp As String
If strTmp = strVal Then
lngTmp = lngTmp + 1
Else
lngTmp = 1
strTmp = strVal
End If
Occurrence = lngTmp
End Function
In MS Access, create a new query with the following SQL, changing YourTable to the name of your table:
update (select t.layout_desc from YourTable as t order by t.layout_desc) q
set q.layout_desc = q.layout_desc & occurrence(q.layout_desc)
With a primary key:
If your table were to include a primary key, say id, of Long Integer data type, you could use the domain aggregate function DCount in the following way:
update YourTable t
set t.layout_desc = t.layout_desc &
dcount("*","YourTable","layout_desc = '" & t.layout_desc & "' and id <= " & t.id)

Related

Split multi-value field into different rows using SQL in Microsoft Access

I’ve got a simple table in Microsoft Access that looks like this:
Primary Key
Applications List
123
<Value>|<Value>,<Value>|<Value>
456
<Value>|<Value>,<Value>|<Value>
I need to break out the list of applications into separate rows using the “,” as a delimiter so the end result is a table that looks like this:
Primary Key
Applications List
123
<Value>|<Value>
123
<Value>|<Value>
456
<Value>|<Value>
456
<Value>|<Value>
I’ve tried using the Split function but can’t figure out how to split on the “,” and output the results to a different row like the second table above. I would greatly appreciate your help figuring this one out. Thanks so much!
If you can put a limit on the length of Application List you can build a number table
CREATE TABLE NumTable (ID integer)
up-front with as many rows as there are characters in the longest Application List. Assuming that the longest [Application List] is 1000 characters long, ID=1, ID=2, ..., ID=1000), and then use something like this:
select T.[Primary Key],
mid(T.AL,NT.ID+1
, iif(instr(NT.ID+1,t.AL,',')>0
, instr(NT.ID+1,t.AL,',')-1-nt.ID,1000)) as
from (select [Primary Key], ',' & [Application List] as AL from tblYourTable) as T
inner join
NumTable as NT
on mid(t.AL,NT.ID,1)=','
You can build the number table in EXCEL and paste it; or write a little VBA routine, or even create it dynamically (*).
I can't imagine it performing very well if the volume is high.
Let us know how you proceed!
(*)Generate numbers 1 to 1000000 in MS Access using SQL
If you setup a Table1 and a Table2 with the same field names, you can run this function to do it for you - it basically loops through Table1 records, splits the Applications List into multiple fields and then inserts new records for each field.
Public Sub SplitDataIntoNewTable()
Const DATA_SEPARATOR As String = ","
Dim qdf As DAO.QueryDef
Dim rsOld As DAO.Recordset
Dim rsNew As DAO.Recordset
Dim i As Integer
Dim lngNumAdded As Long
Dim lngKey As Long
Dim strList As String
Dim strNewList As String
Dim varLists As Variant
Set rsOld = CurrentDb.OpenRecordset("Table1", dbOpenDynaset, dbReadOnly)
Set rsNew = CurrentDb.OpenRecordset("Table2", dbOpenDynaset)
With rsOld
While Not .EOF
lngKey = ![Primary Key]
strList = ![Applications List]
varLists = Split(strList, DATA_SEPARATOR)
With rsNew
For i = LBound(varLists) To UBound(varLists)
' Add new record for every list split out of old data
.AddNew
' Note that this CANNOT actually be defined as a PRIMARY KEY - it will have duplicates
![Primary Key] = lngKey
![Applications List] = varLists(i)
lngNumAdded = lngNumAdded + 1
.Update
Next i
End With
.MoveNext
Wend
rsNew.Close
.Close
End With
MsgBox "Added " & lngNumAdded & " New Records"
Set rsOld = Nothing
Set rsNew = Nothing
End Sub
For example I had Table1 look like this:
And resulting Table2 ended up like this

Automatically Increment Number by Group and Entry

I am currently trying to create a number that increases per entry and group in my table via a form.
The form is rather simple it utilizes a ComboBox to select the group from a different table. The generated number should start at 1 and increase for every new entry separately for each selected group.
Code:
My code attempts to create the number BeforeUpdate by searching for the DMax() of the variable for the group selected via ComboBox. Unfortunately, in this current state, the code does not increment the variable called Nummer but it does not throw an Error either.
Private Sub Nummer_BeforeUpdate(Cancel As Integer)
Nummer = Nz(DMax("[Nummer]", "Bau-Tagesbericht", "[Baustelle] = Kombinationsfeld354.Value")) + 1
End Sub
Variables:
Group: Baustelle
Variable that should be incremented: Nummer
Name of the Comobox: Kombinationsfeld354
I appreciate any kind of help, thank you!
You need to concatenate the variable - and set 0 (zero) for Null:
Nummer = Nz(DMax("[Nummer]", "Bau-Tagesbericht", "[Baustelle] = " & Kombinationsfeld354.Value & ""), 0) + 1
If text value, use single quotes:
Nummer = Nz(DMax("[Nummer]", "Bau-Tagesbericht", "[Baustelle] = '" & Kombinationsfeld354.Value & "'"), 0) + 1

Create Variable or Method to use value in multiple forms

Fairly new to VBA. I have a list box on a form within Access which is populated with data from a table. Selecting a value from the list box gives and ID which is then used to perform a query. I need this ID to be available for use in another form to perform a query based on the Value. What is the best way of achieving this?
`Dim IDValue As String
IDValue = Me.lstBoxCompanyName.Value
CompDetailSQL = "SELECT * FROM Companies WHERE Companies.CompanyID = " & IDValue`
In a module
Dim IDValue AS Integer
Sub setIDValue(id As Integer)
IDValue = id
End Sub
Function getIDValue() As Integer
getIDValue = IDValue
End Function
Then from any of your code
'This will set you ID
setIDValue YOUR_VALUE
'This will retrieve the value
getIDValue
'so your code could be
setIDValue Me.Me.lstBoxCompanyName.Value
CompDetailSQL = "SELECT * FROM Companies WHERE Companies.CompanyID = " & getIDValue
Obviously this could use some Error Handling in the event that No IDValue is set. Also I used integer even though I see you are using a String the data type should be the same type as CompanyID so you can change Integer to String if needed but your will also have to change your query to SELECT * FROM Companies WHERE Companies.CompanyID = '" & getIDValue & "' because your query implies a number currently.
You can gain access to another forms controls by specifying the full path. Example :
Forms!MyFormsName!MyControlsName.Value
and
Forms!frmCustomer!CboCustomer.Column(0)
So if you want to access a value in VBA that is contained in another form and use it as part of a query it would look like:
CompDetailSQL = "SELECT * " & _
"FROM Companies " & _
"WHERE CompanyID = " & Forms!frmCustomer!lstBoxCompanyName.Value
in ms Access you can also simply use form_NAMEofFORM.NAMEofCONTROL
Example you can reference
dim myAmount as double
myAmount = form_receipt.total
to access the value showing on form receipt textbox total.

SQL statement for combobox row source

I'm trying to define a SQL statement to use as the Row Source for a ComboBox on an MSAccess form. The SQL should select records from a table tblI where a particular table field matches a variant parameter varS set by the user; however, if varS is Null or not present in another table tblS, the SQl should select all records in tblI.
I can code the first parts of this (varS matches or is null):
SELECT tblI.ID, tblI.S FROM tblI WHERE ((tblI.S = varS) OR (varS Is Null)) ORDER BY tblI.ID;
Where I'm struggling is incorporating the final element (varS not present in tblS). I can code a test for the absence of varS in tblS:
Is Null(DLookup("[tbls.ID]","tblS","[tblS.ID]= " & varS))
but I can't work out how to incorporate this in the SQL statement. Should this work?
SELECT tblI.ID, tblI.S FROM tblI WHERE tblI.S = varS OR varS Is Null OR DLookup("[tbls.ID]","tblS","[tblS.ID]= " & varS) Is Null ORDER BY tblI.ID;
When run as a query it returns every record in tblS no matter the value of varS.
Table structure:
tblI contains 2 fields, Autonumber ID and Long S
tblS contains 1 field, Autonumber ID
My own approach to this problem would be something like this:
Private Sub SetComboSource(vID as Variant)
Dim sSQL as String
sSQL = "SELECT tblI.ID, tblI.S " & _
"FROM tblI "
If IsNull(vID) = False Then
If IsNumeric(vID) = True Then
If DCount("ID", "tblS", "ID = " Clng(vID)) > 0 Then
sSQL = sSQL & "WHERE tblI.S = " & CLng(vID)
End If
End If
End If
sSQL = sSQL & " ORDER BY tblI.ID"
Me.cboComboBox.RowSource = sSQL
End Sub
BTW, I recommend you give your tables and fields more descriptive names and then use aliasing in your SQL, especially for table names. I also think it's best to avoid using Variant variables. I usually use Longs for something like this and I take a value less than 1 to mean that the user didn't select anything, or selected ALL, or whatever meaning you want to derive from it. In other words, my ID's are always a number greater than zero and an ID of less than 1 in a variable means that the ID is empty. Which I use as a signal to create a new record, or to return all records, or whatever meaning you want to derive from it in the given context.
The following should work;
SELECT tblI.ID, tblI.S
FROM tblI
WHERE tbl.ID=varS
OR varS NOT IN(SELECT ID from tblS)

MS access SELECT INTO in vba

I'm having some issues with some functionality of my application. There is a particular instance where I have an instance of a 'pending class' on a form for an administrator to review. The form is populated with students associated with this pending class. After their grades are finished, I have a button at the footer that will delete this class from my 'pending' table and add the grades to all of the students. This works.
However, I want to essentially copy this pending class, which just has the class name, date, and teacher to a completed class table before it's deleted from pending. Since no data about this class other than the primary key(class number) persists throughout this form, I can't populate the other fields(class name, date) of the row into my completed class table.
I am trying a "SELECT INTO" operation in VBA to get these values. It's going like this:
dim cname as String
dim classdate as Date
dim pid as integer
dim teacher as String
dim qry as String
pid = [Forms]![frmClasses]![txtID]
qry = "Select className INTO cname FROM tblPending WHERE tblPending.id = " & " ' " & pid & " ' " & ";"
db.execute qry
debug.print qry
debug.print cname
From here, I do the same operations for each other variable, build my INSERT query, and execute it. The problem is-- my select into's are not working. Debug.print shows that the local variables were never initialized from the SELECT INTO statement. Any thoughts?
First, having all classes in one table and just setting a "NotPending" or "Completed" column would be better.
Having two identical tables for classes and moving values from one into the other to indicate status changes is bad database design.
If you really need to do this by using two tables and copying rows, then you need an INSERT INTO query (and not SELECT INTO), as already mentioned by Remou in the comments, because SELECT INTO creates a new table (or overwrites an existing one with the same name, if already there).
The syntax for INSERT INTO looks like this:
INSERT INTO CompletedClassTable (ClassName, Teacher)
SELECT ClassName, Teacher FROM tblPending WHERE id = 123
And finally, you asked this in a comment:
So SELECT INTO is completely different in Access than Oracle? In Oracle and PL/SQL, you can select a row into a variable OR a table. In Access can you not select into a variable?
To load a row into a variable, you need to use a Recordset.
Example code to load your query into a Recordset and output the ClassName field:
Dim RS As DAO.Recordset
Set RS = CurrentDb.OpenRecordset("SELECT * FROM tblPending WHERE id = 123")
If Not RS.EOF Then
Debug.Print RS("classname")
End If
RS.Close
Set RS = Nothing
Seems you want to retrieve a text value, className, from tblPending where tblPending.id matches the value found in your text box, txtID, and store that text value in a string variable named cname.
If that interpretation is correct, you needn't bother with a query and recordset. Just use the DLookup Function to retrieve the value, similar to this untested code sample.
Dim cname As String
Dim pid As Integer
Dim strCriteria As String
pid = [Forms]![frmClasses]![txtID]
strCriteria = "id = " & pid
cname = Nz(DLookup("className", "tblPending", strCriteria), vbNullString)
Debug.Print "cname: '" & cname & "'"
Notes:
I assumed the data type of the id field in tblPending is numeric. If it is actually text data type, change strCriteria like this:
strCriteria = "id = '" & pid & "'"
DLookup() returns Null if no match found. Since we are assigning the function's return value to a string variable, I used Nz() to convert Null to an empty string. Alternatively, you could declare cname As Variant (so that it can accept a Null value) and get rid of Nz().