Split delimited field into Different rows - sql

So each day I'm given an Excel worksheet with orders, they look something like this:
Date Vendor OrderID/Quantity Total
12/28/2013 Nike 1111111-8;2222222-12 20
12/29/2013 Adidas 3333333-5;4444444-10 15
12/30/2013 Wrangler 5555555-3 3
It's usable to most people I work with but not to me, as I want to identify each OrderID separate from the quantity. The "-" between the 7 digit number is to separate the ID from how many units are associated to it. But essentially when I import this table into access I want to create another table that splits these values.
Date Vendor OrderID Quantity
12/28/2013 Nike 1111111 8
12/28/2013 Nike 2222222 12
12/29/2013 Adidas 3333333 5
12/29/2013 Adidas 4444444 10
12/30/2013 Wrangler 5555555 3
This is much more useful to me but has been a daunting task to produce with two delimiters("-" and ";"). I am ok with VBA but I am struggling to find a solution to my conflict. So how would I go about doing this?

The most straightforward way I can think of is the VBA Split function. Please note that I set up the tblStaging staging table with all the fields as Text type for the import from Excel, but I set the tblOrders table up with (what I assume are) the correct types: Date as Date, Vendor as Text, OrderID as Number and Quantity as Number. See the comments in the code for details.
Public Sub SplitOrders()
Dim rsStaging As Recordset
Dim rsOrder As Recordset
Dim arrOrders() As String
Dim arrOrderDetails() As String
'Rename these for whatever your tables are called'
Set rsStaging = CurrentDb.OpenRecordset("tblStaging")
Set rsOrder = CurrentDb.OpenRecordset("tblOrders")
rsStaging.MoveFirst
While Not rsStaging.EOF
'Split into an array of Orders'
arrOrders = Split(rsStaging.Fields("OrderID/Quantity"), ";")
For i = 0 To UBound(arrOrders)
'Split the OrderID and Quantity for each Order'
arrOrderDetails = Split(arrOrders(i), "-")
'Create the new record in tblOrders'
With rsOrder
.AddNew
!Date = CDate(rsStaging!Date)
!Vendor = rsStaging!Vendor
!OrderID = CLng(arrOrderDetails(0)) 'If the OrderID can contain letters, symbols or leading zeros, omit the CLng( ... ) call'
!Quantity = CLng(arrOrderDetails(1))
.Update
End With
Next
rsStaging.MoveNext
Wend
End Sub

I'd look to break this down into different pieces rather than trying to parse the data all at once. For example, step 1 might be to import the file to a staging table (that looks like the first data example from your question). Step 2 would be to query the table to detect any rows that contain ';' (perhaps using the InStr function or a wildcard search like '*;*'. Take these records and parse them into two or more records. Third, identify any records that do not contain ';' and parse those into single records. All of the resulting (clean) records can go into your destination table for further analysis.

Related

How to map two column using another column data

I have Five columns.
E.g.
Column1: Name
Column2: surname
Column3: mapping
Column4: Mapped data
Columns contain data like
Name Surname Mapping Name1 Surname1
1 ABC 1 AAAA 3 ABC QQQQ
2 XYZ 2 XXXX 1 XYZ AAAA
3 OPQ 3 QQQQ 4 OPQ RRRR
4 RST 4 RRRR 2 RST XXXX
Now my aim is to map name column to surname by using mapping column and result should be stored at Name1 and Surname1 column. I have more data in Name and Surname column, by writing number in Mapping column it will automatically map the surname to Name (the choice is given to user for entering number in mapped column then map the data accordingly) and result should be copied in Name1 and Surname1.
I am not getting any idea to achieve this using VBA. coding Plz help me.....
Amar, there are certainly plenty of ways to go about this using Excel's built in functions, however, since you asked about a VBA solution, here you go:
Function Map(n)
Map = Cells(n + 1, 2)
End Function
Placing the above code into the VBA editor of your project will allow you to use this custom function in the same way you would any of Excel's builtin functions. That is, entering =Map(C3) into any cell should give you the result you're after (where C3 is the cell containing your mapping number). The function works by returning the data in [row n (defined in your mapping column) +1 (to account for the header row); column 2 (the column containing your surname)]. The data in column "Name1" will always be the same as that in column "Name" (so it seems). So the function in your "Name1" column would simply be =A2
If this does not solve your problem, or you need further guidance, please let me know.
Supplement
#Amar, the comment by #freakfeuer is spot on. VBA is really overkill for something as simple as this and, as he points out, portability and security are both significant drawbacks. Offset is a fine alternative.

Spitting long column values to managable size for presenting data neatly

