在查询循环中生成总计

时间:2018-04-08 22:34:05

标签: coldfusion cfquery cfloop

我有一个简单的查询,它通过ID来提取记录列表:

<cfquery name="resTotals" datasource="#datasource#">
   SELECT ticket_id
   FROM   closed_tickets
   WHERE  YEAR(closed_date) = '2017' 
   AND    ticket_type = 'residential' 
</cfquery>

然后我尝试在不同表的另一个查询中遍历这些ID,以便检查是否存在付款记录。目标是总计所有支付记录,以获得支付美元的总金额。

我有这样的查询,但是它会抛出一个错误:不能将字符串[99.00]转换为类型为[Array]的值

<cfloop query="resTotals">
     <cfquery name="resPaymentTotals" datasource="#datasource#">
        SELECT payment_amount
        FROM   payments
        WHERE  ticket_id = #resTotals.ticket_id#
     </cfquery>

 </cfloop>

 Amount of Sales: $ #ArraySum(resPaymentTotals.payment_amount)#

我做错了什么?我是在正确的轨道上吗?

3 个答案:

答案 0 :(得分:5)

正如错误消息所示,您正在使用一个需要数组的字符串。换句话说,ArraySum()需要一个数组,但是你传入一个字符串。从技术上讲,查询列是幕后的一个数组,但显然ArraySum并不是设计用于那样对待它。在这种情况下,CF将resPaymentTotals.payment_amount视为payment_amount列中第1个值的快捷方式,这是一个简单的值,而不是数组。

虽然您可以使当前代码工作,但在循环内查询效率非常低。你真正需要的是一个JOIN,而不是一个循环。要在单个查询中计算总票数 - 每个ticket_id:

<cfquery>
SELECT ct.ticket_id, SUM(p.payment_amount) AS TotalSales
FROM   closed_tickets ct LEFT JOIN payments p ON ct.ticket_id = p.ticket_id
WHERE  ct.closed_date >= '2017-01-01'
AND    ct.closed_date < '2018-01-01'
AND    ct.ticket_type = 'residential'
GROUP BY ct.ticket_id
</cfquery>

如果您想要所有票证的总票数,只需从SELECT中省略ticket_id。

<cfquery>
SELECT  SUM(p.payment_amount) AS TotalSales
FROM   closed_tickets ct LEFT JOIN payments p ON ct.ticket_id = p.ticket_id
WHERE  ct.closed_date >= '2017-01-01'
AND    ct.closed_date < '2018-01-01'
AND    ct.ticket_type = 'residential'
</cfquery>

答案 1 :(得分:2)

我只会使用一个查询,就像Ageax所说,只是JOIN表。执行此操作时,您必须考虑可能获得的重复主表行。

对于SQL:

SQL Fiddle

此安装程序假定是MS SQL Server。可以忽略它。

CREATE TABLE closed_tickets ( 
      ticket_ID int identity
    , closed_date date
    , ticket_type varchar(50) 
) ;
INSERT INTO closed_tickets (closed_date, ticket_type)
VALUES 
      ('2017-01-01','residential')
    , ('2017-01-02','commercial')
    , ('2017-01-03','residential')
    , ('2017-07-01','residential') /* No Payments */
    , ('2018-02-01','residential')
;

CREATE TABLE payment_amount ( 
      payment_ID int identity
    , ticket_id int
    , amount decimal(12,2) 
) ;
INSERT INTO payment_amount(ticket_id, amount)
VALUES 
      ( 1, 100.50 )
    , ( 2, 50.00 ) 
    , ( 3, 50.00 ) 
    , ( 2, null ) 
    , ( 1, 10.00 ) 
    , ( 2, 0.50 ) 
    , ( 1, 20.00 ) 
    , ( 2, 75.00 ) 
    , ( 5, 500.00 )
    , ( 3, 5 ) 
    , ( 3, null ) 
    , ( 3, 25 ) 
;

查询

<cfquery name="resTotals" datasource="#datasource#">
    SELECT ct.ticket_ID
      , sum(COALESCE(pa.amount,0))  AS totalPaymentAmount
      , sum(pa.amount)  AS badPaymentAmount /* What about NULLs? */
    FROM closed_tickets ct
    LEFT OUTER JOIN payment_amount pa ON ct.ticket_ID = pa.ticket_ID
    WHERE ct.ticket_type = 'residential'
      AND year(ct.closed_date) = 2017   /* year() returns INT *//* And you lose index use */
    GROUP BY ct.ticket_ID
</cfquery>

<强> This Gives You

| ticket_ID | totalPaymentAmount | badPaymentAmount |
|-----------|--------------------|------------------|
|         1 |              130.5 |            130.5 |
|         3 |                 80 |               80 |
|         4 |                  0 |           (null) |

所以现在我假设你将使用那些ticket_ID做一些事情;可能循环通过它们。 注意:我重命名查询以描述您要返回的数据。我们改变了它的定义。

<cfloop query="resTotals"> 
    [DO STUFF]

    Amount of Sales: $ #totalPaymentAmount)#
</cfloop>

注意ticket_id 3和4,以及totalPamentAmountbadPaymentAmount之间的差异。两者都有NULL付款值:3有明确的NULL,4有隐式NULL,因为没有匹配的行。通常情况下,如果您添加1+NULL,则会NULL,因为NULL没有价值;你想要1+0。 SQL sum()将考虑显式NULL,但不会考虑隐式NULL。对于隐式NULL,我们可以使用coalesce()返回第一个非NULL值。然后,我们可以为0提交NULL并为totalPaymentAmount进行数学运算。

答案 2 :(得分:0)

<cfset total = 0 >
<cfloop query="resTotals">
     <cfquery name="resPaymentTotals" datasource="#datasource#">
     select payment_amount
     from payments
     where ticket_id = #resTotals.ticket_id#
     </cfquery>
 <cfset total = total + resPaymentTotals.payment_amount >
 </cfloop>
 Amount of Sales: $ #total#