简体   繁体   English

sql select number除法汇总和功能

[英]sql select number divided aggregate sum function

I have this schema 我有这个架构

在此处输入图片说明

and I want to have a query to calculate the cost per consultant per hour per month. 我想查询一下每月每个顾问每小时的费用。 In other words, a consultant has a salary per month, I want to divide the amount of the salary between the hours that he/she worked that month. 换句话说,顾问每月有薪水,我想将薪水数额除以他/她当月工作的小时数。

SELECT
    concat_ws(' ', consultants.first_name::text, consultants.last_name::text) as name,
    EXTRACT(MONTH FROM tasks.init_time) as task_month,
    SUM(tasks.finish_time::timestamp::time - tasks.init_time::timestamp::time) as duration,
    EXTRACT(MONTH FROM salaries.payment_date) as salary_month,
    salaries.payment
FROM consultants
INNER JOIN tasks ON consultants.id = tasks.consultant_id
INNER JOIN salaries ON consultants.id = salaries.consultant_id
WHERE EXTRACT(MONTH FROM tasks.init_time) = EXTRACT(MONTH FROM salaries.payment_date)
GROUP BY (consultants.id, EXTRACT(MONTH FROM tasks.init_time), EXTRACT(MONTH FROM salaries.payment_date), salaries.payment);

It is not possible to do this in the select 在选择中无法执行此操作

salaries.payment / SUM(tasks.finish_time::timestamp::time - tasks.init_time::timestamp::time)

Is there another way to do it? 还有另一种方法吗? Is it possible to solve it in one query? 是否可以在一个查询中解决?

Assumptions made for this answer: 对此答案的假设:

The model is not entirely clear to me, so I am assuming the following: 该模型对我来说并不完全清楚,所以我假设以下内容:

  • you are using PostgreSQL 您正在使用PostgreSQL
  • salaries.date is defined as a date column that stores the day when a consultant was paid salaries.date定义为date列,用于存储支付顾问的date
  • tasks.init_time and task.finish_time are defined as timestamp storing the data & time when a consultant started and finished work on a specific task. tasks.init_timetask.finish_time定义为timestamp用于存储顾问开始和完成特定任务的数据和时间。

Your join on only the month is wrong as far as I can tell. 据我所知,您参加一个月的活动是错误的。 For one, because it would also include months from different years, but more importantly because this would lead to a result where the same row from salaries appeared several times. 首先,因为它也将包括不同年份的月份,但更重要的是,因为这将导致同一行salaries出现多次的结果。 I think you need to join on the complete date: 认为您需要在完整的日期加入:

FROM consultants c 
  JOIN tasks t ON c.id = t.consultant_id
  JOIN salaries s ON c.id = s.consultant_id 
                 AND t.init_time::date = s.payment_date --<< here

If my assumptions about the data types are correct, the cast to a timestamp and then back to a time is useless and wrong. 如果我对数据类型的假设是正确的,则将其强制转换为timestamp然后再转换为time是无用且错误的。 Useless because you can simply subtract to timestamps and wrong because you are ignoring the actual date in the timestamp so (although unlikely) if init_time and finish_time are not on the same day, the result is wrong. 无用的原因是您可以简单地减去时间戳,而错误的则是您忽略了时间戳中的实际日期,因此(尽管不太可能)如果init_timefinish_time不在同一天,则结果是错误的。

So the calculation of the duration can be simplified to: 因此,持续时间的计算可以简化为:

t.finish_time - t.init_time

To get the cost per hour per month, you need to convert the interval (which is the result when subtracting one timestamp from another) to a decimal indicating the hours, you can do this by extracting the seconds from the interval and then dividing that by 3600, eg 要获取每月的每小时成本,您需要将interval (这是从另一个时间戳中减去一个时间戳的结果)转换为表示小时的小数,可以通过从间隔中提取秒数然后除以3600,例如

extract(epoch from sum(t.finish_time - t.init_time)) / 3600)

If you divide the sum of the payments by that number you get your cost per hour per month: 如果您将付款总额除以该数字,则得出每月每小时的费用:

SELECT concat_ws(' ', c.first_name, c.last_name) as name,
       to_char(s.payment_date, 'yyyy-mm') as salary_month,
       extract(epoch from sum(t.finish_time - t.init_time)) / 3600 as worked_hours, 
       sum(s.payment) / (extract(epoch from sum(t.finish_time - t.init_time)) / 3600) as cost_per_hour
FROM consultants c 
  JOIN tasks t ON c.id = t.consultant_id
  JOIN salaries s ON c.id = s.consultant_id AND t.init_time::date = s.payment_date
GROUP BY c.id, to_char(s.payment_date, 'yyyy-mm') --<< no parentheses!
order by name, salary_month;

As you want the report broken down by month you should convert the month into something that contains the year as well. 如果您希望按月细分报告,则应将月份也转换为包含年份的内容。 I used to_char() to get a string with only year and month. 我使用to_char()获得只有年份和月份的字符串。 You also need to remove salaries.payment from the group by clause. 您还需要从group by子句中删除salaries.payment

You also don't need the "payment month" and "salary month" because both will always be the same as that is the join condition. 您也不需要“付款月份”和“薪水月份”,因为两者始终是相同的,即加入条件。

And finally you don't need the cast to ::text for the name columns because they are most certainly defined as varchar or text anyway. 最后,您不需要将name列强制转换为::text ,因为无论如何它们肯定被定义为varchartext

The sample data I made up for this: http://sqlfiddle.com/#!15/ae0c9 我为此准备的样本数据: http : //sqlfiddle.com/#!15/ae0c9


Somewhat unrelated, but: You should also not put the column list of the group by in parentheses. 有点不相关,但是:您也不要在括号中放置分组依据的列列表。 Putting a column list in parentheses in Postgres creates an anonymous record which is something completely different then having multiple columns. 将列列表放在Postgres的括号中会创建一个匿名记录,该记录与具有多个列的记录完全不同。 This is also true for the columns in the select list. 对于选择列表中的列也是如此。

If at all the target is putting it in one query, then just confirming, have you tried to achieve it using CTEs? 如果目标只是将其放在一个查询中,那么只需确认一下,您是否尝试过使用CTE实现它? Like 喜欢

    ;WITH cte_pymt
    AS
    (
    //Your existing query 1
    )
    SELECT <your required data> FROM cte_pymt

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM