how to get attachment files using union query in ms access - ms-access

I have two independent tables in ms access now i want to get all data from both tables and show as one table using union query.
all is work but the problem is the attachment fields doesn't not loaded and its Empty.
SELECT *
FROM Table1
UNION ALL select * from Table2;
--This is my query which gets all records from both table but the attachment type field is empty

This example may be usefull
Set rsChild = rsDocs.Fields("DocText").Value
rsChild.Fields("FileData").SaveToFile newFileName
rsChild.Close
where rsDocs - ADO (or DAO) recordset as query result
DocText - attachment field name
rsChild.Fields("FileData") - stream object
SaveToFile - method of attachment field
rsDocs.Fields("DocText").Value.Fields("FileData").SaveToFile

The below example uses an attachment field named AttachmentField. You can pass the name of the file you're looking through the parameter.
If you run only the SQL, the parameter window will pop-up asking for the parameter value. To search, find the file and open it, you will need to use VBA.
See an example:
PARAMETERS [FileNameParam] Text (255);
SELECT T.ID, T.FileName
FROM (
SELECT ID, AttachmentField.FileName AS FileName
FROM Table1
UNION ALL
SELECT ID, AttachmentField.FileName AS FileName
FROM Table2
) AS T
WHERE T.FileName Like '*' + [FileNameParam] + '*'
Dim q As DAO.QueryDef
Set q = CurrentDb().QueryDefs("Query1")
q.Parameters("[FileNameParam]").Value = "Blank.pdf"
Dim r As DAO.Recordset
Set r = q.OpenRecordset(dbOpenSnapshot)
'nothing found
If r.EOF Then
MsgBox "File not found."
Exit Sub
End If
Dim filepath As String
filepath = r![FileName]
'open
Application.FollowHyperlink filepath
'clean up
If Not r Is Nothing Then r.Close
If Not q Is Nothing Then q.Close

Related

Return Unique Records for Field

We have an access database create a csv list in an access table for each userid that logs into a computer. The problem is that if a userid logs in multiple times on one computer, than the userid is duplicated.
SO let's say this is what the table structure looks like
computer DailyUsers
ABC-123 ml12, rs12, ml12, ml12, ee13
DEF-456 zx44, aa33, zx44
And this is what I want a query to return
Computer DailyUsers
ABC-123 ml12, rs12, ee13
DEF-456 zx44, aa33
I tried using both Group By and Distinct but that looks on a row by row basis, not a field basis
How can this be achieved in Access 2013?
You can create a custom function in VBA to return only the unique users.
Add this in a VBA module:
Option Compare Database
Option Explicit
Public Function DistinctOnly(txt As String)
Dim arr, v, rv As String
Dim d As Object
arr = Split(txt, ",")
If UBound(arr) > 0 Then
Set d = CreateObject("scripting.dictionary")
For Each v In arr
d(Trim(v)) = 1
Next v
rv = Join(d.keys, ",")
Else
rv = txt
End If
DistinctOnly = rv
End Function
Usage:
SELECT Table1.[ID], Table1.[Users], DistinctOnly(Table1.[Users]) as UsersX
FROM Table1;
Input data and query results:

Returned Recordset is closed (mysql DB accessed via ODBC in VBA)

