2017-08-26 56 views
1

我很新,所以我想知道是否有人可以帮我满足以下要求。我试图创建一个查询来提取表值将它们组合成一个独特的标识符(ID)分组在同一行。将多行转换为具有唯一键的列

我看过试图使用交叉表查询,但他们似乎更多的执行计算和使用PIVOT功能,但它也似乎不符合我的要求。

任何帮助真的很感激。请看下面的数据例子。

我的数据格式如下

ID EMAIL     COMMENTS 
1  [email protected]  <TEXT> 
1  [email protected]  <TEXT> 
2  [email protected]  <TEXT> 
2  [email protected]  <TEXT> 
2  [email protected]  <TEXT> 

所需的输出

ID COL1    COL2 COL3    COL4 COLN     COLN+1 
1 [email protected] <TEXT> [email protected] <TEXT> 
2 [email protected] <TEXT> [email protected] <TEXT> [email protected]  <TEXT> 
+0

数据中N是否有限制? Access具有255列的内在限制。 –

+0

您的原始数据是否有其他可用的唯一密钥?我意识到ID不是唯一的,但是电子邮件地址和注释的来源是否有另一个唯一的密钥? –

+0

不会/不应该达到255.从查看当前源代码可以看出的最高值是155.我可以用数据创建新表并添加一个新的自动生成的密钥,以为每行提供一个唯一的标识符。这会有帮助吗? –

回答

1

室内今天等待了飓风,所以我想我会创建这个定制的解决方案。所有这些步骤的答案都可以在其他地方找到,但是通过所有设计的解决方案进行排序并不容易,所以我希望这个答案更有用。

将行更改为列的基本答案是here。但与此问题的数据不同,该答案的样本数据已经正确排序。只要列值[ID]和[电子邮件]形成唯一对并且具有NO NULL值,则可以使用聚合子查询或对Access聚合函数的调用生成正确的顺序。为了减少查询的总数,我继续在同一个查询中生成转换后的列名。 (如果值不是唯一的或者具有空值,则排序将关闭,最终结果将丢失一些数据。)

这里的第二个挑战是有两列需要转换,但是访问SQL Transform语句(即交叉表查询)每个查询只允许一个变换列。创建两个单独的查询然后加入它们相当简单,但是由于交叉表查询会生成一个动态(即未确定)数量的列,因此如果不手动地按顺序选择每列,就不可能交织电子邮件和评论列。此外,明确指定查询中的哪些列会破坏交叉表查询的动态方面,并且会留下额外的列,或者如果减少了整个列数,它将生成错误。

更新:只是张贴原液(现在标签方案二)后,我意识到,我可以真的要落后一步解决列交错的问题...... 首先生成更多的行 --one行对于每个电子邮件地址和每个评论的单独行 - 在最终转换之前将它们再次放置在同一行上。

解决方案1 ​​

