SQL 查询仅显示单个食品的最近购买记录

J. *_*lor 8 join ms-access sorting date distinct

我正在使用 MS Access 2013 中的食品采购/发票系统,并尝试创建一个 SQL 查询,该查询将返回每个单独食品的最新购买价格。

这是我正在使用的表的图表: MS Access 数据库中的表

我对 SQL 的理解是非常基本的,我尝试了以下(不正确的)查询,希望它只返回每个项目的一条记录(因为DISTINCT运算符)并且它只会返回最近购买的(因为我做了ORDER BY [Invoice Date] DESC)

SELECT DISTINCT ([Food items].Item), 
    [Food items].Item, [Food purchase data].[Price per unit], [Food purchase data].[Purchase unit], Invoices.[Invoice Date]
FROM Invoices
INNER JOIN ([Food items] 
    INNER JOIN [Food purchase data] 
    ON [Food items].ID = [Food purchase data].[Food item ID]) 
ON Invoices.ID = [Food purchase data].[Invoice ID]
ORDER BY Invoices.[Invoice Date] DESC;
Run Code Online (Sandbox Code Playgroud)

然而,上面的查询只是返回所有的食品购买(即 中每个记录的多条记录[Food items]),结果按日期降序排序。有人可以向我解释我对DISTINCT运营商的误解吗?也就是说,为什么它不只为 中的每个项目返回一条记录[Food items]

更重要的是 -考虑到上面显示的表格结构,对我来说,为每个单独的食品提取最新的食品购买数据最简单方法是什么?我并不真正关心效率和简单性(我正在使用的数据库相当小 - 它甚至需要数年才能达到数万条记录的范围)。我更关心查询对于对 SQL 知之甚少的人是否可以理解。

更新: 所以我尝试了下面建议的两个答案,但它们都不起作用(它们只是抛出语法错误)。

根据以下建议并进一步阅读在线内容,我使用聚合函数max()GROUP BY子句编写了以下新查询:

SELECT [Food purchase data].[Food item ID], [Food purchase data].[Price per unit], max(Invoices.[Invoice Date]) AS MostRecentInvoiceDate
FROM [Food purchase data], Invoices
GROUP BY [Food purchase data].[Food item ID], [Food purchase data].[Price per unit];
Run Code Online (Sandbox Code Playgroud)

但我仍然有同样的问题:也就是说,我仍然看到每种食品的结果不止一个。谁能解释为什么这个查询不仅返回每个食品的最近购买?

更新 2(已解决!)

下面的答案都没有完全解决,但基于对下面Vladimir 的答案的一些重大修改,我能够创建以下查询,这些查询似乎给出了正确的结果。

首先,我创建了这个视图并将其命名为“LatestInvoices”:

SELECT InvoicesMaxDate.ItemID, InvoicesMaxDate.MaxDate, InvoicesMaxDate.MaxID
FROM [Food purchase data], Invoices, (SELECT [Food purchase data].[Food item ID] AS ItemID, MAX(Invoices.[Invoice Date]) AS MaxDate, MAX(Invoices.[Invoice ID]) AS MaxID
                FROM [Food purchase data], Invoices
                WHERE Invoices.[Invoice ID] = [Food purchase data].[Invoice ID]
                GROUP BY [Food purchase data].[Food item ID]
         )  AS InvoicesMaxDate
WHERE InvoicesMaxDate.MaxID = [Food purchase data].[Invoice ID] AND
                      InvoicesMaxDate.ItemID = [Food purchase data].[Food item ID] AND 
                      InvoicesMaxDate.MaxDate = Invoices.[Invoice Date]
GROUP BY InvoicesMaxDate.ItemID, InvoicesMaxDate.MaxDate,  InvoicesMaxDate.MaxID
Run Code Online (Sandbox Code Playgroud)

然后我写了另一个查询来拉入我需要的字段:

SELECT [Food items].ID AS FoodItemID, [Food items].Item AS FoodItem, [Food purchase data].[Price], [Food purchase data].[Price per unit], [Food purchase data].[Purchase unit], LatestInvoices.MaxDate as InvoiceDate
FROM [Food items], [Food purchase data], LatestInvoices
WHERE LatestInvoices.[MaxID] = [Food purchase data].[Invoice ID] AND
             LatestInvoices.ItemID = [Food purchase data].[Food item ID] AND
             LatestInvoices.ItemID = [Food items].ID
ORDER BY [Food items].Item;
Run Code Online (Sandbox Code Playgroud)

感谢所有花时间帮助我解决这个问题的人!

Vla*_*nov 7

MS Access 相当有限。

我假设同一日期可能有多个发票。在这种情况下,我将选择 ID 最高的发票。

