I have been given a list of UK postcodes, with the following format L15TG or TS14TGU.
I need to be able to match these postcodes to a list of postcodes I have stored in the database, however, my list are only UK outcodes, so L15TG is just L1 and TS14TGU is just TS14.
So I need to match the records and return the part of the string that matches i.e take L1 from L15TG.
Okay, so we have a table named [BadCodes]
BadCode
-------
L15TG
TS14TGU
and a table named [OutCodes]
OutCode
-------
L1
TS14
I would add two Text fields to the [BadCodes] table: [OuterCode] and [InnerCode]
Then I would use the following VBA code to populate [BadCodes].[OuterCode]
Option Compare Database
Option Explicit
Public Sub PopulateOuterCodes()
Dim cdb As DAO.Database, qdf As DAO.QueryDef, rstOutCodes As DAO.Recordset
Set cdb = CurrentDb
Set qdf = cdb.CreateQueryDef("", _
"PARAMETERS prmOuterCode TEXT(255), prmLike TEXT(255);" & _
"UPDATE BadCodes SET OuterCode=[prmOuterCode] " & _
"WHERE BadCode LIKE [prmLike] AND OuterCode IS NULL")
Set rstOutCodes = cdb.OpenRecordset( _
"SELECT OutCode FROM OutCodes " & _
"ORDER BY Len(OutCode) DESC, OutCode", _
dbOpenSnapshot)
Do Until rstOutCodes.EOF
qdf!prmOuterCode = rstOutCodes!OutCode
qdf!prmLike = rstOutCodes!OutCode & "*"
qdf.Execute
rstOutCodes.MoveNext
Loop
rstOutCodes.Close
Set rstOutCodes = Nothing
Set qdf = Nothing
Set cdb = Nothing
End Sub
After running that VBA code my [BadCodes] table would look like
BadCode OuterCode InnerCode
------- --------- ---------
L15TG L1
TS14TGU TS14
Then I could populate [BadCodes].[InnerCode] with the query
UPDATE BadCodes
SET InnerCode = Replace(BadCode,OuterCode,"",1,1)
and my [BadCodes] table would now look like
BadCode OuterCode InnerCode
------- --------- ---------
L15TG L1 5TG
TS14TGU TS14 TGU
Related
I have two table one (skillsMatrix) the other table is (elementTree) with columns [mediumElement], [ID] in table skillsMatrix the mediumElement is a lookup dropdown of the mediumElements in table two. I want to write a macro to update skills matrix table to add a new record "name", "new topic","" and not duplicate any of the other records when a new mediumElement is added to elementTree.
Table: skillsMatrix
id
employee
mediumElement
completionDate
autoNumber
Dave
Walking
10/27/2020
Table: elementTree
Id
mediumElement
26
Walking
27
Running
I'd like the skillsMatrix table to look like this after running the code
id
employee
mediumElement
completionDate
autoNumber
Dave
Walking
10/27/2020
autoNumber
Dave
Running
I have tried the following to troubleshoot for building out the logic. The following prints out with RS always starting with 1 and ME starting with the proper ID for the mediumElement in element tree.
rs
1
ME
26
rs
2
ME
27
rs
3
ME
28
rs
4
ME
29
rs
5
ME
30
rs
6
ME
31
rs
7
ME
32
rs
8
ME
33
rs
9
ME
34
rs
10
ME
35
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim mediumElements As DAO.Recordset
Dim employeeTable As DAO.Recordset
Dim strSQL As String
Dim strSQLName As String
Dim strSQLintegrityCheck As String
Dim idValue As Long
Dim recordExists As Boolean
If Me.Dirty = True Then Me.Dirty = False 'Save any unsaved data
Set db = CurrentDb
strSQLName = "SELECT employeeTable.ID, employeeTable.[Employee Name] FROM employeeTable WHERE (((employeeTable.[Employee Name])=""" & Me.employeeName & """));"
Set employeeTable = db.OpenRecordset(strSQLName)
idValue = employeeTable.Fields("ID")
Debug.Print (idValue)
strSQLintegrityCheck = "Select skillsMatrix.employee, skillsMatrix.mediumElement From skillsMatrix Where skillsMatrix.employee = " & idValue & ""
Set rs = db.OpenRecordset("skillsMatrix")
strSQL = "Select elementTree.[ID], elementTree.[mediumElement] From elementTree Where ( elementTree.plantPosition = " & Me.jobPosition & ")"
'Debug.Print strSQL
Set mediumElements = db.OpenRecordset(strSQL)
Debug.Print employeeTable.Fields("ID")
If Not mediumElements.BOF And Not mediumElements.EOF Then
mediumElements.MoveFirst
rs.MoveFirst
While (Not mediumElements.EOF)
Debug.Print ("rs")
Debug.Print rs.Fields("mediumElement").Value
Debug.Print ("ME")
Debug.Print mediumElements.Fields("id")
If (rs![employee] <> employeeTable.Fields("ID") And rs![mediumElement] <> mediumElements.Fields("ID")) Then
With rs
.AddNew
![employee] = employeeTable.Fields("ID")
![mediumElement] = mediumElements.Fields("ID")
.Update
End With
End If
rs.MoveNext
mediumElements.MoveNext
Wend
End If
rs.Close
Set rs = Nothing
Set mediumElements = Nothing
Set employeeTable = Nothing
Nothing happens/wrong thing happens as the rs.Fields("mediumElement") does not give the what I would expect as the correct value. Instead of rs.[mediumElement] displaying the lookup ID of element from the elementTree table it always displays 1 through number of records in RS for rs.Fields("mediumElement"). There is an employees table and the IDs are being saved in skillsMatrix. Although I used the lookup wizard when building the connections so that could be the problem. I apologize for my poor vernacular I'm pretty new to access and SQL.
I do not want all employees to be updated with the associated new element. The code is a sub controlled by a button press and the employee to be updated is selected on that form with the control employeeName
EDIT:
On of the suggestion looking into Insert Select
the following works for adding the mediumElements to the skills matrix table,
based on whether or not they exist for a user. Is there a way to also add the employee name to the skills matrixTable with the same Insert Into?
Dim sqlString As String
Dim name As String
Dim strSQLName As String
Dim db As DAO.Database
Set db = CurrentDb
Dim employeeTable As DAO.Recordset
strSQLName = "SELECT employeeTable.ID, employeeTable.[Employee Name] FROM employeeTable WHERE (((employeeTable.[Employee Name])=""" & Me.employeeName & """));"
Set employeeTable = db.OpenRecordset(strSQLName)
idValue = employeeTable.Fields("ID")
Debug.Print (name)
sqlString = "INSERT INTO skillsMatrix (mediumElement)" _
& "SELECT elementTree.ID FROM elementTree " _
& "WHERE NOT EXISTS(SELECT * FROM skillsMatrix Where skillsMatrix.mediumElement = elementTree.ID AND skillsMatrix.employee = " & idValue & " ) "
DoCmd.RunSQL sqlString
End Sub
If employee is selected via a combobox on form, there is no need to open a recordset just to get employee ID. EmployeeID should be a hidden column of combobox and combobox should have that as its value.
idValue = Me.employeeName
If Employee ID is not available on form, a recordset is still not needed. Use DLookup.
idValue = DLookup("ID", "employeeTable", "[Employee Name]='" & Me.employeeName & "'")
Include employee field in INSERT clause and concatenate idValue to produce a calculated field in the SELECT from elementTree clause.
sqlString = "INSERT INTO skillsMatrix (employee, mediumElement) " _
& "SELECT " & idValue & " AS Emp, elementTree.ID FROM elementTree " _
& "WHERE NOT EXISTS(SELECT * FROM skillsMatrix WHERE skillsMatrix.mediumElement = elementTree.ID AND skillsMatrix.employee = " & idValue & " ) "
If employee and mediumElement are defined as a compound index in table, then don't really need the WHERE criteria since duplicate pairs will not be allowed. I don't know if this WHERE criteria slows or improves performance.
If new element ID can be captured from form, simplify code:
sqlString = "INSERT INTO skillsMatrix (employee, mediumElement) " _
& "VALUES(" & idValue & "," & idElement & ")"
Use CurrentDb.Execute instead of DoCmd.RunSQL and won't get warning popups.
I am creating a new query in MS Access that updates an existing record based on the "Branch" and "Employee" fields. How can I set the criteria to reference cell values? Say A2 holds the "Branch" ID for Access and B2 holds the value for the "Employee" ID in Access.I want to update my Access "Notes" Field. My query works when running in Excel, but only because I have specified what the "Employee" & "Branch" ID's are. Nothing updates when i run my code below:
Code
Sub modify_record()
Dim ac As Object
Dim branchid As String
Dim employeeid As String
Dim notesF As String
Set ac = CreateObject("Access.Application")
branchid = Sheets("Sheet4").Range("A2")
employeeid = Sheets("Sheet4").Range("B2")
notesF = Sheets("Sheet4").Range("C2")
Dim strDatabasePath As String
strDatabasePath = "C:\Users\johnsmith\OneDrive\pbsbackup.mdb"
With ac
.OpenCurrentDatabase (strDatabasePath)
Dim db As Object
Set db = .CurrentDb
db.Execute "Update_Records"
End With
End Sub
Query in MS Access. Saved as Update_Records
UPDATE pbsmaster SET pbsmaster.notes = "notesF" WHERE
(((pbsmaster.branch)="branchid") AND((pbsmaster.employee)="employeeid"));
Your variables don't magically transfer into the query, just because they have the same name.
You need to specify the parameters in the Access query, and pass them via a DAO.QueryDef object in the Excel VBA code.
Here is an example: https://stackoverflow.com/a/2317225/3820271
Dim qd As Object ' DAO.QueryDef
Set qd = db.QueryDefs("Update_Records")
qd.Parameters("branchid") = branchid
' etc.
qd.Execute
Here is my solution from what I learned from #Andre. I am able to execute my code, I noticed working with Parameters is much quicker than opening a recordset with DAO.
Sub foo()
Dim db As Database
Dim qdf As QueryDef
Set db = OpenDatabase("C:\Users\employee\OneDrive\samplefile.mdb")
Set qdf = db.CreateQueryDef("", _
"PARAMETERS pbsbranch text , pbsnotes text; " & _
"UPDATE pbsmaster SET pbsmaster.notes=[pbsnotes] " & _
"WHERE pbsmaster.branch=[pbsbranch] " & _
"")
qdf!pbsbranch = Sheets("Sheet4").Range("A2")
qdf!pbsnotes = Sheets("Sheet4").Range("C2")
qdf.Execute dbFailOnError
Set qdf = Nothing
Set cdb = Nothing
End Sub
I'm inserting data problematically into tables. When I do this from another table, it's swift, only slowed very slightly if there are a lot of records. Even then, it's a matter of seconds.
When I insert from a query to a table, it goes into minutes - roughly a minute for every 1,000 records inserted.
The source query itself, when just run as a select query, takes maybe 1 - 2 seconds. Is the query running for every record that's inserted? I'd hoped that it would run once for the whole data set. Or is there something else that's causing the function to run so slowly when compared to inserting "flat" data from another table.
The VBA I'm using is fairly innocuous:
CurrentDb.Execute "SELECT [Extra Value Concatenation].* _
INTO [" & strTableName & "] FROM [Extra Value Concatenation];"
and the source query is below - it uses Allen Browne's Concatenate function.
SELECT [Extra Fields - Fee Protection Insurance Concatenate].ContactID,
ConcatRelated('[Fee Protection Insurance]',
'[Extra Fields - Fee Protection Insurance Concatenate]',
'ContactID = ' & [ContactID])
AS [Fee Protection Insurance]
FROM [Extra Fields - Fee Protection Insurance Concatenate];
EDIT: In answer to Fionnuala's comment, but I couldn't format it properly in the comments.
Using fictional data, here's roughly what I want.
T1 contains client records.
ContactID Name
1 Example Limited
2 Another Company Limited
T2 contains extra fields. ContactID is there as a foreign key, and may be duplicated if multiple records are held.
ContactID FieldValue
1 Value 1
1 Value 2
2 Value 3
2 Value 4
2 Value 5
When I left join the tables, the duplicates from T2 show up, so I get
ContactID Name FieldValue
1 Example Limited Value 1
1 Example Limited Value 2
2 Another Company Limited Value 3
2 Another Company Limited Value 4
2 Another Company Limited Value 5
when what I want is
ContactID Name FieldValue
1 Example Limited Value 1; Value 2
2 Another Company Limited Value 3; Value 4; Value 5
Hence concatenating the data in a temporary table seemed like a good idea, but is slowing everything down. Is there another way I should be looking at my query?
I have written a pretty basic module that should accomplish this for you very quickly compared to your current process. Note you will need to re-name your project to something other than "Database" on the project navigation pane for this to work
I have assumed that table1 and table2 are the same as you have above
table3 is simply a list of all records in table 1 with a blank "FieldValues" field to add
the required "value1, value2" etc. This should result in Table3 being populated with your desired result
IMPORANT: For anyone using recordset .edit and .update functions make sure you remove record level locking in the access options menu, it can be found under the "client settings" section of Access options, failing to do so will cause extreme bloating of your file as access will not drop record locks until you compact and repair the database. This may cause your database to become un-recoverable once it hits the 2gb limit for windows.
Function addValueField()
'Declarations
Dim db As Database
Dim rs1 As DAO.Recordset
Dim rs2 As DAO.Recordset
Dim qry As String
Dim value As String
Dim recordcount as Long
Set db = CurrentDb()
'Open a select query that is a join of table 1 and table 2
'I have made Contact ID a foreign key in the second table
qry = "SELECT Table1.[Contact ID], Table1.Name, Table2.FieldValue FROM Table1 INNER JOIN Table2 ON Table1.[Contact ID] = Table2.[Contact ID(FK)] ORDER BY [Contact ID];"
Set rs1 = db.OpenRecordset(qry, dbOpenDynaset)
'Table 3 was filled with each record from table1, with a 3rd "Field Value" field to
'be filled with your Value 1, Value 2 etc.
qry = "SELECT * FROM Table3 ORDER BY [Contact ID]"
Set rs2 = db.OpenRecordset(qry, dbOpenDynaset)
'Ensure you have enough file locks to process records
recordcount = rs1.recordcount
DAO.DBEngine.SetOption DAO.dbMaxLocksPerFile, recordcount + 1000
rs1.MoveFirst
rs2.MoveFirst
'Here we test to see if "Name" is the same in both recordsets, if it is, add the FieldValue
'to the FieldValue in Table3, otherwise move to the next record in table 3 and compare again
Do While Not rs1.EOF
If IsNull(rs2![FieldValue]) = True Then
If rs2![FieldValue] = "" Then
rs2.Edit
rs2![FieldValue] = rs1![FieldValue]
rs2.Update
rs1.MoveNext
Else
rs2.Edit
rs2![FieldValue] = rs2![FieldValue] & "; " & rs1![FieldValue]
rs2.Update
rs1.MoveNext
End If
Else
rs2.MoveNext
End If
Loop
rs1.close
rs2.close
db.close
set db = nothing
set rs1 = nothing
set rs2 = nothing
End Function
You are using a user defined function (UDF) ConcatRelated, so the UDF runs for each record, otherwise, usually Access SQL works in the normal way.
Building on pegicity's answer, my eventual code was:
Option Compare Database
Sub Concatenate(strTableToConcatenate As String, strFieldToConcatenate As String, strIDField As String)
Dim rsSource As DAO.Recordset
Dim rsDestination As DAO.Recordset
Dim qry As String
Dim strSourceTable As String
Dim i As Integer
Dim strFieldName As String
Dim strValue As String
Dim intConcatenateID As Integer
Dim intSortID As Integer
strSourceTable = strTableToConcatenate & " (Concatenate)" 'Creates a duplicate copy of the table to be concatenated and empties the original table'
DeleteTable (strSourceTable)
DoCmd.CopyObject , strSourceTable, acTable, strTableToConcatenate
qry = "DELETE FROM [" & strTableToConcatenate & "]"
CurrentDb.Execute (qry)
qry = "ALTER TABLE [" & strTableToConcatenate & "] ALTER COLUMN [" & strFieldToConcatenate & "] memo" 'Changes the DataType of the field to be concatenated to Memo, as the result may be considerably longer than the original data'
CurrentDb.Execute (qry)
i = 0
intCurrentID = 0
qry = "SELECT * FROM [" & strSourceTable & "] ORDER BY [" & strIDField & "], [" & strFieldToConcatenate & "]"
Set rsSource = CurrentDb.OpenRecordset(qry, dbOpenDynaset)
qry = "SELECT * FROM [" & strTableToConcatenate & "]"
Set rsDestination = CurrentDb.OpenRecordset(qry, dbOpenDynaset)
For Each fld In rsSource.Fields 'Finds the column number of the fields you are sorting by and concatenating from your source table.'
strFieldName = rsSource.Fields(i).Name
If strFieldName = strFieldToConcatenate Then
intConcatenateID = i
ElseIf strFieldName = strIDField Then
intSortID = i
End If
i = i + 1
Next
If rsSource.recordcount <> 0 Then
rsSource.MoveFirst
intCurrentID = rsSource.Fields(intSortID).Value
strConcatenateValue = ""
While Not rsSource.EOF 'The source recordset is sorted by your designated sort field, so any duplicates of that field will be next to each other. If the row below has the same id as the row above, the sub continues to build the concatenated value. If the row changes, it adds the concatenated value to the destination record set.'
If intCurrentID = rsSource.Fields(intSortID).Value Then
strConcatenateValue = strConcatenateValue & "," & rsSource.Fields(intConcatenateID).Value
rsSource.MoveNext
Else
rsDestination.AddNew
i = 0
If Len(strConcatenateValue) > 0 Then
strConcatenateValue = Right(strConcatenateValue, Len(strConcatenateValue) - 1)
End If
For Each fld In rsSource.Fields
strFieldName = rsSource.Fields(i).Name
If strFieldName = strFieldToConcatenate Then
strValue = strConcatenateValue
ElseIf strFieldName = strIDField Then
strValue = intCurrentID
Else
strValue = rsSource.Fields(i).Value
End If
rsDestination.Fields(strFieldName) = "" & strValue & ""
i = i + 1
Next
rsDestination.Update
intCurrentID = rsSource.Fields(intSortID).Value
strConcatenateValue = ""
End If
Wend
End If
rsSource.Close
rsDestination.Close
Set rsSource = Nothing
Set rsDestination = Nothing
End Sub
I want to multiply all records of a single field in Access 2010.
I tried mult(Field name) and product(field name) to no avail.
Can anyone help me is there any function in Access to do so?
Example:
I have a table having the field S1
S1
---
557
560
563
566
569
572
575
578
581
and the output should be in another table having the field result
Result
--------
6.25E+24
Unfortunately, there is no PRODUCT() function in Access SQL that would allow you to do
SELECT PRODUCT([S1]) AS Result FROM [YourTable]
However, you can use VBA to "roll your own" DProduct() domain aggregate function, similar to the built-in DSum() function:
Option Compare Database
Option Explicit
Public Function DProduct(Expr As String, Domain As String, Optional criteria) As Variant
Dim SQL As String, Result As Double
Dim cdb As DAO.Database, rst As DAO.Recordset
On Error GoTo DProduct_Error
Set cdb = CurrentDb
SQL = "SELECT " & Expr & " AS Expr1 FROM [" & Domain & "]"
If Not IsMissing(criteria) Then
SQL = SQL & " WHERE " & criteria
End If
Set rst = cdb.OpenRecordset(SQL, dbOpenSnapshot)
If rst.BOF And rst.EOF Then
DProduct = Null
Else
Result = 1
Do Until rst.EOF
Result = Result * rst!Expr1
rst.MoveNext
Loop
DProduct = Result
End If
rst.Close
Set rst = Nothing
Set cdb = Nothing
Exit Function
DProduct_Error:
DProduct = Null
End Function
Testing with the sample data in your question
?DProduct("S1", "YourTable")
6.24666417941851E+24
I am curious to know if there is a way to make a table based off priorities?
As in you have a form, subform (datasheet), and 2 buttons.
The subform takes data from a query which takes data from a table.
From here, the query shows projects. You can select the project on the subform and click a button to dec priority, which moves it DOWN on the list by 1 project. If you click the inc button, it moves it UP. If it's at the very bottom and you click the decrease button it will pop up saying "This project is already the lowest priority!" same with the increase, but it'll say it's already the highest.
Is this possible? I really don't know any VBA to access a subform's datasheet and modify it, and I'd like to learn.
UPDATE:
I have 1 table, with 5 priority types, and 1 key.
The table is named ProjectsT, the key is named ProjectID and the 5 priorities are:
CuttingPriority, ProjPriority, EngineerPriority, CutplanPriority, HardwarePriority. Each priority is listed as a number datatype.
This is one set of code I have so far for the buttons from an answer below:
Up button:
Dim strSQL As String
Dim intSavePos As Integer
Dim intSavePosMin As Integer
Dim intSavePosMax As Integer
'Save start and end value (It's assumed you start with 1 ! The value 0 (zero) is used for swapping value's)
intSavePosMin = DMin("CuttingPriority", "ProjectsT")
intSavePosMax = DMax("CuttingPriority", "ProjectsT")
'When the subform is linked to a keyfield use that field for a WHERE like:
'intSavePosMin = DMin("sequence", "tblTableNico5038", "Function='" & Me.sfrmFunctionTables.Form.Function & "'")
'intSavePosMax = DMax("sequence", "tblTableNico5038", "Function='" & Me.sfrmFunctionTables.Form.Function & "'")
intSavePos = Me.txtCuttingPriority
'is it the first ? ==> no action
If intSavePos = intSavePosMin Then Exit Sub
'switch positions
DoCmd.SetWarnings False
strSQL = "UPDATE ProjectsT SET ProjectsT.CuttingPriority = 0 WHERE CuttingPriority=" & intSavePos & ";"
'When the subform is linked to a keyfield use that field for a WHERE like:
'strSQL = "UPDATE tblTableNico5038 SET tblTableNico5038.Sequence = 0 WHERE Function='" & Me.sfrmTableNico5038.Form.Function & "' AND sequence=" & intSavePos & ";"
DoCmd.RunSQL strSQL
strSQL = "UPDATE ProjectsT SET ProjectsT.CuttingPriority = " & intSavePos & " WHERE CuttingPriority=" & intSavePos - 1 & ";"
'When the subform is linked to a keyfield use that field for a WHERE like:
'strSQL = "UPDATE tblTableNico5038 SET tblTableNico5038.Sequence = " & intSavePos & " WHERE Function='" & Me.sfrmTableNico5038.Form.Function & "' AND sequence=" & intSavePos - 1 & ";"
DoCmd.RunSQL strSQL
strSQL = "UPDATE ProjectsT SET ProjectsT.CuttingPriority = " & intSavePos - 1 & " WHERE CuttingPriority=0;"
'When the subform is linked to a keyfield use that field for a WHERE like:
'strSQL = "UPDATE tblTableNico5038 SET tblTableNico5038.Sequence = " & intSavePos - 1 & " WHERE Function='" & Me.sfrmTableNico5038.Form.Function & "' AND sequence=0;"
DoCmd.RunSQL strSQL
DoCmd.SetWarnings True
Me.Refresh
Me.ProjectsTCuttingSubF.SetFocus
SendKeys ("{up}")
Down button:
Dim strSQL As String
Dim intSavePos As Integer
Dim intSavePosMin As Integer
Dim intSavePosMax As Integer
intSavePosMin = DMin("CuttingPriority", "ProjectsT")
intSavePosMax = DMax("CuttingPriority", "ProjectsT")
intSavePos = Me.txtCuttingPriority
'is it the last ? ==> no action
If intSavePos = intSavePosMax Then Exit Sub
'switch positions
DoCmd.SetWarnings False
strSQL = "UPDATE ProjectsT SET ProjectsT.CuttingPriority = 0 WHERE CuttingPriority=" & intSavePos & ";"
DoCmd.RunSQL strSQL
strSQL = "UPDATE ProjectsT SET ProjectsT.CuttingPriority = " & intSavePos & " WHERE CuttingPriority=" & intSavePos + 1 & ";"
DoCmd.RunSQL strSQL
strSQL = "UPDATE ProjectsT SET ProjectsT.CuttingPriority = " & intSavePos + 1 & " WHERE CuttingPriority=0;"
DoCmd.RunSQL strSQL
DoCmd.SetWarnings True
Me.Refresh
Me.ProjectsTCuttingSubF.SetFocus
SendKeys ("{down}")
--
I was curious to see if I could come up with a solution that didn't resort to "SQL glue-up". The result is available for download here (Access 2010 or later).
The key components are a [Managers] table
ID ManagerName
-- --------------
1 Thompson, Gord
2 Elk, Anne
a [Projects] table
ID ManagerID Description Priority
-- --------- -------------------- --------
1 1 buy bacon 1
2 1 wash the car 2
3 1 clean out the garage 3
4 2 test1 1
5 2 test2 2
two saved parameter queries (QueryDefs) to locate the next highest/lowest-priority project
[GetHigherPriorityProject]
PARAMETERS prmManagerID Long, prmCurrentPriority Long;
SELECT TOP 1 Projects.ID, Projects.Priority
FROM Projects
WHERE (((Projects.Priority)<[prmCurrentPriority])
AND ((Projects.ManagerID)=[prmManagerID]))
ORDER BY Projects.Priority DESC , Projects.ID;
[GetLowerPriorityProject]
PARAMETERS prmManagerID Long, prmCurrentPriority Long;
SELECT TOP 1 Projects.ID, Projects.Priority
FROM Projects
WHERE (((Projects.Priority)>[prmCurrentPriority])
AND ((Projects.ManagerID)=[prmManagerID]))
ORDER BY Projects.Priority, Projects.ID;
one more saved parameter query to update the priority of a given project
[SetProjectPriority]
PARAMETERS prmNewPriority Long, prmID Long;
UPDATE Projects SET Projects.Priority = [prmNewPriority]
WHERE (((Projects.ID)=[prmID]));
a dead-simple Class just to hold a couple of Properties
[projectInfo]
Option Compare Database
Option Explicit
Private pID As Long, pPriority As Long
Public Property Get ID() As Long
ID = pID
End Property
Public Property Let ID(Value As Long)
pID = Value
End Property
Public Property Get Priority() As Long
Priority = pPriority
End Property
Public Property Let Priority(Value As Long)
pPriority = Value
End Property
a rudimentary form with a subform
and the code behind that form
Option Compare Database
Option Explicit
Private Sub cmdMoveDown_Click()
AdjustPriority "lower"
End Sub
Private Sub cmdMoveUp_Click()
AdjustPriority "higher"
End Sub
Private Sub AdjustPriority(Direction As String)
Dim cdb As DAO.Database, rst As DAO.Recordset, qdf As DAO.QueryDef
Dim currentProjectID As Long, otherProject As projectInfo
Set rst = Me.ProjectsSubform.Form.RecordsetClone
rst.Bookmark = Me.ProjectsSubform.Form.Recordset.Bookmark
currentProjectID = rst!ID
Set otherProject = GetOtherProject(rst!ManagerID, rst!Priority, Direction)
If otherProject.ID = 0 Then
MsgBox "There is no project with a " & Direction & " priority."
Else
Set cdb = CurrentDb
Set qdf = cdb.QueryDefs("SetProjectPriority")
' swap priorities
qdf!prmNewPriority = rst!Priority
qdf!prmID = otherProject.ID
qdf.Execute
qdf!prmNewPriority = otherProject.Priority
qdf!prmID = currentProjectID
qdf.Execute
Set qdf = Nothing
Set cdb = Nothing
Me.ProjectsSubform.Requery
' now restore the previous current record in the subform
Set rst = Me.ProjectsSubform.Form.RecordsetClone
rst.FindFirst "ID=" & currentProjectID
Me.ProjectsSubform.Form.Recordset.Bookmark = rst.Bookmark
End If
rst.Close
Set rst = Nothing
Set otherProject = Nothing
End Sub
Private Function GetOtherProject(prmManagerID As Long, _
prmCurrentPriority As Long, _
Direction As String) As projectInfo
Dim cdb As DAO.Database, qdf As DAO.QueryDef, rst As DAO.Recordset
Dim rtn As New projectInfo
Set cdb = CurrentDb
If Direction = "higher" Then
Set qdf = cdb.QueryDefs("GetHigherPriorityProject")
Else
Set qdf = cdb.QueryDefs("GetLowerPriorityProject")
End If
qdf!prmManagerID = prmManagerID
qdf!prmCurrentPriority = prmCurrentPriority
Set rst = qdf.OpenRecordset(dbOpenSnapshot)
If rst.EOF Then
rtn.ID = 0
rtn.Priority = 0
Else
rtn.ID = rst!ID
rtn.Priority = rst!Priority
End If
rst.Close
Set rst = Nothing
Set qdf = Nothing
Set cdb = Nothing
Set GetOtherProject = rtn
Set rtn = Nothing
End Function
EDIT re: comment
is there a way to make it automatically add the next priority number on the list if you are adding a record through another form?
Yes. I forgot to mention that in the existing sample solution there is a Before Change Data Macro on the [Projects] table to do just that:
If [IsInsert] Then
If Not IsNull([ManagerID]) Then
SetField
Name Priority
Value = Nz(DMax("Priority", "Projects", "ManagerID=" & [ManagerID]), 0) + 1
End If
End If
Read through the ENTIRE conversation here:
re-order a records sequence using up and down buttons
You're going to need at aleast a mediocre understanding of VBA and how to apply your specific data to a pre-existing example. This person was looking to do almost precisely what your question describes, and the person who was working with him was an Access MVP. I say to read through the entire conversation because there were several iterations and tweaks performed over the course of the solution.
If you have any specific questions after integrating this solution, feel free to come back and ask.
I have a work ticket system in my work place and to create priorities I created two tables: Work_Tickets and Work_Ticket_Criteria. The criteria table has a list of low, low-medium, etc... and a field in Work_Tickets pulls from that 2nd table. Then sort by date.
It doesn't give a single ticket a numerical priority like you are looking for because to do that I believe you would have to create a separate field and then modify the numerical field of each record after every update. Switch 1 with 2, or make the new record 1 and then add 1 to each and every record's priority field to move them down the list.
Or if you start with a number like 1000 then you can make records more or less than 100 in increments of 5, 10, or 20 but then you'll eventually run out of numbers...
Update
If you're willing to go my method of adding another column, then I would just add column and name the field Priority_Numbers or something. Then you would mark each as 1 - whatever but you may want to make check to make sure your number doesn't already exist by making it a key or giving it a check.
Then each time you would want to view your tickets you would use something like:
Dim strSQL As String
Dim myR As Recordset
strSQL = "SELECT * FROM table_name WHERE criteria_here ORDER BY Priority_Numbers ASC"
Set myR = CurrentDb.OpenRecordset(strSQL, dbOpenDynaset)
And bam you have your prioritized list.
Now to change the priority you would need to select that recordset, do a findfirst to get the record with the value you want replaced, +1 or -1 to each priority number in a While not EOF loop and keep moving next. This will become tedious if your tickets become too high I'm sure.