Check if data already exist in Access table - vba

I'm trying to check if a date type exists in a table. The code I've had is this:
If CurrentDb.OpenRecordset("SELECT tbl_Fechas_Proceso_Contable.Fecha FROM tbl_Fechas_Proceso_Contable WHERE tbl_Fechas_Proceso_Contable.Fecha= " & Forms!frm_Proceso_Contable!txt_Fecha_Creacion & ";").Fields(1) > 0 Then
MsgBox "La fecha ingresada ya ha sido consultada"
End If
I've seen a similar question, but with Integer, not sure how to proceed with date type, also I don't understand why the expression > 0. I executed like this and Access says Item not in this collection Error 3265.
My table tbl_Fechas_Proceso_Contable is like this:
|---------------------|
| ID | Fecha |
|---------------------|
| 1 | 16/10/2018 |
|---------------------|
| 2 | 17/10/2018 |
|---------------------|

Date is a reserved word in Access. Try it with brackets, and your date value in the query has to be enclosed in #'s. Also, indexes often begin with zero, so fields(0) there.
If CurrentDb.OpenRecordset("SELECT tbl_Fechas_Proceso_Contable.[Fecha] FROM tbl_Fechas_Proceso_Contable WHERE tbl_Fechas_Proceso_Contable.[Fecha]=#" & Forms!frm_Proceso_Contable!txt_Fecha_Creacion & "#;").Fields(0) > 0 Then
MsgBox "La fecha ingresada ya ha sido consultada"
End If

Related

How to generate a time phased table in Access

I have an Access query that contains order quantities and reorder frequencies, e.g:
+-------+---------------------------+---------------------------+
|Product| Order Qty (pallets) | Order Interval (wks) |
+-------+---------------------------+---------------------------+
| 1234 | 2.5 | 7 |
+-------+---------------------------+---------------------------+
| 1235 | 3.4 | 10 |
+-------+---------------------------+---------------------------+
I want to generate a time phased table of orders, like this:
+-------+--------+--------+--------+--------+--------+--------+--------+
|Product| Wk1 | Wk2 | Wk3 | Wk4 | Wk5 | Wk6 | Wk7 |
+-------+--------+--------+--------+--------+--------+--------+--------+
| 1234 | 2.5 | | | | | | 2.5 |
+-------+--------+--------+--------+--------+--------+--------+--------+
I'm familiar with MySQL but it seems that I will need to create a VBA subroutine to do this in Access. I'd very much appreciate if someone could point me in the right direction.
You are already in the 'right' direction - need VBA and a 'temp' table. Build temp table with enough WkX fields to accommodate the highest possible interval (max 254). Most likely code will involve opening recordset object, looping records to read values and save records with appropriate data. Maybe this will get you started:
Dim db As DAO.Database, rs As DAO.Recordset
Set db = CurrentDb
Set rs = CurrentDb.OpenRecordset("SELECT * FROM table")
CurrentDb.Execute "DELETE FROM PhaseTable"
Do While Not rs.EOF
db.Execute "INSERT INTO PhaseTable(Product, Wk1, Wk" & rs!Interval) " & _
"VALUES('" & rs!Product & "," & rs!Qty & "," & rs!Qty & ")"
rs.MoveNext
Loop

In VBA/ADO creating a rs field based on concatenating field values from many records