首先,我们会找到每个食品项目的最大发票日期。

SELECT
    FPD1.[Food item ID] AS ItemID
    ,MAX(I1.[Invoice Date]) AS MaxDate
FROM
    [Food purchase data] AS FPD1
    INNER JOIN Invoices AS I1 ON I1.ID = FPD1.[Invoice ID]
GROUP BY
    FPD1.[Food item ID]
Run Code Online (Sandbox Code Playgroud)

由于找到的最大日期可能有几张发票,我们将选择一张带有每个项目最大 ID 的发票

基于嵌套连接MS Access 语法并使用文档中的此示例:

SELECT fields 
FROM 
  table1 INNER JOIN 
  (
      table2 INNER JOIN 
      (
          table3 INNER JOIN tablex ON table3.field3 = tablex.fieldx
      ) ON table2.field2 = table3.field3
  ) ON table1.field1 = table2.field2
;
Run Code Online (Sandbox Code Playgroud)

让我们试着把它放在一起:

SELECT
    InvoicesMaxDate.ItemID
    ,InvoicesMaxDate.MaxDate
    ,MAX(I2.ID) AS MaxInvoiceID
FROM
    (
        SELECT
            FPD1.[Food item ID] AS ItemID
            ,MAX(I1.[Invoice Date]) AS MaxDate
        FROM
            [Food purchase data] AS FPD1
            INNER JOIN Invoices AS I1 ON I1.ID = FPD1.[Invoice ID]
        GROUP BY
            FPD1.[Food item ID]
    ) AS InvoicesMaxDate INNER JOIN
    (
        [Food purchase data] AS FPD2 
        INNER JOIN Invoices AS I2 ON I2.ID = FPD2.[Invoice ID]
    ) ON
        InvoicesMaxDate.ItemID = FPD2.[Food item ID] AND
        --- you may need to put extra "ON" here as well, not sure
        InvoicesMaxDate.MaxDate = I2.[Invoice Date]
GROUP BY
    InvoicesMaxDate.ItemID
    ,InvoicesMaxDate.MaxDate
Run Code Online (Sandbox Code Playgroud)

现在我们有该项目的最后一张发票的 ItemID 和 ID。将此连接到原始表以获取其他详细信息(列)。

SELECT
    FI3.Item
    ,FI3.Item
    ,FPD3.[Price per unit]
    ,FPD3.[Purchase unit]
    ,I3.[Invoice Date]
FROM
    (
        SELECT
            InvoicesMaxDate.ItemID
            ,InvoicesMaxDate.MaxDate
            ,MAX(I2.ID) AS MaxInvoiceID
        FROM
            (
                SELECT
                    FPD1.[Food item ID] AS ItemID
                    ,MAX(I1.[Invoice Date]) AS MaxDate
                FROM
                    [Food purchase data] AS FPD1
                    INNER JOIN Invoices AS I1 ON I1.ID = FPD1.[Invoice ID]
                GROUP BY
                    FPD1.[Food item ID]
            ) AS InvoicesMaxDate INNER JOIN
            (
                [Food purchase data] AS FPD2 
                INNER JOIN Invoices AS I2 ON I2.ID = FPD2.[Invoice ID]
            ) ON
                InvoicesMaxDate.ItemID = FPD2.[Food item ID] AND
                InvoicesMaxDate.MaxDate = I2.[Invoice Date]
        GROUP BY
            InvoicesMaxDate.ItemID
            ,InvoicesMaxDate.MaxDate
    ) AS LastInvoices INNER JOIN
    (
        [Food items] AS FI3 INNER JOIN
        (
            [Food purchase data] AS FPD3
            INNER JOIN Invoices AS I3 ON I3.ID = FPD3.[Invoice ID]
        ) ON FI3.ID = FDP3.[Food item ID]
    ) ON
        LastInvoices.MaxInvoiceID = I3.ID AND
        LastInvoices.ItemID = FI3.ID
Run Code Online (Sandbox Code Playgroud)

在实践中,我会使用单个连接为第一个查询创建一个视图。然后我会创建第二个视图,将第一个视图与表连接起来,然后是第三个视图,依此类推,以避免嵌套连接或最小化它们。整体查询会更容易阅读。


根据您在问题中提出的最终解决方案进行编辑以阐明我的意思。

最后一次尝试传达我的信息。

这是根据我上面的建议写的:

SELECT
    InvoicesMaxDate.ItemID
    ,InvoicesMaxDate.MaxDate
    ,Invoices.[Invoice ID]
