6

使用的表:

1) v(日期 d, 姓名 c(25), desc c(50), 借方 n(7), 贷方 n(7))

'v' 中的名称指的是 vn 表中的名称

2)vn(日期d,名称c(25),类型c(25),obal n(7))

'vn' 中的名称是主键,不同的名称按类型分组

例如:名称 abc、def、ghi 属于类型“bank”,名称 xyz、pqr 属于类型“ledger”,...

我有这样的查询:

SELECT vn.type, SUM(vn.obal + IIF(v.date < sd, v.credit-v.debit, 0)) OpBal, ;
    SUM(IIF(BETWEEN(v.date, sd, ed), v.credit-v.debit, 0)) CurBal ;
    FROM v, vn WHERE v.name = vn.name GROUP BY vn.type ;
    ORDER BY vn.type HAVING OpBal + CurBal != 0

它工作正常,但唯一的问题是,obal 是一个值,在表“vn”中每个名称只输入一次,但是对于表“v”中的每个贷记借方计算的查询,obal 被多次添加并显示在欧宝。当查询修改如下:

SELECT vn.type, vn.obal + SUM(IIF(v.date < sd, v.credit-v.debit, 0)) OpBal, ;
    SUM(IIF(BETWEEN(v.date, sd, ed), v.credit-v.debit, 0)) CurBal ;
    FROM v, vn WHERE v.name = vn.name GROUP BY vn.type ;
    ORDER BY vn.type HAVING OpBal + CurBal != 0

它显示一条错误消息,例如“Group by 子句丢失或无效”!

RDBMS 使用 MS Visual Foxpro 9。sd 和 ed 是用于查询的日期类型变量,其中 sd < ed。

请帮助我得到预期的结果。非常感谢。

4

3 回答 3

0

几分钟前,我第一次看到了带有 VFP 的 SQL 的 SQL 语法,所以这很可能充满了错误,但作为一种“猜测性的预感”:

SELECT vn.type, 
       SUM(vn.obal + (SELECT SUM(IIF(v.date < sd, v.credit-v.debit, 0)) 
                      FROM v 
                      WHERE v.name = vn.name)) OpBal,
       SUM(SELECT SUM(IIF(BETWEEN(v.date, sd, ed), v.credit-v.debit, 0))
           FROM v 
           WHERE v.name = vn.name) CurBal
FROM vn
GROUP BY vn.type
ORDER BY vn.type 
HAVING OpBal + CurBal != 0

基本上,我刚刚将选择从 v 转换为子选择,以避免重复 vn.obal。v 无关紧要,它首先获得个人的总和,然后再将它们加在一起。

于 2012-07-07T12:27:46.567 回答
0

只是几件事。VFP 9 有一个设置,即不要求对所有非聚合进行分组,以实现向后兼容性和类似的结果,如 MySQL,并非所有列都必须是聚合。例如从客户记录中查询额外的列,无论您在其 PK 列(姓名、地址、电话等)上加入多少条记录,这些列永远不会改变。

SET ENGINEBEHAVIOR 80

VFP 9 的默认值

SET ENGINEBEHAVIOR 90

要求所有非分组列都是聚合以遵守。

接下来...看起来您正在处理的表中有非常糟糕的列... VFP 中的 3 个保留字...“日期”、“名称”和“类型”,但是您可以通过将它们限定在带有 alias.column 参考的查询。

以下示例代码将创建您在问题中描述的结构的临时表(游标)。我还插入了一些示例数据并模拟了您的“sd”(开始日期)和“ed”(结束日期)变量

CREATE CURSOR vn;
   ( date d, ;
     name c(25), ;
     type c(25), ;
     obal n(7) )

INSERT INTO vn VALUES ( CTOD( "5/20/2012" ), "person 1", "person type 1", 125 )
INSERT INTO vn VALUES ( CTOD( "5/20/2012" ), "person 2", "another type ", 2155 )

CREATE CURSOR v;
   ( date d, ;
     name c(25), ;
     desc c(50), ;
     debit n(7), ;
     credit n(7))

INSERT INTO V VALUES ( CTOD( "6/1/2012" ), "person 1", "description 1", 10, 32 )
INSERT INTO V VALUES ( CTOD( "6/2/2012" ), "person 1", "desc 2", 235, 123 )
INSERT INTO V VALUES ( CTOD( "6/3/2012" ), "person 1", "desc 3", 22, 4 )
INSERT INTO V VALUES ( CTOD( "6/4/2012" ), "person 1", "desc 4", 53, 36 )
INSERT INTO V VALUES ( CTOD( "6/5/2012" ), "person 1", "desc 5", 31, 3 )
INSERT INTO V VALUES ( CTOD( "6/1/2012" ), "person 2", "another 1", 43, 664 )
INSERT INTO V VALUES ( CTOD( "6/4/2012" ), "person 2", "more desc", 78, 332 )
INSERT INTO V VALUES ( CTOD( "6/6/2012" ), "person 2", "anything", 366, 854 )

sd = CTOD( "6/3/2012" )      && start date of transactions
ed = DATE()  && current date as the end date...