I have an MDB table with 2 fields:
+-------------+------------------+
| salesperson | Transaction_Date |
+-------------+------------------+
| John | 12/11/2018 |
| John | 13/11/2018 |
| John | 18/11/2018 |
| Steeve | 23/12/2018 |
| Steeve | 29/12/2018 |
+-------------+------------------+
In VBA (in Excel) I want through ADO to create the following format:
+-------------+----------------------------------+
| salesperson | Transaction_Date_Concatenated |
+-------------+----------------------------------+
| John | 12/11/2018-13/11/2018-18/11/2018 |
| Steeve | 23/12/2018-29/12/2018 |
+-------------+----------------------------------+
The number of Transaction_Dates for each salesperson may vary from 1 to 30.
The following SQL creates a record for each salesperson but it doesn't, of course, produces the concatenated field although it looks as in the direction of what I want
SQL = " SELECT SalesPerson, max(Date) as value1 FROM 0TargetTemplate GROUP BY SalesPerson"
To my knowledge, this cannot be achieved using SQL alone. You'll need to use a VBA function to iterate over the records and construct the delimited string.
Allen Browne has created an existing example which may be found here.
For your particular task, you might call the function in the following manner:
select
[0TargetTemplate].SalesPerson,
ConcatRelated
(
"[0TargetTemplate].Transaction_Date",
"[0TargetTemplate]",
"[0TargetTemplate].SalesPerson = '" & [0TargetTemplate].SalesPerson & "'",
"[0TargetTemplate].Transaction_Date",
"-"
) as Transaction_Date_Concatenated
from
[0TargetTemplate];
Using the suggested code and function from the link I wrote a procedure in VBA Access but get an error "Too few parameters" in the Set rs= statement. I've tried in VBA EXCEL with proper connections but get an error about "ConcatRelated" function not recognized, although properly referenced in a VBA module
Sub test()
SQL = "SELECT [tblOrders].CompanyName, ConcatRelated ('[tblOrders].Transaction_Date',[tblOrders], [tblOrders].SalesPerson =' [tblOrders].SalesPerson', [tblOrders].Transaction_Date) AS Transaction_Date_Concatenated FROM tblOrders;"
Set rs = CurrentDb.OpenRecordset(SQL)
On Error GoTo resultsetError
dbValue = rs!Variable
MsgBox dbValue, vbOKOnly, "RS VALUE"
resultsetError:
MsgBox "Error Retrieving value from database", vbOKOnly, "Database Error"
End Sub

Count number of table columns that are not null - MS Access SQL

I have a table which I have shown a simplified example of below:
ID | Item1 | Item2 | Item3 | Item4 | Item5
------------------------------------------
A | NULL | NULL | YES | YES | NULL
B | NULL | NULL | NULL | YES | NULL
C | NULL | NULL | NULL | NULL | NULL
I want to return the following data set:
ID | Count
------------
A | 2
B | 1
C | 0
I.e. I want a count of how many of the columns are NOT NULL for that ID
One potential solution would be
SELECT
ID,
SUM(
IIf(Item1 is NULL,0,1)
+
IIf(Item2 is NULL,0,1)
+
IIf(Item3 is NULL,0,1)
+
IIf(Item4 is NULL,0,1)
+
IIf(Item5 is NULL,0,1)
) 'Count'
FROM
tableName
GROUP BY
ID
However in practice the real table I am using has over a hundred columns and I would prefer to avoid having to write out the names of each column. Is there a simpler way to do this?
You can use VBA to loop through every record and field:
Function CountFields()
Set db = CurrentDb()
db.Execute ("delete * from ItemCounts")
Set RS = db.OpenRecordset("select * from [DataTable]")
RS.MoveFirst
Do While Not RS.EOF
Id = RS.Fields("ID").Value
Count = 0
For Each Item In RS.Fields
If (Item.Name <> "ID" And RS.Fields(Item.Name).Value <> "") Then Count = Count + 1
Next Item
db.Execute ("insert into ItemCounts (ID,[count]) select " & Id & "," & Count)
RS.MoveNext
Loop
MsgBox ("done")
End Function
This puts the counts in a table called ItemCounts, which needs to be set up before the VBA is executed. The fields in that table are ID and Count.
And, if you can reformat the source data, I agree with Minty - but I know that's not always feasible.
Your data is not normalised and therefore you are having to perform gymnastics in your code to work around the problem.
Your data should be stored vertically not horizontally;
ID | ItemNo | Value
---------------------
A | 2 | 1
A | 3 | 1
B | 4 | 1
This would make your query a simple total query, and allow for any number of items. You are also only storing data when you have some not for every case.
Edit: This will loop through the fields
Dim Rst As Recordset
Dim f As Field
Set Rst = CurrentDb.OpenRecordset(TableName)
For Each f In Rst.Fields
Debug.Print (f.name)
Next
Rst.Close
You can reduce it a little:
SELECT
ID,
ABS(SUM((Item1 is Not NULL)+(Item2 is Not NULL)+(Item3 is Not NULL)+(Item4 is Not NULL)+(Item5 is Not NULL))) As [Count]
FROM
tableName
GROUP BY
ID

I want to transpose rows into columns in ms-access

