Database access excel formatting or if statement or grouping - database

I have a database formatting problem in which I am trying to concatenate column "B" rows based on column "A" rows. Like So:
https://docs.google.com/spreadsheet/ccc?key=0Am8J-Fv99YModE5Va3hLSFdnU0RibmQwNVFNelJCWHc
Sorry I couldn't post a picture. I don't have enough reputation points YET. I'LL Get them eventually though
So I'd like to solve this problem within Excel or Access. Its currently an access database, but I can export it to excel easily. As you can see, I want to find "userid" in column A and where there are multiple column A's such as "shawn" I'd like to combine the multiple instances of shawn and concatenate property num as such.
Even though there are multiple instances of column A still, I could just filter all unique instances of the table later. My concern is how to concatenate column B with a "|" in the middle if column A has multiple instances.
This is just a segment of my data (There is a lot more), so I would be very thankful for your help.
The pseudo code in my head so far is:
If( Column A has more than one instance)
Then Concatenate(Column B with "#"+ "|" +"#")
I'm also wondering if there is a way to do this on access with grouping.
Well Anyways, PLEASE HELP.

In excel we can achieve it easily by custom function in vba module. Hopefully using vba(Macros) is not an issue for you.
Here is the code for the function which can be added in vba. (Press Alt+F11, this will take you to visual editor, right click the project and add a module. Add the below code in module)
Public Function ConcatenatePipe(ByVal lst As Range, ByVal values As Range, ByVal name As Range) As String
ConcatenatePipe = ""
Dim i As Integer
For i = 1 To lst.Count
If name.Value = lst.Item(i).Value Then ConcatenatePipe = ConcatenatePipe & "|" & values.Item(i).Value
Next
ConcatenatePipe = Mid(ConcatenatePipe, 2)
End Function
This function you can use in excel in F Column of your example. Copy the below formulla in F2 and the copy paste the cell to rest of F column. =ConcatenatePipe($A$2:$A$20,$B$2:$B$20,E2)

I believe you can solve this with an SQL GROUP BY function. At least, here's how I'd do it in MySQL or similar:
SELECT userid, GROUP_CONCAT(propertynum SEPARATOR '|') FROM Names GROUP BY userid
as described in this stack overflow post: How to use GROUP BY to concatenate strings in MySQL?
Here's a link on how to use SQL in MS Access: http://www.igetit.net/newsletters/Y03_10/SQLInAccess.aspx
Unfortunately there is not a GROUP_CONCAT function in MSAccess, but this other SO post explains some ways round that: is there a group_concat function in ms-access?

Related

How to reference another cell as a wildcard from a separate worksheet, within the same workbook, when using SUMIF function?

I am trying to use the contents of a cell from a different worksheet as part of the wildcard criteria within my SUMIF function elsewhere. Here is what I have that is returning the correct value:
=SUMIF('PASTE SAP DATA HERE'!H:H,'PASTE SAP DATA HERE'!L:L,D23&"*")
I would like to use the same value within D23 but from a separate worksheet where it should be stored:
=SUMIF('PASTE SAP DATA HERE'!H:H,'PASTE SAP DATA HERE'!L:L,'March SAP Core Data'!A18&"*")
The second attempt currently returns the value 0. Any help would be greatly appreciated!
"Look at the data, make sure there are no spaces or other non visible characters that might be throwing off the matches. If the matches are numbers or dates, make sure that both are true numbers or dates and not text that look like numbers or dates. The formula looks like it should work. – Scott Craner"
-From comments

nested if statment with so many text conditions ERROR

