I'm creating a database in Access that will have many similar tables with identical and the names of them will all be named in sequential order, such as table_0, table_1...
I wanted to know if there is a way to make a loop in VBA to first make the tables, and them altering them, such as adding a field easier.
So if I were to make the tables, this would be the process, but in VBA
while(i=0, i<20,i++){
CREATE TABLE table_i(
field_1 int PRIMARY KEY,
field_2 CHAR(255));
}
In VBA, you can loop through your numbered table names like this ...
Dim strTable As String
Dim i As Long
For i = 0 To 20
strTable = "table_" & CStr(i)
Debug.Print strTable
Next i
But you want to do something with each table name other than print it. So say you've already worked out your CREATE TABLE statement. Maybe it looks like this ...
CREATE TABLE table_name (
field_1 INTEGER PRIMARY KEY,
field_2 TEXT(255));
So you could load that DDL statement into a string variable, then each time through your For loop replace table_name with the current value of strTable, and execute that DDL statement.
Dim strTable As String
Dim i As Long
Dim strSql As String
strSql = "CREATE TABLE table_name (" & vbCrLf & _
"field_1 INTEGER PRIMARY KEY," & vbCrLf & _
"field_2 TEXT(255));"
For i = 0 To 20
strTable = "table_" & CStr(i)
CurrentProject.Connection.Execute Replace(strSql, _
"table_name", strTable)
Next i
That Replace() function first became available with Access 2000. If you're using an older Access version, you'll need a different method to handle your SQL string.
If you want to execute an ALTER TABLE statement, you could do that instead.
So I think doing what you want can be easily done with VBA. However I'm uncertain whether you should do it. Multiple tables with identical structure is generally a red flag signalling a design error. There is no evidence to conclude it's a design error in your case. OTOH, there is no evidence to conclude it's not a design error. ;-)
Related
Access table Allowances_3_15_18 has 5 columns. I want to insert a calculated field from a form EmployeeSalary) into one of the columns Amount in the table.
Each value will link with the relevant primary ID's from the form and the table which are the same JobID. How do I do this in VBA?
I currently have done it in the afterUpdate event in the property sheet.
Private Sub ProjectedDollarAmount_AfterUpdate()
Dim strSQL As String
Dim ProjectedDollarAmount As Currency
strSQL = "INSERT INTO [Allowances_3_15_18] ([Amount]) VALUES (" & _
PrepareSQLNumber(Me.ProjectedDollarAmount) & ") WHERE JobID = " & _
PrepareSQLNumber(Me.JobID) & ";"
Call ExecuteMyCommand(strSQL)
End Sub
You need to get away from SQL concatenation and start using parameters.
Create a query with two parameters, the amount to be inserted and the JobId. The query's SQL should be something like this:
PARAMETERS [prmAmount] Currency, [prmJobId] Long;
UPDATE [Allowances_3_15_18] SET [Amount] = [prmAmount]
WHERE JobID = [prmJobId];
Then in code, simply pass the parameter values and execute the above query:
Sub Add()
With CurrentDb().QueryDefs("qryName")
.Parameters("[prmAmount]").Value = PrepareSQLNumber(Me.ProjectedDollarAmount)
.Parameters("[prmJobId]").Value = PrepareSQLNumber(Me.JobID)
.Execute dbFailOnError
End With
End Sub
You need to change the qryName to the actual name of the query.
You can read more about parameter queries here.
I followed the tips by others to produce an access query.
I have two tables. See figure1. And the result is figure2.
Figure1
http://img.libk.info/f1.png http://img.libk.info/f1.png
Figure2
http://img.libk.info/f2.png http://img.libk.info/f2.png
The method to generate the result query is solved in another question.
The query script :
TRANSFORM Nz(Count([number]),0) AS CountValue
SELECT Table1.ID
FROM Table1, Table2
WHERE (((Table2.number) Between [table1].[start] And [table1].[end]))
GROUP BY Table1.ID
PIVOT DatePart("yyyy",[ndate]);
My question is:
Is there anyway to write back the query result to table 1?
I want to add two new columns in table 1. And be able to add or update the query value to the field base on its "ID".
I'm not sure my description is clear or not. Hope you may understand and thanks for your help!
You won't be able to do it directly. However, here are two ways it could be done indirectly.
Method 1: Temp Table
This method is best for a quick-and-dirty one-time solution.
Create a Make-Table query based on your query and use it to make a temporary table.
Use the temporary table joined to [Table 1] to update your two new fields.
Delete the temporary table
Method 2: VBA Routine
This method is best when you want a repeatable method. It's overkill if you're only going to do it once. However, if you want calculated values for every year, you'll need to run it again.
Read the query into a recordset
Loop through the Recordset and for each ID, either
Run a sql statement to update table 1, or
open a second recordset querying by the ID and Edit/Update
Here's a simple version that updates the value for a single year.
Public Sub UpdateAnnualTotal(ByVal nYear As Long)
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim sSQL As String
Dim sId As String
Dim nTotal As Long
Set db = CurrentDb
sSQL = "SELECT [ID],[" & nYear & "_count"] FROM AnnualTotalsQuery"
Set rs = db.OpenRecordset(sSQL)
With rs
Do Until .EOF
sId = .Fields("ID").Value
nTotal = .Fields(nYear & "_count").Value
sSQL = "UPDATE [Table 1] SET [" & nYear & "_count"] = " & nTotal _
& " WHERE [ID] = '" & sId & "'"
db.Execute sSQL
.MoveNext
Loop
.Close
End With
End Sub
In Microsoft Access, I would like to merge a specific column from tables having an identical key (=being duplicates) into a new table.
The source table would look like this
Key Content
Apple X
Banana B
Banana D
Banana E
Banana G
Lemon A
Lemon X
Orange A
I would like to create a new table where there is only one entry per key, each key having a field consisting of all corresponding “content” fields accumulated into one field. Each “content” value should be delimited with something, example:
Apple X
Banana B<x>D<x>E<x>G
Lemon A<x>X
Orange A
I would prefer to have it like above, but it could also work if they are in different fields/columns like below:
Apple X
Banana B D E G
Lemon A X
Orange A
I would really appreciate help with this. When googling on this I have found a quit a few of third party add-ons (like this one http://www.tucows.com/preview/1586663/MS-Access-Join-Two-Tables-Software) that seems to be solving this, but surely this can be done with MS Access itself….or…?
Another approach would be to create the table with the two columns (Key, Content) then run the function below to copy the data into the new table. You'll have to replace "ExistingTableName" and "NewTableName" with your table names.
Sub CreateNewTable()
Dim rs As Recordset
Dim rsContent As Recordset
Dim strContent As String
'Select and loop through all keys
Set rs = CurrentDb.OpenRecordset("SELECT DISTINCT Key FROM [ExistingTableName]")
Do Until rs.EOF
'Select all content records for this key and combine into a string
Set rsContent = CurrentDb.OpenRecordset("SELECT Content FROM [ExistingTableName] WHERE Key = " & Chr(34) & Nz(rs!Key) & Chr(34))
strContent = ""
Do Until rsContent.EOF
strContent = strContent & rsContent!Content & ","
rsContent.MoveNext
Loop
If Len(strContent) > 0 Then
strContent = Mid(strContent, 1, Len(strContent) - 1)
End If
CurrentDb.Execute "INSERT INTO [NewTableName] (Key, Content) VALUES (" & Chr(34) & Nz(rs!Key) & Chr(34) & ", " & Chr(34) & Nz(strContent) & Chr(34) & ")"
rs.MoveNext
Loop
Set rs = Nothing
Set rsContent = Nothing
End Sub
I don't think there is a way to do this without VBA.
Seeing as you are using a relational database you should consider using one table to store keys and another table to store content (one content per row/record), then link them up either by using a third table or by adding the 'key' as a foreign key in the content table. I would also always use an autonumber as the primary key in all MS Access tables, if not for every other reason this is a good idea, simply to avoid corruption and enable you to change a spelling mistake like 'aple' to 'apple' without breaking your relationships.
One version would be to use a UDF and this query:
SELECT Distinct Fruit.Key,
ConcatADO("SELECT Content FROM Fruit WHERE Key='" & [Key] & "'","<x>","<x>")
AS AllRows
INTO NewFruit
FROM Fruit
User Defined Function (UDF)
Function ConcatADO(strSQL As String, strColDelim, strRowDelim)
Dim rs As New ADODB.Recordset
Dim strList As String
On Error GoTo Proc_Err
If strSQL <> "" Then
rs.Open strSQL, CurrentProject.Connection
strList = rs.GetString(, , strColDelim, strRowDelim)
strList = Mid(strList, 1, Len(strList) - Len(strRowDelim))
End If
ConcatADO = strList
Exit Function
Proc_Err:
ConcatADO = "***" & UCase(Err.Description)
End Function
Working within the query design window, you can create a crosstab
TRANSFORM Min(Fruit.Content) AS MinOfContent
SELECT Fruit.Key
FROM Fruit
GROUP BY Fruit.Key
PIVOT Fruit.Content;
Which would return
Key A B D E G X
Apple X
Banana B D E G
Lemon A X
Orange A
You could then save the crosstab and create a new query based on the cross tab. This query could be a Make Table query to get the new table, but as you can see, you have several columns.
If you have a predetermined number of possible rows for each key, there are other approaches.
Finally, you must ask yourself, is de-normalizing really the way to go?
I have a MS Access DB with a primary key on the parent table and 82 other tables.
New data will be coming with new name for the primary key
Before New
RS182 X182RS
RS188 X188RS
RD301 X301RD
Is there a way to rename the primary key value in bulk on all the tables in the DB because I want to associate all previous historical data to the new name value.
Based on that sample, it seems there is a consistent pattern between the new and old primary key values.
? "X" & Right("RS182", 3) & Left("RS182", 2)
X182RS
If that is true, then use a series of UPDATE statements to replace the old values with the new. But first make a backup copy of your database for safekeeping.
For example, if the primary key field for YourTable is named ID:
UPDATE YourTable
Set ID = "X" & Right(ID, 3) & Left(ID, 2);
If YourTable is included in any defined relationships, you will first need to drop those relationships (or at least uncheck the "enforce referential integrity" option for them), then restore the relationships after updating the primary key values.
Also removing the primary key property from ID should allow the UPDATE to complete faster. Re-assign the primary key afterward.
Since you have 82 tables which require this conversion, you could create a VBA procedure to do it.
Public Sub ConvertPKeyValues(ByVal pTable As String, _
ByVal pField As String)
Dim db As DAO.Database
Dim strSql As String
strSql = "UPDATE [" & pTable & "]" & vbCrLf & _
"Set [" & pField & "] = 'X' & " & _
"Right([" & pField & "], 3) & " & _
"Left([" & pField & "], 2);"
Set db = CurrentDb
db.Execute strSql, dbFailOnError
Set db = Nothing
End Sub
Call the procedure with each table name and the name of the relevant field in that table. You should also add an error handler for any problems which dbFailOnError exposes.
It's not that hard to code, at least if I understand the following correct from your question:
the primary key column exists in every table
it has the same name in every table (I'll use ID in my example)
the existing values are all in the same format ("RS182" --> two letters and three numbers)
To get a list of all tables in your database, you can take a look at the hidden table MSysObjects.
And then you just have to loop through the tables and update the ID column.
A quick example (works on my machine):
Public Function Test()
Dim RS As DAO.Recordset
Dim SQL As String
SQL = "select name from msysobjects where type = 1 and name not like 'msys*'"
Set RS = CurrentDb.OpenRecordset(SQL)
Do While Not RS.EOF
SQL = "update " & RS("name") & " set ID = 'X' & Mid([ID],3) & Left([ID],2);"
CurrentDb.Execute SQL, dbFailOnError
RS.MoveNext
Loop
RS.Close
Set RS = Nothing
End Function
You cannot modify a PK if you have related records in the other tables.
So the trick here is to temporarily modify all those relationships (I suppose they exist and that Referential Integrity is enabled - otherwise I wouldn't even talk to you :), and enable the Cascade Update option.
Don't forget to turn that option off once your data goes back in prod !!
I use functions like the following to make temporary tables out of crosstabs queries.
Function SQL_Tester()
Dim sql As String
If DCount("*", "MSysObjects", "[Name]='tblTemp'") Then
DoCmd.DeleteObject acTable, "tblTemp"
End If
sql = "SELECT * INTO tblTemp from TblMain;"
Debug.Print (sql)
Set db = CurrentDb
db.Execute (sql)
End Function
I do this so that I can then use more vba to take the temporary table to excel, use some of excel functionality (formulas and such) and then return the values to the original table (tblMain). Simple spot i am getting tripped up is that after the Select INTO statement I need to add a brand new additional column to that temporary table and I do not know how to do this:
sql = "Create Table..."
is like the only way i know how to do this and of course this doesn't work to well with the above approach because I can't create a table that has already been created after the fact, and I cannot create it before because the SELECT INTO statement approach will return a "table already exists" message.
Any help? thanks guys!
I can think of the following ways you can achieve this
1. Create, then insert
You can do a CREATE TABLE tblTemp with all the columns you need. Of course, you will have more columns than TblMain contains, so your insert will contain column definitions.
INSERT INTO tblTemp (Col1, Col2, Col3) SELECT Col1, Col2, Col3 from TblMain
2. Insert Into, then add column
You can do your insert into, then add columns using multiple ways
In VBA, use the TableDef object to point to tblTemp and then add a column to it
Execute DoCmd.RunSQL "ALTER TABLE tblTemp Add Column MyNewColumn (OTTOMH)
There always more than one way to skin a feline. You could use DAO? This has the advantage of being able to set the various properties of the newly created field that get when creating new field via the user interface within Access. Just an idea :-)
This the sub l created and tested, in Access 2007 should be compatable with any version though.
Public Sub AddField()
Dim dbs As DAO.Database
Dim tdf As DAO.TableDef
Dim fld As DAO.Field
Dim strTable As String
Set dbs = CurrentDb
strTable = "tblTemp"
'create the TableDef object that
'wish to change
Set tdf = dbs.TableDefs(strTable)
'create new field
Set fld = tdf.CreateField("Name", dbText)
'set property of new created field
fld.AllowZeroLength = True
'append the new field to the tabledef
tdf.Fields.Append fld
End Sub