我有一个简单的查询,它通过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)#
我做错了什么?我是在正确的轨道上吗?
答案 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:
此安装程序假定是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,以及totalPamentAmount
和badPaymentAmount
之间的差异。两者都有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#