I need to transpose rows into columns in MS Access database, VBA, SQL both the codes are welcome.
Table
| Name | Ticker | ID | Innovation | Quality | Year |
| XYZ | PQR | 11 | 1 | 1 | 2009 |
| XYZ | PQR | 11 | 0 | 1 | 2010 |
| XYZ | PQR | 11 | 1 | 0 | 2011 |
| XYZ | PQR | 11 | 1 | 1 | 2012 |
Desired Table
| Year | 2009 | 2010 | 2011 | 2012 |
| Name | XYZ | XYZ | XYZ | XYZ |
| Ticker | PQR | PQR | PQR | PQR |
| ID | 11 | 11 | 11 | 11 |
| Innovation | 1 | 0 | 1 | 1 |
| Quality | 1 | 1 | 0 | 1 |
As you can see from the desired table, I am trying to have the Year row as Column and list all the columns apart from Year as my rows.
I have tried using Tranform and Pivot function in MS Access but it only Pivots one variable. Let me know your thoughts on it.
The below code failed in transposing all the variables.
TRANSFORM Max([Quality])
SELECT Ticker
FROM Table
Where Ticker = "XYZ"
GROUP BY Ticker
PIVOT Year;
Also, if possible I want to publish it as PDF document.
Thanks in advance,
RVG
Access TRANSFORM is not really intuitive and easy to use and I do not think you can use it that way. Each result row is supposed to be an aggregate of your table. I know of no way to get the previous field names into a new column.
See a working example:
TRANSFORM and PIVOT in Access 2013 SQL
What you really seem to want is just a new presentation to the existing data.
Excel might help.
I have never exported pdf from Access but from Excel is easy. Here is an example:
Sub ExportPdf(path As String, Optional openAfter As Boolean)
''calculate best range for print area
Dim lastCol As Integer
Dim firstRow As Integer
Dim lastRow As Integer
lastCol = pt.TableRange2.Columns(pt.TableRange2.Columns.Count).Column
firstRow = pt.TableRange2.Rows(1).Row
lastRow = ms.Cells(pt.TableRange2.Rows.Count * 3, 1).End(xlUp).Row
Worksheets(ContextSheet).PageSetup.PrintArea = Range(Cells(firstRow, 1), Cells(lastRow, lastCol)).Address
Worksheets(ContextSheet).ExportAsFixedFormat Type:=xlTypePDF, Filename:= _
path & "\Area " & getPivotTablePageFilters(getPivotTable()) & ".pdf", Quality:= _
xlQualityStandard, IncludeDocProperties:=True, IgnorePrintAreas:=False, _
OpenAfterPublish:=openAfter
End Sub
This is a vb script that takes the data from TableSource and transposes it into TableTranspose. The 2nd table has to be set up with a column named FName to take the field names, and columns for each year in the source table.
Function Transpose()
Set db = CurrentDb()
db.Execute ("delete * from TableTranspose")
Set RS = db.OpenRecordset("TableSource")
Set YrList = db.OpenRecordset("select distinct [Yr] from [TableSource] Group by [Yr]")
For Each F In RS.Fields
FN = F.Name
INS = "Insert Into TableTranspose (FName"
SQL = "Select '" & FN & "'"
YrList.MoveFirst
Do While Not YrList.EOF
YR = YrList.Fields("YR").Value
INS = INS & ",[" & YR & "]"
SQL = SQL & ",max(iif(YR=" & YR & ",[" & FN & "])) AS [" & YR & "]"
YrList.MoveNext
Loop
SQL = SQL & " From TableSource"
db.Execute (INS & ") " & SQL)
Next F
MsgBox ("Done")
End Function
This works by processing one field at a time to match the layout of the desired output, and looping through each year of TableSource to find the data to make up the row in TableTranspose. It shouldn't matter how many fields there are or what they are named.
It will create a row in the output for the Year, which will be redundant - you can delete it, or add logic to skip that field if necessary.
This seems to work fine with the 4 years of data in your sample, and should extend OK to more years. It's possible that you will hit a limit on SQL command length if there are too many years in the data, but I think not.
If you are filtering the records from TableSource, you can add the WHERE clause on the line just from the db.execute near the bottom.
Is the data always the same set of field names and years? If so, you might be able to use a UNION query, something like:
Select "Name" as [FName], max(iif(year="2009",name))as [2009], max(iif(year="2010"
,name)) as [2010], max(iif(year="2011",name)) as [2011], max(iif(year="2012", name)) as [2012] from Table group by FName
Union all Select "Ticker", max(iif(year="2009",ticker)), max(iif(year="2010"
,ticker)), max(iif(year="2011",ticker)), max(iif(year=-"2012",ticker)) from Table group by FName
Union all Select "ID", max(iif(year="2009",id)), max(iif(year="2010"
,id)), max(iif(year="2011",is)), max(iif(year="2012",id)) from Table group by FName
Union all Select "Innovation", max(iif(year="2009",innovation)), max(iif(year="2010"
,innovation)), max(iif(year="2011",innovation)), max(iif(year=-"2012",innovation)) from Table group by FName
Union all Select "Quality", max(iif(year="2009",quality)), max(iif(year="2010"
,quality)), max(iif(year="2011",quality)), max(iif(year=-"2012",quality)) from Table group by FName

