室内今天等待了飓风,所以我想我会创建这个定制的解决方案。所有这些步骤的答案都可以在其他地方找到,但是通过所有设计的解决方案进行排序并不容易,所以我希望这个答案更有用。
将行更改为列的基本答案是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
数据中N是否有限制? Access具有255列的内在限制。 –
您的原始数据是否有其他可用的唯一密钥?我意识到ID不是唯一的,但是电子邮件地址和注释的来源是否有另一个唯一的密钥? –
不会/不应该达到255.从查看当前源代码可以看出的最高值是155.我可以用数据创建新表并添加一个新的自动生成的密钥,以为每行提供一个唯一的标识符。这会有帮助吗? –