Long time viewer first time poster. I'm working on a database application with a front-end in Excel. Hence I am using VBA to access a MySQL database. The driver I'm using is the ODBC driver (Windows) and I have a valid connection to the database as lots of other queries function well.
What I'm trying to do is return the results from a series of SQL statements rather than trying to combine it all into one massive statement (which would probably work but be difficult to maintain/understand). I have added the flag FLAG_MULTI_STATEMENTS = 67108864 to the connection string which suppressed driver syntax errors.
But now when I run the following:
queryDB.Open SQLquery, conn
The recordset (queryDB) remains closed with no apparent error. The sql statement can be found here.
I may be generating errors that aren't being returned to VBA so any help here would be much appreciated.
NOTE: The sql statements work as I can paste that statement into phpMyAdmin and it returns the correct (non-empty) results. I don't know if the statements specifically are the problem but perhaps the use of CREATE TEMPORARY TABLE ... or the use of multiple statements in general.
Also I guess that the driver may be trying to return a result for each sql statement and VBA is only getting the first or something...
EDIT: the sql statement for future reference.
CREATE TEMPORARY TABLE tmpOrders AS
SELECT
o.customerName,
SUM(o.Sales) AS Sales,
SUM(TotalFobCost + TotalLandedCost + TotalLocalCost + TotalCmtCost) AS TotalCost,
YEAR(o.deliveryDate) AS YEAR,
MONTH(o.deliveryDate) AS MONTH
FROM devere_costing.orders_fixed_extras AS o
WHERE o.orderApproved = TRUE
AND o.orderCanceled = FALSE
AND o.deliveryDate BETWEEN '2014-01-01' AND '2014-03-31'
GROUP BY customerName, YEAR, MONTH
ORDER BY YEAR ASC, MONTH ASC, customerName ASC;
CREATE TEMPORARY TABLE tmpProj AS
SELECT p.customerName,
IF(p.MONTH > 9, p.YEAR, p.YEAR - 1) AS TrueYear,
1 + ((p.MONTH + 2) MOD 12) AS TrueMonth,
SUM(p.actualSalesInvoiced) AS salesInvoiced,
SUM(p.budget) AS budget
FROM devere_costing.sales_projection_data AS p
GROUP BY p.customerName, p.YEAR, p.MONTH
HAVING TrueYear BETWEEN YEAR('2014-01-01') AND YEAR('2014-03-31')
AND TrueMonth BETWEEN MONTH('2014-01-01') AND MONTH('2014-03-31');
CREATE TEMPORARY TABLE tmpLeft AS
SELECT
IF(o.customerName IS NULL, p.customerName, o.customerName) AS customerName,
p.budget AS TotalBudget,
o.Sales AS Sales,
p.salesInvoiced,
0 AS varianceToBudget,
o.TotalCost,
0 AS directMargin,
0 AS directMarginPercent,
IF(o.YEAR IS NULL, p.TrueYear, o.YEAR) AS YEAR,
IF(o.MONTH IS NULL, p.TrueMonth, o.MONTH) AS MONTH
FROM tmpOrders AS o
LEFT JOIN tmpProj AS p
ON (o.customerName = p.customerName
AND o.YEAR = p.TrueYear
AND o.MONTH = p.TrueMonth);
CREATE TEMPORARY TABLE tmpRight AS
SELECT
IF(o.customerName IS NULL, p.customerName, o.customerName) AS customerName,
p.budget AS TotalBudget,
o.Sales AS Sales,
p.salesInvoiced,
0 AS varianceToBudget,
o.TotalCost,
0 AS directMargin,
0 AS directMarginPercent,
IF(o.YEAR IS NULL, p.TrueYear, o.YEAR) AS YEAR,
IF(o.MONTH IS NULL, p.TrueMonth, o.MONTH) AS MONTH
FROM tmpOrders AS o
RIGHT JOIN tmpProj AS p
ON (o.customerName = p.customerName
AND o.YEAR = p.TrueYear
AND o.MONTH = p.TrueMonth);
(SELECT * FROM tmpLeft) UNION DISTINCT (SELECT * FROM tmpRight);
I have answered my own question!
The secret lies here:
So I was right in that there was more than one recordset returned. I just had to iterate through them to find the data that I want. The collection isn't indexed so you have to search through each one. In my case every sql statement does not return a recordset (that's why my recordset remained closed when I tried to open it). The only exception is the last sql statement which returns records. My loop looks like:
Dim rs As ADODB.Recordset
Set rs = queryDB(Sql)
' Loop through returned recordsets to find the data
Do
If Not rs Is Nothing Then
If rs.State = adStateOpen Then
' we have an open recordset. This means that the final select statement
' has returned this data.
Exit Do
Else
' Otherwise iterate through to the next recordset
Set rs = rs.NextRecordset
End If
Else
MsgBox "No recordset returned by sql statement"
GoTo ExitCode
End If
Loop
Answer copied from the question body:
I have answered my own question!
The secret lies here:
So I was right in that there was more than one recordset returned. I just had to iterate through them to find the data that I want. The collection isn't indexed so you have to search through each one. In my case every sql statement does not return a recordset (that's why my recordset remained closed when I tried to open it). The only exception is the last sql statement which returns records. My loop looks like:
Dim rs As ADODB.Recordset
Set rs = queryDB(Sql)
' Loop through returned recordsets to find the data
Do
If Not rs Is Nothing Then
If rs.State = adStateOpen Then
' we have an open recordset. This means that the final select statement
' has returned this data.
Exit Do
Else
' Otherwise iterate through to the next recordset
Set rs = rs.NextRecordset
End If
Else
MsgBox "No recordset returned by sql statement"
GoTo ExitCode
End If
Loop

