2

我面临一个问题,我们有 3 张桌子。它应该显示供应仓库是否没有该项目(已删除)或者甚至没有设置,但是根据源矩阵它应该是供应仓库。

简化的布局应该是这样的。

MainData

  • 字段ItemNo - 个人标识符,无重复 (PK)
  • Field ItemType - 有一定的选择,我必须过滤一些特定的值。

仓库数据

  • Field ItemNo - 与 MainData 相关,但 PK 与下一个字段结合
  • 字段ItemLocation - 给出使用 ItemNo 描述的此类项目的特定位置
  • 字段ItemCategory - 需要加入 SourceWHMatrix
  • Field ItemDeleted - 它是一个 char 字段(因为导出的数据),如果项目被“删除”,则值为“X”,否则为 Null(非空!)

源WHMatrix

  • Field ItemLocation - 前两个字段是主键
  • 字段项目类别
  • 字段SourceLocation - 它应该提供物品的交付地点

我需要检查以下内容:如果ItemNo属于某个ItemType(MainData 表)并且 ItemDeleted Is Null 那么这个 ItemNo 是否SourceLocation SourceWHMatrix基于ItemLocation ItemCategory确定)。需要列出在SourceLocation中“删除”的所有ItemNo(在这种情况下,在表WarehouseData字段ItemLocation中检查返回的SourceLocation值)或者甚至没有为此类位置设置。

注意:如果不同 ItemLocation 的ItemNo相同ItemCategory并不总是匹配。而且每个ItemLocation都有很多不同的ItemCategory来确定源仓库。

我尝试了许多 LEFT JOIN 组合,但似乎我无法列出那些未设置的值。(通过创建 WarehouseData 的重复表,我能够在供应仓库中列出已删除的项目。)我也可以在 VBA 中使用 ADODB 执行此操作,但我想将所有内容保存在 SQL 代码中,甚至不使用自定义函数。它在 Access 2010 中,在其 JET SQL 引擎上运行。

这是当前不起作用的查询

SELECT MD.itemno, 
       MD.itemtype, 
       WD.itemlocation, 
       SMat.sourcelocation, --this could be empty
       WD.itemdeleted AS "SourceWHDelFlag" 
FROM   maindata AS MD 
       INNER JOIN (warehousedata AS WD 
                   INNER JOIN (sourcewhmatrix AS SMat 
                               LEFT JOIN wd 
                                      ON SMat.sourcelocation = WD.itemlocation) 
                           ON ( WD.itemlocation = SMat.itemlocation 
                                AND WD.itemcategory = SMat.itemcategory ) ) 
               ON MD.itemno = WD.itemno 
WHERE  ( MD.itemtype = 'Value1' 
          OR MD.itemtype = 'Value2' ) 

这是我经过进一步思考后能想到的。但是,查询现在在我的 i5 上运行了 20 分钟。所以这不是最优的,而且当我可以检查 Access 的一些预先结果时,似乎将别名语句添加到 where 子句会导致错误,因为不再在同一个 where 子句中的其他字段上正确过滤。所以这是我的“解决方案”:

SELECT MD.ItemNo,
       MD.ItemType
       WD.itemlocation, 
       SMat.sourcelocation, --this could be empty
       (SELECT WD.ItemDeleted FROM WD WHERE WD.ItemNo = MD.ItemNo) AS "SourceWHDelFlag"
FROM MainData AS MD INNER JOIN (WarehouseData AS WD
                                LEFT JOIN SourceWHMatrix AS SMat
                                       ON (WD.ItemLocation = SMat.ItemLocation
                                           AND WD.ItemCategory = SMat.ItemCategory))
                                ON MD.ItemNo = WD.ItemNo
WHERE (MD.ItemType = 'Value1' OR MD.ItemType = 'Value2')
      AND WD.ItemDeleted Is Null
      AND WD.ItemCategory Is Not Null
      AND WD.ItemCategory Not Like '##' -- This is another filter value, and it seems to be buggy
                                        -- with SELECT clause in WHERE statement
      AND (SELECT WD.ItemDeleted FROM WD WHERE WD.ItemNo = MD.ItemNo) Is Not Null

提前感谢您的帮助!

更新 我做了一些VBA,这段代码实际上是有效的。我在代码中注释了所有必要的信息,但它仍然非常缓慢。(如果您想在合理的时间范围内显着加快运行速度,我愿意。) 200 条记录大约需要 10 分钟,因此运行 16-17 千条记录需要 15 小时。我可以在 Excel 中使用一些 VLookup 更快地在 Excel 中执行此操作......

Private Sub Command0_Click()

'initialize variables
Dim connDB As ADODB.Connection
Dim filtRecSet As ADODB.Recordset
Dim tmpRecSet As ADODB.Recordset
Dim tmpLineText As String
Dim tmpCounter As Integer
Dim filePath As String
Dim tmpFSO As New FileSystemObject
Dim tmpStream As TextStream
Dim startTime, endTime As Double

'set values
Set connDB = New ADODB.Connection
Set connDB = CurrentProject.Connection
Set filtRecSet = New ADODB.Recordset
Set tmpRecSet = New ADODB.Recordset
filePath = "C:\data\output.txt"
Set tmpStream = tmpFSO.CreateTextFile(filePath, True)

startTime = Now()

'this is a test database
'I previously deleted all not required MD.ItemType to test speed of SQL queries
'it's the reason for no filtering on MD.ItemType

