在 SQL 和 Access 中按年份并排数据比较

Posted

技术标签:

【中文标题】在 SQL 和 Access 中按年份并排数据比较【英文标题】:Side by side data comparison by year in SQL with Access 【发布时间】:2015-04-09 09:38:01 【问题描述】:

我需要逐年比较数据。

我使用的是 Microsoft Access 2013。

我想要得到的是这样的表格:

╔══════╦══════════╦ ... ╦═══════╦═══════════════╦══════════════╦════════════╗
║ Zone ║ Customer ║ ... ║ Month ║ Previous year ║ Current year ║ Difference ║
╠══════╬══════════╬ ... ╬═══════╬═══════════════╬══════════════╬════════════╣
║ PD   ║ A        ║ ... ║     1 ║ 100€          ║ 150€         ║ +50€       ║
║ PD   ║ B        ║ ... ║     2 ║ 150€          ║ 100€         ║ -50€       ║
║ PD   ║ C        ║ ... ║     1 ║ 200€          ║ 0€           ║ -200€      ║
║ PD   ║ D        ║ ... ║     2 ║ 0€            ║ 100€         ║ +100€      ║
╚══════╩══════════╩ ... ╩═══════╩═══════════════╩══════════════╩════════════╝

("..." stands for other columns)

确定上一年是2014年,现在是2015年,用这张表我可以看看:

一位 2014 年的客户在 2015 年购买了更多(客户 A); 一位 2014 年的客户在 2015 年购买了 less(客户 B); 一位 2014 年的客户尚未在 2015 年购买(客户 C); 有一个 2015 年的新客户(客户 D)。

当我有一个“老”(2014 年)客户在 2015 年没有购买时我需要在当前年份列(2015 年)中看到具有“0€”值的行,等等对于 2015 年的新客户,显然 2014 年没有购买任何东西也是如此。

这些是我的桌子:

客户(id、company_name、id_zone、id_customer_category) CUSTOMER_CATEGORIES(ID、描述) 订单(id、id_customer、日期、数量、金额 (€)、id_product_category、 id_import) PRODUCT_CATEGORIES(ID、供应商、描述) ZONES(ID,城市) 进口(ID、年、月、供应商)

我创建了一个视图 (V_YEAR_SALES):

SELECT Z.ID AS ID_ZONE, Z.CITY, C.COMPANY_NAME AS CUSTOMER, CAT.DESCRIPTION AS CUSTOMER_CATEGORY, P.SUPPLIER, P.DESCRIPTION AS PRODUCT_CATEGORY, I.MONTH, I.YEAR, SUM(O.AMOUNT) AS AMOUNT
FROM ((((ORDERS AS O 
    INNER JOIN CUSTOMERS AS C ON O.ID_CUSTOMER = C.ID) 
    INNER JOIN ZONES AS Z ON Z.ID = C.ID_ZONE) 
    INNER JOIN IMPORTS AS I ON I.ID = O.ID_IMPORT) 
    INNER JOIN CUSTOMER_CATEGORIES AS CAT ON CAT.ID = C.ID_CUSTOMER_CATEGORY)
    INNER JOIN PRODUCT_CATEGORIES AS P ON O.ID_PRODUCT_CATEGORY = P.ID
GROUP BY Z.ID, Z.CITY, C.COMPANY_NAME, CAT.DESCRIPTION, P.SUPPLIER, P.DESCRIPTION, I.MONTH, I.YEAR
ORDER BY Z.CITY, P.SUPPLIER, P.DESCRIPTION, CAT.DESCRIPTION, C.COMPANY_NAME, I.YEAR, I.MONTH;

每当我需要对一年和上一年进行比较时,我都会使用这个视图 (V_SALES_COMPARISON) 来过年:

SELECT S1.ID_ZONE, S1.CITY, S1.ID_CUSTOMER, S1.CUSTOMER, S1.ID_CUSTOMER_CATEGORY, S1.CUSTOMER_CATEGORY, S1.SUPPLIER, S1.ID_PRODUCT_CATEGORY, S1.PRODUCT_CATEGORY, S1.YEAR, S1.MONTH, S1.AMOUNT AS CURRENT_YEAR, S2.AMOUNT AS PREVIOUS_YEAR, S1.AMOUNT - S2.AMOUNT AS DIFFERENCE
FROM 
    (SELECT * FROM V_YEAR_SALES) AS S1 
    LEFT JOIN 
    (SELECT * FROM V_YEAR_SALES) AS S2 
        ON (S1.ID_ZONE = S2.ID_ZONE) 
        AND (S1.SUPPLIER = S2.SUPPLIER) 
        AND (S1.ID_PRODUCT_CATEGORY = S2.ID_PRODUCT_CATEGORY) 
        AND (S1.ID_CUSTOMER_CATEGORY = S2.ID_CUSTOMER_CATEGORY) 
        AND (S1.ID_CUSTOMER = S2.ID_CUSTOMER) 
        AND (S1.YEAR = S2.YEAR + 1) 
        AND (S1.MONTH = S2.MONTH);

参考上面的期望结果表,通过这个视图我只能获得客户A、B、D的行而不是C

我需要包括那些尚未在 2015 年购买的 2014 年客户!

我正在使用 JOIN,但我不知道这是否是最好的方法。也许我可以使用 PIVOTING,但我不知道是否以及如何使用 Access 实现。

【问题讨论】:

