Query of concatenation on Access [duplicate] - ms-access

Any help that can be provided to a Access and VB noob would be greatly appreciated. What I'm trying to do is concatenate the values from one table and insert it as a comma delimited value into a field in another table. I'm trying to take all the server names that are say Linux boxes and concatenate them into a different field.
Table A looks like this
Machine Name | Zone | Operating System
----------------------------------------
Server01 Zone A Linux
Server02 Zone B Linux
Server03 Zone A Windows
Server04 Zone C Windows
Server05 Zone B Solaris
Table B has the field I want to insert into: Affected_Machine_Names.
Now, I've tried looking through the Concatenate/Coalesce posts, but the SQL view in Access doesn't like the Declare statements. My VB skills suck badly and I can't seem to get the code to work in VB for Applications. Unfortunately, I can't get this database converted into our SQL farm cause I don't have a server available at the moment to host it.
Can anyone point me in the right direction?

You can use Concatenate values from related records by Allen Browne for this. Copy the function code from that web page and paste it into a new standard module. Save the module and give the module a name different from the function name; modConcatRelated would work.
Then I think you should be able to use the function in a query even though you're not proficient with VBA.
First notice I changed the field names in TableA to replace spaces with underscores. With that change, this query ...
SELECT
sub.Operating_System,
ConcatRelated("Machine_Name", "TableA",
"Operating_System = '" & sub.Operating_System & "'") AS Machines
FROM [SELECT DISTINCT Operating_System FROM TableA]. AS sub;
... produces this result set:
Operating_System Machines
Linux Server01, Server02
Solaris Server05
Windows Server03, Server04
If you can't rename the fields as I did, use a separate query to select the distinct operating systems.
SELECT DISTINCT TableA.[Operating System]
FROM TableA;
Save that as qryDistinctOperatingSystems, then use it in this version of the main query:
SELECT
sub.[Operating System],
ConcatRelated("[Machine Name]", "TableA",
"[Operating System] = '" & sub.[Operating System] & "'") AS Machines
FROM qryDistinctOperatingSystems AS sub;