this is my data table
I'm writing this formula in openoffice not excel, that's why you will see ";" instead of ","
my questions is that I'm trying to put the currency of each country's capital name, and I did it but the thing is that I'm unable to make more than 42 conditions!!!!!
Is there another way or another formula can I use???
Here is the formula I did, and it's working
=IF(D3="AMSTERDAM";"EUR";IF(D3="FRANKFURT";"EUR";IF(D3="OSLO";"NOK";IF(D3="COPENHAGEN";"MULTI";IF(D3="ALICANTE";"EUR";IF(D3="BARCELONA";"EUR";IF(D3="BERLIN TXL";"EUR";IF(D3="VILNIUS";"EUR";IF(D3="BRUSSELS";"EUR";IF(D3="CATANIA";"EUR";IF(D3="DUSSELDORF";"EUR";IF(D3="FARO";"EUR";IF(D3="GRAN CANARIA";"EUR";IF(D3="HELSINKI";"EUR";IF(D3="MALAGA";"EUR";IF(D3="MUNICH";"EUR";IF(D3="PARIS CDG";"EUR";IF(D3="RIGA";"EUR";IF(D3="SANTA CRUZ PALMA";"EUR";IF(D3="SEVILLA";"EUR";IF(D3="TENERIFE";"EUR";IF(D3="BUDAPEST";"HUF";IF(D3="ANTALYA";"TRY";IF(D3="GAZIPASA";"TRY";IF(D3="ISTANBUL";"TRY";IF(D3="BERGEN";"NOK";IF(D3="STAVANGER";"NOK";IF(D3="STAVANGER VIA ESBJERG";"NOK";IF(D3="LONDON CITY";"GBP";IF(D3="LONDON LHR";"GBP";IF(D3="LONDON STN";"GBP";IF(D3="MANCHESTER";"GBP";IF(D3="FUERTEVENTURA";"ISK";IF(D3="LANZAROTE";"ISK";IF(D3="PORTO SANTO";"ISK";IF(D3="GLASGOW";"SCP";IF(D3="GDANSK";"PLN";IF(D3="CLUJ­NAPOCA";"RON";IF(D3="STOCKHOLM";"SEK";IF(D3="PRAGUE";"CZK";""))))))))))))))))))))))))))))))))))))))))
I'd suggest you use a table in another section of your spreadsheet then use VLOOKUP to match the currency to your country.
=VLOOKUP(D3;Currency_Table;2;FALSE}
Which is lookup D3 in the table named Currency_Table and return the exact match (from FALSE) in the second column which will give you your currency.
Or if you want the formula to exist without dependency upon another table you could use something like:
=VLOOKUP(D3;{"AMSTERDAM"\,"EUR";"FRANKFURT"\,"EUR";"OSLO"\,"NOK"; etc...};2;FALSE}
NB: I've added an escape \ before the comma because I'm assuming you are from a language area that uses , as a decimal by your language settings I'm assuming you'll need that in your array for that to work.

MS Excel: "MATCH()" does not find cells containing text if lookup array is too large

