GoodDay to you all!
I am working on an excel project and I am looking for some help. I don't mind whether this is solved via a formula or a script.
To begin with I will provide the big picture. I have a whole bunch of SQL Queries that call on various fields from different locations. These field titles have now changed. I would like to update the SQL Queries with the new names of the fields. As such, I was going to copy all the queries into an excel spreadsheet and then lookup the field names in the string and replace them with the new field names from a table in another sheet. The problem is that there is approximately 10 field names per query that need replacing.
So this is what I was thinking. Essentially, if I have a string in cell A1 with the words: "We are the very best". For every word that exists in the string I would like to check if it exists in a table in another sheet. If the word exists in the table, replace it. If not then move onto the next word. Continue for every word in the cell.
Any help that can be provided is very much appreciated. Thanks in advance!
Consider:
Sub FixIt()
Dim r As Range, A As Range
Dim s1 As Worksheet, s2 As Worksheet
Set s1 = Sheets("Sheet1")
Set s2 = Sheets("Sheet2")
Set A = s1.Range("A:A").Cells.SpecialCells(xlCellTypeConstants)
For Each r In A
v = " " & r.Value & " "
For j = 1 To 10
oldv = " " & s2.Cells(j, 1).Text & " "
newv = " " & s2.Cells(j, 2).Text & " "
v = Replace(v, oldv, newv)
Next j
r.Value = Trim(v)
Next r
End Sub
In this example the replacement table is in Sheet2, cells A1 thru B10...............I pad with "spaces" to insure whole word replacement and to avoid the need to Split and Join.
Related
Bear with me; I have very little MS Access experience, by plenty of SQL experience.
This is the basic data structure:
I have an Employee table, a Training table, and a joining Employee_Training table (many-to-many relationship). There is a list of employees, and a list of possible training that those employees can do. Any employee may have attended several training sessions, where each entry has a recorded attendance date.
I am trying to make a form that performs in the following way:
You select an employee from a combobox at the top of the page
The form contains a list of all possible training sessions, each with a label and a textbox. If the employee has attended a training session, the corresponding textbox has the attendance date in it, otherwise the text box is blank.
I want to be able to enter a date in a blank textbox, or change the date in a non-blank textbox.
I can't use a DataSheet view because there are about 150 training types, and this needs to be converted into a printable form, so the information is compressed in columns on the form, like below:
Rough Layout:
Select Employee: [ John Smith | v ]
Training type 1: [ ] Training type 25: [ ]
Training type 2: [ ] Training type 26: [ 05/06/15 ]
Training type 3: [ ] Training type 27: [ ]
... ...
The form was originally created by hand, but now is created by a VBA script, though I don't think that will be too relevant here.
How do I structure the form's record source and the control source of the text boxes to allow me to see what I want to see, and edit what I need to edit?
you have to drop out the databinded forms.
if you use Recordset objects, you yan use a query binded to comboboxes and
some small vba code to save your data to tables.
I think the printing in access more flexible if u use the reports instead of the forms :)
First, for printing out data, you will create a report. So drop the idea that because of repeating data you have some problem or issue here. You can format your report like an invoice or whatever. The idea that you have “many” choices effects this choice is simply a non-issue. You form/report likely not to include all 150 choices, and makes it hard to read. And if you really must, then you can include all 150 choices along with the “only” ones chosen showing a date.
Next up: Attempting to display 150 choices on the form is going to be REALLY hard – you run out of room fast on the form. And even your above mock-up shows a datasheet like repeating data display – if it goes on for 150 rows, you not likely to fit all that on a screen without using some kind of repeating data display anyway.
Think of ANY existing software you used – VERY rare to see 150 choices all at once – you have to “choose” from a list. Think of an invoice form in an accounting package – you can display all the choices – you have to scroll or “search” a bit to narrow down choices.
The most easy would be to have the main form based on the employee (so name, phone and all that jazz). The sub form would then display a combo box to select the training/course, and then a date box to enter the date. (The date could even automatic be defaulted to today, or the course start date or whatever).
The result is you point + shoot, point + shoot – much like a click, click click type of process.
Here is an example of an invoice form in Access. To make this animation I did not touch the keyboard – it was just mouse click, mouse click etc. However the combo box DOES allow partial matching and search to narrow down the list presented (again with your long list you don’t want to torture users!). So you can type in a “few” characters into the combo box to narrow down the list of choices.
I mean, either way you going to force the user to pick from 150 choices. However, having to “scroll” through 150 choices ALWAYS to JUST view the courses the person has taken is going to be torture to the user.
So display JUST the courses selected – that way when you bring up a student, you can EASY see what courses they have, and as this follow animation shows, it would be a snap to add additional courses.
As noted, the droop down list could “remove” existing choices, or display the date if any course has already been selected. Simple logic to prevent duplicates would be added to the combo box before update event.
There are a ton of possible solutions for this question. I'm gonna try to stay close to your rough layout. I imagine a database with 3 tables like this :
* table: Employee (EmpID, EmpName)* table: Training (TraID, TraName)* table: Employee_Training (EmpTraID, ET_EmpID, ET_TraID, ET_Date)
Create a continuous form based on a query like:
SELECT Training.TraID, Training.TraName, Employee_Training.EmpTraID, Employee_Training.ET_EmpID, Employee_Training.ET_Date
FROM Training LEFT JOIN Employee_Training ON Training.TraID = Employee_Training.ET_TraID;
In the header-section of the main form, place a combobox called "ChosenEmpoyee". As recordsource for this combobox
"SELECT EmpID, EmpName FROM Employee", so 2 columns, and the ColumnWidths set to "0;5" to hide the first column. The column with EmpID is needed to filter the records.
When selecting an employee from the combobox, the afterupdate event will change the recordsource of the form to a query like this example:
SELECT Training.TraID, Training.TraName, A.EmpTraID, A.ET_EmpID, A.ET_Date
FROM ( SELECT Employee_Training.EmpTraID, Employee_Training.ET_EmpID, Employee_Training.ET_TraID, Employee_Training.ET_DateFROM Employee_Training WHERE Employee_Training.ET_EmpID = 3 ) As ARIGHT JOIN Training ON Training.TraID = A.ET_TraID
The code to perform this :
Private Sub ChosenEmployee_AfterUpdate()
Dim SQL As String
SQL = "" & _
" SELECT Training.TraID, Training.TraName, A.EmpTraID, A.ET_EmpID, A.ET_Date " & _
" FROM (SELECT EmpTraID, ET_EmpID, ET_TraID, ET_Date " & _
" FROM Employee_Training WHERE ET_EmpID = " & ChosenEmployee & _
" ) As A " & _
" RIGHT JOIN Training ON Training.TraID = A.ET_TraID"
Me.RecordSource = SQL
Me.Requery
End Sub
A second part of the question; having a contineous form with 5 columns, presenting the information in 5 textboxes with the same name:
TraID / TraName / EmpTraID / ET_EmpID / ET_Date
The employee is chosen by selecting in the combobox in the header.
The training will be chosen by double clicking on the textbox in the
desired row. If there was already a date, the EmpTraID will
have a value greater then 0.
The code to perform this :
Private Sub AT_Date_DblClick(Cancel As Integer)
Dim S As String
Dim D As Date
Dim SQL As String
Dim EmpTraID As Long
S = "Please give the date of attendance."
S = InputBox(S)
If Not IsDate(S) Then
S = "I'm sorry, the entered value > is not recognised as a date."
MsgBox S, vbExclamation
'Cancel = True
Else
D = CDate(S)
S = "Are you sure to enter the following date of attendance ?" & vbCrLf & Format(D, "dddd, dd mmmm yyyy")
If MsgBox(S, vbYesNo + vbDefaultButton2 + vbQuestion) = vbYes Then
'
' the following 2 lines will result into troubles ...
' Ms Access is not able to alter values
' in a recordset based on a query with a JOIN-statement
'
'Me.AT_Date = CDate(S)
'Me.Refresh
'
' if there was already a date then
' change the date by using AttTraiID
' else
' insert a record in AttTrai_T
' end if
'
EmpTraID = Nz(Me.EmpTraID.Value, 0)
If EmpTraID <> 0 Then
SQL = ""
SQL = SQL & "UPDATE Employee_Training "
SQL = SQL & " SET ET_Date = "
SQL = SQL & " #" & Format(D, "mm/dd/yyyy") & "# " ' US-date-format needed when using '#' in a query in MsAccess
SQL = SQL & " WHERE EmpTraID = " & EmpTraID
Else
SQL = ""
SQL = SQL & "INSERT INTO Employee_Training "
SQL = SQL & " (ET_EmpID, AT_TraID, ET_Date) "
SQL = SQL & " VALUES "
SQL = SQL & " (" & ChosenEmployee
SQL = SQL & " ," & Nz(Me.TraID.Value, 0)
SQL = SQL & " , #" & Format(D, "mm/dd/yyyy") & "# " ' US-date-format needed when using '#' in a query in MsAccess
SQL = SQL & " )"
End If
CurrentDb.Execute SQL
DoEvents: DoEvents: DoEvents
Me.Requery
End If
End If
End Sub
Above is a table that shows what I want to prevent. It allows the entry of a record where the same machine, 1, is related to two contracts at the same time. How do I make a table where I can only insert a new record if all other records with the same idMachine have the cell contracEnded NOT NULL? Thanks for the help.
Assuming you are using a form to enter your data (which you should be) you could try putting the following VBA code in the BeforeUpdate event of the control you are using to enter the data. Here I'm assuming this control is a text box named txtIdMachine, which is bound to the idMachine field in your table (which I'm assuming is named tblMachines).
Private Sub txtIdMachine_BeforeUpdate(Cancel As Integer)
Dim numMachines As Integer, numEnded As Integer
numMachines = DCount("idMachine", "tblMachines", _
"idMachine = " & Me.txtIdMachine)
numEnded = DCount("*", "tblMachines", _
"contractEnded Is Not Null And idMachine = " & Me.txtIdMachine)
If (numMachines - numEnded > 0) Then
Cancel = True
MsgBox "Machine " & Me.txtIdMachine & " is already in use." & _
vbNewLine & "Select a new machine."
End If
End Sub
The DCount(A,B,C) function will return the number of matching entries from field A in a table or query B that matches the search string C (basically the WHERE clause from an SQL query). A,B,C are strings. The "*" tells it to search all columns.
In the end you just have to subtract the number of instances of the machine number the user is trying to enter Me.txtIdMachine from the number of non-Null end dates for that machine, and check to see if there are any idMachine without an end date. Since it's done BeforeUpdate, the current entry doesn't exist yet, so it doesn't count.
my VBA dynamically writes several tables depending on criteria and works quite well.
For example here is adding a row, formatting it and advancing to the next row.
oDoc.Tables(t).Rows.Add 'add a row below control number
oDoc.Tables(t).Rows(i).Range.ParagraphFormat.Alignment = wdAlignParagraphCenter
oDoc.Tables(t).Rows(i).Shading.BackgroundPatternColor = oTableHeaderColor
i = i + 1 'advance to row below control number row.
oDoc.Tables(t).Cell(i, 2).Range.Text = "Check Definition"
I do it in this manner because I just don't know how many rows any given table is - so I create the rows as I need them, might be clumsy but it works.
What I need to do is add a check-able checkbox to rows with text like this.
Planned: ☐
I've tried several ways that just don't seem to work. as far as I can tell it's because i'm not creating the table then selecting it. I've tried recording a macro and that just shows the bit about selection first.
Here's what I've got, it pops an error.
oDoc.Tables(t).Cell(i, 2).Range.Text = "Planned: " & oDoc.Tables(t).Cell(i, 1).Range.ContentControls.Add(wdContentControlCheckBox)
I get "requested member of the collection doesn't exist.
If I try to put it on two lines it will just overwrite cell 1. with the checkbox and I can't seem to position it to the end of the cell first. Any ideas? I've tried insertbefore and that works but I have to insert several checkboxes in the same cell.
Any ideas?
thank you.
Getting text + checkbox + other text + second checkbox into one cell is tricky.
This may be one of the few cases, where you actually have to use the Selection object.
This works for me in Word:
Dim oDoc As Word.Document
Set oDoc = ThisDocument
Const t = 1
Const i = 1
Dim rng As Word.Range
Dim iCheck As Long
Dim sLabel As String
Set rng = oDoc.Tables(t).Cell(i, 2).Range
rng.Select
With Selection
.Collapse Direction:=wdCollapseStart
For iCheck = 1 To 3
sLabel = Choose(iCheck, "Planned: ", " Done: ", " Perhaps: ")
.TypeText Text:=sLabel
.Range.ContentControls.Add wdContentControlCheckBox
' move cursor after checkbox
.MoveRight Unit:=wdCharacter, Count:=2
Next iCheck
End With
In Access, use oWord.Selection instead (or what you have as reference to Word).
The following works for a single checkbox, but I didn't manage to create a second one after additional text.
Dim rng As Word.Range
Set rng = oDoc.Tables(t).Cell(i, 2).Range
rng.Text = "Planned: "
' set range to end of cell
rng.Collapse Direction:=wdCollapseEnd
' I'm not entirely sure why this is needed, but without the checkbox goes into the next cell
rng.MoveEnd Unit:=wdCharacter, Count:=-1
rng.ContentControls.Add (wdContentControlCheckBox)
My goal: to compare the filenames in a directory with the names in a spreadsheet. If there is a match, then I wish to append the corresponding account number of that name to the filename.
I have used the dir command to retrieve all of the filenames in a directory, then pasted the list into a column in the Excel spreadsheet.
I now have 4 columns: Account number, LastName, FirstName, and filename. The main problem here is that the filenames are inconsistent. They're in the form of "lastname, firstname date", but they vary in the forms of "Smith, John 010112", "Smith, J. 010112", "Smith J 010112". This means that when it comes to the first name, I'll only be comparing the first letter of the string.
So essentially, for each filename I need to check the lastname against the lastname column. If a match is found, then I need to check the first letter of the filename's firstname against the first letter of the firstname in the same row as the matching lastname. If this is also a match, then I need to grab the account number in that row and append it to the filename.
How could I do this? I'm pretty new to Excel functions, but I do have a little experience with coding in Java and C from some college classes.
Since you already have the filenames in a column, you can solve the rest using an Excel Formula
=IF(SEARCH(B2&", "&LEFT(C2,1),D2,1)>0,A2&"-"&D2,IF(SEARCH(B2&" "&LEFT(C2,1),D2,1)>0,A2&"-"&D2,""))
This formula will hold true for both Jake Smith and John Smith.
Snapshot
Note:
A2&"-"&D2 part in the formula adds the Ac. No to the Old Filename. If you want Ac. No to be added in the end then change the above to D2&"-"&A2
Well dealling with inconsistent strings can be tricky. Here's a function that can determine the matching last name, and intial of the first name, provided the string pattern doesn't vary outside of your example. Add it to a module, then you can access it by typing in the formula =AppendFileName into a cell.
Public Function AppendFileName(ByVal LName As String, ByVal FName As String, ByVal FileName As String, ByVal AccN As String) As String
If LName = Left(FileName, Len(LName)) Then
If Mid(FileName, Len(LName) + 1, 1) = "," Then 'Check if the string contains a comma
If Mid(FileName, Len(LName) + 3, 1) = Left(FName, 1) Then
AppendFileName = FileName & " " & AccN
End If
Else 'If no comma then assume just one space
If Mid(FileName, Len(LName) + 2, 1) = Left(FName, 1) Then
AppendFileName = FileName & " " & AccN
End If
End If
End If
If AppendFileName = "" Then AppendFileName = False
End Function
You can create a loop around this code to go through all the files and names and automate with the dir function, eg.
Dim x as integer, DirFile as string
DirFile = Dir(Path)
Do While DirFile <> ""
x = x + 1 'To track how many files, and to assign variables as in below line of code
'Set each string variable like FName = Range("A1").offset(x,0).value
'Then assess the the names and file names with the If statements above
'Do something with appended FileName
DirFile = Dir
Loop
Hope this helps.
I have an Excel spreadsheet of about 1000 rows and about 15 columns. What I would like to do is to import this data into a table, but selectively. I want data from Row 5(say) onwards till about 5 rows from the end of the sheet. This is because the number of rows may vary, but I am sure that I will not need the last 4-5 rows irrespective of the number of rows in the spreadsheet. Additionally, I would like to insert only a few columns, as, Col1-Col5, Col7-Col9, and Col12-Col15. Is there one command/sequence of commands that I can use to achieve this? Please let me know ASAP. Thanks a lot!
The code down there creates a file with SQL instructions based on what's in your xls. You just have to add a Macro, paste it, and change a few things (add a while to fill the ColFields Collection with the title line where the column names must match the table field names, declare what's not, give a value to fileName...).
Then execute the Macro and you'll have a file with all the insert you want, then you'll just have to execute this file on your base.
Sub Macro1()
Dim NbOfLines = Worksheets(Sheet1).Range("A65536").End(xlUp).Row - 5 'Not the 5 last lines
Dim ColFields As New Collection 'Do a while on the title line to fill the collection with wanted columns titles (do not add ignored columns)
Dim StartSql As String
StartSql = "INSERT INTO " + TableName + "("
For Each loopField In ColFields
StartSql = StartSql + loopField + ","
Next
StartSql = Left(StartSql, Len(StartSql) - 1)
StartSql = StartSql + ") SELECT "
Dim Value As String
For i = 1 To NbOfLines
Sql = ""
j = 1
For Each loopField In ColFields
Value = Worksheets(SheetName).Cells(i, j).Value
Sql = Sql + IIf(Value = "", "NULL", "'" + Replace(Value, "'", "''") + "'") + ","
j = j + 1
Next
Sql = Left(Sql, Len(Sql) - 1)
Sql = StartSql + Sql + vbCrLf
Call WriteLine(Sql, FileName)
Next
End Sub
Public Sub WriteLine(Ligne As String, FileName As String)
Open FileName For Append As #1
Print #1, Ligne
Close
End Sub
edit : I know it's not the best method (nor the most beautiful one), I gave it to you cos' I used it a few weeks ago to import data from a DB to another (but I needed to do it only once, not everyday).
I also know there is a way to do it with an OpenRowSet you're right about it, but I just don't know how (I'll go often on this page wishing someone'll teach me).
Finally, I encourage you to read this page : A Blog Page With A Solution
(You'll find the great procedure 'uftReadfileAsTable' here : The Procedure Code)
Good Luck!