我觉得是因为这条线:AND (S1.YEAR = S2.YEAR + 1) .. 也试试OR (S1.YEAR = S2.YEAR)..?这只是在黑暗中拍摄 @Invent-Animate:不,那样我只能为每个年/月/订单获得许多重复的行:[PREVIOUS_YEAR] = 100 欧元(左右),[CURRENT_YEAR] = 100 欧元(左右),[差异] = 0€。 【参考方案1】:

你可以这样:

SELECT 
    Customer, 
    Sum(IIf(DateDiff("yyyy",[InvoiceDate],Date())=1,[Amount],0)) AS LastYear, 
    Sum(IIf(DateDiff("yyyy",[InvoiceDate],Date())=0,[Amount],0)) AS ThisYear, 
    Sum(IIf(DateDiff("yyyy",[InvoiceDate],Date())=0,[Amount],0)) 
       - Sum(IIf(DateDiff("yyyy",[InvoiceDate],Date())=1,[Amount],0)) AS Diff
FROM 
    Invoices
GROUP BY 
    Customer;

我看到你有一个单独的年份字段,所以试试这个方法:

SELECT 
    Customer, 
    Sum(IIf(Year(Date())-[YearField]=1,[Amount],0)) AS LastYear, 
    Sum(IIf(Year(Date())-[YearField]=0,[Amount],0)) AS ThisYear, 
    Sum(IIf(Year(Date())-[YearField]=0,[Amount],0)) 
       - Sum(IIf(Year(Date())-[YearField]=1,[Amount],0)) AS Diff
FROM 
    Invoices
GROUP BY 
    Customer;

【讨论】:

我试图用我的表来实现你的代码,但我只得到一个客户列表,每个 SUM 列都是 0 欧元。您能否对我的表格进行有效查询或用您的示例进行更多详细说明? 查看修改后的代码。它汇总了匹配年份的金额。 没有区别,每列总是 0 欧元。无论如何,我需要一个基于我必须加入的所有数据的查询。 然后发生了其他事情,或者您的数据与您的描述不同。代码在这里测试和验证。 我不使用ORDERS 表的date 字段来获取年份。相反,我需要使用 IMPORTS 表中的 yearmonth 字段。所以我需要在ORDERSIMPORTS 之间至少有一个JOIN 操作。然后必须从CUSTOMERS 表中获取company_name(或客户)字段,并且需要另一个JOIN。因此,将您的代码“按原样”使用我的表格无法使用,然后我不得不重新排列它。但我没有得到任何结果。【参考方案2】:

最后我找到了使用 PIVOTING 的解决方案。

V_YEAR_SALES 视图被替换为:

TRANSFORM
IIF(SUM(O.AMOUNT) IS NULL, 0, SUM(O.AMOUNT))
SELECT Z.ID AS ID_ZONE, Z.CITY, C.COMPANY_NAME AS CUSTOMER, CAT.DESCRIPTION AS CUSTOMER_CATEGORY, P.SUPPLIER, P.DESCRIPTION AS PRODUCT_CATEGORY, I.MONTH, I.YEAR
FROM ((((ORDERS AS O 
    INNER JOIN CUSTOMERS AS C ON O.ID_CUSTOMER = C.ID) 
    INNER JOIN ZONES AS Z ON Z.ID = C.ID_ZONE) 
    INNER JOIN IMPORTS AS I ON I.ID = O.ID_IMPORT) 
    INNER JOIN CUSTOMER_CATEGORIES AS CAT ON CAT.ID = C.ID_CUSTOMER_CATEGORY)
    INNER JOIN PRODUCT_CATEGORIES AS P ON O.ID_PRODUCT_CATEGORY = P.ID
WHERE I.YEAR BETWEEN 
    (SELECT MAX(YEAR) FROM IMPORTS) AND (SELECT MAX(YEAR)-3 FROM IMPORTS)
GROUP BY Z.ID, Z.CITY, C.COMPANY_NAME, CAT.DESCRIPTION, P.SUPPLIER, P.DESCRIPTION, I.MONTH, I.YEAR

V_SALES_COMPARISON 视图已被删除,因为不再需要。我的查询现在仅适用于 V_YEAR_SALES

查询新的V_YEAR_SALES也比V_SALES_COMPARISON

如果需要从Access中查询视图或者不带参数:

SELECT S.*
FROM V_YEAR_SALES AS S
WHERE S.YEAR IN (2015, 2014) AND S.MONTH BETWEEN 1 AND 12;

相反,如果您需要使用 Jet Driver 从代码中查询视图并且还想使用参数,则需要这样做:

PARAMETERS P0 TEXT(255), P1 TEXT(255), P2 TEXT(255), P3 TEXT(255);
SELECT S.*
FROM V_YEAR_SALES AS S
WHERE S.YEAR IN (@P0, @P1) AND S.MONTH BETWEEN @P2 AND @P3;

小心包含 PARAMETERS 声明(在 SELECT 声明之前还有 ;),否则您会从 Jet Driver 收到错误:

The Microsoft Jet database engine does not recognize '[P0]' as a valid field name or expression.

【讨论】:

以上是关于在 SQL 和 Access 中按年份并排数据比较的主要内容,如果未能解决你的问题,请参考以下文章

从访问数据库中按年份获取不同的计数

在python中按特定年份对数据进行分组

如何在R中按国家和年份查找最大值? [复制]

Access 2010 SQL--在交叉表查询中按聚合函数对行进行排序

access-sql 和 VBA:SUM 当前月份和年份(2017 年 2 月)和当前月份和去年(2016 年 2 月)

通过运行 SQL 查询在 MS Access 2012 上更改年份日期格式的错误