How to split a row into multiple based on a quantity column - ms-access

In MS Access, I have a table like this:
TblA_AutoID SKU Qty Price
1 A3323 2 4.53
2 A3313 1 22.20
3 A3323 3 4.59
I want to create a new table (preferably one that populates automatically as new rows are added to the first table) that splits the first table into 1 row per quantity, like this:
TblB_AutoID TblA_AutoID SKU Price
1 1 A3323 4.53
2 1 A3323 4.53
3 2 A3313 22.20
4 3 A3323 4.59
5 3 A3323 4.59
6 3 A3323 4.59
I work on a couple machines, one with Access 2007, one with Access 2013. I'm guessing this needs to be done in VBA, but if there's a simpler way, that's preferable.

Since all you want at this time is to add Qty number of rows into TableB from each row of TableA, you can load each table into a separate DAO.Recordset and use the data in the TableA recordset to drive additions to the other recordset.
I tested this code with your sample data in Access 2010. It populates TableB as you requested.
Dim db As DAO.Database
Dim rsA As DAO.Recordset
Dim rsB As DAO.Recordset
Dim lngQuantity As Long
Dim i As Long
Set db = CurrentDb
Set rsA = db.OpenRecordset("TableA", dbOpenSnapshot)
Set rsB = db.OpenRecordset("TableB", dbOpenTable, dbAppendOnly)
With rsA
Do While Not .EOF
lngQuantity = !Qty.Value
For i = 1 To lngQuantity
rsB.AddNew
rsB!TblA_AutoID.Value = !TblA_AutoID.Value
rsB!SKU.Value = !SKU.Value
rsB!Price.Value = !Price.Value
rsB.Update
Next ' i
.MoveNext
Loop
End With
rsB.Close
rsA.Close
That part is fairly simple. However, you indicated in a comment that ultimately you intend to synchronize TableB with subsequent changes (updates and deletions) of the data in TableA. Those reconciliation efforts will be much more challenging.

Related

How can I select from 2 tables and in every 5 row in the first loop, show 1 item from table 2?

I have a mysql db and use .asp classic.
I want to select from 2 tables, the first is products and the second one is adds. And I want to display 1add and then 5 products and then 1 add and so on, like this.
Add1
Product1
Product2
Product3
Product4
Product5
Add2
Product6
Product7...
So with this I just select from the both tables and then all the adds ar displayed at first and then all products.
sql = " SELECT *
FROM produkt,annonser
where produkt.publicera='true'
AND produkt.antal > "&nr&"
AND annonser.publicera='true'
AND annonser.antal > "&nr&"
order by
produkt.datum DESC,
annonser.datum DESC,
produkt.artikel ASC,
annonser.artikel ASC limit 10"
set rs = conn.Execute (sql)
So how can I select the way I want?
Any input appreciated, thanks.
Ok, so this works.
For i = 1 to 10
If i mod 5 = 1 Then
[Get the current Add and display it]
if not rsAdds.EOF Then
rsAdds.MoveNext()
End If
End If
if not rsProducts.EOF Then
[Display the Product]
rsProducts.MoveNext()
Next
But now Im trying to loop it all again, and I have tried to use rs.MoveFirst in different way, but I only get rs.eof or rs.buf is true so it is not moving the cursor to the first record and loop again?
So I would like to loop the above first and then loop it again, something like this.
For i = 1 to 10
If i mod 5 = 1 Then
[Get the current Add and display it]
if not rsAdds.EOF Then
rsAdds.MoveNext()
End If
End If
if not rsProducts.EOF Then
[Display the Product]
rsProducts.MoveNext()
Next
//Set the rs to move first so that the below will run-----
For x = 1 to 10
If x mod 5 = 1 Then
[Get the current Add and display it]
if not rsAdds.EOF Then
rsAdds.MoveNext()
End If
End If
if not rsProducts.EOF Then
[Display the Product]
rsProducts.MoveNext()
Next
So where or how do I move the rs to the first post after the first loop so it can run the second code?
Thanks.
I assume by ASP classic you mean actual VBA (or javascript?) in a file, without .NET or controls.
You're going to have to do this with a combination of procedural code and SQL. Since it looks like your Adds and your Products aren't related to each other, you probably should pull them in separate queries.
So the code would look something like this:
Dim rsAdds = [query and code to get the Adds]
Dim rsProducts = [query and code to get the Products]
' I'm not sure if you want just 10 products displayed, or all of them. I'm going
' I'm going to go with 10.
For i = 1 to 10
If i mod 5 = 1 Then
[Get the current Add and display it]
if not rsAdds.EOF Then
rsAdds.MoveNext()
End If
End If
[Display the Product]
rsProducts.MoveNext()
Next i
If all you want is just to interpolate data from two unrelated tables, without concern for what the data actually is, don't waste time trying to trick SQL into doing it for you. Do it procedurally.