'open base recordset, which are not deleted (WD.ItemDeleted)
'and belong tospecific MD.ItemType values
'and can match certain filters on WD.ItemCategory
With filtRecSet
    .ActiveConnection = connDB
    .Source = "SELECT MD.ItemNo, WD.ItemLocation, MD.ItemType, WD.ItemCategory, SMat.SourceLocation FROM MainData As MD INNER JOIN (WarehouseData As WD LEFT JOIN SourcwWHMatrix As SMat ON (WD.ItemLocation = SMat.ItemLocation AND WD.ItemCategory = SMat.ItemCategory)) ON MD.ItemNo = WD.ItemNo WHERE WD.ItemCategory Is Not Null AND WD.ItemCategory Not Like '[0-9][0-9]' AND WD.ItemDeleted Is Null"
    .LockType = adLockOptimistic
    .CursorType = adUseClient
End With
'RecordCount: 16610

'open control recordset for all appropiate MD.ItemType
With tmpRecSet
    .ActiveConnection = connDB
    .Source = "SELECT MD.ItemNo, WD.ItemLocation, MD.ItemType, WD.ItemCategory, SMat.SourceLocation FROM MainData As MD INNER JOIN (WarehouseData As WD LEFT JOIN SourcwWHMatrix As SMat ON (WD.ItemLocation = SMat.ItemLocation AND WD.ItemCategory = SMat.ItemCategory)) ON MD.ItemNo = WD.ItemNo"
    .LockType = adLockOptimistic
    .CursorType = adUseClient
    .Filter = adFilterNone
End With
'RecordCount: 138713

filtRecSet.Open
'tmp variable to see how many records have been processed
tmpCounter = 1
If Not filtRecSet.EOF Then
    'select first record
    filtRecSet.MoveFirst
    Do While Not filtRecSet.EOF
        'find SourceLocation
        tmpRecSet.Filter = "MATNR = '" & filtRecSet(0).Value & "' AND WERKS = '" & filtRecSet(5).Value & "'"
        tmpRecSet.Open
        'check how many records in recordset; there should not be more than one, that's why it considered as error
        If tmpRecSet.RecordCount = 1 Then
            tmpRecSet.MoveFirst
            tmpLineText = filtRecSet(0).Value & "|" & filtRecSet(1).Value & "|" & filtRecSet(2).Value & "|" & filtRecSet(3).Value & "|" & filtRecSet(4).Value & "|" & filtRecSet(5).Value & "|" & tmpRecSet(3).Value
        ElseIf tmpRecSet.RecordCount > 1 Then
            tmpLineText = "ERROR"
        'in case EOF is True -> no records
        Else
            tmpLineText = filtRecSet(0).Value & "|" & filtRecSet(1).Value & "|" & filtRecSet(2).Value & "|" & filtRecSet(3).Value & "|" & filtRecSet(4).Value & "|" & filtRecSet(5).Value & "|"
        End If
        Debug.Print "Record no.: " & tmpCounter
        'write out text file
        tmpStream.WriteLine tmpLineText
        filtRecSet.MoveNext
        tmpRecSet.Close
        tmpCounter = tmpCounter + 1
    Loop
End If

tmpStream.Close
endTime = Now()

Debug.Print "Elapsed time: " & CStr((endTime - startTime) * 24 * 60 * 60) & " seconds."

End Sub
4

1 回答 1

1

我只是想出了一些解决方案,昨天的想法几乎是好的,但是有一个关于速度的问题。因此,我尝试在我的测试数据库中为所有这些字段设置一些索引(我没有使用此功能,因为我也面临有关 Access 文件 2 GB 限制的问题,并希望保留所有可能的空间)。此外,我在MD.ItemNoWD.ItemNo之间强制执行了引用完整性。已建立主键和关系;索引允许重复,除了MD.ItemNo。结果是,它在几秒钟内运行,并返回正确的结果。

现在我只需要修改我的导入脚本来生成这样的 XML,这些 XML 会在必要的字段上创建索引。这也是一个问题,因为所有字段都是必需的,这就是整个导入脚本跳过其中三分之二(总共大约 800 个字段)的原因。

这也加快了 VBA 代码的运行速度,但仍然非常非常慢。

这是有效的 SQL:

SELECT MD.ItemNo,
       MD.ItemType
       WD.ItemLocation, 
       WD.ItemDeleted
       SMat.SourceLocation, --this could be empty
       (SELECT WD.ItemDeleted
           FROM WD
           WHERE WD.ItemNo = MD.ItemNo
                 AND WD.ItemLocation = SMat.SourceLocation
       ) AS SourceDeleted
FROM MainData AS MD INNER JOIN (WarehouseData AS WD
                                LEFT JOIN SourceWHMatrix AS SMat
                                       ON (WD.ItemLocation = SMat.ItemLocation
                                           AND WD.ItemCategory = SMat.ItemCategory))
                                ON MD.ItemNo = WD.ItemNo
WHERE (MD.ItemType = 'Value1' OR MD.ItemType = 'Value2')
      AND WD.ItemDeleted Is Null
      AND WD.ItemCategory Is Not Null
      AND WD.ItemCategory Not Like '[0-9][0-9]' -- there's an issue about wildcards
      AND (SMat.SourceLocation Is Null -- to display not set up ItemNo on SourceLocation
           OR (SELECT WD.ItemDeleted
                  FROM WD
                  WHERE WD.ItemNo = MD.ItemNo
                        AND WD.ItemLocation = SMat.SourceLocation
              ) Is Not Null) -- check if ItemDeleted on SourceLocation
于 2013-03-20T13:03:35.507 回答