Beginner at access, so sorry if this is a question that has been asked before (I've searched for ages, so maybe im searching for the wrong thing?). Anyway, in my database I have a form that is used to submit records, and a query that checks for double bookings (it shows all records that overlap with the inputted dates). I have created a 'submit record' button on the form, and all I am trying to do is validate that there are no double bookings by running the double booking query and checking if there are no records.
I have tried using a Dcount function for this, however I cannot seem to find how to run the query as it has parameters (I need to pass the inputs of the form to the query). I am unsure if this is the most efficient way to check for double bookings, but I just need a solution what works at the moment :P. This is my current code, which obviously doesnt work as the sql query requires parameters:
If DCount("*","Double Booked")=0 Then ...
(Where 'Double Booked' is the name of my query in the database)
Sorry if I was unclear, thanks in advance.
EDIT: Here is the double booked query code:
SELECT *
FROM Booking
WHERE (
([Your VanID] = [Booking].[VanID])
AND (
( ([Your Collection Date] >= [Booking].[Collection date]) AND ([Your Collection Date] <= [Booking].[Return date]))
OR ([Your Return Date] >= [Booking].[Collection date]) AND ([Your Return Date] <= [Booking].[Return date])
OR (([Your Collection Date] <= [Booking].[Collection date]) AND ([Your Return Date] >= [Booking].[Return date]))
)
);
If you only want to know if there are double bookings (not how many), DCount is unnecessary. Opening a recordset is enough.
Queries with form fields as parameters run fine when opened directly, or as a RecordSource, but not when opened in VBA.
To be able to do this, I have this function in a public module:
Public Sub EVal_Params(QD As DAO.QueryDef)
On Error GoTo EVal_Params_Err
Dim i As Integer
Dim par As DAO.Parameter
For i = 0 To QD.Parameters.Count - 1
Set par = QD.Parameters(i)
' This is the key line: Eval "evaluates" the form field and gets the value
par.Value = Eval(par.Name)
Next i
EVal_Params_Exit:
On Error Resume Next
Exit Sub
EVal_Params_Err:
MsgBox Err.Description, vbExclamation, "Runtime-Error " & Err.Number & " in EVal_Params"
Resume EVal_Params_Exit
End Sub
With that you can open a recordset on the query, and its .EOF property will tell you whether there are double bookings:
Dim DB As DAO.Database
Dim QD As DAO.QueryDef
Dim RS As DAO.Recordset
Set DB = CurrentDb
Set QD = DB.QueryDefs("Double Booked")
' Get form field values into query parameters
Call EVal_Params(QD)
Set RS = QD.OpenRecordset(dbOpenSnapshot)
If RS.EOF Then
' no doubles
Else
' there are double bookings
End If
RS.Close
Edit
If the query parameters are not references to form controls, but "normal" named parameters, you'd do instead:
Set QD = DB.QueryDefs("Double Booked")
QD.Parameters("Your VanID") = someValue
QD.Parameters("Your Collection Date") = someOtherValue
Set RS = QD.OpenRecordset(dbOpenSnapshot)
Related
I am running a simple query on a SQL Server table:
SELECT * FROM MyTable
The table contains three columns, with types int, bit, and datetime2(0). I am using code along the lines of:
Dim cnn As ADODB.Connection
Dim rst As ADODB RecordSet
Dim rngDest As Range
:
: ' etc
:
rst.Open "SELECT * FROM MyTable", cnn
rngDest.CopyFromRecordset rst
This works well, except that it seems to be getting the field types wrong: it says the field types are 3, 11, 202, which are, according to the documentation, Integer (correct), Boolean (correct, I guess), and NVarChar2 -- incorrect. The third field should be a date, surely ("Date" type is 135).
As background: I migrated the data from Access to SQL Server using SSMA. This table now has the three column types I mentioned. Also, I understand that it is a well-known problem that CopyFromRecordset can get the field types wrong, and I initially thought that was why the third column (the date) was appearing in Excel as a string, but upon closer inspection I can see that when the VBA has read the Recordset it already believes the field is text, before the CopyFromRecordset line. I feel if I could get the VBA to somehow recognise that the third field should be a date then I could convert it internally. I would rather not create a solution for this table specifically because I have many, many Tables and Views that have to be handled, so I would prefer to find an approach that works for all of them.
If it's important, the connection string I am using is:
Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=True;Data Source=[REDACTED];Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Use Encryption for Data=False;Tag with column collation when possible=False;Initial Catalog=[REDACTED];
Can anyone help please?
The backwards compatible SQLOLEDB driver that ships with Windows has no notion SQL Server data types introduced over the last 20 years, like date. Try the latest SQL Server OLEDB driver, MSOLEDBSQL.
I ran a quick ADO test using the VbScript below and a date column returned ADO type 133 (adDBDate).
Set connection = CreateObject("ADODB.Connection")
connection.Open "Provider=MSOLEDBSQL;Data Source=.;Integrated Security=SSPI"
Set recordset = connection.Execute("SELECT CAST(SYSDATETIME() AS date);")
MsgBox recordset.Fields(0).Type
connection.Close
Here is the solution I have found to work (while I'm waiting for the support guys to update the driver):
[TL;DR: Read the Recordset into a variant varOrig using rst.GetRows; transpose, then plonk into an Excel sheet; using .Value2 = causes Excel to convert it to a proper date. [We have to transpose because .GetRows, despite its name, returns columns of data.]]
Hope this is useful for anyone else in the future having a similar problem.
Public Sub RecordSetToRange(ByRef rst As ADODB.Recordset, ByRef lobDst As ListObject)
Const strROUTINE As String = "RecordSetToRange"
Dim iRow As Long
Dim iCol As Long
Dim varOrig As Variant
Dim varTxp() As Variant
' -- Error Handling, Validation, Initialization.
On Error GoTo ErrHandler
' -- Procedure.
' Headers first.
With lobDst.HeaderRowRange.Cells(1)
For iCol = 0 To rst.Fields.Count - 1
.Offset(0, iCol).Value2 = rst.Fields(iCol).Name
Next iCol
End With
' Then data. Note that, despite the name of the function, GetRows returns columns of data, so we have
' to transpose it. Don't do anything if there is no data.
If rst.RecordCount > 0 Then
varOrig = rst.GetRows
ReDim varTxp(UBound(varOrig, 2), UBound(varOrig, 1))
For iRow = 0 To UBound(varOrig, 2)
For iCol = 0 To UBound(varOrig, 1)
varTxp(iRow, iCol) = varOrig(iCol, iRow)
Next iCol
Next iRow
lobDst.DataBodyRange.Resize(UBound(varOrig, 2) + 1, UBound(varOrig, 1) + 1).Value2 = varTxp
End If
'
ErrHandler: ' -- Error handling and Routine termination.
If Err.Number <> 0 Then If DspErr(mstrMODULE, strROUTINE) Then Stop: Resume Else End
End Sub
Your date field may be string. so you convert the date. The following example converts a character date to date data. You will need to specify differently depending on what your character type is.
20130604060133 -> 2013/06/04 06:01:33
select
convert(datetime,SUBSTRING( '20130604060133',1,8),112) +
convert(datetime,
( substring( '20130604060133',9,2) + ':' +
substring( '20130604060133',11,2) + ':' +
substring( '20130604060133',13,2) )
,120) as myDate
The problem seems to be somewhere else, but it's not clear. But there is a way to change the data. See below.
Dim cnn As ADODB.Connection
Dim rst As ADODB.Recordset
Dim rngDest As Range
:
: ' etc
:
rst.Open "SELECT * FROM MyTable", cnn
rngDest.CopyFromRecordset rst
Dim vDB
Dim rngDB As Range
Set rngDB = rngDest.CurrentRegion
vDB = rngDB
rngDB.NumberFormat = "General"
rngDB = vDB
I have quite a conundrum which I have been trying to troubleshoot. I have a stored procedure in a MySql database, which I call through an Excel VBA application. The VBA application passes the recordset into an Array, and then I use a For Loop to place each of the items in the Array onto a worksheet.
Here's the problem: two of the values in the recordset keep coming back blank in Excel. Oddly, the two are in the middle of the Array, not the beginning or end. However, if I call the stored procedure through another query program such as HeidiSql, I receive ALL values back. I'm at a loss as to why I'm not receiving all of the values through Excel... or why the Array isn't receiving them all, at any rate.
Thanks in advance for your help.
Here is my code:
Sub StartHereFlexFunderCust()
On Error GoTo ErrorHandler
Dim Password As String
Dim SQLStr As String
'OMIT Dim Cn statement. Cn stands for Database Connection
Dim Server_Name As String
Dim User_ID As String
Dim Database_Name As String
Dim custID As String
Dim myArray()
'OMIT Dim rs statement. rs stands for Database Recordset and is the Recordset of what is returned
Set RS = CreateObject("ADODB.Recordset")
Server_Name = Range("O10").Value
Database_Name = Range("O11").Value ' Name of database
'id user or username. We need to write code to insert the current user into this variable (Application.Username) if possible. But they may not be consistent across all machines.
'For example mine is "Ryan Willging" and we would have to shorten it to rwillging but others may be rwillging.
'This is important because if we do not do this all queries will come from the same person and that is not good for debugging.
User_ID = Range("O12").Value
Password = Range("O13").Value
custID = Range("C4").Value 'Deal Number from Start here that we are passing into the stored procedure
'This is the storedprocedure call and it passes in the value of the DealId to the Stored Procedure
SQLStr = "call flexFundByCustomer(" + custID + ")"
Set cn = CreateObject("ADODB.Connection") 'NEW STATEMENT
'This statement takes the variables from the checklist and passes them into a connection string
cn.Open "Driver={MySQL ODBC 5.1 Driver};Server=" & _
Server_Name & ";Database=" & Database_Name & _
";Uid=" & User_ID & ";Pwd=" & Password & ";"
'This statement queries the database using the SQL string and the connection string.
'The adOpenStatic variable returns a static copy of a set of records that you can use to find data or generate reports. There are other variables that
'could be used but I think this one will suffice.
RS.Open SQLStr, cn, adOpenForwardOnly
Debug.Print msg 'or MsgBox msg
'Take all of the info from the queries and put them into the spreadsheet
myArray = RS.getrows()
Dim Fld_Name As String
Dim Val_of_Field As String
Dim starthere As Worksheet
Fld_Name = UBound(myArray, 1)
Val_of_Field = UBound(myArray, 2)
Set starthere = ThisWorkbook.Sheets("Start Here")
MsgBox "No error yet defined Start Here!"
'This little loop works well to dump the recordset into excel. We can then map the correct fields 'k inputs the headers and R inputs the rows returned in the Recordset
For K = 0 To Fld_Name ' By using a For loop the data is inputed into excel one row at a time
starthere.Range("U4").Offset(0, K).Value = RS.fields(K).Name
For R = 0 To Val_of_Field
starthere.Range("U4").Offset(R + 1, K).Value = myArray(K, R)
Next
Next
RS.Close
Set RS = Nothing
cn.Close
Set cn = Nothing
ErrorHandler:
MsgBox "There's been an error!"
Exit Sub
End Sub
Consider using Range.CopyFromRecordset method to avoid any use of arrays. Or if memory does not allow, use a Do While Loop across Recordset columns:
' COLUMN HEADERS
For i = 1 To RS.Fields.Count
starthere.("Results").Range("U4").Offset(0, i) = RS.Fields(i - 1).Name
Next i
' DATA ROWS
' COPYFROMRECORDSET APPROACH
starthere.Range("U5").CopyFromRecordset RS
' DO WHILE LOOP APPROACH
starthere.Activate
starthere.Range("U5").Activate
row = 5
Do While Not RS.EOF
For i = 0 To RS.Fields.Count - 1
ActiveCell.Offset(0, i) = RS.Fields(i)
Next i
row = row + 1
ActiveCell.Offset(row, 21)
RS.MoveNext
Loop
As for the values returning empty that may be a MySQL and Excel incompatibility of data types. For instance, you may have a table field set to MySQL's maximum decimal (65, 30) which denotes max digits of 65 and max 30 decimal points which cannot be reflected on a spreadsheet. Current precision limit of a cell value is 15 decimal points.
Alternatively, you may have a VARCHAR(65535) which is the 65,535 byte limit or the open-ended TEXT column of no limit that also cannot be displayed on spreadsheet. Current limit of characters in one cell is 32,767.
Try modifiying column to a smaller type:
ALTER TABLE `tableName` MODIFY COLUMN `largenumberfield` DECIMAL(10,7);
ALTER TABLE `tableName` MODIFY COLUMN `largetextfield` VARCHAR(255);
Why the other programs such as HeidiSQL retrieve values? It might be due to their internal conversion features forcing data values into a specific format (i.e., removing whitespaces, truncating values) which then renders adequately in Excel.
I have an Access database with about 500,000 records. There is a specific column which has the transaction reference.
This is of the form:
Transaction_Ref
CDY1053N1
CDY1053N2
CDY1053N3
JFD215D1
JFD215D2
Where CDY1053N and JFD215D are customer references, and the 1,2,3, etc which follows is the transaction number.
What I am looking for is a loop which will update a column called "Group". This will go to row 1, and loop through the database to find transaction references similar to CDY1053N and assign a group ID, for example:
Transaction_Ref Group_ID
CDY1053N1 1
CDY1053N2 1
CDY1053N3 1
JFD215D1 2
JFD215D2 2
Any ideas please?
Thanks for the help.
This might not be the best or most elegant way to do this (particularly with the number of records you have), but this worked on my small set of test records.
I've assumed Transaction_Ref and Group_ID are in the same table and I've called that table tblTransactions.
I've also assumed that you might want to run this on new data so have nulled the Group_ID before looping through and resetting the values. This could mean that a different value for Group_ID gets assigned for a group of records (for example, were your records change order between subsequent runs of this sub).
If that's a problem you'll need to tweak this a bit.
Public Sub AssignGroupID()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim sql As String
Dim i As Integer
Set db = CurrentDb
' Clear the Group_ID column (in case you want to run this more than once)
sql = "UPDATE tblTransactions Set Group_ID = Null"
db.Execute sql
' Open your table with the Transaction_Ref and Group_ID fields
Set rs = db.OpenRecordset("tblTransactions")
' Zero the counter
i = 0
' Start the loop (set it to end when it gets to the last record)
Do While Not rs.EOF
' Only update Group_IDs that haven't got a value yet
If IsNull(rs!Group_ID) Then
' Push the counter on
i = i + 1
' Update all Group_IDs with current counter number that
' match the customer reference of the current record
sql = "UPDATE tbltransactions Set Group_ID = " & i & " WHERE " _
& "Left(tblTransactions.Transaction_Ref, Len(tblTransactions.Transaction_Ref) -1) = '" _
& Left(rs!Transaction_Ref, Len(rs!Transaction_Ref) - 1) & "'"
db.Execute sql
End If
' Move to the next record
rs.MoveNext
Loop
'clean up
rs.Close
Set rs = Nothing
Set db = Nothing
End Sub
I need some help with some VBA for Access.
I have a table "Client_Table" with 100 rows of data. I have another table "SalesRep_Table" where I have 10 distinct Sales Rep ID numbers (such as: AA1111, and so on).
My goal is to run a procedure that takes the first ID record "AA1111" and places it in the appropriate column on the Clients table named "AssignedSalesRepID" for the first 10 rows, then the next ID number in the SalesRep_Table gets inserted into the next 10 cells in the Clients table, and the process repeats through a loop until all 10 IDs are now in 10 rows each to fill the 100 rows of data in the Clients table.
I went about it by creating two recordsets and trying a loop through SQL Update. However I end up with all 100 records containing just the last Sales Rep ID 100 times repeating. Can you take a look at my code and let me know where it needs to be fixed?
Public Sub Command01_Click()
Dim strSQL
Dim ClientsTableQuery, SalesRepList
Dim DataB as Database
Dim ClientQD as QueryDef
Dim SalesQD as QueryDef
Dim rstClient as Recordset
Dim rstSalesRep as Recordset
ClientTableQuery = "Clients"
SalesTableQuery = "SalesRepList"
'Creates a recordset with 100 client records named "Clients"
strSQL = "Select * from Client_Table"
Set DataB = CurrentDB()
Set ClientQD.CreateQueryDef(ClientTableQuery, strSQL)
Set rstClient = DataB.OpenRecordset(ClientTableQuery)
'Creates a recordset with 10 sales rep records named "SalesRepList"
strSQL = "Select SalesRepID from SalesRep_Table"
Set DataB = CurrentDB()
Set SalesQD.CreateQueryDef(SalesTableQuery, strSQL)
Set rstSalesRep = DataB.OpenRecordset(SalesTableQuery)
rstSalesRep.MoveFirst
rstClient.MoveFirst
Do Until rstSalesRep.EOF = True
'SQL Query to update the top 10 cells in the "Assigned Sales Rep ID" column in the
Clients recordset with the Sales Rep ID from the SalesRepList recordset
strSQL = "Update Clients, SalesRepList SET Clients.AssignedSalesRepID =
SalesRepList.SalesRepID where Clients.ClientIDNumber in (Select Top 10
Clients.ClientIDNumber FROM Clents where Clients.AssignedSalesRepID is Null)"
DoCmd.RunSQL (strSQL)
rstSalesRep.MoveNext
Loop
MsgBox "Finished Looping"
rstSalesRep.Close
End Sub
I hate to be the one to tell you this, but you should reconsider using SQL to do this update. I see that you have already written a lot of code and might feel like if you switch back to SQL that you will then have wasted all this vb code. I have felt like that myself in times past. But you can solve this problem with SQL with an order of magnitude less code(or nearly so).
Steps for SQL solution:
Sequence rows in both sets
mod A set sequence by B set sequence max
update A set on mod = b seq
You are making a Join call in your query, without defining how those 2 tables are being joined. You are not mentioning anywhere, which record of the rstSalesRep recordset you wish to set the assignedSalesRepId to.
Also I would reduce all your code down to the following:
Dim strSQL
Dim DataB As Database
Dim rstSalesRep As Recordset
Set DataB = CurrentDb()
Set rstSalesRep = DataB.OpenRecordset("Select SalesRepID from SalesRep_Table ")
Do Until rstSalesRep.EOF = True
strSQL = "Update Client_Table, SalesRep_Table SET Client_Table.AssignedSalesRepID = SalesRep_Table.SalesRepID " & _
"where Client_Table.ClientIDNumber in (Select Top 2 Client_Table.ClientIDNumber FROM Client_Table where Client_Table.AssignedSalesRepID is Null)" & _
" and SalesRep_Table.SalesRepID = '" & rstSalesRep("SalesRepID") & "'"
DoCmd.RunSQL (strSQL)
rstSalesRep.MoveNext
Loop
MsgBox "Finished Looping"
rstSalesRep.Close
In a SQL database I have a table, Table1. This table is related to another table, Table2 which in turn is related to Table3. There is a query Query1 that selects certain records from Table1.
This database is linked to in an Access database project
A form Table1Data is based on Table1, with a datasheet containing related Table2 data (and subsequently Table3 data). This form is opened by another form (Switchboard). The problem comes when the form is opened. I want the form to be filtered, but when I set up a macro and open the form and set the Filter to Query1, the data in the form is not filtered. Why does this happen, is this not the way to do it? Query1 selects all the columns from Table1, so mismatching columns should not be an issue.
Additionally I want to lock it down - only certain people can execute Query1, same with other queries (Query2, Query3 etc). So they can only edit the data that they are permitted to edit.
My preferred solution is to set the recordsource in the Form Open event. This gives me the most control over what is going on.
Here is my boilerplate for doing this. It also includes looking up the OpenArgs which are passed on calling the form. You can just comment out or remove the If/Then statement if you aren't looking to specify anything from the calling form in your SQL.
Private Sub Form_Open(Cancel As Integer)
' Comments :
' Parameters: Cancel -
' Modified :
' --------------------------------------------------
On Error GoTo Err_Form_Open
Dim strSQL As String
Dim strVariable As String
Dim strDateVariable As String
Dim dteDateVariable As String
Dim i As Integer
Dim n As Integer
'Get variables from Left and right of | in OpenArgs
If Not (IsNull(Me.OpenArgs)) Then
i = InStr(1, Me.OpenArgs, "|")
n = Len(Me.OpenArgs)
strVariable = Left(Me.OpenArgs, n - (n - i + 1))
strDateVariable = Right(Me.OpenArgs, (n - i))
dteDateVariable = CDate(strDateVariable)
Else
GoTo Exit_Form_Open
End If
strSQL = "SELECT ... " _
& "FROM ... " _
& "WHERE (((Field1)='" & strVariable & "') " _
& " AND ((Field2)=#" & dteDateVariable & "#));"
Me.RecordSource = strSQL
Me.Requery
Exit_Form_Open:
Exit Sub
Err_Form_Open:
Select Case Err.Number
Case Else
Call ErrorLog(Err.Number, Err.Description, "Form_Open", "frmName", Erl)
GoTo Exit_Form_Open
End Select
End Sub