compare all fields in access using vba/sql - sql

I know I can compare values between two tables, but I have not needed to do it for more than 2 or 3 fields up to this point so comparing them each individually hasnt been an issue, I used code such as this:
DoCmd.RunSQL "INSERT INTO Issues
SELECT Eligibility.[Member Id]
, Eligibility.[Sex Code]
, Eligibility.State
FROM Eligibility LEFT JOIN Ref
ON Eligibility.[Sex Code] = Ref.[Sex Code]
WHERE (((Ref.[Sex Code]) Is Null));"
now however, i need to compare about 140 different fields. is there a better way to do this than writting 140 sql statements and running them all one by one?
i want it to find where fields dont contain the same info and then pull the entire row from both tables,or at the very least the value in the 5th column, member id, and then i can run another query to pull the entire row off of that value if need be (so i can look at both at the same time) and paste them into another table and highlight the cells where the mismatches occur.
both tables are in the same database
both tables have the same structure, but the second table might not have all of the values from the first, so i need to find a way to have it match the rows based on the member ID before it starts comparing the rows.

You can compare using DAO pretty easily. Using the .Fields() argument on a recordset you get all the different fields in the actual recordset.
This lets you do something like:
Sub exampleSQLComparison()
Dim rs1 As DAO.Recordset
Dim rs2 As DAO.Recordset
'.... set those recordsets via SQL statements
For Each f In rs1.Fields
If rs1.Fields(f) <> rs2.Fields(f) Then
Debug.Print "Mismatch found for " + f
End If
Next f
End Sub
If your queries are similar and the only thing you are changing is a single field (for example an ID) you should be able to modify the logic accordingly.

Related

How to process a table serially for count

I need to process through a list of technical skills one by one and get a count of the number of developers we have in 3 locations who have that skill. For example, car type = "Java". How many persons have this skill listed in their resume.
I have 2 tables:
Skills: contains a single column listing skills - "Java" for example
Resources: contains 4 columns, Resource-ID, Name, Location, and a long text field called "Resume" containing text of their resume.
If this were a single skill I would process the SQL something like below (SQL syntax not exact)
SELECT count FROM [Resources] WHERE ([Resources].[Resume] Like "SKILL-ID*");
I want to process the Skills table serially printing the "Skill" and the count in each location.
Help appreciated
I've only used Access as a DB for single record retrieval, never using table values as input to loop through a process. I suspect that this is a simple execution in MS Access.
Ok, so we have to process that table.
I would simple "send out" a row with Location and skill for each match. We could write some "messy" code to then group by, but that is what SQL is for!!!
We could quite easy keep/have/use/enjoy the results in code, but its better to send the results out to a working table. Then we can use what sql does best - group and count that data.
So, then, the code could be this:
Sub CountSkills()
' empty out our working report table
CurrentDb.Execute "DELETE * FROM ReportResult"
Dim rstSkills As DAO.Recordset
Dim rstResources As DAO.Recordset
Dim rstReportResult As DAO.Recordset
Dim strSQL As String
Set rstSkills = CurrentDb.OpenRecordset("Skills")
strSQL = "SELECT Location, Resume FROM Resources " & _
"ORDER BY Location"
Set rstResources = CurrentDb.OpenRecordset(strSQL)
Set rstReportResult = CurrentDb.OpenRecordset("ReportResult")
Do While rstResources.EOF = False
' now for each resource row, process skill set
rstSkills.MoveFirst
Do While rstSkills.EOF = False
If InStr(rstResources!Resume, rstSkills!Skill) > 0 Then
rstReportResult.AddNew
rstReportResult!Location = rstResources!Location
rstReportResult!Skill = rstSkills!Skill
rstReportResult.Update
End If
rstSkills.MoveNext
Loop
rstResources.MoveNext
Loop
End Sub
Now, the above will wind up with a table looking like this:
So, now we can query (and count) against above data.
So, this query would do the trick:
SELECT Location, Skill, Count(1) AS SkillCount
FROM ReportResult
GROUP BY Location, Skill
And now we get this:
And you can flip the above query to group by skil, then location if you wish.
so, at the most simple level?
We write out ONE row + location for every match, and then use SQL on that to group by and count.
We COULD write code to actually count up by location, but that VBA code would as noted be a bit messy, and just spitting out rows of location and skill means we can then group by skill count, skill location count, or location, skill counts just by using "simple" sql against that list of location and skill record list.
So, now use the report wizard on that query above, and we get something like this:
Of course it is simple to change around the above report, but you get the idea for such a simple task as you noted.
Summarizing count of developers by skill and location can be accomplished with SQL. It requires a dataset of all possible skill/location pairs. Consider this simple example:
Resources
ID
Name
Location
Resume
1
a
x
Java,Excel
2
b
x
Excel
3
c
y
Excel
4
d
z
VBA,Java
SELECT Skills.SkillName, Resources.Location,
Sum(Abs([Resume] Like '*' & [SkillName] & "*")) AS DevCt
FROM Skills, Resources
GROUP BY Skills.SkillName, Resources.Location;
SkillName
Location
DevCt
Excel
x
2
Excel
y
1
Excel
z
0
Java
x
1
Java
y
0
Java
z
1
VBA
x
0
VBA
y
0
VBA
z
1
This approach utilizes a Cartesian product of Skills and Resources tables to generate the data pairs. This type of query can perform slowly with large dataset. If it is too slow, saving the pairs to a table could improve performance. Otherwise, a VBA solution will be only recourse and would likely involve looping recordset object.
Regardless of approach, be aware results will be skewed if Resume has content like "excellence". Bad data output is pitfall of poor database design.
Solution:
Resource and Skill table were added into MS_Access
Step 1: Create a query that executes the below SQL to get a counts (here named "Step1Query"):
SELECT Skills.SkillName, Resources.Location,
Sum(Abs([Resume] Like '*' & [SkillName] & "*")) AS DevCt
FROM Skills, Resources
GROUP BY Skills.SkillName, Resources.Location;
Step 2: Create a second query that uses the Step 1 query as input. (you can do this via the wizard):
TRANSFORM Sum(Step1Query.DevCt) AS SumOfDevCt
SELECT Skills.SkillName, Resources.Location,
Sum(Abs([Resume] Like '*' & [SkillName] & "*")) AS DevCt
FROM Skills, Resources
GROUP BY Skills.SkillName, Resources.Location
PIVOT Step1Query_qry.[Location];
Result lists out a matrix form. Thanks all for your help.

