PostgreSQL,带条件的聚合和

PostgreSQL,带条件的聚合和,postgresql,Postgresql,我有两张代表账单的桌子。 账单清单和账单内容及其相关数据应在逻辑上“连接”以获得适当的结果。示例表列名是非常自描述性的 DROP TABLE IF EXISTS my_list; CREATE TABLE my_list (indexl int PRIMARY KEY, docnum int, mytime text, rebate double precision, status text); INSERT INTO my_list (indexl, docnum, mytime,

我有两张代表账单的桌子。
账单清单和账单内容及其相关数据应在逻辑上“连接”以获得适当的结果。示例表列名是非常自描述性的

DROP TABLE IF EXISTS my_list;
CREATE TABLE my_list (indexl int PRIMARY KEY, docnum int, mytime text, rebate double precision, status text);

INSERT INTO my_list    
(indexl, docnum,  mytime,              rebate, status) VALUES 
    (10,      5,  '01.01.2014 15:20:31',    0,    ''), 
    (11,      6,  '02.01.2014 11:10:11',   10,    ''),  
    (12,      7,  '02.01.2014 09:15:01',    0,    ''), 
    (14,      8,  '03.01.2014 12:12:49',   12,    ''),  
    (17,      9,  '04.01.2014 08:19:19',   10,    ''), 
    (18,     10,  '04.01.2014 10:10:10',    0,   'S'), 
    (19,     11,  '04.01.2014 01:04:14',    0,   'B'),
    (21,     12,  '05.01.2014 02:49:14',    0,    ''),
    (22,     13,  '12.01.2014 08:12:17',    0,    '');

DROP TABLE IF EXISTS my_content;
CREATE TABLE my_content (indexc int PRIMARY KEY, docnum int, code int, 
       aname text, price double precision, qty double precision, secondtax double precision, meas text);

INSERT INTO my_content 
(indexc, docnum, code, aname,      price, qty, secondtax, meas) VALUES 
    (10,      5,  587, 'spaghetti',   75,   1,         0, 'kg'), 
    (15,      6, 3432, 'salt',         3,   2,         0, 'kg'), 
    (16,     12,   32, 'olive oil',    4, 1.5,         5, 'kg'), 
    (29,      7, 3432, 'salt',         3,   1,         0, 'kg'), 
    (17,      6,  449, 'sugar',        5,   2,         0, 'kg'), 
    (18,      7, 1582, 'dried eggs',  50, 1.2,         0, 'kg'), 
    (19,      8,  210, 'tomato',      80, 5.5,         0, 'kg'), 
    (20,      9,  211, 'mustard',      5,   3,         5, 'kg'), 
    (22,     10, 2014, 'clove',        1,   1,         0, 'kg'), 
    (23,      9,    8, 'oil',        120,   4,         0, 'lit'), 
    (24,     11,  816, 'laurel',       4,   1,         0, 'kg'), 
    (25,      8, 1582, 'dried eggs',  10, 0.2,         0, 'kg'), 
    (26,     12,   32, 'olive oil',    4,   1,         0, 'kg'), 
    (28,     13,   67, 'corned beef', 40, 0.5,         0, 'kg'); 
为了分析这些账单,我做了一个查询,这个查询几乎可以很好地工作,但它肯定可以写得更好、更短、更优雅、更恰当

SELECT s.code, 
   s.aname, 
   SUM(   s.qty) AS sumused, 
   SUM( s.price * s.qty) AS bruttoprice,
   SUM((          s.price/100 * l.rebate) * s.qty) AS sumrebate, 
   SUM((s.price - s.price/100 * l.rebate) * s.qty) AS clearprice,
   SUM((s.price - s.price/100 * l.rebate) * s.qty/100 * s.secondtax) AS sumsecondtax,  
   SUM((s.price - s.price/100 * l.rebate) * s.qty - (s.price - s.price/100 * l.rebate) * s.qty/100 * s.secondtax) AS sumwithoutsecondtax  
FROM   my_content s, my_list l 
WHERE  s.docnum = l.docnum 
       AND NOT l.status='S' 
       AND l.mytime BETWEEN '02.01.2014 00:00:00' AND '05.01.2014 23:59:59' 
GROUP  BY s.code, s.aname, l.status 
ORDER  BY sumused 
1) 是否可以将查询中的表达式(s.price/100*l.Retrieve)*s.qty)替换为变量,这样就不必一直写入?理想的情况是,如果我能写出例如SUM(clearprice*s.secondtax)

2) 如果bill在列表中有satus'S,则必须跳过内容中的行,而不是l.status='S',这是通过条件解决的。但如果状态为“B”,则意味着具有该docnum的值必须减去(而不是相加)以求和。也许更优雅的解决方案是将这些行中的数量乘以值0-qty。
在显示的查询中,状态“B”被忽略,因为我不知道如何应用它。 怎么做

3) 实际上,我只需要按s.code对结果进行分组,但我无法从group by中删除s.aname和l.status,因为这样的查询不想工作。在实际情况下,如果我更改某些代码的名称,它将单独显示不需要的内容。
是否可以只按代码对结果进行分组,但s.aname(比如最后一个)将显示在结果中

我尽我所能创建示例表并以轻松/即时的方式进行查询。
请就具体问题提供建议或/和示例。
谢谢

编辑 我通过“kordirko”解决了我的问题