Add new entries into sql from ms access front end

I have two tables which can be represented by this query (I have made this query the Recordsource of the form):
SELECT tblrcmtask.id, tblrcmtask.rcmtask,tblrcmtaskoptions.id,
tblrcmtaskoptions.rcm_id,
tblrcmtaskoptions.rcmtaskoptions
FROM tblrcmtask
INNER JOIN tblrcmtaskoptions
ON tblrcmtask.id=tblrcmtaskoptions.rcm_id
I want the user to be able to add new entries into these table via a form in access 2007.
Columns tblrcmtask.id and tblrcmtaskoptions.id are the primary keys of the tables tblrcmtask and tblrcmtaskoptions respectively.
I do not understand how do I create new ID in both the tables while the user adds new entries.The user can add only tblrcmtaskoptions.rcmtaskoptions and tblrcmtask.rcmtask in the form.Also, there are multiple rows in the table tblrcmtaskoptions for each tblrcmtask.id.
I want the user to be able to add new rows in the table tblrcmtaskoptions for an existing tblrcmtask.id
I tried using dropdowns for these two but I am facing problem while creating the new ID as Maximum of the ID + 1.
Dim MyRecords As DAO.Recordset
Dim Myfield As DAO.Fields
SQL = "SELECT Max(tblRCMTASK.ID) AS MaxOf_RCMTASKID FROM tblRCMTASK;"
Set MyRecords = dbTHIS.OpenRecordset(SQL)
Set Myfield = MyRecords.Fields
Me.txtRCMTASKID = Myfield("MaxOf_RCMTASKID") + 1
Me.txtRCMTASKID.DefaultValue = Myfield("MaxOf_RCMTASKID") + 1
MyRecords.Close
End If
Dim MyRecords1 As DAO.Recordset
Dim Myfield1 As DAO.Fields
SQL = "SELECT Max(tblRCMTASKOPTIONS.ID) AS MaxOf_RCMOPTIONSID FROM tblRCMTASK;"
Set MyRecords = dbTHIS.OpenRecordset(SQL)
Set Myfield1 = MyRecords1.Fields
Me.txtRCMOPTIONSID = Myfield1("MaxOf_RCMOPTIONSID") + 1
Me.txtRCMOPTIONSID.DefaultValue = Myfield("MaxOf_RCMOPTIONSID") + 1
MyRecords1.Close
I am getting an error which says you can't asign a value to this object and points to this line: Me.txtRCMTASKID = Myfield("MaxOf_RCMTASKID") + 1
How do I do this?
Access gives you trouble when trying to do operations on an autonumber field. If you would like to do these kinds of operations, you may be better off just using a regular number as a PK.
To get a recently inserted autonumber field to insert the same number in a related table, this is the VBA:
assuming recordset and database are declared, rs and db
dim id as integer
set db = CurrentDb
set rs = db.openrecordset("firstTable", dbOpenDynaSet)
With rs
.addNew
.Fields("field1").Value = Me.control1 'adds to column1 of your table the value of control1
.Fields("field2").Value = Me.control2
.update 'updates the record. If it is an autonumber, it will be automatically assigned. I will show you how to access this for your next insert
end with
'To get the autoID of the entry we just inserted, do this
id = db.OpenRecordSet("SELECT##IDENTITY")(0)
'Now you have the autoID of the recent insertion, so you may use it for your next one.
This is a classic form/subform set up. Create a form based solely on tblrcmtask with a subform tblrcmtaskoptions. The link child and master fields should be set to the common id. The wizards will do this for you. There is no code required. The id will be automatically added by the link fields.
You can see an example for in the 2007 version of the Northwind sample database.

