“Flatten” multiple rows containing different data but with a common reference into a single row

大憨熊 提交于 2021-02-07 17:30:24

问题


I've tried searching Stackoverflow and Google for an answer to this but haven't found it yet. I think part of my problem is I'm not sure what the keyword(s) for what I'm trying to do would be.

My data looks something like this:

ID    Var1    Var2    Name
01    0001    0002    Bill
01    0001    0002    Jim
01    0001    0002    Sally
02    0003    0004    Sam
02    0003    0004    Kyle

You'll see that I have multiple rows with the same ID and same Var1 and Var2 but each row has a unique name. I want to "flatten" the rows so there is only a single row for each ID and each row has as many "Name" columns as are necessary to fit all of the data.

Like this:

ID    Var1    Var2    Name1    Name2    Name3
01    0001    0002    Bill     Jim      Sally
02    0003    0004    Sam      Kyle

Does anyone know how to do this or what it's called?

Thanks!

Update based on comments: My data source is .csv file and I'm trying to manipulate it with Excel. Excel macros or VBA solutions would be best. Unfortunately my SQL is very elementary so learning to apply an SQL solution would be time-prohibitive.


回答1:


Something like this is very quick using variant arrays and a dictionary object

The code dumps the output from A1:Dx to F1

Update: fixed name numerals

enter image description here

Sub ReCut()
Dim X
Dim Y
Dim C

Dim lngRow As Long
Dim lngCol As Collection
Dim lngCnt1 As Long
Dim lngCnt As Long
Dim objDic As Object

Set objDic = CreateObject("scripting.dictionary")
X = Range([a1], Cells(Rows.Count, "C").End(xlUp)).Value2
Y = X

ReDim Y(1 To UBound(Y), 1 To 100)

For lngCnt1 = 1 To (UBound(Y, 2) - 3)
Y(1, lngCnt1) = "Name" & lngCnt1
Next

For lngRow = 1 To UBound(X, 1)
    If objDic.exists(X(lngRow, 1) & X(lngRow, 2) & X(lngRow, 3)) Then
'find first blank entry in relevant array row
        C = Split(Join(Application.Index(Y, lngCnt), "| "), "|")
        Y(lngCnt, Application.Match(" ", C, 0)) = X(lngRow, 4)
    Else
        lngCnt = lngCnt + 1
        Y(lngCnt, 1) = X(lngRow, 1)
        Y(lngCnt, 2) = X(lngRow, 2)
        Y(lngCnt, 3) = X(lngRow, 3)
        Y(lngCnt, 4) = X(lngRow, 4)
        objDic.Add X(lngRow, 1) & X(lngRow, 2) & X(lngRow, 3), lngCnt
    End If
Next

[f1].Resize(UBound(Y, 1), UBound(Y, 2)) = Y

End Sub



回答2:


If you are going for a SQL request, you might take a look at triggers. First, you need to loop through your data.

CREATE TRIGGER tg AFTER INSERT ON `myTable`
FOR EACH ROW
BEGIN
insert into myTable(MyNewName) values (Name);
END
;

Also you need to work with Alter Table in order to add a new column

ALTER TABLE myTable
ADD MyNewName VARCHAR

http://www.w3schools.com/sql/sql_alter.asp

And to make a comparison if Var1 already exists you do an IF-condition

IF previousrecord = nextrecord 
BEGIN
     ALTER TABLE myTable
     ADD MyNewName VARCHAR
END

http://msdn.microsoft.com/fr-fr/library/ms182717.aspx

Now combine all of them with a trigger:

CREATE TRIGGER tg AFTER INSERT ON `myTable`
FOR EACH ROW
BEGIN
      IF previousrecord = currentrecord // where at beginning previousrecord=firstrecord
      BEGIN
           ALTER TABLE myTable
           ADD MyNewName VARCHAR // MyNewName you can create a variable that increases
                                 // smth like $added=0 then increase it
           insert into myTable(MyNewName) values (Name);
      END

END
;

It is a supposition when you read the data from your input that it is sorted by ID. It's maybe not the final solution, but it is supposed to help you out.




回答3:


Formula-only requires quite a lot of explanation but is merely a sequence of operations that individually are quite familiar to many:

  1. Select your four columns (say A:D).
  2. Data > Outline – Subtotal with At each change in: ID, Use function: Count, Add subtotal to: check all, check Replace current subtotals and Summary below data and OK.
  3. Filter A:E and for ColumnA choose Text Filters, Contains C, OK.
  4. Enter, in B5 =B4 and copy across to D5
  5. In F5:

    =IF(COLUMN()<6+$E5,OFFSET($E5,COLUMN()-6-$E5,),"")

  6. Copy F5 across to say Z5 (further to the right if necessary).
  7. Copy B5:D5 and F5:Z5 down to last grouped row.
  8. Unfilter (select all).
  9. Copy whole sheet and paste over the top with Paste Special, Values.
  10. Remove All in Subtotal.
  11. Filter ColumnA to select Grand Count and (Blanks) only.
  12. Delete rows numbered in blue.
  13. Delete ColumnA.
  14. Drag D1 to E1 and append 1.
  15. Drag E1 across to the right as far as required.
  16. Delete ColumnD.


来源:https://stackoverflow.com/questions/17748781/flatten-multiple-rows-containing-different-data-but-with-a-common-reference-in

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!