I am performing the below query. I need to get the average of (avg_per_deal). Each email can have multiple deals, and each deal can have many averages. I want to get the query to average ALL the averages of the different deals, in order to have one aggregate average per email. I'm not sure how to write another subquery to do that with what I currently have:
WITH purs AS (
SELECT
u.email,
di.internal_name,
dh.id,
dh.created_at,
dh.deal_id,
dh.new_demand_status
FROM
deal.deal_history dh
JOIN deal.deal d ON d.id = dh.deal_id
JOIN deal.deal_intent di ON di.id = d.supply_intent
JOIN axd.user u ON u.public_id = di.user_public_id
WHERE
dh.stat = 'purs'
AND dh.ostat = 'teas'
AND di.type != 'wav'
),
signda AS (
SELECT
dh.id,
dh.created_at,
dh.deal_id,
dh.stat
FROM
deal.deal_history dh
JOIN deal.deal d ON d.id = dh.deal_id
JOIN deal.deal_intent di ON di.id = d.supply_intent_id
WHERE
stat = 'signda'
AND di.type != 'wav'
)
SELECT
p.email,
p.internal_name,
AVG(age(sn.created_at,p.created_at)) AS avg_per_deal
FROM
pur p
JOIN signda sn ON sn.deal_id = p.deal_id
WHERE p.created_at > now() - interval '1 month'
GROUP BY 1,2;
This gives me the average for the different deals for each email. I need the average of the different averages (per unique email).
You can enclose the main select as a subquery and then compute the average there.
For example, you can change the main select from:
select p.email, p.internal_name,
avg(age(sn.created_at,p.created_at)) as avg_per_deal
from pur p
join signda sn on sn.deal_id = p.deal_id
where p.created_at > now() - interval '1 month'
group by 1,2
to:
select email, avg(avg_per_deal)
from (
select p.email, p.internal_name,
avg(age(sn.created_at,p.created_at)) as avg_per_deal
from pur p
join signda sn on sn.deal_id = p.deal_id
where p.created_at > now() - interval '1 month'
group by 1,2
) x
group by email
Related
I have a small problem with GROUP BY and INNER JOIN. I try to get spent hours in incidents group by user, to see that whitch user are more efficient.
In this moment, I only get the error
'Column 'XXX' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause'
I understand that all of the fields in SELECT statement are necessary in GROUP BY, but if I group for all of this fields, I don't receive that I want.
I think that my problem is because I don't understand GROUP BY and INNER JOIN correctly, so I was trying to learn a lot of websites, but at least in this moment I can't see my error.
Here is my code:
SELECT
id_incident_project AS ID,
i.title AS title,
companyname as social_name,
username as tech_name,
ia.DESCRIPTION_TEXT as description,
CONVERT(varchar, ia.ACTIONDATE, 101) as action_date,
CAST(TIME as INT) as acting_time,
CAST(actions_time as INT) AS total_time
FROM
incident i
INNER JOIN incident_0001 i1 ON i1.ID_INCIDENT = i.ID_INCIDENT
INNER JOIN incident_action ia ON ia.ID_INCIDENT = i.ID_INCIDENT
INNER JOIN agent a ON a.ID_AGENT = ia.ID_AGENT
INNER JOIN username u on u.ID_USERNAME = a.ID_USERNAME
WHERE
ia.ACTIONDATE BETWEEN '## START DATE (YYYYMMDD)##' AND '## END DATE (YYYYMMDD)##'
GROUP BY username
All columns in the SELECT clause that you aren't grouping by must go through an aggregate function. To just get something, you can pass them through MAX:
SELECT
MAX(id_incident_project) AS ID,
MAX(i.title) AS title,
MAX(companyname) as social_name,
username as tech_name,
MAX(ia.DESCRIPTION_TEXT) as description,
MAX(CONVERT(varchar, ia.ACTIONDATE, 101)) as action_date,
MAX(CAST(TIME as INT)) as acting_time,
MAX(CAST(actions_time as INT)) AS total_time
FROM
incident i
INNER JOIN incident_0001 i1 ON i1.ID_INCIDENT = i.ID_INCIDENT
INNER JOIN incident_action ia ON ia.ID_INCIDENT = i.ID_INCIDENT
INNER JOIN agent a ON a.ID_AGENT = ia.ID_AGENT
INNER JOIN username u on u.ID_USERNAME = a.ID_USERNAME
WHERE
ia.ACTIONDATE BETWEEN '## START DATE (YYYYMMDD)##' AND '## END DATE (YYYYMMDD)##'
GROUP BY username
However, when there are multiple rows for the same username after all the joins are completed, this approach will risk values from different source rows being outputted. E.g., if username 'JohnSmith' had (say) two incidents, one on 2019-01-01 with a title of 'Zombie Sighting' and the second on 2019-03-31 with a title of 'Aitch Dropping', then the 'maximum' title pulled out will be 'Zombie Sighting' with a 'maximum' date pulled off of 2019-03-31, so for different incidents.
To avoid this, you can replace the GROUP BY with a partitioning on username instead, order the incidents, and pick out one per username in a consistent fashion:
SELECT
ID, title, social_name, tech_name, description,
action_date, acting_time, total_time
FROM (
SELECT
id_incident_project) AS ID,
i.title AS title,
companyname as social_name,
username as tech_name,
ia.DESCRIPTION_TEXT) as description,
CONVERT(varchar, ia.ACTIONDATE, 101) as action_date,
CAST(TIME as INT) as acting_time,
CAST(actions_time as INT) AS total_time,
ROW_NUMBER () OVER (
PARTITION BY username
ORDER BY
ia.ACTIONDATE DESC, -- pick out latest
i1.ID_INCIDENT DESC -- tie breaker
) AS OrderNum
FROM
incident i
INNER JOIN incident_0001 i1 ON i1.ID_INCIDENT = i.ID_INCIDENT
INNER JOIN incident_action ia ON ia.ID_INCIDENT = i.ID_INCIDENT
INNER JOIN agent a ON a.ID_AGENT = ia.ID_AGENT
INNER JOIN username u on u.ID_USERNAME = a.ID_USERNAME
WHERE
ia.ACTIONDATE BETWEEN '## START DATE (YYYYMMDD)##' AND '## END DATE (YYYYMMDD)##'
) t
WHERE t.OrderNum = 1
I'm getting an aggregated count of records for orders and I'm getting the expected count on this basic query:
SELECT
count(*) as sales_180,
180/count(*) as velocity
FROM custgroup g
WHERE g.cstnoc = 10617
AND g.framec = 4847
AND g.covr1c = 1763
AND g.colr1c = 29
AND date(substr(g.extd1d,1,4)||'-'||substr(g.EXTD1d,5,2)||'-'||substr(g.EXTD1d,7,2) ) between current_Date - 180 DAY AND current_Date
But as soon as I add back in my joins and joined values then my count goes from 1 (which it should be) to over 200. All I need from these joins is the customer ID and the manager number. so even if my count is high, I'm basically just trying to say "for this cstnoc, give me the slsupr and xlsno"
How can I perform this below query without affecting the count? I only want my count (sales_180 and velocity) coming from the custgroup table based on my where clause, but I then just want one value of the xcstno and xslsno based on the cstnoc.
SELECT
count(*) as sales_180,
180/count(*) as velocity,
c.xslsno as CustID,
cr.slsupr as Manager
FROM custgroup g
inner join customers c
on g.cstnoc = c.xcstno
inner join managers cr
on c.xslsno = cr.xslsno
WHERE g.cstnoc = 10617
AND g.framec = 4847
AND g.covr1c = 1763
AND g.colr1c = 29
AND date(substr(g.extd1d,1,4)||'-'||substr(g.EXTD1d,5,2)||'-'||substr(g.EXTD1d,7,2) ) between current_Date - 180 DAY AND current_Date
GROUP BY c.xslsno, cr.slsupr
You are producing multiple rows when joining, so your count is now counting all the resulting rows with all that [unintended] multiplicity.
The solution? Use a table expression to pre-compute your count, and then you can join it to the other tables, as in:
select
g2.sales_180,
g2.velocity,
c.xslsno as CustID,
cr.slsupr as Manager
from customers c
join managers cr on c.xslsno = cr.xslsno
join ( -- here the Table Expression starts
SELECT
count(*) as sales_180,
180/count(*) as velocity
FROM custgroup g
WHERE g.cstnoc = 10617
AND g.framec = 4847
AND g.covr1c = 1763
AND g.colr1c = 29
AND date(substr(g.extd1d,1,4)||'-'||substr(g.EXTD1d,5,2)
||'-'||substr(g.EXTD1d,7,2) )
between current_Date - 180 DAY AND current_Date
) g2 on g2.cstnoc = c.xcstno
You can also use a Common Table Expression (CTE) that will produce the same result:
with g2 as (
SELECT
count(*) as sales_180,
180/count(*) as velocity
FROM custgroup g
WHERE g.cstnoc = 10617
AND g.framec = 4847
AND g.covr1c = 1763
AND g.colr1c = 29
AND date(substr(g.extd1d,1,4)||'-'||substr(g.EXTD1d,5,2)
||'-'||substr(g.EXTD1d,7,2) )
between current_Date - 180 DAY AND current_Date
)
select
g2.sales_180,
g2.velocity,
c.xslsno as CustID,
cr.slsupr as Manager
from customers c
join managers cr on c.xslsno = cr.xslsno
join g2 on g2.cstnoc = c.xcstno
The query below returns a somatory of all groups in the year.
How can I return all groups that exists in IND_GRUPO but grouped by the month, if doesn't exist the group for the current month, the name should appear, but the somatory will be 0. All joins should be kept.
SELECT SUM(p.valor), c.nm_grupo, c.cd_grupo, YEAR(p.dt_emissao)
FROM ind_receita p
JOIN ind_equipto o ON p.cd_equipto = o.cd_equipto
JOIN ind_grupo c ON o.cd_grupo = c.cd_grupo
WHERE YEAR(p.dt_emissao) = YEAR(GETDATE())
GROUP BY YEAR(p.dt_emissao), c.nm_grupo, c.cd_grupo
ORDER BY 1 DESC
Try this:
SELECT c.nm_grupo, c.cd_grupo, YEAR(GETDATE()),
(SELECT SUM(p.valor)
FROM ind_receita p
JOIN ind_equipto o ON p.cd_equipto = o.cd_equipto
JOIN ind_grupo c2 ON o.cd_grupo = c2.cd_grupo
WHERE c2.cd_grupo = c.cd_grupo
AND YEAR(p.dt_emissao) = YEAR(GETDATE())) AS valor
FROM ind_grupo c
I have write a query to get the total number assignments which had no activity from last 5 months including those assignments which were created five months ago and never had any visit on it.
Can i shorten this query any further so i don't have to use the outer query.
SELECT #NumNoActivity = COUNT(QnoActivity.AssignmentID) FROM
(
SELECT a.AssignmentID
FROM Assignments a
LEFT JOIN VISITS v ON v.AssignmentID = a.AssignmentID
WHERE a.CurrentStatus = 1
AND a.StaffID = #StaffID
GROUP BY a.AssignmentID, a.CreatedDate
HAVING DATEDIFF(MONTH, ISNULL(MAX(v.VisitDate),a.CreatedDate ), GETDATE()) > =5
) QnoActivity
You could use NOT EXISTS to remove the join:
SELECT #NumNoActivity = COUNT(a.AssignmentID)
FROM Assignments a
WHERE a.CurrentStatus = 1
AND a.StaffID = #StaffID
AND NOT EXISTS
(SELECT * FROM VISITS v
WHERE v.AssignmentID = a.AssignmentID
AND v.VisitDate>DATEADD(month,-5,GETDATE()))
UPDATE
Based on Gordon Linoff's comment, the query was missing the Assignments.CreatedDate condition. Here's an updated version:
SELECT #NumNoActivity = COUNT(a.AssignmentID)
FROM Assignments a
WHERE a.CurrentStatus = 1
AND a.StaffID = #StaffID
AND (SELECT ISNULL(MAX(VisitDate), a.CreatedDate) FROM VISITS v
WHERE v.AssignmentID = a.AssignmentID) <= DATEADD(month,-5,GETDATE())
Here is SQL Fiddle to show how it works. Assignment results are:
1 - not included as there's a recent visit
2 - included as there's no visit and create date is old
3 - included as there's only a recent visit
4 - not included as there's no visit but create date is recent
5 - not included as there's a recent visit
There is an alternative way to write this query. You can look at it as the difference etween the total number of "assignments" and the total number of "assignments" that have had activity in the past 4/5 months.
The following takes this approach. It excludes new "assignments" in the where clause and the counts the different in the select clause:
SELECT (count(distinct a.AssignmentID) -
count(distinct case when datediff(MONTH, v.VisitDate, GETDATE()) < 5 or
v.VisitDate is null
then a.AssignmentId end)
)
FROM Assignments a LEFT JOIN
VISITS v
ON v.AssignmentID = a.AssignmentID
WHERE a.CurrentStatus = 1 AND
a.StaffID = #StaffID and
a.CreatedDate <= DATEADD(month, -5, GETDATE());
Hi I want to generate the report which will contain following things:
Daily report by country/destination against counts.
Eg.
No of contributions
No videos
No of experiences
Based on count yesterday, total for week, total for year for each destination.
I have written following query to count total for week.
select a.name, count(e.id),count(uc.id),count(v.id)
from accommodation a, user_contribution uc,experience_video ev, video v,
(select id "ID",user_contribution_id,accommodation_id,case
when modified_on is null then trunc(created_on)
else trunc(modified_on) end last_modified from experience)e
where
e.last_modified between trunc(sysdate)-7 and trunc(sysdate) and
e.accommodation_id = a.id and
e.user_contribution_id = uc.id and
e.id = ev.experience_id and
v.id = ev.video_id
group by a.name
union
select l.name, count(ex.id),count(uc.id),count(v.id),count(r.id)
from location l, user_contribution uc,experience_video ev, video v,
(select id "ID",user_contribution_id,location_id,case
when modified_on is null then trunc(created_on)
else trunc(modified_on) end last_modified from experience)ex
where
ex.last_modified between trunc(sysdate)-7 and
trunc(sysdate) and
l.id = ex.location_id and
ex.user_contribution_id = uc.id and
ex.id = ev.experience_id and
v.id = ev.video_id
group by l.name;
I can similarly write the query to extract weekly and yearly count.
basically I want all counts in one row for each destination.
Is there any better way of writing this query?
Would appreciate your help.