将行转换为列

时间:2009-02-13 21:30:43

标签: sql ms-access

在Access中执行此操作的最佳方法是什么?

Create table tmp
(
  plant int,
  material vchar(20),
  workcenter int,
  setuptime vchar(20)
)

insert into tmp values( 1, mat1, 2, 30)
insert into tmp values( 1, mat1, 3, 30)
insert into tmp values( 1, mat2, 3, 30)
insert into tmp values( 1, mat2, 4, 30)
insert into tmp values( 2, mat1, 4, 30)
insert into tmp values( 2, mat1, 5, 30)

结果需要看起来像。

Plant  Material  Workcenter1  Setuptime1  Workcenter2  Setuptime2
1      Mat1      2            30          3            30
1      Mat2      3            30          4            30
2      Mat1      4            30          5            30

我当时认为这可行,但必须有更好的方法。

SELECT t.Plant, 
    t.Material, 
    t.Workcenter as Workcenter1, 
    t.setuptime as SetupTime1
    t2.Workcenter as Workcenter2, 
    t2.setuptime as SetupTime2
FROM tmp t
LEFT JOIN tmp t2
  on t.plant = t2.plant
  and t.material = t2.material

非常感谢。

5 个答案:

答案 0 :(得分:1)

您是否尝试过Crosstab查询?

修改

(重新格式化你的问题后,我的格式更好)

你的解决方案几乎可以工作。但是你得到的将是双重条目(如下所示):

Plant  Material  Workcenter1  Setuptime1  Workcenter2  Setuptime2
1      Mat1      2            30          2            30
1      Mat1      2            30          3            30
1      Mat1      3            30          2            30
1      Mat1      3            30          3            30
1      Mat2      3            30          4            30
1      Mat2      3            30          3            30
1      Mat2      4            30          3            30
1      Mat2      4            30          4            30
2      Mat1      4            30          5            30
2      Mat1      4            30          4            30
2      Mat1      5            30          5            30
2      Mat1      5            30          4            30

如果你有两个以上的工作中心,那就更糟了。

所以你想要的是一个列指示器

Create table tmp
(
  plant int,
  material vchar(20),
  workcenter int,
  setuptime vchar(20),
  myCol int
);

(顺便说一句,vchars将无法在ms-access / Jet中使用...但是字符将会出现)

insert into tmp values( 1, mat1, 2, 30, 1);
insert into tmp values( 1, mat1, 3, 30, 2);
insert into tmp values( 1, mat2, 2, 30, 1);
insert into tmp values( 1, mat2, 3, 30, 2);
insert into tmp values( 2, mat1, 4, 30, 1);
insert into tmp values( 2, mat1, 5, 30, 2);

SELECT tmp.plant, tmp.material, tmp.workcenter,   tmp.setuptime,
                                tmp_1.workcenter, tmp_1.setuptime
FROM tmp INNER JOIN tmp AS tmp_1 ON (tmp.material = tmp_1.material)
    AND (tmp.plant = tmp_1.plant)
WHERE   (((tmp.myCol)=1)
    AND ((tmp_1.myCol)=2));

当然,如果您可以编辑原始表,也许您只想添加第二个Workcenter和Setuptime列,

答案 1 :(得分:1)

您的样本输出不明确 - 我认为它看起来像:

Plant | Mat  | Wkcntr1 | STime1 | Wkcntr2 | STime2 
1     | Mat1 | 2       | 30     | 3       | 30 
1     | Mat2 | 3       | 30     | 4       | 30 
2     | Mat1 | 4       | 30     | 5       | 30 

我会说桌子没有理想的设置。看起来它没有主键,因此不清楚哪一条记录属于1列,哪条记录属于2列。

我会为RecordNumber添加一个额外的字段(1或2),然后你有一个Plant,Material和RecordNumber的主键。

您的查询将如下所示:

SELECT tmp.plant, tmp.material, tmp.workcenter AS W1, tmp.setuptime AS S1, tmp_1.workcenter AS W2, tmp_1.setuptime AS S2
FROM tmp INNER JOIN tmp AS tmp_1 ON (tmp.material = tmp_1.material) AND (tmp.plant = tmp_1.plant)
WHERE (((tmp.recordNum)=1) AND ((tmp_1.recordNum)=2));

这只适用于有限(且已知)数量的数字(无论1和2代表什么)。

答案 2 :(得分:1)