I am creating a large and complicated schedule, and I want one view which shows the schedule as a day-time grid, and another which allows one to look up a speaker by name from an alphabetical list. I have posted a simplified example here:
In the alphabetical list, the day and time should be populated by a function using MATCH. Just as an example, I manually typed what I would like to have happen for Jones.
I cannot get MATCH() to locate the speaker's name in the timetable correctly. There are no hidden characters: notice that in cell D15, Excel correctly recognizes that G2 and C7 are identical.
Here is what happens if I put various code in H2:
=MATCH(G2,$A$1:$D$9) results in #N/A
=MATCH(G2,$C$2:$C$9) results in #N/A
=MATCH(G2,$B$7:$D$7) results in 2 (correctly!)
=MATCH(G2,$A$7:$D$7) results in #N/A
What I would like is to put =MATCH(G2,$A$1:$D$9) into H2 and then fill cells down to H25, and have Excel indicate the column number of the day in which the adjacent name appears, then use INDIRECT or something to convert this number into the day of the week.
It may be that including column A in the search array causes problems because of the different data types. As an experiment, I made the first column into TEXT, and in this case =MATCH(G2,$A$7:$D$7) incorrectly returns 1!
And even so, I cannot understand why $B$7:$D$7 works but neither $C$2:$C$9 nor $B$7:$D$8 will.
Any workarounds or alternative strategies would be greatly appreciated, thanks.
To do this you need to add in some other logic to find the correct column and row. This AGGREGATE() Function does the job.
For Day use:
=INDEX($A$1:$D$1,AGGREGATE(15,6,COLUMN($A$2:$D$9)/(($A$2:$D$9=G2)),1))
For Hour:
=INDEX($A$1:$A$9,AGGREGATE(15,6,ROW($B$1:$D$9)/(($B$1:$D$9=G2)),1))
The AGGREGATE() Function was introduced in Excel 2010.
For other Versions:
Pre 2010, they will need to be Array Formulas:
Day:
=INDEX($A$1:$D$1,MIN(IF($A$2:$D$9=G2,COLUMN($A$2:$D$9))))
Hour:
=INDEX($A$1:$A$9,MIN(IF($B$1:$D$9=G2,ROW($B$1:$D$9))))
Being an Array Formula it must be confirmed with Ctrl-Shift-Enter when exiting Edit mode. When done correctly Excel will automatically put {} around the formula to denote an array formula.
Newest Office 360 or online:
Day:
=INDEX($A$1:$D$1,MINIFS(COLUMN($A$2:$D$9),$A$2:$D$9,G2))
Hour:
=INDEX($A$1:$A$9,MINIFS(ROW($B$1:$D$9),$B$1:$D$9,G2))
As to the reason MATCH will not work in this case:
MATCH() only works with a single row or column and not a multiple column/row range. It is set up to return a number equal to the order place found and therefore must be a 1 dimensional array.
The most efficient way to do this given your dataset is to use three MATCH queries - one for each column.
For the Day, that looks like this:
=IF(ISERROR(MATCH(G2,$B$2:$B$10,0)),"",$B$1)&IF(ISERROR(MATCH(G2,$C$2:$C$10,0)),"",$C$1)&IF(ISERROR(MATCH(G2,$D$2:$D$10,0)),"",$D$1)
For the Time, that looks like this:
=INDEX($A$2:$A$10,IFERROR(MATCH(G2,$B$2:$B$10,0),0) + IFERROR(MATCH(G2,$C$2:$C$10,0),0) + IFERROR(MATCH(G2,$D$2:$D$10,0),0))
...but truth be told, on small datasets such as this one, you won't notice any performance difference on this approach vs Scott's AGGREGATE approach. On large datasets (thousands of rows) you probably will.
Note that another reason your initial approach failed is that you did not specify the 3rd argument of MATCH, and so Excel used the default value that assumes your list data is sorted alphabetically. You almost never want to omit that argument, and you almost always want to use FALSE (or Zero, which means FALSE to Excel)
Alternative solution with vba & listobjects (you need to give the two tables the names as appear in the code below)
sheet screenshot
Public Sub makeAppointmentList()
Dim aSheet As Worksheet
Set aSheet = ThisWorkbook.Worksheets("sheet1")
Dim aSchedule As ListObject
Set aSchedule = aSheet.ListObjects("schedule")
Dim anAppointmentList As ListObject
Set anAppointmentList = aSheet.ListObjects("appointmentList")
On Error Resume Next
anAppointmentList.DataBodyRange.Delete
On Error GoTo 0
Dim c As ListColumn
Dim r As ListRow
Dim newRow As ListRow
For Each c In aSchedule.ListColumns
For Each r In aSchedule.ListRows
If c.Index > 1 And Intersect(c.Range, r.Range) <> "" Then
Set newRow = anAppointmentList.ListRows.Add
Intersect(newRow.Range, anAppointmentList.ListColumns("Name").Range).Value = Intersect(c.Range, r.Range)
Intersect(newRow.Range, anAppointmentList.ListColumns("Day").Range).Value = Intersect(c.Range, aSchedule.HeaderRowRange)
Intersect(newRow.Range, anAppointmentList.ListColumns("Time").Range).Value = Intersect(aSchedule.ListColumns(1).Range, r.Range)
End If
Next r
Next c
anAppointmentList.Sort.SortFields.Clear
anAppointmentList.Sort.SortFields.Add Key:=Intersect(anAppointmentList.HeaderRowRange, _
anAppointmentList.ListColumns("Name").Range)
anAppointmentList.Sort.SortFields.Add Key:=Intersect(anAppointmentList.HeaderRowRange, _
anAppointmentList.ListColumns("Day").Range), _
CustomOrder:="Mon,Tue,Wed,Thu,Fri,Sat,Sun"
anAppointmentList.Sort.SortFields.Add Key:=Intersect(anAppointmentList.HeaderRowRange, _
anAppointmentList.ListColumns("Time").Range)
anAppointmentList.Sort.Apply
Dim s As SortField
End Sub

VB.Net - Excel application get showing values of an range

I'm trying get an Excel Range and copy into an array of objects with Vb.Net.
This is not a problem. I use the following code:
Dim vValues(,) As Object = ExcelApp.Range(vRange).Value
And works fine; but I have a the following case:
In the column "C"; the value has a specific format and internally has another value.
My question is:
Somebody know the way to get the information exact as the user see?
I'm trying to get the information without use a For ... Each or some kind of cycle.
I'm also tried to avoid use "text to columns" function.
Both seems right solutions, but would impact the performance with a lot of data.
FYI: Also I can get the information through the ODBC connection; but I'm searching the solution using a Range
Exactly what the user sees is the Text property. But you cannot have an array of that, you will have to query each cell individually.
You are getting a Double value in your array instead of a DateTime value because you have "Time" formatting applied in Excel. If you had a format from the "Date" category, Excel would instead send a proper Variant/Date, not a Double that represents it.
Another option would be constructing the DateTime objects on the .NET side, provided you know in which columns they should be.

Copy data from lookup column with multiple values to new record Access 2007