现在,查询...您正在尝试按类型获取组,但每个人(姓名)需要首先在每个人的基础上预先聚合。现在,您似乎正在尝试获取开始日期 (sd) 之前交易的总期初余额作为给定时间点的基础,然后查看相关开始/结束日期内的活动。首先执行此操作,但不要处理从“vn”表中添加“obal”列。由于它需要非按列分组的聚合,我将只使用列的“MAX()”。由于它是基于 PK(名称)的,因此您最终会得到任何结果,但会使用汇总的事务总数,但通过...将所有数据预先汇总到一行中

select;
      vn.name,;
      vn.type,;
      MAX( vn.obal ) as BalByNameOnly,;
      SUM( IIF( v.date < sd, v.credit-v.debit, 000000.00 )) OpBal, ;
      SUM( IIF( BETWEEN(v.date, sd, ed), v.credit - v.debit, 000000.00 )) CurBal ;
   FROM ;
      v,;
      vn ;
   WHERE ;
      v.name = vn.name;
   GROUP BY ;
      vn.Name,;
      vn.Type;
   INTO ;
      CURSOR C_JustByName READWRITE 

有了这个结果(来自我的样本数据)看起来像......

Name      Type            BalByNameOnly   OpBal   CurBal
person 1  person type 1       125         -90     -63 
person 2  another type       2155         621     742

您按类型获取的最终聚合,您只需查询上述结果“光标”(C_JustByName)并使用 IT 来按类型、拥有等进行分组......类似

SELECT ;
      JBN.type, ;
      JBN.BalByNameOnly - JBN.OpBal as OpBal,;
      JBN.CurBal ;
    FROM ;
       C_JustByName JBN ;
    GROUP BY ;
       vn.type ;
    ORDER BY ;
       vn.type ;
    HAVING ;
       OpBal + CurBal != 0;
    INTO ;
       CURSOR C_Final

现在,我只是简化上述内容,因为我不知道您真正在寻找什么作为“VN”中的日期(看起来像一个客户表),日期不清楚其目的和它的 oBal 列关于事务表。

VFP 的好处是您可以在不创建永久表的情况下查询临时游标,并使用 IT 作为之后任何查询的基础......它有助于提高可读性,而不必在查询中嵌套查询。它还允许您查看每一层的结果,并知道您正在获得您期望的答案,然后再继续下一个查询阶段......

希望这将帮助您朝着您要解决的问题的方向前进。

于 2012-07-14T02:10:17.167 回答
0

再一次,这个问题被推到了前面:(在我发表评论之前:

不幸的是,这个 6 年前的问题被撞到了头版 :( 原因是这个问题渴望得到一些解释,而设计中充满了缺陷。不知道这些细节,没有答案是好的。1)从不永远依靠旧的引擎行为来解决此错误。这是一个错误并已更正。依靠错误不是要走的路。2)要创建一个日期值,永远不要通过从字符串转换来做到这一点。这取决于设置。最好使用可靠的 date()、datetime() 函数或严格的 date\datetime 文字。

无论如何,该评论仍然存在,还有更多。

让我们根据我对问题的理解添加更多关于缺陷和回复的信息。

在问题中,OP 说:“

SELECT vn.type, SUM(vn.obal + IIF(v.date < sd, v.credit-v.debit, 0)) OpBal, ;
    SUM(IIF(BETWEEN(v.date, sd, ed), v.credit-v.debit, 0)) CurBal ;
    FROM v, vn WHERE v.name = vn.name GROUP BY vn.type ;
    ORDER BY vn.type HAVING OpBal + CurBal != 0

工作正常。”

但是当然,任何使用 SQL 的经验丰富的开发人员都会立即发现它不能正常工作,结果可能只是巧合。让我们弄清楚为什么它不会真正起作用。首先让我们创建一些描述 OP 数据的游标:

CREATE CURSOR vn ( date d, name c(25), type c(25), obal n(7) )

INSERT INTO vn (date, name, type, obal) VALUES ( DATE(2012,5,20), "abc", "bank", 100 )
INSERT INTO vn (date, name, type, obal) VALUES ( DATE(2012,5,20), "def", "bank", 200 )
INSERT INTO vn (date, name, type, obal) VALUES ( DATE(2012,5,20), "ghi", "bank", 300 )
INSERT INTO vn (date, name, type, obal) VALUES ( DATE(2012,5,20), "xyz", "ledger", 400 )
INSERT INTO vn (date, name, type, obal) VALUES ( DATE(2012,5,20), "pqr", "ledger", 500 )

CREATE CURSOR v ( date d, name c(25), desc c(50), debit n(7), credit n(7))