根据我对您要解决的问题的理解,我认为使用标准SQL查询(至少不在Access中)是不可行的。

我试图破解一些符合你想要的代码(我认为)。

如何使用

只需将以下代码复制/粘贴到VBA模块中即可 从代码中,或者,如果要测试它,请从VBA IDE立即窗口中调用:

ExpandTable

<强>假设

  • 表格temp包含您要展开的数据,其中包含工厂/材料/工作/等
  • temp表不为空(我省略了一些代码检查以避免样本膨胀)。
  • 扩展表将在名为result
  • 的新表中创建

<强>代码

Public Sub ExpandTable()

    Dim db As DAO.Database
    Dim rs As DAO.Recordset, rs2 As DAO.Recordset
    Dim td As DAO.TableDef
    Dim fd As DAO.Field
    Dim maxWorkCenters As Integer
    Dim i As Integer
    Dim sql As String

    Set db = CurrentDb

    ' Delete the old result table if there was one '
    On Error Resume Next
    db.TableDefs.Delete "result"
    On Error GoTo 0

    ' Create the result table '
    Set td = db.CreateTableDef("result")
    td.Fields.Append td.CreateField("Plant", dbInteger)
    td.Fields.Append td.CreateField("Material", dbText)
    ' Get the maximum number of workcenters we will need '
    ' for a given Plan/Material combination '
    sql = "SELECT Count(*) FROM Temp GROUP BY Plant, Material"
    Set rs = db.OpenRecordset(sql, dbOpenSnapshot)
    maxWorkCenters = Nz(rs.Fields(0).Value, 0)
    rs.Close
    Set rs = Nothing
    ' Create as many columns as we need to fit all these combinations '
    For i = 1 To maxWorkCenters
        td.Fields.Append td.CreateField("WorkCenter" & i, dbText)
        td.Fields.Append td.CreateField("SetupTime" & i, dbInteger)
    Next i
    db.TableDefs.Append td

    ' Now get the data into the new table '
    Dim lastPlant As Variant, lastMaterial As Variant
    Dim curcol As Integer
    sql = "SELECT Plant, Material, Workcenter, Setuptime FROM Temp ORDER BY Plant, Material, WorkCenter"
    Set rs = db.OpenRecordset(sql, dbOpenSnapshot)
    Set rs2 = db.OpenRecordset("result", dbOpenDynaset)
    With rs
        lastPlant = 0
        lastMaterial = ""
        Do While Not .EOF
            If (Nz(!Plant) <> lastPlant) Or (Nz(!Material) <> lastMaterial) Then
                If rs2.EditMode = dbEditAdd Then
                    ' Save the previously edited record if any '
                    rs2.Update
                End If

                ' Different plant/material, so we add a new result '
                rs2.AddNew
                rs2!Plant = !Plant
                rs2!Material = !Material
                rs2!WorkCenter1 = !WorkCenter
                rs2!SetupTime1 = !Setuptime
                lastPlant = Nz(!Plant)
                lastMaterial = Nz(!Material)
                curcol = 1
            Else
                ' Same plant/material combi, so we fill the next column set '
                curcol = curcol + 1
                rs2.Fields("Workcenter" & curcol).Value = !WorkCenter
                rs2.Fields("SetupTime" & curcol).Value = !Setuptime
            End If
            .MoveNext
        Loop
        If rs2.EditMode = dbEditAdd Then
            ' Save the last result '
            rs2.Update
        End If
    End With

    Set rs2 = Nothing
    Set rs = Nothing
    Set db = Nothing

End Sub

关于代码

  • 每次运行result时都会重新创建ExpandTable表。
  • 其他WorkCenterXSetupTimeX列的数量会适应实际的唯一工厂/材料对的数量。
  • 您必须修改代码以满足您的确切需求。
  • 你还需要清理一下,因为有些东西可能会表达得更好但你会开玩笑。

测试数据库

您可以从http://blog.nkadesign.com/wp-content/uploads/SO/SO547777.zip下载测试Access 2000数据库。

无论如何,希望它做你想要的或至少让你更接近。

答案 3 :(得分:0)

我不太清楚访问语法给你一个完整的答案,但是为了帮助你自己的调查,我可以告诉你,你想要做的事情被称为“交叉表”查询或“数据透视”。 / p>

在谷歌中使用这些关键字可以帮助你。

答案 4 :(得分:0)

在查询向导中有一个交叉表选项,可以将表格水平放置。这不是你想要的吗?