I am copying a record from one table to another in Access 2007. I iterate through each field in the current record and copy that value to the new table. It works fine until I get to my lookup column field that allows multiple values. The name of the lookup column is "Favorite Sports" and the user can select multiple values from a dropdown list.
I believe the values of a multivalued field are stored in an array but I cannot access the values in VBA code! I've tried myRecordset.Fields("myFieldName").Value(index) but it didn't work. I don't understand how Access stores multiple values in one field.
I saw something about ItemsSelected on another forum but I don't know what Object is associated with that method.
Thanks for any help!
I would recommend against using multivalue fields for precisely the reason you're running into, because it's extremely complex to refer to the data stored in this simple-to-use UI element (and it's for UI that it's made available, even though it's created in the table design).
From your mention of "ItemsSelected," you seem to be assuming that you access the data in a multivalue field the same way you would in a multiselect listbox on a form. This is not correct. Instead, you have to work with it via a DAO recordset. The documentation for working with multivalue fiels explains how to do it in code, something like this:
Dim rsMyField As DAO.Recordset
Set rsMyField = Me.Recordset("MyField").Value
rsChild.MoveFirst
Do Until rsChild.EOF
Debug.Print rsChild!Value.Value
rsChild.MoveNext
Loop
rsChild.Close
Set rsChild = Nothing
Now, given that you can usually access the properties of a recordset object through its default collections, you'd expect that Me.Recordset("MyField").Value would be returning a recordset object that is navigable through the default collection of a recordset, which is the fields collection. You'd think you could do this:
Me.Recordset("MyField").Value!Value.Value
This should work because the recordset returned is a one-column recordset with the column name "Value" and you'd be asking for the value of that column.
There are two problems with this:
it doesn't actually work. This means that Me.Recordset("MyField").Value is not reallly a full-fledged recordset object the way, say, CurrentDB.OpenRecordset("MyTable") would be. This is demonstrable by trying to return the Recordcount of this recordset:
Me.Recordset("MyField").Value.Recordcount
That causes an error, so that means that what's being returned is not really a standard recordset object.
even if it did work, you'd have no way to navigate the collection of records -- all you'd ever be able to get would be the data from the first selected value in your multivalued field. This is because there is no way in this shortcut one-line form to navigate to a particular record in any recordset that you're referring to in that fashion. A recordset is not like a listbox where you can access both rows and columns, with .ItemData(0).Column(1), which would return the 2nd column of the first row of the listbox.
So, the only way to do this is via navigating the child DAO recordset, as in the code sample above (modelled on that in the cited MSDN article).
Now, you could easily write a wrapper function to deal with this. Something like this seems to work:
Public Function ReturnMVByIndex(ctl As Control, intIndex As Integer) As Variant
Dim rsValues As DAO.Recordset
Dim lngCount As Long
Dim intRecord As Integer
Set rsValues = ctl.Parent.Recordset(ctl.ControlSource).Value
rsValues.MoveLast
lngCount = rsValues.RecordCount
If intIndex > lngCount - 1 Then
MsgBox "The requested index exceeds the number of selected values."
GoTo exitRoutine
End If
rsValues.MoveFirst
Do Until rsValues.EOF
If intRecord = intIndex Then
ReturnMVByIndex = rsValues(0).Value
Exit Do
End If
intRecord = intRecord + 1
rsValues.MoveNext
Loop
exitRoutine:
rsValues.Close
Set rsValues = Nothing
Exit Function
End Function
Using that model, you could also write code to concatenate the values into a list, or return the count of values (so you could call that first in order to avoid the error message when your index exceeded the number of values).
As cool as all of this is, and as nice as the UI that's presented happens to be (it would be really nice if they'd added selection checkboxes as a type for a multiselect listbox), I'd still recommend against using it precisely because it's so much trouble to work with. This just takes the problem of the standard lookup field (see The Evils of Lookup Fields in Tables) and makes things even worse. Requiring DAO code to get values out of these fields is a pretty severe hurdle to overcome with a UI element that is supposed to make things easier for power users, seems to me.
For a quick and dirty way of getting the values out of a multivalued ('complex data') column, you can use an ADO Connection with the Jet OLEDB:Support Complex Data connection property set to False e.g. the connection string should look something like this:
Provider=Microsoft.ACE.OLEDB.12.0;
Data Source=C:\dbs\TestANSI92.accdb;
Jet OLEDB:Engine Type=6;
Jet OLEDB:Support Complex Data=False
The multivaled type column will now be of type MEMO (adLongVarWChar) with each value separated by a semicolon ; character.
But that's only half the problem. How to get data into a multivalued column?
The Access Team seem to have neglected to enhance the Access Database Engine SQL syntax to accommodate multivalued types. The 'semicolon delimiter' trick doesn't work in reverse e.g.
INSERT INTO TestComplexData (ID, weekday_names_multivalued)
VALUES (5, 'Tue;Thu;Sat');
fails with the error, "Cannot perform this operation", ditto when trying to update via ADO recordset :(

Resources