MS Access Memo field truncated - sql

This issue sounds familiar, but please read it to the end, as it has a twist to similar questions I found in this forum or elsewhere.
I am using Access 2010 to build a simple app to create some code to execute in another system. Part of it is to convert number of records per user into a single record, with all user entries separated by comma. To illustrate, here is the sample of my input:
USER MBR
---- ----
USR1 DRG
USR1 ABC
USR1 XYZ
USR2 123A
USR2 ABS2
And I need it in this format:
USER MBR_LIST
----- ---------------
USR1 DRG, ABC, XYZ
USR2 123A, ABS2
So far, so familiar. I've used the code posted by Allen Brown back in 2008 (http://allenbrowne.com/func-concat.html), but I've run into an issue.
The code works fine - when I debug it, I can see that my output string ("strOut") has all MBRs for a single user, as expected. Mind you, sometimes this string is over 7,000 char long. So, again following some great advice found around, I've created a table with the MBR_LIST field set to MEMO, and I execute Allen's function as:
INSERT INTO Table2 ( [USR], MBR_LIST )
SELECT B.USR, ConcatRelated("MBR","Table1","Table1.USR = '" & B.USR & "'","USR") AS Mbr_List
FROM (SELECT Table1.USR FROM Table1 GROUP BY Table1.USR) AS B;
(NOTE: the query is built as such to avoid doing Group By on the Memo field, which is known to be truncating the Memo field)
Still, after doing this, my MBR_LIST field in the table shows only 320 chars (?).
I even tried adding an empty row, as suggested in this post: MS Access Create Table is Truncating Memo Field, but with no success - the field still gets truncated to 320 chars:
INSERT INTO Table2 ( [USR], MBR_LIST )
SELECT B.USR, "" as Mbr_List
FROM [Table2] as B Where (False)
UNION ALL
SELECT B.USR, ConcatRelated("MBR","Table1","Table1.USR = '" & B.USR & "'","USR") AS Mbr_List
FROM (SELECT Table1.USR FROM Table1 GROUP BY Table1.USR) AS B;
As a last resort, using the MID() I've created 20 "chunks" of 300 chars each (Mbr_1 to Mbr_20) in my SELECT statement, and I got them all back fine. Then I wrapped this into another SELECT with those chunks concatenated (Mbr_1 & Mbr_2 & ... & Mbr_20) AS Mbr_LIST, and got - 320 chars!
Any ideas why and how to insert/display the full string created by the function?
Thanks,
tribe

There is a workaround I created years ago using DLookup in a helper function.
Not the fastest, of course, but if anything else fails ...
Access Query truncates Memo Field
To insert a long text, use VBA when you have retrieved the values for USR and MBR_LIST:
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset("Select [USR], MBR_LIST From Table2")
rs.AddNew
rs!USR.Value = USR
rs!MBR_LIST.Value = MBR_LIST
rs.Update
rs.Close
Set rs = Nothing

Related

MS Access Between dates query is not working