Hi I was wondering if there is a way to split long column values in this case I am using SSRS to get the distinct values with the number of product ID against a category into a matrix/pivot table in SSRS. The problem lies with the amount of distinct category makes it a nightmare to make the report look pretty shall we say. Is there a dynamic way to split the columns in say groups of 10 to make the table look nicer and easy to read. I was thinking of using in operator then the list of values but that means managing the data every time a new category gets added. Is there a dynamic way to present the data in the best way possible? There are 135 distinct category values
Also I am open to suggestions to make the report to nicer if anyone has any thoughts. I am new to SSRS and trying to get to grips with its.
Here is an example of my problem
enter image description here
Are your column names coming back from the database under the SubCat field you note in the comments above? If so I imagine your dataset looks something like this
Subcat | Logno
---------+---------------
SubCatA | 34
SubCatB | 65
SubCatC | 120
SubCatD | 8
SubCatE | 19
You can edit this so that there is an index of each individual category being returned also, using the Row_Number() function. Add the field
ROW_NUMBER() OVER (ORDER BY SubCat ASC) AS ColID
To your query. This will result in the following.
Subcat | LogNo | ColID
-----------+--------------+----------
SubCatA | 34 | 1
SubCatB | 65 | 2
SubCatC | 120 | 3
SubCatD | 8 | 4
SubCatE | 19 | 5
Now there is a numeric identifier for each column you can perform some logic on it to arrange itself nicely on the page.
This solution involves a Tablix, nested inside a Matrix nested inside a Matrix as follows
First create a Matrix (Matrix1), and set it’s datasource to your dataset. Set the Row Group Properties to group on the following expression where ‘4’ is the number of columns you wish to display horizontally.
=CInt(Floor((Fields!ColID.Value - 1) / 4))
Then in the data section of the Matrix (bottom right corner) insert a rectangle and on this insert a new Matrix (Matrix 2). Remove the leftmost row. Set the column header to be the Column Name SubCat. This will automatically set the column grouping to be SubCat.
Finally, in the Data Section of Matrix 2 add a new Rectangle and Add a Tablix on it. Remove the Header Row, and set it to be one column wide only. Set the Data to be the information you wish to display, i.e. LogNo.
Finally, delete the Leftmost and Topmost rows/columns from Matrix 1 to make it look tidier (Note Delete Column Row only! Not associated groups!)
Then when the report is run it should look similar to the following. Note in my example SubCat = ColName, and LogNo = NumItems, and I have multiple values per SubCat.
Hopefully you find this helpful. If not, please ask for clarification.
Can you do something like this:
The following gives the steps (in two columns, down then across)

Excel [VBA] find duplicate data and delete the oldest one