Record edited twice if edited column has index

TableA has one column 'fielda' of type Long.
There are three records in the table with values 3,4 and 5 respectively.
After running the code below the values should be 18, 19 and 20.
This is the case if there isn't an index on fielda but if there is then the vaues will be
33, 19 and 20.
One record gets edited twice. Is this a bug in DAO or is this normal behaviour?
Dim rs As Recordset
Dim s1 As String
s1 = "select * from tableA"
Set DB = OpenDatabase(DBAddress)
Set rs = MyDB.OpenRecordset(s1)
If Not rs.BOF Or Not rs.EOF Then
rs.MoveFirst
Do While Not rs.EOF
rs.Edit
rs.Fields("fielda").Value = rs.Fields("fielda").Value + 15
rs.Update
rs.MoveNext
Loop
End If
While I was unable to recreate the behaviour you describe I can offer one possible explanation. As you are stepping through the records you may hit the same record more than once if the Recordset periodically checks for changes that may have been made to the underlying table by other users.
Say your Recordset starts out as
3 4 5
and you update the first record so the table now contains
18 4 5
if the Recordset then tries to "refresh" itself and the index on [fielda] controls the order in which the records appear in the Recordset it could end up being
3 4 5 18
and if it continues updating until .EOF the final result could be
3 19 20 33
Possible workarounds would be to
create the Recordset with a SQL statement that includes an ORDER BY clause on some other field so the order of the records will not change as you modify them, or
apply the update via SQL, e.g. UPDATE tableA SET fielda = fielda + 15

Select all rows that have at least a list of features

I have EXPERIMENTAL_RUNS (runId), each of which have any number of SENSORS (sensorId) associated with them. With that in mind, I have an RS table to join the two:
==========
RS
==========
runId, sensorId
Thus if the run with runId=1 had sensors with sensorId=1, sensorId=6, sensorId=8 in it, there would be 3 entries in the RS table:
(runId=1, sensorId=1)
(runId=1, sensorId=6)
(runId=1, sensorId=8)
Is this really how I would return all EXPERIMENTAL_RUNS that have sensors {11,13,15}? From what I've read, what I seem to want is a nested hash join... Is this what's going to happen?
SELECT a.runId
FROM rs a, rs b, rs c
WHERE
a.runId=b.runId AND
b.runId=c.runId AND
a.sensorId=11 AND
a.sensorId=13 AND
b.sensorId=15
To clarify, I want to return only the EXPERIMENTAL_RUNS that have sensors 11 AND 13 AND 15.
Assuming runId, sensorId are unique in the rs table, this will find the runIds that have all 3 sensorIds:
SELECT runId, COUNT(c) ct
FROM rs
WHERE sensorId IN (11, 13, 15)
GROUP BY runId
HAVING ct = 3

How can I efficiently compare string values from two tables in Access?

