I have two tables (lets call them Parameters 1 and 2) which both have a many-many relationship with a third table (Options). I need to group the third table records into three groups:
Those exclusively related to [specific Parameter 1 record],
Those exclusively related to [specific Parameter 2 record] and
Those related to both [specific Parameter 1 record] and [specific Parameter
2 record].
I can ignore Option records not related to either of them.
I need to be able to specify which Parameter 1 and 2 records apply in a form (using combo boxes), and have VBA juggle the three lists in the background, updating them as the Option records they contain are "used" elsewhere in the form (with check boxes).
At the risk of asking a bad question I'll submit the code I have - even though it's not a code that fails, just the framework for one that isn't even finished enough to debug yet. I simply haven't got the tools to complete it, as I don't know what methods/properties of what things to use to do it, and can't seem to find the answers in my own research thus far. Comments directing me to other resources will be appreciated, even if you don't have an answer that you're sure is best practice.
Function SetOptions()
If IsNull(cmbParam1) Or IsNull(cmbParam2) Then
MsgBox "You must select both an Param1 and a Param2!", vbCritical, "Wait!"
Exit Function
End If
'Recordsets of allowed Options
Dim Param1Opt, Param2Opt, OverlapOpt
'create recordset of tblOption.Option(s) referenced in qryPr1Opt with Param1 from cmbParam1
Param1Opt = CurrentDb.OpenRecordset("SELECT tblPr1Opt.Option FROM tblPr1Opt " &_
"WHERE Param1 = '" & cmbParam1 & "';")
'create recordset of tblOption.Option(s) referenced in qryPr2Opt with Param2 from cmbParam2
Param2Opt = CurrentDb.OpenRecordset("SELECT tblPr2Opt.Option FROM tblPr2Opt " &_
"WHERE Param2 = '" & cmbParam2 & "';")
'create recordset of tblOption.Option(s) in qryOptOvrlp with Param2 and Param1 from form
OverlapOpt = CurrentDb.OpenRecordset("SELECT qryOptOvrlp.Option FROM qryOptOvrlp " &_
"WHERE Param1 = '" & cmbParam1 & "' AND Param2 = '" & cmbParam2 & "';")
OverlapNum = Param1Num + Param2Num
'Steps remaining:
'1. Get Param1Opt and Param2Opt to only include Options not in overlap
For Each oOpt In OverlapOpt
For Each aOpt In Param1Opt
If aOpt.Value = oOpt.Value Then
'filter this record out of Param1Opt
End If
Next aOpt
For Each gOpt In Param2Opt
If gOpt.Value = oOpt.Value Then
'filter this record out of Param2Opt
End If
Next gOpt
Next oOpt
'2. Get the data in Param1Opt, Param2Opt and OverlapOpt, as well as their
'corresponding Nums to be accessible/editable in other functions/subs
End Function
You can reference the values of controls in SQL statements run in the context of Access, using the following syntax:
Forms!FormName!ControlName
or using square brackets if needed:
Forms![Form Name]!ControlName
Therefore, instead of opening multiple recordsets, you can express each of your points as a single SQL statement with joined tables. You can then either set the RowSource of a combobox or listbox to the statement (if you are only using the statement in one place); or you can save the statement as an Access query, and use the query name as the RowSource (if you need the statement in multiple places).
Given the following schema:
and two comboboxes: cmbParam1 and cmbParam2 on a form named Form1, you can use SQL statements as follows:
1. Records from Options which match tblPr1Opt but have no match in tblPr2Opt
SELECT DISTINCTROW Options.Option
FROM (Options
INNER JOIN tblPr1Opt ON Options.Option = tblPr1Opt.Option)
LEFT JOIN tblPr2Opt ON Options.Option = tblPr2Opt.Option
WHERE tblPr2Opt.Option IS NULL
AND tblPr1Opt.Param1 = Forms!Form1!cmbParam1
Or using the query designer (note the arrow head next to tblPr2Opt; this indicates a left join):
2. Records from Options which match tblPr2Opt but have no match in tblPr1Opt:
SELECT DISTINCTROW Options.Option
FROM (Options
INNER JOIN tblPr2Opt ON Options.Option = tblPr2Opt.Option)
LEFT JOIN tblPr1Opt ON Options.Option = tblPr1Opt.Option
WHERE tblPr1Opt.Option IS NuLL
AND tblPr2Opt.Param2 = Forms!Form1!cmbParam2;
or in the query designer:
3. Records from Options which match on both:
SELECT Options.Option
FROM (Options
INNER JOIN tblPr1Opt ON Options.Option = tblPr1Opt.Option)
INNER JOIN tblPr2Opt ON Options.Option = tblPr2Opt.Option
WHERE tblPr1Opt.Param1 = Forms!Form1!cmbParam1
AND tblPr2Opt.Param2 = Forms!Form1!cmbParam2
Or in the query designer:
I am trying to get an Access SQL query that does this (semi-pseudocode below)
UPDATE SignIn SET SignIn.Complete=True, CompletedBy=(Select [FirstName] & " " & [LastName] AS EmployeeName From UserList where POid = Forms!HiddenUserCheck!txtPOid), CompletedDateTime=Now()
So after the query would run, the data in the database would look like
Complete EmployeeName CompletedDateTime
True John Smith 3/23/2017 8:34:10 AM
THe update query doesn't work because of syntax and not sure how to fix it.
The exact error message is
Invalid Memo, OLE, or HyperLink Object in subquery '[FirstName] & " "
& [LastName]'.
The query could be throwing a fit because of the Double Exclamation marks. Instead of
Forms!HiddenUserCheck!txtPOid
Try
Forms!HiddenUserCheck.txtPOid
You also have an extra ) at the end of your WHERE Statment
OK, then your issue may be that the subquery may return more than one record:
UPDATE
SignIn
SET
SignIn.Complete=True,
CompletedBy =
(Select First([FirstName] & " " & [LastName]) AS EmployeeName
From UserList
Where POid = Forms!HiddenUserCheck!txtPOid),
CompletedDateTime = Now()
If your name fields are Memo/LongText fields, that may be the source of the error. If so, try:
UPDATE
SignIn
SET
SignIn.Complete=True,
CompletedBy =
(Select First(Left([FirstName], 255) & " " & Left([LastName], 255)) AS EmployeeName
From UserList
Where POid = Forms!HiddenUserCheck!txtPOid),
CompletedDateTime = Now()
Edit.
You may try using DLookup for the subquery:
UPDATE
SignIn
SET
SignIn.Complete=True,
CompletedBy =
DLookup("[FirstName] & " " & [LastName]", "UserList", "POid = " & Forms!HiddenUserCheck!txtPOid & ""),
CompletedDateTime = Now()
I spent some time looking around at other questions on the board, but didn't find anything similar so here it goes:
I have a report that runs up against the limits of CRM 2011 online in regards to the max number of characters in a Fetch Query. I would like to have a drop box that has an option for "All Contacts" or "Select up to Five Contacts" to avoid that issue.
What I would like to do is remove the condition from the Fetch statement if the "All Contacts" option is selected, and include the condition only if the select up to five is chosen.
How would I go about doing this?
Let me know if you have any further questions.
Nick
You can build your SQL using custom code because the SQL itself can be a string expression that you build manually. Let's say your SQL looks like this at the moment:
SELECT ThisField, ThatField
FROM Contacts
WHERE ContactId IN #Contacts
Let's change that to a string expression that builds the SQL:
="SELECT ThisField, ThatField "
& "FROM Contacts "
& "WHERE ContactId IN #Contacts "
Now we need to pull the #Contacts parameter apart and only select by ContactId when there are five or less contacts selected:
Function ContactsSQL(ByVal parameter As Parameter) AS String
Dim Result As String
Result = ""
If parameter.IsMultiValue Then
If parameter.Count <= 5 Then
Result = "WHERE ContactId IN ("
For i As integer = 0 To parameter.Count-1
Result = Result + CStr(parameter.Value(i)) + ", "
Next
Result = Left(Result, Result.Length - 2) + ") "
End If
End If
Return Result
End Function
This function builds the SQL WHERE clause to select the contacts only if five or less have been selected, otherwise it passes back an empty string. Now we use this function in our SQL expression:
="SELECT ThisField, ThatField "
& "FROM Contacts "
& Code.ContactsSQL(Parameters!Contacts)
Note that you are passing the Contacts parameter object here, not the Value property. We access the Value property in the custom code function.
I want to insert multiple values into an Access 2010 table, but I can't seem to find a way.
MySQL had a nice way:
INSERT INTO Production.UnitMeasure
VALUES
(N'FT2', N'Square Feet ', '20080923'),
(N'Y', N'Yards', '20080923'),
(N'Y3', N'Cubic Yards', '20080923');
Can something like this be done in SQL Server too?
As marc_s has pointed out, for SQL Server 2008 and later, you can just use table value constructors. For previous versions, you can use insert and select...union all, e.g.:
INSERT INTO Production.UnitMeasure
SELECT N'FT2',N'Square Feet ','20080923' union all
SELECT N'Y', N'Yards', '20080923' union all
SELECT N'Y3', N'Cubic Yards', '20080923'
(Specific documentation on Table Value Constructors in SQL Server. I can't find specific separate documentation on row value constructors, but that's what they are)
Use this confirm working query:
INSERT INTO Product (Code,Name,IsActive,CreatedById,CreatedDate )
SELECT * FROM
(
SELECT '10001000' AS Code,
'Blackburn sunglasses' AS Name,
1 AS IsActive,
1 AS CreatedById,
'2/20/2015 12:23:00 AM' AS CreatedDate
FROM Product
UNION
SELECT '10005200' AS Code,
'30 panel football' AS Name,
1 AS IsActive,
1 AS CreatedById,
'2/20/2015 12:23:09 AM' AS CreatedDate
FROM Product
) ;
For SQL-Server: Yes, and it can exactly like you write. Just be certain that the column values are in the same order as they appear in the table. Also: you must supply a value for each existing column.
For Access 2010: No. At least not by hardcoded values in the sql, but only by selecting multiple records from a table (in the same or in another database). See also the link in the answer of Khepri.
SQL Server definitely allows this: EDIT: [As of SQL Server 2008, thank you Marc_s]
INSERT INTO [Table]
([COL1], [COL2])
VALUES
('1#1.com', 1),
('2#2.com', 2)
As for the Access requirement, I'm no access guru but I found this MSDN documentation that shows how to do multiple inserts at once.
INSERT INTO target [(field1[, field2[, …]])] [IN externaldatabase]
SELECT [source.]field1[, field2[, …] FROM tableexpression
Doing some cursory reading beyond this, you can use a "dummy" from table if all of your values are known ahead of time as in your example.
Create a table called OneRow with a single integer column. Insert one row.
Then:
INSERT INTO Production.UnitMeasure
SELECT 'FT2', 'Square Feet ', '20080923' FROM OneRow
UNION ALL SELECT 'Y', 'Yards', '20080923' FROM OneRow
UNION ALL SELECT 'Y3', 'Cubic Yards', '20080923' FROM OneRow
Your exact syntax works on SQL Server 2008. For earlier use my above query without the FROM clauses and no helper table.
I know its late to answer, but there are a couple of methods which are still useful today, (not mentioned here).
There are two general methods.
Loop through using a VBA script with the 'Docmd.RunSQL' statement. - This is generally quite slow especially as the number of rows increases but easy to understand.
Copy your 'data array' into an excel worksheet and then use a database query link to the excel file (my preferred method) - Benefits of this is it is almost as fast as if the table was already in the database and not necessarily slowed down by the number of records as the previous method - slightly slower than the above when you have a small number of records however
The Docmd Method:
Option Compare Database
Option Base 1
'------- method created by Syed Noshahi --------
'https://www.linkedin.com/in/syed-n-928b2490/
Sub DoCmdRoutine()
Dim arr() As Variant ' create an unsized array ready for data
'--------------For the purposes of the Example, put some data in the array----------
ReDim arr(5, 5)
For i = LBound(arr) To UBound(arr)
For j = LBound(arr) To UBound(arr)
arr(i, j) = i * j
If i = LBound(arr) Then arr(i, j) = "col_" & arr(i, j) 'Append "col_" before the column names
Next
Next
'-------------Importing the data to a table in MS ACCESS----------------------------
sSQL = "INSERT INTO [#TableTemp] " ' change table to actual table name
DoCmd.SetWarnings False 'turn warnings off - no popups!
For i = 2 To UBound(arr) 'start at 2 assuming that the first columns are headers
vals = "" 'go through each column and copy the data to SQL string format
'replace any single quote with double quotes so it does not error importing into SQL
For j = 1 To UBound(arr, 2)
If IsDate(arr(i, j)) Then 'if a date, convert to a number and let access re-covert to date (best chance at success)
vals = vals & " cdate('" & CDbl(arr(i, j)) & "'),"
ElseIf IsNumeric(arr(i, j)) Then 'if a number put through as a number
vals = vals & arr(i, j) & ","
Else 'otherwise treat as a text value
vals = vals & Replace(arr(i, j), "'", "''", , , 1) & "',"
End If
Next
vals = " VALUES(" & Left(vals, Len(vals) - 1) & ")" 'put in correct sql format
DoCmd.RunSQL sSQL & vals 'Run the SQL statement and import into the database
Next
DoCmd.SetWarnings True 'turn warnings on
End Sub
The Excel Link Method:
Option Compare Database
Option Base 1
'------- method created by Syed Noshahi --------
'https://www.linkedin.com/in/syed-n-928b2490/
Sub ExcelLinkRoutine()
Dim arr() As Variant ' create an unsized array ready for data
Dim oExcel As Object ' Excel instance - late binding
' works with access 2007+, access 2003 has a different SQL syntax
'--------------For the purposes of the Example, put some data in the array----------
ReDim arr(5, 5)
For i = LBound(arr) To UBound(arr)
For j = LBound(arr) To UBound(arr)
arr(i, j) = i * j
If i = LBound(arr) Then arr(i, j) = "col_" & arr(i, j) 'Append "col_" before the column names
Next
Next
'----------------------------output the array to an excel file ---------------------
Set oExcel = CreateObject("Excel.Application")
oExcel.Workbooks.Add 1
Set wb = oExcel.ActiveWorkbook
'network file path & normal path examples below
'fileNameWithExtention = "\\networkpath\location\example999.xlsb" ' note that xlsb file format must be used
' other formats can be used by changing 'xlExcel12'
' ONLY change the path not the FILE NAME
fileNameWithExtention = "C:\Users\public\documents\example999.xlsb" ' same as above
checkFileExists = Dir(fileNameWithExtention)
If Len(checkFileExists) > 0 Then
'only delete the file if its called example999!
If checkFileExists = "example999.xlsb" Then
Kill fileNameWithExtention
End If
End If
With wb
.Sheets(1).Cells(1, 1).Resize(UBound(arr), UBound(arr, 2)).Value2 = arr()
.SaveAs fileNameWithExtention, 50 ' 50 means xlExcel12
.Close False
End With
Set wb = Nothing
Set oExcel = Nothing
'------------ Importing the data to a table in MS ACCESS-----------------------------
'NOTE1: The saved down excelfile MUST be named Sheet1
'NOTE2: if the file path contains special characters such as ,-'
' you may need find the correct way to input (or remove the special chars)
sSQL = "SELECT T1.* INTO [#TableTemp] " ' change table to actual table name
sSQL = sSQL & " FROM [Excel 12.0;HDR=YES;IMEX=2;ACCDB=YES;DATABASE=" & fileNameWithExtention & "].[Sheet1$] as T1;" ' linked table format
DoCmd.SetWarnings False 'turn warnings off - no popups!
DoCmd.RunSQL sSQL 'Run the SQL statement and import into the database
DoCmd.SetWarnings True 'turn warnings on
End Sub
OUTPUT:
Col_1
Col_2
Col_3
Col_4
Col_5
2
4
6
8
10
3
6
9
12
15
4
8
12
16
20
5
10
15
20
25
MS Access does not allow multiple insert from same sql window. If you want to insert, say 10 rows in table, say movie (mid, mname, mdirector,....), you would need to
open the sql windows,
type the 1st stmt, execute 1st stmt, delete 1st stmt
type the 2nd stmt, execute 2nd stmt, delete 2nd stmt
type the 3rd stmt, execute 3rd stmt, delete 3rd stmt ......
Very boring.
Instead you could import the lines from excel by doing:
Right-click on the table name that you have already created
Import from Excel (Import dialog box is opened)
Browse to the excel file containing the records to be imported in the table
Click on "Append a copy of the records to the table:"
Select the required table (in this example movie)
Click on "OK"
Select the worksheet that contains the data in the spreadsheet
Click on Finish
The whole dataset in the excel has been loaded in the table "MOVIE"
I know I'm a bit late to the game, but I was wanting to do the exact same thing you guys mentioned in your example. I was trying to insert a new list of default rows into a table/list using Access because I've had a lot of SQL experience, I was trying to do it the same way, however as you posters have noted, it's not possible to do the Unions and such.
However I just wanted to post a reply up here because in the case where you're manually typing in the values (string default values in this case) you can simply open Access in datasheet view, copy your data from Excel and just paste it into your Access table (or in my case, SharePoint list). You'll need to make sure you're columns are lined up exactly, but if you were going to manually type in your "insert" sql statements, just putting that info into an Excel spreadsheet shouldn't be a big deal.
In my case, my table/list only had a single column as a lookup, so I just copied the column from notepad++ and pasted it into the datasheet view.
Good luck everyone!
Check following,
INSERT INTO [Customer] ([Id],[FirstName],[LastName],[City],[Country],[Phone])VALUES(1,'Maria','Anders','Berlin','Germany','030-0074321')
INSERT INTO [Customer] ([Id],[FirstName],[LastName],[City],[Country],[Phone])VALUES(2,'Ana','Trujillo','México D.F.','Mexico','(5) 555-4729')