Why the Recordset from VBA just return one record?

I have 3 tables, "persons", "per_resi" and "residence"
This three tables form a many to many relation.
Table "person" fields: id, name etc....
Table "residence" fields: id, Street etc.....
Table "per_resi" fields: person_id and residence_id (together principal index)
Well, the problem is when I design a query in the graphic Access tool it Works as it should be.
But if I do in VBA it only return 1 record.
Dim svivienda As String
Dim rvivienda As Recordset
svivienda = "SELECT tbl_persona.Id, tbl_vivienda.Calle, tbl_vivienda.Numero " _
& "FROM tbl_vivienda INNER JOIN (tbl_persona INNER JOIN tbl_perso_viv ON tbl_persona.Id = tbl_perso_viv.Id_persona) " _
& "ON tbl_vivienda.Id = tbl_perso_viv.Id_vivienda WHERE tbl_persona.Id = " & 168 & ";"
Set rvivienda = CurrentDb.OpenRecordset(svivienda, dbOpenDynaset)
I have tried LEFT JOIN and RIGHT JOIN but always the same just one record on the recordset.
Any ideas?
MS access 2013
Thanks in advance.
Thank guys,
This was a very novel question.
Here is the answer.
The RecordCount property does not report the amount of records you have.
The value of the RecordCount property equals the number of records
that have actually been accessed. For example, when you first create a
dynaset or snapshot, you have accessed (or visited) only one record.
If you check the RecordCount property immediately after creating the
dynaset or snapshot (assuming it has at least one record), the value
is 1. To visit all the records, use the MoveLast method immediately
after opening the Recordset, and then use MoveFirst to return to the
first record. This is not done automatically because it may be slow,
especially for large result sets.
Count the number of records in a DAO Recordset
Thanks!!!
Add following statement :
rvivienda.MoveNext
will return the next record of the recordset
or :
rvivienda.MoveLast
will return the last record of the recordset
You will see the result.
The goosie2018's answer is CORRECT. I just show you the simple way to understand.
SUMMARY
So, I think the recordset you get from the database will not show the result look like an Array or a List, but a cursor. And the default cursor points to the first row, so if you use :
rvivienda.RecordCount
you should receive the number of records you actually got.
Sorry for my English ! And thanks for reading.

Access Check If DataSet contains string via vba Macro