I have created a VBA function in Access 2010 to compare a list of terms in one table against a list of terms in another table. If the values are alike (not necessarily an exact match), I sum the value from a column from the second table for each match. TableA has approximately 150 terms. TableB has approximately 50,000 terms with a related count (integer).
Example tables:
TableA TableB
--------- ----------
ID ID
Term Term
Count
I have a simple SQL query which calls a VBA function to compare the terms and SUM the count if they have a fuzzy match.
SQL:
SELECT TableA.[Term], TermCheck(TableA.[Term]) AS [Term Count] FROM TableA ORDER BY 2 DESC;
VBA:
Option Compare Database
Public Function TermCheck(Term) As Long
Dim rst As DAO.Recordset
Set rst = CurrentDb.OpenRecordset("TableB", dbOpenDynaset)
Dim ttl As Long
ttl = 0
With rst
While Not .EOF
If rst(1) Like "*" & Term & "*" Then
ttl = ttl + rst(2)
End If
.MoveNext
Wend
End With
rst.Close
Set rst = Nothing
CurrentDb.Close
TermCheck = ttl
End Function
The issue I have is that it uses about 50% of my CPU and I'd like to make it as lightweight as possible. Is there a more efficient way to accomplish this task using Access? Moving to a purely SQL alternative is not an option at this point, although it would make me happier. I'm not an Access or VBA guru, but feel that I'm missing something obvious in my query that would improve performance.
EDIT:
The expected result would list out all terms in TableA with a sum of the count column from TableB where a fuzzy match occurred.
Example Data:
TableA
-------------
1 blah
2 foo
3 bar
4 zooba
TableB
-------------
1 blah 16
2 blah2 9
3 foo 7
4 food 3
5 bar 3
Example result:
Term Count
---------------------
blah 25
foo 10
bar 3
zooba 0
SELECT
TableA.Term,
Nz(subq.SumOfCount, 0) AS Count
FROM
TableA
LEFT JOIN
(
SELECT a.Term, Sum(b.Count) AS SumOfCount
FROM TableA AS a, TableB AS b
WHERE b.Term ALike '%' & a.Term & '%'
GROUP BY a.Term
) AS subq
ON TableA.Term = subq.Term;
Edit: I used ALike and standard ANSI wild card character. That allows the query to run correctly regardless of whether it is run from SQL-89 or SQL-92 mode. If you prefer the * wild card, use this version of the WHERE clause:
WHERE b.Term Like '*' & a.Term & '*'
Note that will only do the matching correctly when run from SQL-89 mode.
On these lines?
SELECT ta.ID, tb.Term, ta.Term, tb.Count
FROM ta, tb
WHERE ta.Term Like "*" & tb.term & "*";
ID tb.Term ta.Term Count
2 hat hat 2
3 hat the hat 2
3 the hat the hat 4
4 mat mat 6
5 mat matter 6
5 matter matter 8
I typically build an expression using iif:
TestFlag:iif([TableA]![Term] = [TableB]![Term],"Same","Different")

Merge like dates in MS Access

