SQL查询,用于选择表达非聚合值和聚合函数的列

时间:2012-07-07 06:43:32

标签: sql foxpro visual-foxpro

使用的表:

  

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

     'v'中的

名称是指vn table中的名称

     

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会被多次添加并在OpBal下显示。当查询被修改如下:

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

它显示错误消息,例如“分组子句丢失或无效”!

RDBMS使用MS Visual Foxpro 9. sd和ed是用于查询目的的日期类型变量,其中sd&lt;编辑。

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

3 个答案:

答案 0 :(得分: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而言,在将它们全部加在一起之前首先得到个人的总和应该无关紧要。

答案 1 :(得分: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

您按类型获取的最终聚合,您只需查询上面的结果“cursor”(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作为任何查询的基础...它有助于不必在查询内嵌入查询的可读性查询。它还允许您查看每个图层的结果,并知道您在继续下一个查询阶段之前得到了您期望的答案......

希望这会帮助您朝着您想要解决的方向前进。

答案 2 :(得分:0)

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

  

不幸的是,这个6年前的问题被碰到了   到首页:(原因是这个问题饿了一些   说明和设计都在尖叫缺陷。不知道   这些细节,没有答案会很好。 1)永远不要依赖旧的   enginebehavior解决此错误。这是一个错误,已更正。   依靠错误不是解决之道。 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的查询:

本地标准版 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重复了3次,因为v中有3个条目。求和将使其变为300,而恰好是100。

使用诸如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类型(1-to-many的1面)可以。

话虽如此,并添加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