保存下列查询并将其命名为[测序。为了便于在最终输出正确的列排序,我用这个词,而不是“评”“备注”,因为它排序“电子邮件”后:

SELECT Data.ID, Data.Email, Data.Comments, 
    1 + DCount("[ID]","Data","[ID]=" & [ID] & " and [Email]<'" & Replace([Email],"'","''") & "'") AS SeqNum, 
    Format([SeqNum],"000") & ' Email' AS EmailColumn, 
    Format([SeqNum],"000") & ' Remark' AS CommentsColumn 
FROM Data 
ORDER BY Data.ID, Data.Email; 

保存下列查询并将其命名为[向后]:

SELECT ID, EmailColumn AS ColumnName, Email AS [Value] 
FROM Sequenced 
UNION SELECT ID, CommentsColumn AS ColumnName, Comments AS [Value] 
FROM Sequenced 
ORDER BY [ID], [ColumnName]; 

保存以下查询并将其命名为[InterleavedCrosstab]:

TRANSFORM First(Backwards.Value) AS FirstOfValue 
SELECT Backwards.ID 
FROM Backwards 
GROUP BY Backwards.ID 
ORDER BY Backwards.ID, Backwards.ColumnName 
PIVOT Backwards.ColumnName; 

解决方案2

保存下列查询并将其命名为[Sequenced2]:

SELECT Data.ID, Data.Email, Data.Comments, 
    1 + DCount("[ID]","Data","[ID]=" & [ID] & " and [Email]<'" & Replace([Email],"'","''") & "'") AS SeqNum, 
    'Email' & Format([SeqNum],"000") AS EmailColumn, 
    'Comments' & Format([SeqNum],"000") AS CommentsColumn 
FROM Data 
ORDER BY Data.ID, Data.Email; 

保存下列查询并将其命名为[EmailCrosstab]:

TRANSFORM First(Sequenced2.Email) AS FirstOfEmail 
SELECT Sequenced2.ID 
FROM Sequenced2 
GROUP BY Sequenced2.ID 
ORDER BY Sequenced2.ID 
PIVOT Sequenced2.EmailColumn; 

保存下列查询并将其命名为[CommentsCrosstab] :

TRANSFORM First(Sequenced2.Comments) AS FirstOfComments 
SELECT Sequenced2.ID 
FROM Sequenced2 
GROUP BY Sequenced2.ID 
ORDER BY Sequenced2.ID 
PIVOT Sequenced2.CommentsColumn; 

最后,最一般的结果查询将返回所有列,但他们不会被交错和会有重复的[ID]列:

SELECT EmailCrosstab.*, 
     CommentsCrosstab.* 
FROM CommentsCrosstab INNER JOIN EmailCrosstab 
    ON CommentsCrosstab.ID = EmailCrosstab.ID; 

这里的美化版本,但仅拥有高达3个电子邮件和评论栏目:

SELECT EmailCrosstab.ID, 
    EmailCrosstab.Email001,CommentsCrosstab.Comments001, 
    EmailCrosstab.Email002,CommentsCrosstab.Comments002, 
    EmailCrosstab.Email003,CommentsCrosstab.Comments003 
FROM CommentsCrosstab INNER JOIN EmailCrosstab 
    ON CommentsCrosstab.ID = EmailCrosstab.ID; 

解决方案3

我已经键入了以下VBA过程时,我意识到仅查询的解决方案是相当容易的,所以这是一个额外的选择。

Public Sub CustomTransform() 
    '* This code assumes that the field values 
    '* [ID] and [Email] constitute a unique pair 
    '* and that there are NO NULL values. 

    Dim i As Integer, MaxIDRows As Integer 
    Dim IDSeq As Integer 
    Dim lastID As Long 
    Dim IDstring As String 
    Dim tbl As TableDef 
    Dim idx As Index 
    Dim db As Database 
    Dim rsSrc As Recordset2, rsResult As Recordset2 
    Const resultTable As String = "Customer Crosstab" 

    Set db = CurrentDb 

    MaxIDRows = db.OpenRecordset(_ 
     "SELECT Max(Counter.Rows) AS MaxRows" & _ 
     " FROM (SELECT Count(Data.[ID]) AS [Rows]" & _ 
     " FROM Data GROUP BY Data.[ID]) AS Counter" _ 
    ).Fields(0).Value 

    '* Column count <= 254 : ID + N * (Email + Comment columns) 
    If MaxIDRows = 0 Then 
    MsgBox "No data.", vbOKOnly Or vbExclamation, "No Data" 
    Exit Sub 
    ElseIf MaxIDRows >= 252/2 Then 
    MsgBox "Maximum number of columns exceeded.", _ 
     vbOKOnly Or vbExclamation, "Maximum Columns Exceeded" 
    Exit Sub 
    End If 

    On Error Resume Next 
    db.TableDefs.Delete resultTable 
    Err.Clear 
    On Error GoTo 0 

    Set tbl = db.CreateTableDef(resultTable) 
    With tbl 
    ' Create fields and append them to the new TableDef 
    ' object. This must be done before appending the 
    ' TableDef object to the TableDefs collection of the 
    ' Northwind database. 
    .Fields.Append .CreateField("ID", dbLong) 

    For i = 1 To MaxIDRows 
     IDstring = Format(i, "000") 
     .Fields.Append .CreateField("Email" & IDstring, dbText, 255) 
     .Fields.Append .CreateField("Comments" & IDstring, dbText, 255) 
    Next 

    Set idx = .CreateIndex("Primary Key") 
    idx.Fields.Append idx.CreateField("ID") 
    idx.Primary = True 
    .Indexes.Append idx 
    End With 
    db.TableDefs.Append tbl 

    Set rsResult = db.OpenRecordset(resultTable, dbOpenTable) 
    Set rsSrc = db.OpenRecordset(_ 
     "SELECT ID, Email, Comments" & _ 
     " FROM Data" & _ 
     " ORDER BY ID, Email") 

    lastID = -1 
    Do Until rsSrc.EOF 
    If rsSrc!id <> lastID Then 
     If lastID <> -1 Then 
     rsResult.Update 
     End If 

     IDSeq = 0 
     rsResult.AddNew 
     rsResult!id = rsSrc!id 
    End If 
    lastID = rsSrc!id 

    IDSeq = IDSeq + 1 
    IDstring = Format(IDSeq, "000") 

    rsResult.Fields("Email" & IDstring) = rsSrc!email 
    rsResult.Fields("Comments" & IDstring) = rsSrc!Comments 

    rsSrc.MoveNext 
    Loop 
    rsSrc.Close 

    If rsResult.EditMode <> dbEditNone Then 
    rsResult.Update 
    End If 
    rsResult.Close 

    Debug.Print "CustomTransform Done" 
End Sub 
相关问题