This is a fairly basic VBA function that will loop through every row in a column, and concatenate it to a comma-delimited result string. i.e., for your example, it will return "Server01, Server02, Server03, Server04, Server05". (Don't forget to replace the column and table names)
Function ConcatColumn(OS As String) As String
Dim rst As DAO.Recordset
Set rst = CurrentDb.OpenRecordset("Select * from TableA")
Dim result As String
'For every row after the first, add a comma and the field value:
While rst.EOF = False
If rst.Fields("Operating System") = OS Then _
result = result & ", " & rst.Fields("MyValue")
rst.MoveNext
Wend
'Clean it up a little and put out the result
If Left(result, 2) = ", " Then result = Right(result, Len(result) - 2)
Debug.Print result
ConcatColumn = result
End Function
To use this,
1. ConcatColumn("Windows") will return "Server04, Server03"
2. ConcatColumn("Linux") will return "Server01, Server02"
3. ConcatColumn("Solaris") will return "Server05"
4. ConcatColumn("") will return "".

Related

method to update table based on function output

I'm using an access function that loops through multiple record sets using case select statements and displays rows of strings in the VBA immediate window.
Can anyone suggest how I can learn about methods that might be used to update a table with the results that are currently displayed in the VBA immediate window?
So far, my searches have suggested that DoCmd.SQL might work.
Case Is = "1" ' 1 bottle
Debug.Print rsWCol!r4_wcol_outstring & rsBottl!bottleoutstring
displays the string below in the immediate window:
R41602T50 1 00 62 710 C 1120 9800 550 1 00S #135 0
I'd like to be able to use something like the following
Dim writeRecSQL As String ' used to append records to a temp table
writeRecSQL = "INSERT INTO tbl_R_export_TEMP ( R_str ) select rsWCol!r4_wcol_outstring & rsBottl!bottleoutstring;"
Case Is = "1" ' 1 bottle
' Debug.Print rsWCol!r4_wcol_outstring & rsBottl!bottleoutstring
DoCmd.RunSQL writeRecSQL
The select part of my SQL statement does not seem to be getting values.
I understand that, normally, it might be something like select fieldX from tablex
And that my statement is more like: select rs!foo
An 'Enter Parameter Value' message box is raised asking for the value of r4_wcol_outstring
(Following the SELECT in my SQL with a FROM raises VB "Run-time error '3134' syntax error in INSERT INTO statement.")
I could use some advice or an example of how write an SQL statement that will my record set parameter values.
Good place to start is Microsoft's site:
https://msdn.microsoft.com/en-us/library/bb208861(v=office.12).aspx
But if you just want to insert values from a record you just retrieved into another table
try wrapping fields in escaped double quotes and concetanting the values outside the hardcoded string.
As in:
writeRecSQL = "INSERT INTO tbl_R_export_TEMP ( [R_str] ) VALUES (""" & rsWCol!r4_wcol_outstring & rsBottl!bottleoutstring & """);"
Besides Docmd.RunSQL, you can also look at CurrentDB.Execute and predefined parameter queries using the Querydefs object

When querying a MySql DB VBScript is returning asian characters instead of English

I am using VBScript to query a MySQL database that contains only English characters. The query is basically: SELECT * FROM table name PROCEDURE ANALYSE(1,1)
When I run the query directly on the DB it returns the expected results. However, when the query is run through VBScript it returns gibberish (Chinese?). I know for a fact the DB only contains English as I am the one who built it. I've run numerous other queries against the same table and haven't had any problems. Its only when I run the PROCEDURE ANALYSE query that it returns something unexpected.
The VBScript code is as follows:
strSQLcommand = "SELECT * FROM " & strTempTableName & " PROCEDURE ANALYSE(1,1)"
otRecordset.Open strSQLcommand,Connection
If Not otRecordset.EOF Then
otRecordset.movefirst
Do While NOT otRecordset.EOF
wscript.echo otRecordset(0).value
wscript.echo otRecordset(1).value
otRecordset.Movenext
Loop
End If
I've never had a problem with returning values from any other table in this DB. I've run this query numerous times and always get the same results which has me perplexed.
Any thoughts or ideas are greatly appreciated!
Ok, so, it turns out it has nothing to do with the DB per se.
I decided to start checking the data types that were being returned using the VBScript VarTYpe() method. The fields that were returning "gibberish/Chinese" had a data type of 8092. Basically, a byte array. Sprinkling a little Google fairy dust led me to this function:
Function C8209toStr(body8209)
If VarType(body8209) = 8209 Then
Dim i
ReDim aOut(UBound(body8209))
For i = 1 to UBound(body8209) + 1
aOut(i-1) = chr(ascb(midb(body8209,i,1)))
Next
C8209toStr = Join(aOut, "")
End If
End Function
Hope that helps whoever else comes along!

Using IN statement in parameterized crosstab query in Access

I have a crosstab query which queries a bunch of locations and gets their measurement readings. I pivot on the measurement readings so I get a table which has all the measurements for a location/date combo on each line. This works fine for getting all the data. It also works fine for filtering on one value per field. i.e. WHERE LocationID = ? AND MeasureID = ? but what I really need is to have something like WHERE LocationID IN (?) AND MeasureID IN (?) where ? is an array (or whatever gets to job done. Is this possible?
On my forms I'm using a DAO.QueryDef object to build my recordsets. I'd like to avoid building the entire query string in VBA if possible, mostly because this particular query is pretty long and I'd rather it live in a view and not a code module. With that said I can build it all in VBA but it's just not the desired solution.
You can always use replace.
sSQL = "SELECT lots of sql WHERE LocationID IN (qqlocidqq)"
sSQLWithLoc = Replace (sSQL, "qqlocidqq", "1,2,3,4")
Dim qdf As QueryDef
'A query that exists
Set qdf= CurrentDB.QueryDefs("MyJunkQuery")
'Permanently change the sql of that query
qdf.SQL = sSQLWithLoc
Looking into this a little further, it may suit you to use Instr, like so:
SELECT Table1.LocationID
FROM Table1
WHERE InStr([#List],[LocationID])>0
Tested like so:
PARAMETERS Number_List Text(50);
TRANSFORM Count(Table1.AKey) AS CountOfAKey
SELECT Table1.AText
FROM Table1
WHERE InStr([Number_List],[ANumber])>0
GROUP BY Table1.AText
PIVOT Table1.ANumber;
Where Table1 consists of fields AKey, AText, and ANumber. Number_List is a comma separated list of numbers supplied by a parameter. Instr checks for the existence of ANumber from Table1 in the supplied parameter.
There is a problem with overlap 1,2,12, but a creative use of commas may suit:
WHERE InStr("," & [Number_List] & "," , "," & [ANumber] & ",")>0
Of course the delimiter does not have to be a comma, | is often useful.

Efficient Update SQL, updating multiple rows with one SQL statement, Avoiding Loops

I'm trying to avoid database access in loops within a project I am working on. Not being too good with SQL, I'm not sure of the best way to approach this.
I'm updating a stock level database in a sale procedure with multiple stock locations/pick locations.
Therefore, this is what I am doing.
Looping through Product IDs, then looping through the pick locations for each product and updating quantities as it goes, like:
For Each wProductId In calculatedProds.Keys '' loop through products requested passing values of pick locations
For i = 0 To locationCount ' split the location value from the string as per above
Dim thisLocation As Integer = locationID
Dim thisQty As Integer = qtyPicked
Dim sql As String = "UPDATE `stockLevels` SET `stockLevel`=`stockLevel` - '" & thisQty & "' WHERE `stockLocation`='" & thisLocation & "' AND `id`='" & wProductId & "'"
' DO DATA ACCESS WITH SQL ABOVE
Next
Next
Of course this works, but it is opening a new Database Connection for every stock location, for every item.
So how would I work this into a single Update Statement?
http://www.karlrixon.co.uk/writing/update-multiple-rows-with-different-values-and-a-single-sql-query/
That link gets me very close to what I am after, I think, but I am not 100% sure how to dynamically build that SQL statement and how to add two conditions to the CASE.
I need to build a SQL Statement something like:
UPDATE stockLevels
SET stockLevel= CASE id
WHEN '"& wProductId &"' AND stockLocation='"& thisLocation &"' THEN `stockLevel` - '" & thisQty & "'
WHEN '"& NEXTwProductId &"' AND stockLocation='"& NEXTthisLocation &"' THEN `stockLevel` - '" & NEXTthisQty & "'
END
But that's not correct where I am adding the second parameter to the CASE!
I am using MySQL and VB.NET, as usual, any help much appreciated.
Using a case statement in the set clause in your example isn't ideal for a number of reasons.
There is no where clause to help the database execute the query efficiently
The size of the query for large numbers of updates becomes excessive (consider updating 1000 rows like this)
You are manually implementing a join - the database can almost certainly do this more efficiently than you.
Debugging such a query is also difficult.
Instead, you should first measure the performance of the update one at a time approach to see if you actually need to make improvements.
If performance improvements are required, then I would suggest an approach where the updates are first bulk inserted into a temporary table. A suitable table would have the following columns:
wProductID, stockLocation, newStockLevel
The updates can be bulk inserted using the following MySQL syntax:
INSERT INTO temp_stock_updates
(wProductID, stockLocation, newStockLevel)
VALUES
(?,?,?), (?,?,?), (?,?,?), ...
And then a single update is run to update the main table. This query would look something like this:
UPDATE stockLevels s
JOIN temp_stock_updates u USING (wProductID, stockLocation)
SET
s.stockLevel = u.newStockLevel
Your CASE expression is simply incorrect syntactically.
There are two kinds of CASE expressions, almost identical to each other and yet slightly different in syntax.
One has the form of
CASE expr
WHEN value1 THEN result1
WHEN value2 THEN result2
...
ELSE result_else
END
The other looks like this:
CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
...
ELSE result_else
END
And you were essentially attempting to mix these two kinds of CASE.
You probably just need to use the second one (also called search CASE, if I am not much mistaken):
...
CASE
WHEN id = '"& wProductId &"' AND stockLocation='"& thisLocation &"' THEN ...
WHEN id = '"& NEXTwProductId &"' AND stockLocation='"& NEXTthisLocation &"' THEN ...
...
Note that if there's no match and the CASE has no ELSE part, the result will be NULL, so make sure you've covered all the cases, otherwise use an ELSE part like this:
ELSE `stocklevel`
I.e. the CASE will evaluate to the original value of the column being updated, rendering no update for it in the end.

Concatenate fields from one column in one table into a single, comma delimited value in another table

Any help that can be provided to a Access and VB noob would be greatly appreciated. What I'm trying to do is concatenate the values from one table and insert it as a comma delimited value into a field in another table. I'm trying to take all the server names that are say Linux boxes and concatenate them into a different field.
Table A looks like this
Machine Name | Zone | Operating System
----------------------------------------
Server01 Zone A Linux
Server02 Zone B Linux
Server03 Zone A Windows
Server04 Zone C Windows
Server05 Zone B Solaris
Table B has the field I want to insert into: Affected_Machine_Names.
Now, I've tried looking through the Concatenate/Coalesce posts, but the SQL view in Access doesn't like the Declare statements. My VB skills suck badly and I can't seem to get the code to work in VB for Applications. Unfortunately, I can't get this database converted into our SQL farm cause I don't have a server available at the moment to host it.
Can anyone point me in the right direction?
You can use Concatenate values from related records by Allen Browne for this. Copy the function code from that web page and paste it into a new standard module. Save the module and give the module a name different from the function name; modConcatRelated would work.
Then I think you should be able to use the function in a query even though you're not proficient with VBA.
First notice I changed the field names in TableA to replace spaces with underscores. With that change, this query ...
SELECT
sub.Operating_System,
ConcatRelated("Machine_Name", "TableA",
"Operating_System = '" & sub.Operating_System & "'") AS Machines
FROM [SELECT DISTINCT Operating_System FROM TableA]. AS sub;
... produces this result set:
Operating_System Machines
Linux Server01, Server02
Solaris Server05
Windows Server03, Server04
If you can't rename the fields as I did, use a separate query to select the distinct operating systems.
SELECT DISTINCT TableA.[Operating System]
FROM TableA;
Save that as qryDistinctOperatingSystems, then use it in this version of the main query:
SELECT
sub.[Operating System],
ConcatRelated("[Machine Name]", "TableA",
"[Operating System] = '" & sub.[Operating System] & "'") AS Machines
FROM qryDistinctOperatingSystems AS sub;
This is a fairly basic VBA function that will loop through every row in a column, and concatenate it to a comma-delimited result string. i.e., for your example, it will return "Server01, Server02, Server03, Server04, Server05". (Don't forget to replace the column and table names)
Function ConcatColumn(OS As String) As String
Dim rst As DAO.Recordset
Set rst = CurrentDb.OpenRecordset("Select * from TableA")
Dim result As String
'For every row after the first, add a comma and the field value:
While rst.EOF = False
If rst.Fields("Operating System") = OS Then _
result = result & ", " & rst.Fields("MyValue")
rst.MoveNext
Wend
'Clean it up a little and put out the result
If Left(result, 2) = ", " Then result = Right(result, Len(result) - 2)
Debug.Print result
ConcatColumn = result
End Function
To use this,
1. ConcatColumn("Windows") will return "Server04, Server03"
2. ConcatColumn("Linux") will return "Server01, Server02"
3. ConcatColumn("Solaris") will return "Server05"
4. ConcatColumn("") will return "".