I'm an Access DB beginner. I have a database with a SearchForm where the user can enter search criteria, click the Search button and populate the subform with the filtered results.
The query has simple query based on what the user enters in fields in the search form
Like "*" & [Forms]![SampleSearchF]![txtMicrochip] & "*" which work well, but my date filter does not produce any results:
Between [Forms]![SampleSearchF]![DateReceivFrom] And [Forms]![SampleSearchF]![DateReceivTo]
The table fields that the date search is based on are Data Type:Date/Time , Format: Short Date
The Search Form fields are Format Short Date
The subform fields are also Short Date
SearchButton is a requery macro
And when I have the this query criteria in the query, none of the search functions work.
Any suggestions where I could look to solve the issue? Any help is appreciated.
Here is my SQL code for the search query,
FROM IndividualsT INNER JOIN SamplesT ON IndividualsT.AnimalID = SamplesT.AnimalID
WHERE (((IndividualsT.SpeciesName) Like "*" & [Forms]![SampleSearchF]![txtSpeciesName] & "*") AND
((IndividualsT.Microchip) Like "*" & [Forms]![SampleSearchF]![txtMicrochip] & "*") AND
((IndividualsT.Name) Like "*" & [Forms]![SampleSearchF]![txtName] & "*") AND
((SamplesT.Location) Like "*" & [Forms]![SampleSearchF]![txtLocation] & "*") AND
((SamplesT.SampleReceived) Between [Forms]![SampleSearchF]![DateReceivFrom] And [Forms]![SampleSearchF]![DateReceivTo]));
SamplesT
SampleID
AnimalID
SampleReceived
Location
CollectionDate
1
1
18/08/2021
Tassie
10/08/2021
7
1
15/09/2021
Berlin
25/09/2021
13
12
25/09/2021
Sydney
4/09/2021
14
12
24/09/2021
New York
1/09/2021
IndividualsT
AnimalID
SpeciesName
Microchip
Name
1
Parrot
1234
Hugo
12
Koala
853
Sherlock
Likely, your issue is the WHERE logic when form fields are empty. When empty, LIKE expressions return as ** which means anything, so no rows are filtered out. However, empty dates conflict with BETWEEN clause. Consider using NZ to return the column itself if form fields are empty:
(
SamplesT.SampleReceived
BETWEEN NZ([Forms]![SampleSearchF]![DateReceivFrom], SamplesT.SampleReceived)
AND NZ([Forms]![SampleSearchF]![DateReceivTo], SamplesT.SampleReceived)
);
Always specify the data type of the parameters:
Parameters
[Forms]![SampleSearchF]![txtSpeciesName] Text ( 255 ),
[Forms]![SampleSearchF]![txtMicrochip] Text ( 255 ),
[Forms]![SampleSearchF]![txtName] Text ( 255 ),
[Forms]![SampleSearchF]![txtLocation] Text ( 255 ),
[Forms]![SampleSearchF]![DateReceivFrom] DateTime,
[Forms]![SampleSearchF]![DateReceivTo] DateTime;
Select *
From YourTable
Where ...

Concatenating field data into string via Access SQL

Imagine the following table
ID | Name
----------
1 | Shaun
1 | Terrence
2 | Jessica
I need to concatenate the string data in Name based on ID
ID | Name
-----------
1 | Shaun, Terrence
2 | Jessica
I am using an access database. I was thinking I could do a pivot transform and try to concatenate those fields but the problem is its hard to dynamically loop through the total field count. Any ideas?
**Edit: Order does not matter, I just want to concatenate based on ID with , and space being the delimiter. I am calling this sql code using an ADO connection from excel vba.
This is a similar problem I had recently trying to pivot a two column table; MS Access convert and summarise two rows into columns with unique names
'Name' is a reserved word and 'ID' is usually a auto-index with unique numbers so I changed your columns to UserID and UserName respectively.
There are some problems with creating the answer in a single subquery so I ended up doing this:
Create a temporary table with an index:
SELECT t1.UserID, t1.UserName,
(SELECT COUNT(*) + 1
FROM Table1 t2
WHERE t1.UserID = t2.UserID and t2.UserName < t1.UserName) AS [Index]
INTO Table1_indexed
FROM Table1 AS t1;
create a temporary cross tab table:
TRANSFORM First(Table1_indexed.UserName) AS FirstOfUserName
SELECT Table1_indexed.UserID FROM Table1_indexed
GROUP BY Table1_indexed.UserID
PIVOT Table1_indexed.Index;
concatenate the name fields
SELECT Table1_crosstab.UserID, Table1_crosstab.[1], Table1_crosstab.[2],
IIf([1] Is Not Null,[1]) & IIf([2] Is Not Null,", " & [2]) AS ConcatenatedName
FROM Table1_crosstab;
If you have more than two name fields you could adjust the concatenate query to the maximum number you expect.
It might be possible to merge these steps into a single query but I've not yet found a way.
You can create a Visual Basic Function and call it from your query, e.g. something like this (assuming your table is called Names):
Public Function ListOfNames(id as Integer) As String
Dim rs As Recordset
Set rs = CurrentDb.OpenRecordset("Select Name from Names where ID=" & id, DbOpenSnapshot)
ListOfNames = ""
If Not (rs.EOF and rs.BOF) Then
rs.MoveFirst
Do Until rs.EOF = True
If (Len(ListOfNames) > 0) Then
ListOfNames = ListOfNames & “, “
End If
ListOfNames = ListOfNames & rs!Name
rs.MoveNext
Loop
End If
rs.Close
End Function
Then you can for instance call the function from your query:
SELECT ID, ListOfNames([ID]) as Name From Names Group By ID