Hello Stackoverflow community!
I am currently confronted with writing somthing, that automatically cleans up a database after certain requirements. Here is what I have to do:
If a cell in one table contains a certain substr (lets say "Mrs."), in another table a Togglebox is supposed to be checked (Yes if "Mrs." is contained and No if not)
I already worked out something that sets The Toggleboxes to -1 in that column, but I have to include a check, if it even has to be changed and in which row. For that I wanted to bring up an If-construction and a variable that would contain the ID of the first table's row to determine, which of the Rows in the second table have to be changed.
So the thought is like this:
If 1st_Table contains "Mrs." Then
CurrentDb.Execute "UPDATE 2nd_Table SET gender = -1 WHERE foo = 1st_Table.ID_Var"
1st_Table.ID_Var would contain the ID of the row of the first table
Now the more or less obvious question: How do I accomplish that?
This is bascally the first time working with VBA/Access AND SQL so I have no closer thoughts on how to do that.
Thanks in advance
Greetings
geisterfurz007
P.S. The variable names will be changed in the final version; they are just to visualize. Just saw that they were colored by SO.
Try this (SQL update query):
UPDATE 2nd_Table INNER JOIN 1st_Table ON 2nd_Table.ID = 1st_Table.ID
SET 2nd_Table.gender = -1
WHERE 1st_Table.PersonName LIKE '*Mrs.*'
ID -> reference between both tables
PersonName -> column in 1st table containing Mrs.

Calculation based on values in 2 different rows

