在查询循环中生成总数
Generating a total within a query loop
我有一个简单的查询,可以按 ID 提取记录列表:
<cfquery name="resTotals" datasource="#datasource#">
SELECT ticket_id
FROM closed_tickets
WHERE YEAR(closed_date) = '2017'
AND ticket_type = 'residential'
</cfquery>
然后我试图在另一个 table 的另一个查询中循环遍历这些 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)#
我做错了什么?我在正确的轨道上吗?
<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#
如错误消息所示,您在需要数组的地方使用了字符串。换句话说,ArraySum() 需要一个数组,但您传递的是一个字符串。从技术上讲,查询列是幕后的数组,但显然 ArraySum 并非旨在以这种方式处理它。在这种情况下,CF 将 resPaymentTotals.payment_amount
视为 payment_amount 列中第一个值的快捷方式,这是一个简单值,而不是数组。
虽然您可以使当前代码正常工作,但在循环内查询效率非常低。您真正需要的是 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>
如果您想要 all 票的总计,只需从 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>
我只使用一个查询,就像 Ageax 所说的那样,只使用 JOIN
个 table。执行此操作时,您必须考虑可能获得的重复主 table 行。
对于SQL:
此设置假设MS SQL服务器。可以忽略。
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>
| ticket_ID | totalPaymentAmount | badPaymentAmount |
|-----------|--------------------|------------------|
| 1 | 130.5 | 130.5 |
| 3 | 80 | 80 |
| 4 | 0 | (null) |
所以现在我假设你会用这些做一些事情 ticket_IDs;可能循环遍历它们。 注意:我会重命名查询以描述您正在 returning 的数据。我们改变了它的定义。
<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()
到 return 第一个非 NULL
值。然后我们可以用 0
代替 NULL
并为 totalPaymentAmount
.
做数学运算
我有一个简单的查询,可以按 ID 提取记录列表:
<cfquery name="resTotals" datasource="#datasource#">
SELECT ticket_id
FROM closed_tickets
WHERE YEAR(closed_date) = '2017'
AND ticket_type = 'residential'
</cfquery>
然后我试图在另一个 table 的另一个查询中循环遍历这些 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)#
我做错了什么?我在正确的轨道上吗?
<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#
如错误消息所示,您在需要数组的地方使用了字符串。换句话说,ArraySum() 需要一个数组,但您传递的是一个字符串。从技术上讲,查询列是幕后的数组,但显然 ArraySum 并非旨在以这种方式处理它。在这种情况下,CF 将 resPaymentTotals.payment_amount
视为 payment_amount 列中第一个值的快捷方式,这是一个简单值,而不是数组。
虽然您可以使当前代码正常工作,但在循环内查询效率非常低。您真正需要的是 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>
如果您想要 all 票的总计,只需从 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>
我只使用一个查询,就像 Ageax 所说的那样,只使用 JOIN
个 table。执行此操作时,您必须考虑可能获得的重复主 table 行。
对于SQL:
此设置假设MS SQL服务器。可以忽略。
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>
| ticket_ID | totalPaymentAmount | badPaymentAmount |
|-----------|--------------------|------------------|
| 1 | 130.5 | 130.5 |
| 3 | 80 | 80 |
| 4 | 0 | (null) |
所以现在我假设你会用这些做一些事情 ticket_IDs;可能循环遍历它们。 注意:我会重命名查询以描述您正在 returning 的数据。我们改变了它的定义。
<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()
到 return 第一个非 NULL
值。然后我们可以用 0
代替 NULL
并为 totalPaymentAmount
.