我正在查询postgresql 9.4数据库,我想使用同一查询中的列执行计算。
我想要获取的结果是部分值,基于total_days金额超过的天数。 E.g。
如果我今天推出查询,05/01/2016,我想获得:
partial_result = value_x * passed_days / total_days
120 * 5 / 60
在我的数据集中,我有超过100k的记录,我需要按月分组这个部分值(逐月添加部分值)。
=============================================== ==========================
在 MySQL 中,我可以按如下方式进行计算:
SELECT
start_date,
duration_in_months,
@end_date:= DATE_ADD(start_date, INTERVAL duration_in_months MONTH) as end_date,
@total_days:= DATEDIFF(@end_date, start_date),
@passed_days:= DATEDIFF(CURDATE(), start_date),
value_x,
(value_x * @passed_days / @total_days) as partial_result
FROM table;
按照此question previously asked中的说明,我目前在 PostgreSQL 中使用如下查询:
SELECT
start_date,
duration_in_months,
end_date,
total_days,
value_x,
(value_x * passed_days / total_days) as partial_result
FROM (SELECT *,
(start_date + (duration_in_months || ' month')::INTERVAL) as end_date,
EXTRACT(DAY FROM (start_date + (duration_in_months || ' month')::INTERVAL) - start_date) as total_days,
EXTRACT(DAY FROM current_date - start_date) as passed_days
FROM table) as table1;
我需要你的帮助才能:
插入where子句以确保
passed_days> = 0和passed_days< = total_days
非常感谢您提前了解并随时提出更多详情。
答案 0 :(得分:1)
首先,您的MySQL查询无法保证正常工作。 MySQL文档非常明确,SELECT
中表达式的评估顺序可以是任意的。因此,可以在设置变量之前评估最后一个表达式(实际上,它们将设置为上一行中的值)。
在Postgres中,我认为你对子查询或CTE有正确的想法。您只需引用没有@
的列。我不知道具体的日期算术是否正确,但这是等效的查询:
SELECT start_date, duration_in_months, end_date, total_days, value_x,
(value_x * passed_days / total_days) as partial_result
FROM (SELECT t.*,
(start_date + (duration_in_months || ' month')::INTERVAL) as end_date,
EXTRACT(DAY FROM (start_date + (duration_in_months || ' month')::INTERVAL) - start_date) as total_days,
EXTRACT(DAY FROM current_date - start_date) as passed_days
FROM table t
) t;
我extract(day)
看起来不对,但您从interval
而不是日期/时间表达式中提取日期。我认为它符合你的要求。
答案 1 :(得分:1)
因为你的表达式互相使用,你应该使用多个子查询(如果你不想重复任何表达式)。
或者,您可以使用LATERAL
subqueries,f.ex:
SELECT start_date,
duration_in_months,
end_date,
total_days,
passed_days,
value_x,
(value_x * passed_days / total_days) as partial_result
FROM table,
LATERAL (SELECT (start_date + (duration_in_months * INTERVAL '1 month'))::date end_date) end_date,
LATERAL (SELECT end_date - start_date::date total_days) total_days,
LATERAL (SELECT current_date - start_date::date passed_days) passed_days
在PostgreSQL中可以使用DATEDIFF
计算 date1 - date2
,不需要使用EXTRACT
(但参数的类型必须为date
; timestamp(tz)
差异会产生interval
S)。
您可以使用GREATEST
and LEAST
来约束passed_days
(如果您想要选择所有行),但如果您愿意,也可以在passed_days
中使用WHERE
。< / p>
答案 2 :(得分:0)
我在PostgreSQL中找到了一个合适的解决方案:
=============================================== ==========================
WITH time_ranges AS (
SELECT to_date('2014-07-01', 'yyyy-mm-dd') as START_DATE, to_date('2014-07-31', 'yyyy-mm-dd') as END_DATE
UNION SELECT to_date('2014-07-01', 'yyyy-mm-dd'), to_date('2014-08-31', 'yyyy-mm-dd')
UNION SELECT to_date('2014-07-01', 'yyyy-mm-dd'), to_date('2014-09-30', 'yyyy-mm-dd')
UNION SELECT to_date('2014-07-01', 'yyyy-mm-dd'), to_date('2014-10-31', 'yyyy-mm-dd')
UNION SELECT to_date('2014-07-01', 'yyyy-mm-dd'), to_date('2014-11-30', 'yyyy-mm-dd')
UNION SELECT to_date('2014-07-01', 'yyyy-mm-dd'), to_date('2014-12-31', 'yyyy-mm-dd')
UNION SELECT to_date('2014-07-01', 'yyyy-mm-dd'), to_date('2015-01-31', 'yyyy-mm-dd')
UNION SELECT to_date('2014-07-01', 'yyyy-mm-dd'), to_date('2015-02-28', 'yyyy-mm-dd')
UNION SELECT to_date('2014-07-01', 'yyyy-mm-dd'), to_date('2015-03-31', 'yyyy-mm-dd')
UNION SELECT to_date('2014-07-01', 'yyyy-mm-dd'), to_date('2015-04-30', 'yyyy-mm-dd')
UNION SELECT to_date('2014-07-01', 'yyyy-mm-dd'), to_date('2015-05-31', 'yyyy-mm-dd')
UNION SELECT to_date('2014-07-01', 'yyyy-mm-dd'), to_date('2015-06-30', 'yyyy-mm-dd')
UNION SELECT to_date('2014-07-01', 'yyyy-mm-dd'), to_date('2015-07-31', 'yyyy-mm-dd')
UNION SELECT to_date('2014-07-01', 'yyyy-mm-dd'), to_date('2015-08-31', 'yyyy-mm-dd')
UNION SELECT to_date('2014-07-01', 'yyyy-mm-dd'), to_date('2015-09-30', 'yyyy-mm-dd')
UNION SELECT to_date('2014-07-01', 'yyyy-mm-dd'), to_date('2015-10-31', 'yyyy-mm-dd')
UNION SELECT to_date('2014-07-01', 'yyyy-mm-dd'), to_date('2015-11-30', 'yyyy-mm-dd')
UNION SELECT to_date('2014-07-01', 'yyyy-mm-dd'), to_date('2015-12-31', 'yyyy-mm-dd')
UNION SELECT to_date('2014-07-01', 'yyyy-mm-dd'), to_date('2016-01-05', 'yyyy-mm-dd')
)
SELECT time_ranges.end_date, round(SUM(gross_pdu * LEAST(total_days, GREATEST( EXTRACT(DAY FROM(time_ranges.end_date - guarantees_days.start_date)), 0) ) / total_days)::numeric, 2)
FROM
(SELECT
*,
EXTRACT(DAY FROM (start_date + (duration_in_months || ' month')::INTERVAL) - start_date) as total_days
FROM subscribed_guarantees
) as guarantees_days
INNER JOIN
time_ranges ON
time_ranges.start_date <= guarantees_days.start_date AND guarantees_days.start_date <= time_ranges.end_date
WHERE INSURANCE_COMPANY = 'INSURANCE COMPANY' AND TAX = 13.5
GROUP BY
time_ranges.end_date
ORDER BY
time_ranges.end_date