FROM [Food purchase data], Invoices, 
    (
        SELECT 
            [Food purchase data].[Food item ID] AS ItemID
            ,MAX(Invoices.[Invoice Date]) AS MaxDate
        FROM [Food purchase data], Invoices
        WHERE Invoices.[Invoice ID] = [Food purchase data].[Invoice ID]
        GROUP BY [Food purchase data].[Food item ID]
    )  AS InvoicesMaxDate
WHERE
    Invoices.[Invoice ID] = [Food purchase data].[Invoice ID] AND
    InvoicesMaxDate.ItemID = [Food purchase data].[Food item ID] AND 
    InvoicesMaxDate.MaxDate = Invoices.[Invoice Date]
GROUP BY InvoicesMaxDate.ItemID, InvoicesMaxDate.MaxDate, Invoices.[Invoice ID];
Run Code Online (Sandbox Code Playgroud)

这就是我的意思:

SELECT
    InvoicesMaxDate.ItemID
    ,InvoicesMaxDate.MaxDate
    ,MAX(Invoices.[Invoice ID]) AS [Invoice ID]
FROM [Food purchase data], Invoices, 
    (
        SELECT
            [Food purchase data].[Food item ID] AS ItemID
            ,MAX(Invoices.[Invoice Date]) AS MaxDate
        FROM [Food purchase data], Invoices
        WHERE Invoices.[Invoice ID] = [Food purchase data].[Invoice ID]
        GROUP BY [Food purchase data].[Food item ID]
    )  AS InvoicesMaxDate
WHERE
    Invoices.[Invoice ID] = [Food purchase data].[Invoice ID] AND
    InvoicesMaxDate.ItemID = [Food purchase data].[Food item ID] AND 
    InvoicesMaxDate.MaxDate = Invoices.[Invoice Date]
GROUP BY InvoicesMaxDate.ItemID, InvoicesMaxDate.MaxDate;
Run Code Online (Sandbox Code Playgroud)

你看得到差别吗?

每个的InvoicesMaxDate返回 MAX 。如果有两张相同的 MAX 的发票,我们应该在其中选择一张发票。这是通过分组来完成的。这里应该没有分组,因为我们要选择具有最大 ID 的发票。Invoice DateFood item IDFood item IDInvoice DateInvoicesMaxDate.ItemID, InvoicesMaxDate.MaxDateInvoices.[Invoice ID]

将此查询保存为LatestInvoices视图后,它会在您正确编写时进一步使用(请注意,最终查询使用LatestInvoices.[Invoice ID]and LatestInvoices.ItemID,但不使用LatestInvoices.MaxDate):

SELECT 
    [Food items].ID as FoodItemID
    ,[Food items].Item as FoodItem
    ,[Food purchase data].[Price]
    ,[Food purchase data].[Price per unit]
    ,[Food purchase data].[Purchase unit]
    ,Invoices.[Invoice Date]
FROM [Food items], [Food purchase data], Invoices, LatestInvoices
WHERE 
    Invoices.[Invoice ID] = [Food purchase data].[Invoice ID] AND
    [Food items].ID = [Food purchase data].[Food item ID] AND
    LatestInvoices.[Invoice ID] = Invoices.[Invoice ID] AND 
    LatestInvoices.ItemID = [Food items].ID
ORDER BY [Food items].Item
Run Code Online (Sandbox Code Playgroud)

至于为什么您在问题中的最后一个查询每个项目返回几行:

SELECT 
    [Food purchase data].[Food item ID]
    , [Food purchase data].[Price per unit]
    , max(Invoices.[Invoice Date]) AS MostRecentInvoiceDate
FROM [Food purchase data], Invoices
GROUP BY [Food purchase data].[Food item ID], [Food purchase data].[Price per unit];
Run Code Online (Sandbox Code Playgroud)

您在此处按[Food item ID]和分组[Price per unit],因此您将获得与这两列的独特组合一样多的行。

以下查询将每个 返回一行[Food item ID]

SELECT 
    [Food purchase data].[Food item ID]
    , max(Invoices.[Invoice Date]) AS MostRecentInvoiceDate
FROM [Food purchase data], Invoices
GROUP BY [Food purchase data].[Food item ID];
Run Code Online (Sandbox Code Playgroud)

附带说明,您确实应该使用显式INNER JOIN而不是,. 该语法已有 20 年历史。

SELECT 
    [Food purchase data].[Food item ID]
    , max(Invoices.[Invoice Date]) AS MostRecentInvoiceDate
FROM
    [Food purchase data]
    INNER JOIN Invoices ON Invoices.ID = [Food purchase data].[Invoice ID]
GROUP BY [Food purchase data].[Food item ID];
Run Code Online (Sandbox Code Playgroud)