Problems with field names and appending files in Access SQL

Okay, so I have nearly 200 tables in an Access database. The tables are of plant species abundance data, and I would like to combine them into a master data file. Each table contains basically the same columns of species; however, many are spelled slightly differently.
When I run an SQL query in MS Access it won't let me append the tables with each other because of the field names being spelled just a little different.
Any thoughts that would help?
The query I am running is an append query:
INSERT INTO masterTable SELECT * FROM siteTable
and, as an example, the differences in field names are pretty minor
(e.g. "Spp.A" vs "SppA" or "SpeciesOne" vs "Species1")
Thanks for any help,
Paul
You'll need to use vba for this, you'll also need to change the column names I'm using in the masterTable, which in my example are just column1, column2 & column3, and to set the maximum column index in a couple of places (I've stuck some comments in, so you can see what needs to be changed).
If you dont usually use vba, Create a form with a button, and a click event for the button & put this code in it, then open the form and click the button.
Dim db As Database
Dim tdf As TableDef
Dim ii As Long
dim sql as String
Set db = CurrentDb()
docmd.setwarnings false
For Each tdf In db.TableDefs
'change column list as required:
sql = "INSERT INTO masterTable (Column1, Column2, Column3) SELECT "
'change 2 to maximum column number - 1:
for ii = 0 to 2
sql = sql & tdf.Fields(ii).Name
'change 2 to maximum column number - 1 again:
if ii < 2 then
sql = sql & ","
end if
next
sql = sql & ")"
docmd.runsql sql
Next
docmd.setwarnings true
This should work I think. (I'm hoping there's no syntax errors, as I havent tested it, but the logic isnt exactly rocket science)
Hope this helps

how to replace text in a multifield value column in access

I've got a tablea such as below, I know its bad design having multifield value column but I'm really looking for a hack right now.
Student | Age | Classes
--------|------|----------
foo | 23 | classone, classtwo, classthree, classfour
bar | 24 | classtwo, classfive, classeight
When I run a simple select query as below, I want the results such a way that even occurrence of classtwo is displayed as class2
select student, classes from tablea;
I tried the replace() function but it doesnt work on multivalued fields >_<
You are in a tough situation and I can't think of a SQL solution for you. I think your best option would be to write a VB function that will take the string of data, parse it out (replacing) the returning you the updated string that you can update your data with.
I can cook up quite a few ways to solve this.
You can explode the mv by using Classes.Value in your query. This will cause one row to appear for each value in the query and thus you now can use replace on that. However, this will result in one separate row for each class.
So use this:
Select student, classes.Value from tablea
Or, for this example:
Select student, replace(classes.Value,"classtwo","class2") as myclass
from tablea
If you want one line, AND ALSO the multi value classes are NOT from another table (else they will be returning ID not text), then then you can use the following trick
Select student, dlookup("Classes","tablea","id = " & [id]) as sclasses
from tablea
The above will return the classes separated by a space as a string if you use dlookup(). So just add replace to the above SQL. I suppose if you want, you could also do replace on the space back to a "," for display.
Last but not least, if this those classes are coming from another table, then the dlookup() idea above will not work. So just simply create a VBA function.
You query becomes:
Select student, strClass([id]) as sclasses from tablea
And in a standard code module you create a public function like this:
Public Function strClass(id As Variant) As String
Dim rst As DAO.Recordset
If IsNull(id) = False Then
Set rst = CurrentDb.OpenRecordset("select Classes.Value from tableA where id = " & id)
Do While rst.EOF = False
If strClass <> "" Then strClass = strClass & ","
strClass = strClass & Replace(rst(0), "classtwo", "class2")
rst.MoveNext
Loop
rst.Close
Set rst = Nothing
End If
End Function
Also, if you sending this out to a report, then you can DUMP ALL of the above ideas, and simply bind the above to a text box on the report and put the ONE replace command around that in the text box control. It is quite likely you going to send this out to a report, but you did ask how to do this in a query, and it might be the wrong question since you can "fix" this issue in the report writer and not modify the data at the query level. I also think the replace() command used in the report writer would likely perform the best. However, the above query can be exported, so it really depends on the final goal here.
So lots of easy ways to do this.