I have a problem in MS Excel. I´ve a spreadsheet with data like this:
Name | timestamp
------------------------
Smith | 12.05.2015
Smith | 01.01.2015
Smith | 10.05.2015
Simpson | 14.04.2015
Simpson | 10.02.2015
Simpson | 21.03.2015
Simpson | 02.01.2015
The data I´ve is much bigger and komplex and there are duplicates with different timestamps. Now I want to delete the oldes ones and want an output like this:
Name | timestamp
Smith | 12.05.2015
Simpson | 14.04.2015
I know how to remove duplicates, but in this case it´s a little bit different. I hope you can help me to solve the problem.
You may not need VBA.
In my experience the Excel Remove Duplicates code works to remove the first encountered duplicates in a list.
So sort your data by Name ascending and timestamp descending, then remove the duplicates from the Name field only.
You should be left with the most recent names.
I did a bit of testing, and Range.RemoveDuplicates appears to keep the first entry for each duplicate value (at least in a sorted range, which you're going to use). Here's my solution:
Sub SortAndCondense()
'This subroutine sorts a table by name and secondarily by descending date. It then removes
'all duplicates in the name column. By sorting the dates in descending order, only the most
'recent entries for each name are preserved
Dim wrkSht As Worksheet
Set wrkSht = ThisWorkbook.Worksheets("Sheet1")
Dim dateTable As Range
Dim header1 As Range, header2 As Range
Set dateTable = wrkSht.Range("A2:B7") 'insert your actual table range; modify as necessary for column headers
Set header1 = wrkSht.Range("A2")
Set header2 = wrkSht.Range("B2")
'sort the column primarily by name, and secondarily by descending date. The order in which the names are sorted
'is irrelevant.
dateTable.Sort Key1:=header1, Key2:=header2, Order2:=xlDescending
'remove all duplicate names. The way remove duplicates works (so far as I can tell) is that it keeps only the
'topmost entry for each duplicate column.
dateTable.RemoveDuplicates 1
End Sub

Split delimited entries into new rows in Access

So someone gave me a spreadsheet of orders, the unique value of each order is the PO, the person that gave me the spreadsheet is lazy and decided for orders with multiple PO's but the same information they'd just separate them by a "/". So for instance my table looks like this
PO Vendor State
123456/234567 Bob KY
345678 Joe GA
123432/123456 Sue CA
234234 Mike CA
What I hoped to do as separate the PO using the "/" symbol as a delimiter so it looks like this.
PO Vendor State
123456 Bob KY
234567 Bob KY
345678 Joe GA
123432 Sue CA
123456 Sue CA
234234 Mike CA
Now I have been brainstorming a few ways to go about this. Ultimately I want this data in Access. The data in its original format is in Excel. What I wanted to do is write a vba function in Access that I could use in conjunction with a SQL statement to separate the values. I am struggling at the moment though as I am not sure where to start.
About about this:
1) Import the source data into a new Access table called SourceData.
2) Create a new query, go straight into SQL View and add the following code:
SELECT * INTO ImportedData
FROM (
SELECT PO, Vendor, State
FROM SourceData
WHERE InStr(PO, '/') = 0
UNION ALL
SELECT Left(PO, InStr(PO, '/') - 1), Vendor, State
FROM SourceData
WHERE InStr(PO, '/') > 0
UNION ALL
SELECT Mid(PO, InStr(PO, '/') + 1), Vendor, State
FROM SourceData
WHERE InStr(PO, '/') > 0) AS CleanedUp;
This is a 'make table' query in Access jargon (albeit with a nested union query); for an 'append' query instead, alter the top two lines to be
INSERT INTO ImportedData
SELECT * FROM (
(The rest doesn't change.) The difference is that re-running a make table query will clear whatever was already in the destination table, whereas an append query adds to any existing data.
3) Run the query.
If I had to do it I would
Import the raw data into a table named [TempTable].
Copy [TempTable] to a new table named [ActualTable] using the "Structure Only" option.
Then, in a VBA routine I would
Open two DAO recordsets, rstIn for [TempTable] and rstOut for [ActualTable]
Loop through the rstIn recordset.
Use the VBA Split() function to split the [PO] values on "/" into an array.
For Each array item I would use rstOut.AddNew to write a record into [ActualTable]
In Excel, try this:
IF(ISERROR(FIND("/",A1,1))=TRUE,A1, Left(A1, 6))
In the cell next to it, put something like this:
IF(ISERROR(FIND("/",A1,1))=TRUE,"", Right(A1, 6))
This will break out your PO's into 2 columns. From there, write a loop that creates a new record where appropriate.

Create Access report that lists information from one table, which is matched on ID from another table

I'm updating this as the previous answer turned out to not be what I really needed.
I have two tables in my Access database. One is called Billings, one is called Bookings. Each table has a column called Booking Number, which is how they are related. The Billings table also has a field called Container. What I want to do is:
Create a one page report for each Booking Number (which can be expanded to multiple pages depending on the number of containers)
In the body of the report I would like to list each Container (along with other information about each container) on separate rows
Here are my tables:
Bookings
----------------
ID | Booking Number
1 | '1234'
2 | '1235'
Billings
----------------
ID | Booking Number | Container
1 | '1234' | '12'
2 | '1234' | '16'
3 | '1235' | '18'
Based on these two tables, there should be a report for Booking #1234, and Booking #1235. Booking #1234's report should list Container's 12 and 16, while Booking #1235's report would only list 18. I'm a PHP/MySQL developer, so I understand SQL queries. However, the way I would write the query for MySQL obviously does not work for Access. At the moment I am using a Module similar to the answer below, but it does not do what I need it to do. This is my current query:
SELECT
b.*,
ListContainers(b.[Booking Number]) AS Containers
FROM
Bookings AS b
WHERE
((b.[Booking Number]) Is Not Null);
This will create a comma separated list of the containers associated with each Booking Number, but I want to create a separate row for each Container, which will also include other information from the Billings table.
Has anyone had any experience with a similar situation, or know any steps I could take to accomplish what I'm looking to do?
For your new requirements you'll want to use a "subreport" to list the containers (and related information). For details, see my recent answer to a similar question here.
[This is my answer to the original question, before the "spec" changed.]
Create a new Module in Access and paste in the following code
Public Function ListContainers(Booking_Number As String) As String
Dim cdb As DAO.Database, rst As DAO.Recordset, rtn As String
Const Separator = ", "
Set cdb = CurrentDb
rtn = ""
Set rst = cdb.OpenRecordset("SELECT Container FROM Billings WHERE [Booking Number]=""" & Booking_Number & """ ORDER BY Container", dbOpenSnapshot)
Do While Not rst.EOF
rtn = rtn & rst!Container & Separator
rst.MoveNext
Loop
rst.Close
Set rst = Nothing
Set cdb = Nothing
If Len(rtn) > 0 Then
rtn = Left(rtn, Len(rtn) - Len(Separator)) '' trim trailing separator
End If
ListContainers = rtn
End Function
You can then use that Function in a query, like this
SELECT [Booking Number], ListContainers([Booking Number]) AS Containers
FROM Billings
That will return
Booking Number Containers
-------------- ----------
1234 12, 16
1235 18