INSERT INTO V (date,name,desc,debit,credit) VALUES ( DATE(2012,6,1), "abc", "description 1", 50, 0 )
INSERT INTO V (date,name,desc,debit,credit) VALUES ( DATE(2012,6,2), "abc", "description 1", 60, 0 )
INSERT INTO V (date,name,desc,debit,credit) VALUES ( DATE(2012,6,3), "abc", "description 1", 70, 0 )
INSERT INTO V (date,name,desc,debit,credit) VALUES ( DATE(2012,6,1), "def", "description 1", 50, 0 )
INSERT INTO V (date,name,desc,debit,credit) VALUES ( DATE(2012,6,2), "def", "description 1", 60, 0 )
INSERT INTO V (date,name,desc,debit,credit) VALUES ( DATE(2012,6,3), "def", "description 1", 70, 0 )
INSERT INTO V (date,name,desc,debit,credit) VALUES ( DATE(2012,6,1), "ghi", "description 1", 50, 0 )
INSERT INTO V (date,name,desc,debit,credit) VALUES ( DATE(2012,6,2), "ghi", "description 1", 60, 0 )
INSERT INTO V (date,name,desc,debit,credit) VALUES ( DATE(2012,6,4), "xyz", "description 1", 50, 0 )
INSERT INTO V (date,name,desc,debit,credit) VALUES ( DATE(2012,6,5), "xyz", "description 1", 60, 0 )
INSERT INTO V (date,name,desc,debit,credit) VALUES ( DATE(2012,6,6), "pqr", "description 1", 50, 0 )
INSERT INTO V (date,name,desc,debit,credit) VALUES ( DATE(2012,6,7), "pqr", "description 1", 60, 0 )
INSERT INTO V (date,name,desc,debit,credit) VALUES ( DATE(2012,6,8), "pqr", "description 1", 70, 0 )

让我们对这些数据运行 OP 的查询:

LOCAL sd,ed sd = date(2012,6,1) && 交易开始日期 ed = DATE() && 当前日期作为结束日期...

SELECT vn.type, SUM(vn.obal + IIF(v.date < sd, v.credit-v.debit, 0)) OpBal, ;
    SUM(IIF(BETWEEN(v.date, sd, ed), v.credit-v.debit, 0)) CurBal ;
    FROM v, vn WHERE v.name = vn.name GROUP BY vn.type ;
    ORDER BY vn.type HAVING OpBal + CurBal != 0

我们得到如下结果:

TYPE    OPBAL   CURBAL
------- -----   ------
bank     1500     -470
ledger   2300     -290

这显然是不正确的。通过这样的查询,您获得的贷方或借方越多,您的期初余额就越多。让我们看看为什么会发生这种情况,删除分组依据和聚合,检查我们真正总结的内容:

SELECT vn.type, vn.name, v.date, vn.obal, v.credit, v.debit ;
    FROM v, vn ;
    WHERE v.name = vn.name

输出:

  TYPE     NAME DATE         OBAL CREDIT DEBIT
  bank     abc  06/01/2012    100      0    50
  bank     abc  06/02/2012    100      0    60
  bank     abc  06/03/2012    100      0    70
  bank     def  06/01/2012    200      0    50
  bank     def  06/02/2012    200      0    60
  bank     def  06/03/2012    200      0    70
  bank     ghi  06/01/2012    300      0    50
  bank     ghi  06/02/2012    300      0    60
  ledger   xyz  06/04/2012    400      0    50
  ledger   xyz  06/05/2012    400      0    60
  ledger   pqr  06/06/2012    500      0    50
  ledger   pqr  06/07/2012    500      0    60
  ledger   pqr  06/08/2012    500      0    70

您可以看到,假设 'abc' OBal 100 重复了 3 次,因为 v 中有 3 个条目。当它只有 100 时,求和会使它变成 300。

当您使用像 SUM() 或 AVG() 这样的聚合时,您应该先在没有连接的情况下进行聚合,然后再进行连接。您仍然可以使用连接进行聚合,提供,连接会产生一对多的关系。如果上述结果集是:

  TYPE   NAME OBAL CREDIT DEBIT
  bank    abc  100      0   180
  bank    def  200      0   180
  bank    ghi  300      0   110
  ledger  xyz  400      0   110
  ledger  pqr  500      0   180

可以 SUM() BY 类型(一对多的一侧)。

话虽如此,添加 VFP 支持子查询让我们编写一个解决方案:

Local sd,ed
sd = Date(2012,6,1)  && start date of transactions
ed = Date()          && current date as the end date...

Select vn.Type, Sum(vn.OBal - tmp.TotCd) As Opbal, Sum(tmp.Curbal) As Curbal ;
    FROM vn ;
    LEFT Join ;
       (Select v.Name, Sum(Iif(v.Date < sd, v.credit-v.debit, 0)) TotCd, ;
               SUM(Iif(Between(v.Date, sd, ed), v.credit-v.debit, 0)) Curbal ;
        FROM v ;
        GROUP By v.Name ) tmp On tmp.Name = vn.Name ;
    GROUP By vn.Type ;
    ORDER By vn.Type ;
    HAVING Sum(vn.OBal - tmp.TotCd + tmp.Curbal) != 0

我们得到了我们想要的:

TYPE    OPBAL   CURBAL
------- -----   ------
bank      600     -470
ledger    900     -290
于 2019-09-04T11:20:18.767 回答