Query creating an identifier for each packet

Sorry the title is not very descriptive but it is a tricky problem to word.
I have some data, about 200 or more rows of it, and each row has a PacketID, so several rows belong in the same packet. What I need to do, is convert all the PacketIDs from (Example - BDFD-2) to just a number (Example - 1) so all the entries with a packet identifier x need to have a packet identifier of say 3. Is there an SQL query that can do this? Or do I just have to go through manually.
You asked about a query. I wrote a quick VBA procedure instead just because it was so easy. But I'm unsure whether it is appropriate for your situation.
I created tblPackets with a numeric column for new_PacketID. I hoped that will make it clearer to see what's going on. If you truly need to replace PacketID with the new number, you can alter the procedure to store CStr(lngPacketID) to that text field. So this is the sample data I started with:
PacketID new_PacketID packet_data
BDFD-2 a
R2D2-22 aa
BDFD-2 b
R2D2-22 bb
EMC2-0 aaa
EMC2-0 bbb
And this is the table after running the procedure.
PacketID new_PacketID packet_data
BDFD-2 1 a
R2D2-22 3 aa
BDFD-2 1 b
R2D2-22 3 bb
EMC2-0 2 aaa
EMC2-0 2 bbb
And the code ...
Public Sub RenumberPacketIDs()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim lngPacketID As Long
Dim strLastPacketID As String
Dim strSql As String
strSql = "SELECT PacketID, new_PacketID" & vbCrLf & _
"FROM tblPackets" & vbCrLf & _
"ORDER BY PacketID;"
Set db = CurrentDb
Set rs = db.OpenRecordset(strSql)
With rs
Do While Not .EOF
If !PacketID <> strLastPacketID Then
lngPacketID = lngPacketID + 1
strLastPacketID = !PacketID
End If
.Edit
!new_PacketID = lngPacketID
.Update
.MoveNext
Loop
.Close
End With
Set rs = Nothing
Set db = Nothing
End Sub
I think an approach like that could be fine for a one-time conversion. However if this is an operation you need to perform repeatedly, it could be more complicated ... especially if you need each PacketID replaced with the same number from one run to the next ... eg. BDFD-2 was replaced by 1 the first time, so must be replaced by 1 every time you run the procedure.
If you only have a few packet IDs, you can just use update:
UPDATE table_name
SET PacketID =
(
CASE PacketID
WHEN 'BDFD-2' THEN 3
WHEN 'ABCD-1' THEN 5
ELSE 2
END
)
The ELSE is optional.
I am not sure why you even want to convert the packet ids to a number, they seem perfectly fine as they are. You could create a table of packets as follows
SELECT DISTINCT TableOfRows.Packet_id AS PacketId INTO Packets FROM TableOfRows;
You can then use this to select the packet you are interested in and display the corresponding rows