Date continuity in a database (find missing date gaps)

I have a database of users who pay monthly payment. I need to check if there is continuity in these payments.
For example in the table below:
+---------+------------+
| user_id | date |
+---------+------------+
| 1 | 2015-02-01 |
| 2 | 2015-02-01 |
| 3 | 2015-02-01 |
| 1 | 2015-03-01 |
| 2 | 2015-03-01 |
| 3 | 2015-03-01 |
| 4 | 2015-03-01 |
| 1 | 2015-04-01 |
| 2 | 2015-04-01 |
| 3 | 2015-04-01 |
| 4 | 2015-04-01 |
| 5 | 2015-04-01 |
| 1 | 2015-05-01 |
| 2 | 2015-05-01 |
| 3 | 2015-05-01 |
| 4 | 2015-05-01 |
| 5 | 2015-05-01 |
| 1 | 2015-06-01 |
| 2 | 2015-06-01 |
| 3 | 2015-06-01 |
| 5 | 2015-06-01 |
| 3 | 2015-07-01 |
| 4 | 2015-07-01 |
| 5 | 2015-07-01 |
+---------+------------+
Until May everything was ok, but in June user 4 didn't pay although he paid in the next month (July).
In July users 1 and 2 didn't pay, but this is ok, because they could resign from the service.
So in this case I need to have information "User 4 didn't pay in June".
Is it possible to do that using SQL?
I use MS Access if it's necessary information.
From my experience, you cannot just work with paid in table to fill the gaps. If in case all of your user does not pay a specific month, it is possible that your query leaves that entire month out of equation.
This means you need to list all dates from Jan to Dec and check against each user if they have paid or not. Which again requires a table with your requested date to compare.
Dedicated RDBMS provide temporary tables, SP, Functions which allows you to create higher level/complex queries. on the other hand ACE/JET engine provides less possibilities but there is a way around to get this done. (VBA)
In any case, you need to give the database specific date period in which you are looking for gaps. Either you can say current year or between yearX and yearY.
here how it could work:
create a temporary table called tbl_date
create a vba function to generate your requested date range
create a query (all_dates_all_users) where you select the requested dates & user id's (without a join) this will give you all dates x all users combination
create another query where you left join all_dates_all_users query with your user_payments query. (This will produce all dates with all users and join to your user_payments table)
perform your check whether user_payments is null. (if its null user x hasn't paid for that month)
Here is an example:
[Tables]
tbl_date : id primary (auto number), date_field (date/Time)
tbl_user_payments: pay_id (auto number, primary), user_id (number), pay_Date (Date/Time) this is your table modify it as per your requirements. I'm not sure if you have a dedicated user table so i use this payments table to get the user_id too.
[Queries]
qry_user_payments_all_month_all_user:
SELECT Year([date_field]) AS mYear, Month([date_field]) AS mMonth, qry_user_payments_user_group.user_id
FROM qry_user_payments_user_group, tbl_date
ORDER BY Year([date_field]), Month([date_field]), qry_user_payments_user_group.user_id;
qry_user_payments_paid_or_not_paid
SELECT qry_user_payments_all_month_all_user.mYear,
qry_user_payments_all_month_all_user.mMonth,
qry_user_payments_all_month_all_user.user_id,
IIf(IsNull([tbl_user_payments].[user_id]),"Not paid","Paid") AS [Paid?]
FROM qry_user_payments_all_month_all_user
LEFT JOIN tbl_user_payments ON (qry_user_payments_all_month_all_user.user_id = tbl_user_payments.user_id)
AND ((qry_user_payments_all_month_all_user.mMonth = month(tbl_user_payments.[pay_date]) AND (qry_user_payments_all_month_all_user.mYear = year(tbl_user_payments.[pay_date]) )) )
ORDER BY qry_user_payments_all_month_all_user.mYear, qry_user_payments_all_month_all_user.mMonth, qry_user_payments_all_month_all_user.user_id;
[Function]
Public Function FN_CRETAE_DATE_TABLE(iDate_From As Date, Optional iDate_To As Date)
'---------------------------------------------------------------------------------------
' Procedure : FN_CRETAE_DATE_TABLE
' Author : KRISH KM
' Date : 22/09/2015
' Purpose : will generate date period and check whether payments are received. A query will be opened with results
' CopyRights: You are more than welcome to edit and reuse this code. i'll be happy to receive courtesy reference:
' Contact : krishkm#outlook.com
'---------------------------------------------------------------------------------------
'
Dim From_month, To_Month As Integer
Dim From_Year, To_Year As Long
Dim I, J As Integer
Dim SQL_SET As String
Dim strDoc As String
strDoc = "tbl_date"
DoCmd.SetWarnings (False)
SQL_SET = "DELETE * FROM " & strDoc
DoCmd.RunSQL SQL_SET
If (IsMissing(iDate_To)) Or (iDate_To <= iDate_From) Then
'just current year
From_month = VBA.Month(iDate_From)
From_Year = VBA.Year(iDate_From)
For I = From_month To 12
SQL_SET = "INSERT INTO " & strDoc & "(date_field) values ('" & From_Year & "-" & VBA.Format(I, "00") & "-01 00:00:00')"
DoCmd.RunSQL SQL_SET
Next I
Else
From_month = VBA.Month(iDate_From)
To_Month = VBA.Month(iDate_To)
From_Year = VBA.Year(iDate_From)
To_Year = VBA.Year(iDate_To)
For J = From_Year To To_Year
For I = From_month To To_Month
SQL_SET = "INSERT INTO " & strDoc & "(date_field) values ('" & J & "-" & VBA.Format(I, "00") & "-01 00:00:00')"
DoCmd.RunSQL SQL_SET
Next I
Next J
End If
DoCmd.SetWarnings (True)
On Error Resume Next
strDoc = "qry_user_payments_paid_or_not_paid"
DoCmd.Close acQuery, strDoc
DoCmd.OpenQuery strDoc, acViewNormal
End Function
you can call this public function from button or form or debug window:
?FN_CRETAE_DATE_TABLE("2015-01-01","2015-10-01")
this will generate from jan to oct and check whether you received payments or not.
[Screen]:
Something like this, look for user where next month is not paid for, but the month after that is paid for:
select user_id, month(date) + 1
from tablename t1
where not exists (select 1 from tablename t2
where t2.user_id = t1.user_id
and month(t2.date) = month(t1.date) + 1)
and exists (select 1 from tablename t3
where t3.user_id = t1.user_id
and month(t3.date) > month(t1.date) + 2)
Note 1: No dbms is specified, so month() function is just pseudo code. ANSI SQL has extract(month from column_name).
Note 2: in ANSI SQL date is a reserved word and needs to be delimited as "date".
Note 3: Just realized this answer will only return the first month even if several (consecutive) are missing...
I have written a simple query for this but I realize that it's not the best solution. Other solutions are still welcome.
SELECT user_id,
MIN(date) AS min_date,
MAX(date) AS max_date,
COUNT(*) AS no_of_records,
round((MAX(date)-MIN(date))/30.4+1,0) AS months,
(months-no_of_records) AS diff
FROM test
GROUP BY user_id
HAVING (round((MAX(date)-MIN(date))/30.4+1,0)-COUNT(*)) > 0
ORDER BY 6 DESC;
Now we can take a look at columns "no_of_records" and "months". If they are not equal, there was a gap for this user.