I am trying to add two tables and get the total days from two dates. But having the following simple issue: not a single-group group function.
This is what I've tried so far:
SELECT COUNT(status) AS "Present Days",
(SELECT TRUNC(TO_DATE('01/10/2018', 'MM/DD/YYYY') - TO_DATE(k.JOINING_DATE, 'MM/DD/YYYY'))
FROM attendance m
INNER JOIN EMP_OFFICIAL k
ON k.EMPNO = m.EMPNO
WHERE m.empno='EMP00254'
AND m.status='P') AS "Total Days"
FROM attendance
WHERE empno = 'EMP00254'
AND status = 'P';
Can I get the days without using the DUAL?
First off, that subquery is most likely not scalar (it looks like you're assuming multiple rows can come from the attendance table) and second, it contains an unnecessary join..
What I would do instead is something like:
select count(*) as "Present Days",
to_date('01/10/2018', 'mm/dd/yyyy') - k.joining_date "Total Days"
from attendance a
inner join emp_official k on a.empno = k.empno
where a.empno = 'EMP00254'
and a.status = 'P'
group by to_date('01/10/2018', 'mm/dd/yyyy') - k.joining_date;
This does assume that the k.joining_date column is of DATE datatype and that 'k.empno' is a unique column.
If there is only one row in the EMP_OFFICIAL table for each employee then you could do:
SELECT COUNT(a.status) AS "Present Days",
TRUNC( SYSDATE ) - TRUNC( MIN( k.joining_date ) ) AS "Total Days"
FROM attendance a
RIGHT OUTER JOIN EMP_OFFICIAL k
ON ( k.EMPNO = a.EMPNO )
WHERE a.empno = 'EMP00254'
WHERE a.status = 'P';
or you could do the aggregation in a sub-query:
SELECT a."Present Days",
TRUNC( SYSDATE ) - TRUNC( k.joining_date ) AS "Total Days"
FROM (
SELECT EMPNO,
COUNT( status ) AS "Present Days"
FROM attendance
WHERE EMPNO = 'EMP00254'
AND status = 'P'
GROUP BY EMPNO
) a
RIGHT OUTER JOIN EMP_OFFICIAL k
ON ( k.EMPNO = a.EMPNO )
WHERE k.EMPNO = 'EMP00254';
Or you could use UNION ALL to query the two tables and have the result in two rows (rather than two columns):
SELECT 'Present Days' As type,
COUNT(status) AS Days
FROM attendance
WHERE empno = 'EMP00254'
WHERE status = 'P'
UNION ALL
SELECT 'Total Days',
TRUNC( SYSDATE ) - TRUNC( joining_date )
FROM EMP_OFFICIAL
WHERE empno = 'EMP00254';
Either remove COUNT(status) AS "Present Days" part and SELECT inside paranthesis :
SELECT (TRUNC(TO_DATE('01/10/2018', 'MM/DD/YYYY') - TO_DATE(k.JOINING_DATE, 'MM/DD/YYYY')) "time difference" FROM attendance m
INNER JOIN EMP_OFFICIAL k ON k.EMPNO = m.EMPNO where m.empno='EMP00254' and m.status='P') AS "Total Days"
FROM attendance where empno = 'EMP00254' and status = 'P';
OR
use the following (add GROUP BY expression at the end of sql):
SELECT COUNT(status) AS "Present Days",
(TRUNC(TO_DATE('01/10/2018', 'MM/DD/YYYY') - TO_DATE(k.JOINING_DATE, 'MM/DD/YYYY')) "time difference" FROM attendance m
INNER JOIN EMP_OFFICIAL k ON k.EMPNO = m.EMPNO where m.empno='EMP00254' and m.status='P') AS "Total Days"
FROM attendance where empno = 'EMP00254' and status = 'P'
GROUP BY (TRUNC(TO_DATE('01/10/2018', 'MM/DD/YYYY') - TO_DATE(k.JOINING_DATE, 'MM/DD/YYYY'));
Since , groupped and non-groupped items can not be used together.
By the way, you don't need to use dual in your inline select statement like ( SELECT TRUNC(TO_DATE('01/10/2018', 'MM/DD/YYYY') - TO_DATE(k.JOINING_DATE, 'MM/DD/YYYY') FROM DUAL ), just ( TRUNC(TO_DATE('01/10/2018', 'MM/DD/YYYY') - TO_DATE(k.JOINING_DATE, 'MM/DD/YYYY')) is enough.
Related
I have this query in Metabase:
with l1 as (SELECT date_trunc ('day', Ticket_Escalated_At) as time_scale, count (Ticket_ID) as chat_per_day
FROM CHAT_TICKETS where SUPPORT_QUEUE = 'transfer_investigations'
and date_trunc('month', TICKET_ESCALATED_AT) > now() - interval '6' Month
GROUP by 1)
with l2 as (SELECT date_trunc('day', created_date) as week, count(*) as TI_watchman_ticket
FROM jira_issues
WHERE issue_type NOT IN ('Transfer - General', 'TI - Advanced')
and date_trunc('month', created_date) > now() - interval '6' Month
and project_key = 'TI2'
GROUP BY 1)
SELECT l1.* from l1
UNION SELECT l2.* from l2
ORDER by 1
and this one:
with hours as (SELECT date_trunc('day', ws.start_time) as date_
,(ifnull(sum((case when ws.shift_position = 'TI - Non-watchman' then (minutes_between(ws.end_time, ws.start_time)/60) end)),0) + ifnull(sum((case when ws.shift_position = 'TI - Watchman' then (minutes_between(ws.end_time, ws.start_time)/60) end)),0) ) as total
from chat_agents a
join wiw_shifts ws on a.email = ws.user_email
left join people_ops.employees h on substr(h.email,1, instr(h.email,'#revolut') - 1) = a.login
where (seniority != 'Lead' or seniority is null)
and date_trunc('month', ws.start_time) > now() - interval '6' Month
GROUP BY 1)
I would like to divide the output of the UNION of the first one, by the result of the second one, any ideas.
I have 2 tables where I'm trying to grab counts of student interactions by year, then sum them in their respective year. The code I've attached works, but I'm wondering if I've neglected a much easier way of doing this calculation. For example, if I wanted to do this for more than 2 years I'd be cursing myself doing it this way.
select s.id
, coalesce(cl2016.cl2016, 0) + coalesce(wf2016.wf2016, 0) as s2016
, coalesce(cl2017.cl2017, 0) + coalesce(wf2017.wf2017, 0) as s2017
from students s
left join (
select dm.student_id
, count(dm.meeting_id) as cl2016
from dim_meetings dm
where dm.start_time between '2016-01-01' and '2016-12-31'
group by dm.student_id
) cl2016 on cl2016.student_id = s.id
left join (
select dm.student_id
, count(dm.meeting_id) as cl2017
from dim_meetings dm
where dm.start_time between '2017-01-01' and '2017-12-31'
group by dm.student_id
) cl2017 on cl2017.student_id = s.id
left join (
select sub.student_id
, count(sub.id) as wf2016
from submissions sub
where sub.submitted_at between '2016-01-01' and '2016-12-31'
group by sub.student_id
) wf2016 on wf2016.student_id = s.id
left join (
select sub.student_id
, count(sub.id) as wf2017
from submissions sub
where sub.submitted_at between '2017-01-01' and '2017-12-31'
group by sub.student_id
) wf2017 on wf2017.student_id = s.id
Use conditional aggregation:
select s.id, dm.cl2016, cl2017, su.wf2016, su.wf2017,
coalesce(dm.cl2016, 0) + coalesce(su.wf2016, 0) as s2016,
coalesce(dm.cl2017, 0) + coalesce(su.wf2017, 0) as s2017
from students s left join
(select dm.student_id,
sum( (dm.start_time between '2016-01-01' and '2016-12-31')::int) as cl2016,
sum( (dm.start_time between '2017-01-01' and '2017-12-31')::int) as cl2017
from dim_meetings dm
where dm.start_time between '2016-01-01' and '2017-12-31'
group by dm.student_id
) dm
on dm.student_id = s.id left join
(select su.student_id,
sum( (su.start_time between '2016-01-01' and '2016-12-31')::int) as wf2016,
sum( (su.start_time between '2017-01-01' and '2017-12-31')::int) as wf2017
from submissions su
where su.start_time between '2016-01-01' and '2017-12-31'
group by su.student_id
) su
on su.student_id = s.id ;
It is useful to place a case expression inside an aggregate function as shown in the example below:
select
dm.student_id
, count(dm.start_time '2016-01-01'
and < dm.start_time '2017-01-01' then dm.meeting_id end) as cl2016
, count(dm.start_time '2017-01-01'
and < dm.start_time '2018-01-01' then dm.meeting_id end) as cl2017
from dim_meetings dm
where dm.start_time between '2016-01-01' and dm.start_time < '2018-01-01'
group by dm.student_id
;
A case expression returns a value, then that value is used within the aggregate function just as it would any other value.
Please also note that "between" is evil for date ranges, instead use explicit ranges via >= and < as shown above, which involves moving the upper boundary date up to the the next day. See:
Bad habits to kick : mis-handling date / range queries
What do BETWEEN and the devil have in common?
select
d.FK_SOCRD_ID,
d.SUBJECT_NUMBER,
c.PTLASTNAME || ', ' || c.PTFIRSTNAME as "Patient name",
min(b.created) as Data Entry 1,
max(b.created) as Data Entry 2,
min(b.created) - max(b.created)
|| ' days and '
|| TO_CHAR(to_date('01/01/2000', 'MM-DD-YYYY')
+ (b.created - b.created), 'HH24:MI:SS' ) AS Diff
FROM
CR_MDT a
full outer join CR_MDT_VERIFY b
on a.CR_MDT_ID = b.FK_CR_MDT_ID
left join patient c
on a.FK_SOCRD_ID = c.SOCRD_ID
left join PT_STUDY d
on c.SOCRD_ID = d.FK_SOCRD_ID
where
a.CR_MDT_DT between TO_DATE('01/01/2017', 'mm/dd/yyyy')
and TO_DATE('12/31/2017', 'mm/dd/yyyy')
group by
d.FK_SOCRD_ID,
d.SUBJECT_NUMBER,
c.PTLASTNAME,
c.PTFIRSTNAME,
b.created
How can I subtract the max(b.created) from min(b.created)? I'm stuck on this part:
|| TO_CHAR(to_date('01/01/2000', 'MM-DD-YYYY')
+ (b.created - b.created), 'HH24:MI:SS' ) AS Diff
I need help on how to write the subtraction part since b.created max and min are from the same column.
You can put that query in a subquery and do the subtraction outside:
SELECT *
, max_created - min_created as date_difference
FROM
(
SELECT d.FK_SOCRD_ID
, d.SUBJECT_NUMBER
, c.PTLASTNAME || ', ' || c.PTFIRSTNAME AS "Patient name"
, min(b.created) AS min_created
, max(b.created) AS max_created
FROM CR_MDT a
FULL JOIN CR_MDT_VERIFY b
ON a.CR_MDT_ID = b.FK_CR_MDT_ID
LEFT JOIN patient c
ON c.SOCRD_ID = a.FK_SOCRD_ID
LEFT JOIN PT_STUDY d
ON d.FK_SOCRD_ID = c.SOCRD_ID
WHERE a.CR_MDT_DT BETWEEN TO_DATE('01/01/2017', 'mm/dd/yyyy') AND TO_DATE('12/31/2017', 'mm/dd/yyyy')
GROUP BY d.FK_SOCRD_ID
, d.SUBJECT_NUMBER
, c.PTLASTNAME
, c.PTFIRSTNAME
) subq
You might have to change the datediff function depending on the dbms you are using.
EDIT: I updated the date subtraction part now that i know you use Oracle.
You can use floor(), leave only fractional part of difference and convert it to hh24:mi:ss, like here:
select id, mx, mn, floor(diff) ||' days and '||
to_char(trunc(sysdate) + diff - floor(diff), 'hh24:mi:ss') diff
from (select id, max(created) mx, min(created) mn,
max(created) - min(created) diff
from test group by id)
Test data:
create table test (id number(3), created date);
insert into test values (1, timestamp '2017-09-12 01:00:00');
insert into test values (1, timestamp '2017-11-05 11:24:17');
insert into test values (1, timestamp '2017-12-15 13:42:05');
insert into test values (2, timestamp '2017-01-05 11:00:00');
insert into test values (2, timestamp '2017-06-05 23:12:56');
Result:
ID MX MN DIFF
---- ------------------- ------------------- ---------------------
1 2017-12-15 13:42:05 2017-09-12 01:00:00 94 days and 12:42:05
2 2017-06-05 23:12:56 2017-01-05 11:00:00 151 days and 12:12:56
Here is what gave me the desired result:
SELECT d.FK_SOCRD_ID,
d.SUBJECT_NUMBER,
c.PTLASTNAME || ', ' || c.PTFIRSTNAME AS "Patient name",
min(b.created) AS min_created,
max(b.created) AS max_created,
trunc(max(b.created)) - trunc(min(b.created)) || ' days' AS Diff
FROM CR_MDT a
FULL JOIN CR_MDT_VERIFY b
ON a.CR_MDT_ID = b.FK_CR_MDT_ID
LEFT JOIN patient c
ON c.SOCRD_ID = a.FK_SOCRD_ID
LEFT JOIN PT_STUDY d
ON d.FK_SOCRD_ID = c.SOCRD_ID
WHERE a.CR_MDT_DT BETWEEN TO_DATE('01/01/2017', 'mm/dd/yyyy') AND TO_DATE('12/31/2017', 'mm/dd/yyyy')
GROUP BY d.FK_SOCRD_ID,
d.SUBJECT_NUMBER,
c.PTLASTNAME,
c.PTFIRSTNAME
I wrote this query. But, I am wondering if there is any better/professional way to rewrite this.
Find the employees who would receive bonuses in the current year. The condition for receiving bonus is, he would have to sell a specific category of product of the amount of at least $4000.
select yy.CurrentMonth, yy.ID, yy.Name, sum(yy.commission) as comm_total, sum(yy.bonus) as bonus_total
from
(select sysdate as CurrentMonth, ee.ID, ee.Name, cc.commission, (cc.commission * 25 / 100) as bonus
from employee ee,
(select e.id, pc.category_name, sum(quantity) as qty, pc.commission_rate, sum(quantity*pc.commission_rate) as commission
from sales s, product p, product_category pc, employee e
where to_char(sales_date, 'yyyy') = to_char(sysdate, 'yyyy')
and s.employee_id = e.id
and s.product_id = p.id
and p.product_category_name = pc.category_name
group by pc.category_name, e.id, pc.commission_rate
order by e.id) cc
where ee.ID = cc.id
and cc.commission >=4000
) yy
group by yy.ID, yy.Name, yy.CurrentMonth
Edit: Table schema:
CREATE TABLE "XYZ"."EMPLOYEE"
( "ID" NUMBER,
"NAME" VARCHAR2(20 BYTE),
"AREA_NUMBER" NUMBER,
"EMP_TYPE_NAME" VARCHAR2(20 BYTE)
);
--------------------------------------------------------
-- DDL for Table PRODUCT
--------------------------------------------------------
CREATE TABLE "XYZ"."PRODUCT"
( "ID" NUMBER,
"NAME" VARCHAR2(20 BYTE),
"PRODUCT_CATEGORY_NAME" VARCHAR2(20 BYTE)
) ;
--------------------------------------------------------
-- DDL for Table SALES
--------------------------------------------------------
CREATE TABLE "XYZ"."SALES"
( "RECEIPT_NUMBER" NUMBER,
"SALES_DATE" DATE,
"QUANTITY" NUMBER,
"PRODUCT_ID" NUMBER,
"EMPLOYEE_ID" NUMBER
);
--------------------------------------------------------
-- DDL for Table PRODUCT_CATEGORY
--------------------------------------------------------
CREATE TABLE "XYZ"."PRODUCT_CATEGORY"
( "CATEGORY_NAME" VARCHAR2(20 BYTE),
"COMMISSION_RATE" FLOAT(126)
) ;
You can use multi step cte, use indenation, use JOIN syntax, avoid long lines, capitalize keywords, avoid ordering in subquery and so on:
WITH cte AS
(
SELECT e.id
,pc.category_name
,pc.commission_rate
,SUM(quantity) AS qty
,SUM(quantity * pc.commission_rate) AS commission
FROM sales s
JOIN product p
ON s.product_id = p.id
JOIN product_category pc
ON p.product_category_name = pc.category_name
JOIN employee e
ON s.employee_id = e.id
WHERE TO_CHAR(sales_date, 'yyyy') = TO_CHAR(SYSDATE, 'yyyy')
GROUP BY e.id, pc.category_name, pc.commission_rate
), cte2 AS
(
SELECT SYSDATE AS CurrentMonth
,ee.ID
,ee.Name
,cc.commission
,(cc.commission * 25 / 100) AS bonus
FROM cte cc
JOIN employee ee
ON ee.ID = cc.id
WHERE cc.commission >= 4000
)
SELECT CurrentMonth
,ID
,Name
,SUM(commission) AS comm_total
,SUM(bonus) AS bonus_total
FROM cte2
GROUP BY CurrentMonth, ID, Name;
I believe that you don't need the extra join to the employee table - you're just wanting to exclude the rows where the summed commission is < 4000, right? If so, you can easily do that in the group by query by using the having clause, e.g.:
with sum_categories as (select e.id,
e.name,
pc.category_name,
sum(s.quantity) as qty,
pc.commission_rate,
sum(s.quantity * pc.commission_rate) as commission
from sales s
inner join product p on (s.product_id = p.id)
inner join product_category pc on (p.product_category_name = pc.category_name)
inner join employee e on (s.employee_id = e.id)
where trunc(sales_date, 'yyyy') = trunc(sysdate, 'yyyy')
group by pc.category_name,
e.id,
e.name,
pc.commission_rate
having sum (quantity * pc.commission_rate) >= 4000)
select sysdate currentmonth, -- should this be trunc(sysdate, 'mm')?
yy.id,
yy.name,
sum (yy.commission) as comm_total,
sum (yy.commission * 25 / 100) as bonus_total
from sum_categories yy
group by yy.id,
yy.name;
You'll note that I changed your to_char(sales_date, 'yyyy') = to_char(sysdate, 'yyyy') into trunc(sales_date, 'yyyy') = trunc(sysdate, 'yyyy') because I prefer to keep the comparisons between the correct datatypes (you're comparing two dates, so I like to keep them as dates).
I have problem with following query where in which the nested query should be
converted to normal query:
select
count(*) as count,
TO_CHAR(RH.updated_datetime,'DD-MM-YYYY HH:MI:SS') as date,
SUM(
extract (
epoch from (
RH.updated_datetime - PRI.procedure_performed_datetime
)
)/60
)::integer/count(*) as diff
from
procedure_runtime_information PRI,
study S,
report R,
report_history RH
where
RH.report_fk = R.pk AND
R.study_fk = S.pk AND
S.procedure_runtime_fk = PRI.pk AND
RH.old_status_fk = 21 AND
RH.revision = (select max(revision) from report_history where RH.report_fk = RH.report_fk) AND
RH.updated_datetime > TO_DATE('22-01-2013 00:00:00', 'DD-MM-YYYY HH24:MI:SS') AND RH.updated_datetime < TO_DATE('22-01-2014 00:00:00', 'DD-MM-YYYY HH24:MI:SS')
group by date order by date asc;
Assuming this
(select max(revision) from report_history where RH.report_fk = RH.report_fk)
should really be:
(select max(revision) from report_history x where x.report_fk = RH.report_fk)
You could transform the nested (correlated) subquery into a plain subquery like this (one way of many):
SELECT count(*) AS ct
,to_char(rh.updated_datetime,'DD-MM-YYYY HH:MI:SS') AS date -- HH24?
,sum(extract(epoch FROM (RH.updated_datetime
- PRI.procedure_performed_datetime))
/ 60)::int / count(*) AS diff
FROM procedure_runtime_information PRI
JOIN study S ON S.procedure_runtime_fk = PRI.pk
JOIN report R ON R.study_fk = S.pk
JOIN report_history RH ON RH.report_fk = R.pk
JOIN (
SELECT report_fk, max(revision) AS revision
FROM report_history RH1
GROUP BY 1
) RH1 ON RH1.report_fk = RH.report_fk
AND RH1.revision = RH.revision
WHERE RH.old_status_fk = 21
AND RH.updated_datetime > to_date('22-01-2013', 'DD-MM-YYYY') -- >= ?
AND RH.updated_datetime < to_date('22-01-2014', 'DD-MM-YYYY') -- to_timestamp?
GROUP BY date -- where does date come from?
ORDER BY date;