6

我目前正在开发一个使用 ms Access 2010 跟踪我公司库存的程序。我很难获得旨在显示库存的查询以显示我想要的信息。问题似乎是查询多次提取相同的记录,从而夸大了保留和已售产品的总和。

背景:我公司库存钢筋。我们提供将酒吧切成小块。从库存方面,我们想要跟踪每根金条的长度,从它进入仓库的那一刻起,到它在仓库中的时间(它可能会被切成小块),直到整个金条被售出并消失.

数据库:给出问题的查询,正在查询3个表;

  • 棒料(具有以下字段)
    • 批号(所有收到的棒材,属于同一生产热度)
    • BarNo(单个酒吧)
    • 原始长度(在库存收到时棒材的长度

(BatchNumber 和 BarNo 结合,是主键)

  • 销售量

    • ID(主键)
    • 批号
    • 酒吧编号
    • 售出数量
  • 预订(当客户表示有兴趣但需要时间来决定时,卖家会保留一些材料)

    • ID(主键)
    • 批号
    • 酒吧编号
    • 保留数量

我想将三个表中的信息提取到一个列表中,该列表显示:-Barstock.orginial length As Received - Sales.Quantity sold As Sold - Recieved - Sold As On Stock - reservation.Quantity Reserved As Reserved - On Stock -保留为可用。

问题是我不擅长 sql。我已经尽我所能研究了联合和内部连接,但我的努力都是徒劳的。我通常依靠设计视图来生成我需要的 Sql 语句。在设计视图中,我提出了以下 Sql:

SELECT 
  BarStock.BatchNo
  , BarStock.BarNo
  , First(BarStock.OrgLength) AS Recieved
  , Sum(Sales.QtySold) AS SumAvQtySold
  , [Recieved]-[SumAvQtySold] AS [On Stock]
  , Sum(Reservation.QtyReserved) AS Reserved
  , ([On Stock]-[Reserved])*[Skjemaer]![Inventory]![unitvalg] AS Available
FROM 
  (BarStock 
    INNER JOIN Reservation ON (BarStock.BarNo = Reservation.BarNo) AND (BarStock.BatchNo = Reservation.BatchNo)
  ) 
    INNER JOIN Sales ON (BarStock.BarNo = Sales.BarNo) AND (BarStock.BatchNo = Sales.BatchNo)
GROUP BY 
  BarStock.BatchNo
  , BarStock.BarNo

我知道查询多次提取相同的记录,因为;- 当我删除 GROUP BY 术语时,我得到了几条完全相同的记录。- 但是,在相应的表中只有这些记录的一个实例。

我希望我能够正确解释自己,请问我是否需要详细说明。

感谢您花时间查看我的问题!

4

1 回答 1

10

!!!检查一些假设

从您的数据库架构中,似乎:

  • 给定的可能有多个Sales记录BatchNumber/BarNo(例如,我可以想象多个客户可能购买了同一条的子部分)。
  • 给定的可能有多个Reservation记录BatchNumber/BarNo(例如,同一条的多个部分可以“保留”)

要检查这些表中是否确实有多个记录,请尝试以下操作:

SELECT CountOfDuplicates
FROM   (SELECT COUNT(*) AS CountOfDuplicates
        FROM   Sales
        GROUP  BY BatchNumber & "," & BarNo)
WHERE  CountOfDuplicates > 1 

如果查询返回一些记录,则说明存在重复,这可能是您的查询返回不正确值的原因。

从头开始

现在,让你的查询工作的诀窍是真正考虑你想要显示的主要数据是什么,然后从那开始:

  • 你基本上想要一个库存中所有柱的列表。其中一些金条可能已售出,或者可能被保留,但如果没有,您应该显示库存中的可用数量。您当前的查询永远不会向您显示。
  • 对于库存中的每根棒材,您要列出已售出的数量和保留的数量,并将它们组合起来以找出剩余的可用数量。

所以很明显,您的中心数据是库存条的列表。

与其试图立即将所有内容整合到一个大型查询中,不如为每个目标创建简单的查询,并确保我们在每种情况下都获得正确的数据。

只是酒吧

根据您的解释,每个单独的条都记录在BarStock表中。
正如我在评论中所说,据我了解,所有交付的酒吧在BarStock表中都有一条记录,没有重复。因此,您应该衡量库存的主要清单是下BarStock表:

SELECT BatchNumber, 
       BarNo, 
       OrgLength
FROM   BarStock

只是销售

同样,这应该非常简单:我们只需要找出每BatchNumber/BarNo双鞋的总长度:

SELECT BatchNumber, 
       BarNo, 
       Sum(QtySold) AS SumAvQtySold
FROM   Sales
GROUP BY BatchNumber, BarNo

只是预订

与销售相同:

SELECT BatchNumber,
       BarNo,
       SUM(QtyReserved) AS Reserved
FROM   Reservation
GROUP  BY BatchNumber, BarNo 

原始库存对销售

现在,我们应该能够将前 2 个查询合并为一个。我不是想优化,只是为了让数据一起工作:

SELECT BarStock.BatchNumber,
       BarStock.BarNo,
       BarStock.OrgLength,
       S.SumAvQtySold,
       (BarStock.OrgLength - Nz(S.SumAvQtySold)) AS OnStock
FROM   BarStock
LEFT JOIN (SELECT BatchNumber,
                  BarNo,
                  Sum(QtySold) AS SumAvQtySold
           FROM   Sales
           GROUP  BY BatchNumber, BarNo) AS S
  ON (BarStock.BatchNumber = S.BatchNumber) AND (BarStock.BarNo = S.BarNo) 

我们这样做是LEFT JOIN因为库存中可能有尚未售出的金条。
如果我们做了INNER JOIN,我们会在最终报告中错过这些,导致我们相信这些酒吧从一开始就不存在。

全部一起

我们现在可以将整个查询包装在另一个LEFT JOIN针对保留条的查询中以获得最终结果:

SELECT BS.BatchNumber,
       BS.BarNo,
       BS.OrgLength,
       BS.SumAvQtySold,
       BS.OnStock,
       R.Reserved,
       (OnStock - Nz(Reserved)) AS Available
FROM   (SELECT BarStock.BatchNumber,
               BarStock.BarNo,
               BarStock.OrgLength,
               S.SumAvQtySold,
               (BarStock.OrgLength - Nz(S.SumAvQtySold)) AS OnStock
        FROM   BarStock
        LEFT JOIN (SELECT BatchNumber,
                          BarNo,
                          SUM(QtySold) AS SumAvQtySold
                   FROM   Sales
                   GROUP  BY BatchNumber,
                             BarNo) AS S
          ON (BarStock.BatchNumber = S.BatchNumber) AND (BarStock.BarNo = S.BarNo)) AS BS
LEFT JOIN (SELECT BatchNumber,
                  BarNo,
                  SUM(QtyReserved) AS Reserved
           FROM   Reservation
           GROUP  BY BatchNumber,
                     BarNo) AS R
  ON (BS.BatchNumber = R.BatchNumber) AND (BS.BarNo = R.BarNo) 

请注意Nz()连接右侧的 for 项目的使用:如果给定对没有Salesor数据,则and的值将是并且也将呈现null ,而不管库存的实际数量是多少,这不会是我们期望的结果。ReservationBatchNumber/BarNoSumAvQtySoldReservedNullOnStockAvailable

使用 Access 中的查询设计器,您必须分别创建 3 个查询,然后将它们组合起来。请注意,虽然查询设计在处理多个和连接
方面不是很好,所以我认为您不可能一口气写完整个内容。LEFTRIGHT

一些评论

我相信你应该阅读@Remou 在他的评论中给你的信息。对我来说,这个数据库有一些不幸的设计选择:获取基本库存数据应该像保存库存记录的列
一样简单。SUM()

通常,跟踪库​​存的一种简单方法是跟踪每个库存交易:

  • 进货记录有一个+数量
  • 出库记录有一个 - 数量
  • 该记录还应跟踪零件/项目/棒材参考(或 ID)、交易的日期和时间,以及 - 如果您要管理多个仓库 - 涉及哪个仓库 ID。

因此,如果您需要了解所有物品的完整库存,您需要做的就是:

SELECT BarID,
       Sum(Quantity)
FROM   StockTransaction
GROUP BY BarID

在您的情况下,虽然BatchNumber/BarNo是您的自然键,但将它们保存在单独的Bar表中会有一些优势:

  • 您可以使用它Bar.ID来取回您需要的任何地方Bar.BatchNumberBar.BarNo
  • 您可以在,和表BarID中用作外键。它使连接更容易,而不必弄乱复合键的复杂性。BarStockSalesReservation

Access 允许的某些事情并不是真正的好习惯,例如表名和字段中的空格,这最终会降低可读性(至少因为您需要将它们保留在 之间[]),与表示这些的 VBA 变量名称不太一致字段,并且与不接受表和字段名称的字母数字字符以外的任何其他数据库不兼容(如果您希望稍后扩大大小或将数据库与其他应用程序接口)。

此外,通过坚持单一的命名约定来帮助自己,并保持一致:

  • 不要不一致地混合大小写:要么使用 CamelCase,要么使用小写或大写,但始终遵守该规则。
  • 以单数或复数形式命名表,但保持一致。我更喜欢使用单数,比如 tablePart而不是Parts,但这只是一个约定(有其自身的原因)。
  • 拼写正确:Received不是Recieved。在调试某些查询或 VBA 代码不起作用的原因时,仅仅因为有人打错了,这个错误可能会让您付出代价。
  • 每个表应该/必须有一个ID列。通常,这将是一个自动增量,以保证表中每条记录的唯一性。如果您保持该约定,那么外键将变得容易猜测和阅读,并且您不必担心某些业务需求会改变您可能突然发现自己有 2 个相同的事实BatchNumbers,由于某种原因您现在无法理解。

关于数据库设计有很多争论,但是每个人都同意某些“规则”,所以我的建议应该是争取:

  • 简单性:确保每个表记录一种数据,并且不包含来自其他表的冗余数据(规范化)。
  • 一致性:命名约定很重要。无论您选择什么,请在整个项目中坚持下去。
  • 清晰性:确保 3 年后的您和其他人可以轻松阅读表名和字段并了解它们的含义,而无需阅读 300 页的规范。并不总是那么清楚,但这是值得努力的事情。
于 2012-10-29T15:11:42.923 回答