I am a pilot who flies multiple legs in a day. The software I use to log flights spits out a csv file and lists every leg separately. I import the csv file into table 1 in ms access. I would like to merge all flights from the same day into one record on a new table. My problem is combining the route and adding the time.
Table 1
Date Plane From To Time
2009-10-13 111WS CHO LGA 120
2009-10-13 111WS LGA ITH 100
2009-10-13 111WS ITH LGA 90
2009-10-13 111WS LGA BOS 110
Table 2
Date Plane Route Time
2009-10-13 111WS CHO-LGA-ITH-LGA-BOS 420
I would like to use VBA code to do this, but I haven't done any programming in 12 years and unfortunately don't have the time to relearn. I don't think the code has to be too elaborate, it seems pretty straightforward. I just don't know how to do it. I hope someone can help me out. Thanks in advance.
Note:
I am using MS Access 97 (hope that's not a problem)/
The date field is a string, not a date/
The time is in minutes, and can stay that way/
There normally will not be more than 80 records in table 1/
There can be anywhere from one to eight flights in one day/
Create a Totals query, bring in your table, and include the Date and Time as columns. The Date Column should be set to Group By in the Total Row, and the Time should be set to Sum. You will also need another column to get the final entry in the route, so put the To column in the grid also, and set the Totals row for that column to Last.
To get the remainder of the route, you will need to use a combining function like this one:
Return a concatenated list of sub-record values
http://www.mvps.org/access/modules/mdl0004.htm
This will combine the FROM column into a single value, which you can include as another column in the output. Set the Total row for this column to Expression.
To get the complete route, combine the concatenated FROM columm with the LAST TO column.
Note that you don't need to build the entire query at once. Build each of the three pieces (total time, concatenated route, ending destination) individually (in its own query), and make sure each piece works individually, before combining them into a single query.
Add module
Public Function ConcatField(FieldName As String, TableName As String, Where As String, Optional Delimeter = "-", Optional OrderBy = "") As String
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset("SELECT " & FieldName & " FROM " & TableName & " WHERE " & Where & IIf(OrderBy > "", " ORDER BY " & OrderBy, ""))
ConcatField = DLookup("From", "RTE", Where)
While Not rs.EOF
ConcatField = ConcatField + IIf(ConcatField = "", "", Delimeter) + rs.Fields(0)
rs.MoveNext
Wend
rs.Close
Set rs = Nothing
End Function
and run query
Worked on mine
SELECT rte.Date, rte.Plane, ConcatField("to","rte","Date='" & [Date] & "' AND Plane='" & [Plane] & "'") AS Expr1, Sum(rte.Time) AS SumOfTime
FROM rte
GROUP BY rte.Date, rte.Plane, ConcatField("to","rte","Date='" & [Date] & "' AND Plane='" & [Plane] & "'");
enter code here
Unlike ACE (Access 2007), the Jet 3.51 engine (Access97) doesn't have multivalue types. SQL the language (including the Access Database Engine's own proprietary SQL) does not have a 'Concatenate' function because it would be a violation of first normal form (1NF) which requires scalar types. So this isn't something for a SQL query. Sounds to me more like a candidate for a report.
Speaking of 1NF, considering it is possible to fly to the same destination twice in one day, your table lacks a relational key. It sounds like you need to replace you single 'date' column that is typed as 'text' with a pair of DATETIME values representing a period, with the required 'sequenced primary key' e.g. a CHECK constraint to prevent overlapping periods. Temporal databases are definitely non-trivial!
Thanks for all your responses. I used "THEn's" answer, but I had to change a few things (hope that's not a problem). I only needed the flights grouped by date, so I took out the grouping by plane, and just logged the first plane on the first leg of that day. Also I just found out that my software exports the csv file in reverse order, so I changed the module a little to account for this. So this is what the imported data looks like (I start and end in CHO):
Date Plane From To Time
2009-10-14 111WS LGA CHO 120
2009-10-14 111WS BOS LGA 110
2009-10-13 111WS LGA BOS 110
2009-10-13 111WS ITH LGA 90
2009-10-13 111WS LGA ITH 100
2009-10-13 111WS CHO LGA 120
This is the Module:
Public Function ConcatField(FieldName As String, TableName As String, Where As String, Optional Delimeter = "-") As String
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset("SELECT " & FieldName & " FROM " & TableName & " WHERE " & Where)
rs.MoveLast
While Not rs.BOF
ConcatField = ConcatField + IIf(ConcatField = "", "", Delimeter) + rs.Fields(0)
rs.MovePrevious
Wend
ConcatField = ConcatField + "-" + DLookup("To", "rte", Where)
rs.Close
Set rs = Nothing
End Function
This is the query:
SELECT rte.Date, First(rte,plane), ConcatField("From","rte","Date='" & [Date] & "'") AS Expr1, Sum(rte.time) AS [Total Time]
FROM rte
GROUP BY rte.Date;
This causes a problem because I'm using a field called "From" in the openrecordset line, I tried renaming the field to something else and it worked perfectly. However I was hoping to keep the field names the way they are. It worked when I was using the field name "To" in the openrecordset line, but then I was running into a problem with the data being in reverse order. So I was looking for any suggestions, but I would like to keep the field names the same, and I would like to keep the table in reverse order if possible. Thanks again guys.