I have a table in MS Access which has stock prices arranged like
Ticker1, 9:30:00, $49.01
Ticker1, 9:30:01, $49.08
Ticker2, 9:30:00, $102.02
Ticker2, 9:30:01, $102.15
and so on.
I need to do some calculation where I need to compare prices in 1 row, with the immediately previous price (and if the price movement is greater than X% in 1 second, I need to report the instance separately).
If I were doing this in Excel, it's a fairly simple formula. I have a few million rows of data, so that's not an option.
Any suggestions on how I could do it in MS Access?
I am open to any kind of solutions (with or without SQL or VBA).
Update:
I ended up trying to traverse my records by using ADODB.Recordset in nested loops. Code below. I though it was a good idea, and the logic worked for a small table (20k rows). But when I ran it on a larger table (3m rows), Access ballooned to 2GB limit without finishing the task (because of temporary tables, the size of the original table was more like ~300MB). Posting it here in case it helps someone with smaller data sets.
Do While Not rstTickers.EOF
myTicker = rstTickers!ticker
rstDates.MoveFirst
Do While Not rstDates.EOF
myDate = rstDates!Date_Only
strSql = "select * from Prices where ticker = """ & myTicker & """ and Date_Only = #" & myDate & "#" 'get all prices for a given ticker for a given date
rst.Open strSql, cn, adOpenKeyset, adLockOptimistic 'I needed to do this to open in editable mode
rst.MoveFirst
sPrice1 = rst!Open_Price
rst!Row_Num = i
rst.MoveNext
Do While Not rst.EOF
i = i + 1
rst!Row_Num = i
rst!Previous_Price = sPrice1
sPrice2 = rst!Open_Price
rst!Price_Move = Round(Abs((sPrice2 / sPrice1) - 1), 6)
sPrice1 = sPrice2
rst.MoveNext
Loop
i = i + 1
rst.Close
rstDates.MoveNext
Loop
rstTickers.MoveNext
Loop
If the data is always one second apart without any milliseconds, then you can join the table to itself on the Ticker ID and the time offsetting by one second.
Otherwise, if there is no sequence counter of some sort to join on, then you will need to create one. You can do this by doing a "ranking" query. There are multiple approaches to this. You can try each and see which one works the fastest in your situation.
One approach is to use a subquery that returns the number of rows are before the current row. Another approach is to join the table to itself on all the rows before it and do a group by and count. Both approaches produce the same results but depending on the nature of your data and how it's structured and what indexes you have, one approach will be faster than the other.
Once you have a "rank column", you do the procedure described in the first paragraph, but instead of joining on an offset of time, you join on an offset of rank.
I ended up moving my data to a SQL server (which had its own issues). I added a row number variable (row_num) like this
ALTER TABLE Prices ADD Row_Num INT NOT NULL IDENTITY (1,1)
It worked for me (I think) because my underlying data was in the order that I needed for it to be in. I've read enough comments that you shouldn't do it, because you don't know what order is the server storing the data in.
Anyway, after that it was a join on itself. Took me a while to figure out the syntax (I am new to SQL). Adding SQL here for reference (works on SQL server but not Access).
Update A Set Previous_Price = B.Open_Price
FROM Prices A INNER JOIN Prices B
ON A.Date_Only = B.Date_Only
WHERE ((A.Ticker=B.Ticker) AND (A.Row_Num=B.Row_Num+1));
BTW, I had to first add the column Date_Only like this (works on Access but not SQL server)
UPDATE Prices SET Prices.Date_Only = Format([Time_Date],"mm/dd/yyyy");
I think the solution for row numbers described by #Rabbit should work better (broadly speaking). I just haven't had the time to try it out. It took me a whole day to get this far.

Insert textbox value into Access SQL query

Being an amateur in Access and VBA, I've hit a wall with the following...
In MS Access 2007, I have the following query:
SELECT .... WHERE format(NZ(l_f_date),'yyyy-mm-dd')<=**'2012-04-03'**);
I have shortened it a bit of course.
The database has approx 20 queries that need to be run on a daily basis. I have created 4 macros to run groups of queries in the manner that I need them to be run. The problem is that in every one of those queries I first have to change the date (like in the upper query). I am looking for the way to automate it a bit more.
I have an idea to create a Form, place a button for every macro on it and 2 textbox-es to enter the 2 dates I need. Now, I need those dates to appear in the bolded part. As I think about it, I have 2 options:
Create a temporary table in the database to store those two dates and pull those 2 fields in my queries.
Insert the value of the textbox somehow directly into the bolded part of the query.
I think I can manage the first solution, but the second one is making my head hurt.
Can you help?
SQL from comment
select proc_desc,count(s) as broj
into upit3
from ( select distinct a.case_id as s,
a.org_case_id,a.act_date as day,d.prod_id,d.prod_desc,
c.fname,c.dpd,c.due_amount,proc_id,proc_desc
from actions_b as a, cases_old_b as c,processes_b as p,
product_dict_b as d
where a.org_case_id=c.[org_ case_id]
and a.act_date=Forms!Form!Text10 and d.prod_id=c.product
and p.proc_id=c.process and not_lead=1 )
group by proc_desc order by proc_desc;
OK, sample data....
In x.dates, value is exactly like this: 03.04.2012
In a.act_date value is like this: 01.07.2011 13:53:56
so if its not possible with this values as they are, is it possible to use a LIKE statement in the query? Pseudo: WHERE a.act_date LIKE x.date%
If its possible, how to use it? i am a novice in sql and access queries... I google but sometimes, like this time, i get stuck.
Thanks
This structure:
SELECT .... WHERE format(NZ(l_f_date),'yyyy-mm-dd')<='2012-04-03');
Is not a good idea. The general rule is to try and stick to field (column) names on the left side of the equals. So
SELECT .... WHERE l_f_date <=#2012-04-03#
Nulls will not be included and I hope your dates are dates and not strings.
Next add the form:
SELECT .... WHERE l_f_date <= Forms!TheNameOfTheForm!TheNameOfTheTextbox
EDIT re comments
You are using the query design window, yes? Please try this test query:
SELECT a.case_id, a.act_date
FROM actions_b AS a
WHERE a.act_date=Forms!Form!Text10
OK, so i decided to create a simple 2-column table in my database that will be used just for storing yesterdays and todays date... i added 2 text-boxes to a form, 1 button and added the next procedure to the button:
Private Sub Command25_Click()
CurrentDb.Execute "DELETE * FROM Datumi"
Dim tbl As Recordset
Set tbl = CurrentDb.OpenRecordset("Datumi")
tbl.AddNew
tbl!brojka = "1"
tbl!datum = Text8.Value
tbl.Update
tbl.AddNew
tbl!brojka = "2"
tbl!datum = Text10.Value
tbl.Update
End Sub
As you can see, the click on the button will clear the table and store new values into 2 rows... row 1 is yesterday, row 2 is today...
And after that im pulling the values from that table like this:
... where x.brojka=1 and format(a.act_date,'yyyy-mm-dd')=format(x.datum,'yyyy-mm-dd') ...
I'm sure it can be done better but this one works for me...
I Solved this problem with :
[Software: MS Access 2013]
CurrentDb.Execute "INSERT INTO [Inventory Transactions] ([Product ID], [Order ID])" _
& "VALUES ( " & Me.Product_ID & ", " & Me.Order_ID & ")"