SELECT code, 
   MAX(aname)            AS aname, 
   SUM(newqty)           AS sumused, 
   SUM(price   * newqty) AS bruttoprice,
   SUM(crebate * newqty) AS sumrebate, 
   SUM(cprice  * newqty) AS clearprice,
   SUM(cprice  * newqty/100 * secondtax) AS sumsecondtax,  
   SUM(cprice  * newqty - cprice * newqty/100 * secondtax) AS sumwithoutsecondtax 
FROM (
   SELECT s.*, l.*,
          s.price/100 * l.rebate AS crebate,
          s.price - s.price/100 * l.rebate AS cprice, 
          CASE WHEN l.status = 'B' THEN 0 - s.qty ELSE s.qty END AS newqty  
   FROM   my_content s, my_list l 
   WHERE  s.docnum = l.docnum 
   AND NOT l.status='S' 
   AND l.mytime BETWEEN '02.01.2014 00:00:00' AND '05.01.2014 23:59:59'
 ) AS someAliasWhichhavetobehere
GROUP BY code 
ORDER BY sumused;

一切似乎都很好,只是结果中的值为-0,但我认为这不会导致进一步计算的问题。如果你愿意的话,我怎样才能摆脱它呢?

问题1


是的,可以使用子查询。
在子查询中计算变量
some_variable
,并以以下方式在外部查询中使用:

SELECT code, 
   aname, 
   SUM(     qty) AS sumused, 
   SUM(   price *  qty) AS bruttoprice,
   SUM((          some_variable) * qty) AS sumrebate, 
   SUM((price - some_variable) * qty) AS clearprice,
   SUM((price - some_variable) * qty/100 * secondtax) AS sumsecondtax,  
   SUM((price - some_variable) * qty - (price - some_variable) * qty/100 * secondtax) AS sumwithoutsecondtax 
FROM (
  SELECT s.*, l.*,
         s.price/100 * l.rebate some_variable
  FROM   my_content s, my_list l 
  WHERE  s.docnum = l.docnum 
         AND NOT l.status='S' 
         AND l.mytime BETWEEN '02.01.2014 00:00:00' AND '05.01.2014 23:59:59'
) as Alias
GROUP  BY code, aname, status 
ORDER  BY sumused ;
请查看此-->


问题2


请提供更多细节。现在还不清楚到底哪些值需要减去。你是指价格,还是数量,或者整个表达式,或者其他什么 一般来说,这可以通过使用
CASE。。。什么时候然后
表达式,例如:

SELECT ....
     SUM (   CASE WHEN status = 'B' 
                  THEN - price * qty
                  ELSE  price * qty
             END
          ) As column_alias,
     ....
FROM ....
或者可能是这样的:(当状态='B'时,然后乘以-1,否则乘以1):


问题3


我会使用
MAX
MIN
函数,这是最简单的方法。它们检索一个随机名称(最大值或最小值)

请查看此-->


问题4-是什么:
s.*
l.*


基本上,这是我的(坏?)习惯,来自Oracle SQL:)

每个人都知道这种语法:
从某个表中选择*


*
意思是:给我表格中的所有列。

假设我们想要获得所有列,但我们还想在结果集中显示一个计算值,显而易见的方法是:

SELECT *, 
       price * qty AS amount
FROM table
这在PostGeSQL中工作得很好,但不幸的是在Oracle中这会导致语法错误。
Oracle强制我们使用表名或别名:

SELECT table.*, 
       price * qty AS amount
FROM table;

SELECT t.*, 
       price * qty AS amount
FROM table t
类似地,在使用联接时-这项工作非常好:

SELECT *
FROM table1 JOIN table2 ON ....
但这会在Oracle中产生语法错误:

SELECT *,
       price * qty AS amount
FROM table1 JOIN table2 ON ....
我们必须在此处使用表名或别名:

SELECT table1.*,
       table2.*,
       price * qty AS amount
FROM table1 JOIN table2 ON ....

SELECT t1.*,
       t2.*,
       price * qty AS amount
FROM table1 t1  JOIN table2 AS t2  ON ....
幸运的是,这符合ANSI SQL语法,应该适用于所有数据库:)


问题5为什么将子查询结果命名为别名


因为PostgeSQL要求为子查询提供名称(别名)。
类似地,MySql也需要别名:

SELECT ....
FROM(
   subquery
) some_name

... or ....

SELECT ....
FROM(
   subquery
) AS some_name
只有在Oracle中,子查询不需要别名(但可以是),在Oracle中,这很好:

SELECT ....
FROM (
  subquery ....
) 

你好,科迪尔科,非常感谢你的帮助。我需要更多的时间来理解你的建议/例子。s.*和l.*在子查询中的所有字段是什么意思?关于问题2。“B”表示已售出物品被退回,所有正常计算为正值的值必须计算为负值。我认为这可以通过乘以负数量(0-数量)来实现,但我可能错了。3.我明白。太完美了!我也成功地使用了解决方案1。为什么在以后的代码中从未使用过子查询结果的别名?不,问题2我还不能解决。是否可以使用相同的子查询在这方面提供帮助?FROM(选择s.*、l.*、s.price/100*l.Retrieve作为crebate,如果l.status='B'那么数量=-1*数量其他数量=1*数量结束为我的内容s、我的列表l…中的新数量,然后在第一选择中使用新数量的值?我尝试了,但在这里得到错误:SUM(newqty)如前所述,…我已经为我的答案添加了额外的解释,请看一下。太好了!感谢所有信息。我最终解决了我的问题,并将其发布到问题。请告诉我是否有错误。
SELECT ....
FROM(
   subquery
) some_name

... or ....

SELECT ....
FROM(
   subquery
) AS some_name
SELECT ....
FROM (
  subquery ....
)