Microsoft Access: How to create new table that has 1 of every 5 records from the original?

I have a large table and want to create a smaller table that systematically picks out one of every 5 records from the original. How best to do this?
This means it should have every row with id ending in 0 and 5 for example.
Thanks.
Assuming that your IDs as auto-incremented and continuous (with no deleted records), the simplest way could something like:
SELECT *
FROM BigTable
WHERE ID Mod 5 = 0
You would insert that into the small table like this:
INSERT INTO SmallTable
SELECT *
FROM BigTable
WHERE ID Mod 5 = 0
If your primary key ID is not sequential you can do this:
SELECT BigTable.*
FROM (SELECT ID,
(SELECT COUNT(ID) + 1
FROM BigTable AS A
WHERE A.ID < B.ID) AS RowNum
FROM BigTable AS B) AS C
INNER JOIN BigTable
ON C.ID = BigTable.ID
WHERE RowNum Mod 5 = 0
Note though that this is OK for a one-time thing as it is quite slow if you have a lot of records.
Better do it in code if you have lots of records (or maybe you have no primary key, which is usually bad btw).
The code below is generic code to copy all records from one table into another (existing) table:
Public Sub CreateSmallTable(largeTableName As String, _
smallTableName As String, _
interval As Integer)
Dim rsL As DAO.RecordSet
Dim rsS As DAO.RecordSet
Dim db As DAO.Database
Dim rowNum As Integer
Set db = CurrentDb()
' Open the LargeTable in read-only mode '
Set rsL = db.OpenRecordset(largeTableName, dbOpenSnapshot)
If Not (rsL Is Nothing) Then
Set rsS = db.OpenRecordset(smallTableName, dbOpenTable, dbAppendOnly)
With rsL
' We'll use a generic way to copy all fields from the BigTable '
Dim fd As DAO.field
Dim flds As DAO.Fields
Set flds = rsL.Fields
.MoveFirst
' For each record in the BigTable '
Do While Not .EOF()
If rowNum Mod interval = 0 Then
rsS.AddNew
' Copy all fields from BigTable to SmallTable '
For Each fd In flds
rsS.Fields(fd.Name) = .Fields(fd.Name)
Next fd
rsS.Update
End If
rowNum = rowNum + 1
.MoveNext
Loop
.Close
End With
rsS.Close
Set rsS = Nothing
Set rsL = Nothing
End If
End Sub
And call it like that (assuming that both BigTable and SmallTable have the same structure and that there are no existing records in SmallTable that could create a primary key violation when trying to inster duplicate records):
CreateSmallTable "Bigtable", "Smalltable", 5
Edit: following HansUp response, changed SQL queries to use Mod instead of its functional version: (Round(ID/5) = ID/5). He's right, no need to make it more complex than it needs to be.
Consider id Mod 5, and select only those where the remainder is zero.
SELECT f.id, f.foo_text
FROM tblFoo AS f
WHERE (id Mod 5) = 0;

Insert multiple rows using one insert statement in Access 2010

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')