SQL Query - how to create a sum based on grouped numbers - sql

I'd like to know how many messages (voicemailcount) were received for each rank (b.rankcode). Example Column 'b.rankcode' will have x number of people with a rank number who have gotten messages in 'voicemailcount'. If possible, I'd only like to see the voicemailcount and rankcode column.
select
count(*) as [voicemailcount], a.linkedusermailboxname, b.RankCode as 'Rank'
from
UMCallDataRecord a
join
UMADUserAccounts b on a.LinkedUserMailboxName = b.EmailAddress
where
a.CallType = 'callansweringvoicemessage'
and month(a.[date]) = month(GetDate()) - 1 -- change this per month -1 = lastmonth
group by
a.linkedusermailboxname, b.rankcode
order by
b.rankcode
TIA!

I believe that we need to remove "linkedusermailboxname" from the Select and from the GroupBy, and remove the Join table and its On... >>
SELECT
b.rankcode AS 'Rank'
,Count(*) AS [voicemailcount]
FROM umcalldatarecord a
WHERE a.calltype = 'callansweringvoicemessage'
AND Month(a.[date]) = Month(Getdate()) - 1
-- change this per month -1 = lastmonth
GROUP BY b.rankcode
ORDER BY b.rankcode

Seems like you just need to reduce the number of columns that you group by
SELECT
COUNT(*) AS [voicemailcount]
, ua.RankCode AS 'Rank'
FROM UMCallDataRecord AS cdr
JOIN UMADUserAccounts AS ua ON cdr.LinkedUserMailboxName = ua.EmailAddress
WHERE cdr.CallType = 'callansweringvoicemessage'
AND cdr.[date] >= dateadd(month, datediff(month,0, getDate())-1, 0) -- 1st day of last month
AND cdr.[date] < dateadd(month, datediff(month,0, getDate()), 0) -- 1st day of current month
GROUP BY
ua.rankcode
ORDER BY
ua.rankcode
However I would like to propose a different way to define the date range because you could include results from any year in your current approach. The approach above will limit the results to everything from the previous month, and only the previous month. Note also I do not use between which just isn't great for date ranges.
I also concur with "Bad habits to kick : using table aliases like (a, b, c) or (t1, t2, t3)", use meaningful aliases, not aliases based on "sequence in a query"

Related

SQL with as expression shows multiple results

I am writing a SQL query using with as expression. I always get a result in the square of what I required.
This is my query:
DECLARE #MAX_DATE AS INT
SET #MAX_DATE = (SELECT DATEPART(MONTH,FECHA) FROM ALBVENTACAB WHERE NUMALBARAN IN (SELECT DISTINCT MAX(NUMALBARAN) FROM ALBVENTACAB));
;WITH TABLE_LAST AS (
SELECT CONCAT(DATEPART(MONTH,FECHA),'-',DATEPART(YEAR,FECHA)) as LAST_YEAR_MONTH
,SUM(TOTALNETO) AS LAST_YEAR_VALUE
FROM ALBVENTACAB
WHERE DATEPART(YEAR,CURRENT_TIMESTAMP) -1 = DATEPART(YEAR,FECHA) AND NUMSERIE LIKE 'A%'
AND DATEPART(MONTH,FECHA) <= #MAX_DATE
GROUP BY CONCAT(DATEPART(MONTH,FECHA),'-',DATEPART(YEAR,FECHA))
)
,TABLE_CURRENT AS(
SELECT CONCAT(DATEPART(MONTH,FECHA),'-',DATEPART(YEAR,FECHA)) as CURR_YEAR_MONTH
,SUM(TOTALNETO) AS CURR_YEAR_VALUE
FROM ALBVENTACAB
WHERE DATEPART(YEAR,CURRENT_TIMESTAMP) <= DATEPART(YEAR,FECHA) AND NUMSERIE LIKE 'A%'
GROUP BY CONCAT(DATEPART(MONTH,FECHA),'-',DATEPART(YEAR,FECHA))
)
SELECT *
FROM TABLE_CURRENT, TABLE_LAST
When I run the query I get exactly the square of the result.
I want to compare sale monthly with last year.
2-2020 814053.3 2-2019 840295.1
1-2020 1094993.65 2-2019 840295.1
3-2020 293927.3 2-2019 840295.1
2-2020 814053.3 1-2019 1050701.68
1-2020 1094993.65 1-2019 1050701.68
3-2020 293927.3 1-2019 1050701.68
2-2020 814053.3 3-2019 887776.1
1-2020 1094993.65 3-2019 887776.1
3-2020 293927.3 3-2019 887776.1
I should get only 3 rows instead of 9 rows.
You need to properly join your two CTE - the way you're doing it now, you're getting a Cartesian product of each row in either CTE together.
Do something like:
*;WITH TABLE_LAST AS
( ....
),
TABLE_CURRENT AS
( ....
)
SELECT *
FROM TABLE_CURRENT curr
INNER JOIN TABLE_LAST last ON (some join condition here)
What that join condition is going to be - I have no idea, and cannot tell from your question - but you have to define how these two sets of data "connect" ....
It could be something like:
SELECT *
FROM TABLE_CURRENT curr
INNER JOIN TABLE_LAST last ON curr.CURR_YEAR_MONTH = last.LAST_YEAR_MONT
or whatever else makes sense in your situation - but basically, you need to somehow "tie together" these two sets of data and get only those rows that make sense - not just every row from "last" combined with every row from "curr" ....
While you already got the answer on how to join the two results, I thought I'd tell you how to typically approach such problems.
From the same table, you want two sums on different conditions (different years that is). You solve this with conditional aggregation, which does just that: aggregate (sum) based on a condition (year).
select
datepart(month, fecha) as month,
sum(case when datepart(year, fecha) = datepart(year, getdate()) then totalneto end) as this_year,
sum(case when datepart(year, fecha) = datepart(year, getdate()) -1 then totalneto end) as last_year
from albventacab
where numserie like 'A%'
and fecha > dateadd(year, -2, getdate())
group by datepart(month, fecha)
order by datepart(month, fecha);

SQL - Grouping by Last Day of Quarter

I currently have a query running to average survey scores for agents. We use the date range of the LastDayOfTheQuarter and 180 days back to calculate these scores. I ran into an issue for this current quarter.
One of my agents hasn't received any surveys in 2020 which is causing the query to not pull the current lastdayofquarter and 180 days back of results.
The code I am using:
SELECT
Agent,
U.Position,
U.BranchDescription,
(ADDDATE(LastDayOfQuarter, -180)) AS MinDate,
(LastDayOfQuarter) AS MaxDate,
COUNT(DISTINCT Response ID) as SurveyCount,
AVG(CASE WHEN Question ID = Q1_2 THEN Answer Value END) AS EngagedScore,
AVG(CASE WHEN Question ID = Q1_3 THEN Answer Value END) AS KnowledgableScore,
AVG(CASE WHEN Question ID = Q1_6 THEN Answer Value END) AS ValuedScore
FROM qualtrics_responses
LEFT JOIN date D
ON (D.`Date`) = (DATE(`End Date`))
LEFT JOIN `users` U
ON U.`UserID` = `Agent ID`
WHERE `Agent` IS NOT NULL
AND DATE(`End Date`) <= (`LastDayOfQuarter`)
AND DATE(`End Date`) >= (ADDDATE(`LastDayOfQuarter`, -180))
GROUP BY `Agent`, (ADDDATE(`LastDayOfQuarter`, -180))
i know the issue is due to the way I am joining the dates and since he doesn't have a result in this current year, the end date to date join isn't grabbing the desired date range. I can't seem to come up with any alternatives. Any help is appreciated.
I make the assumption that table date in your query is a calendar table, that stores the starts and ends of the quarters (most likely with one row per date in the quarter).
If so, you can solve this problem by rearranging the joins: first cross join the users and the calendar table to generate all possible combinations, then bring in the surveys table with a left join:
SELECT
U.UserID,
U.Position,
U.BranchDescription,
D.LastDayOfQuarter - interval 180 day AS MinDate,
D.LastDayOfQuarter AS MaxDate,
COUNT(DISTINCT Q.ResponseID) as SurveyCount,
AVG(CASE WHEN Q.QuestionID = 'Q1_2' THEN Q.Answer Value END) AS EngagedScore,
AVG(CASE WHEN Q.QuestionID = 'Q1_3' THEN Q.Answer Value END) AS KnowledgableScore,
AVG(CASE WHEN Q.QuestionID = 'Q1_6' THEN Q.Answer Value END) AS ValuedScore
FROM date D
CROSS JOIN users U
LEFT JOIN qualtrics_responses Q
ON Q.EndDate >= D.Date
AND Q.EndDate < D.Date + interval 1 day
AND U.UserID = Q.AgentID
AND Q.Agent IS NOT NULL
GROUP BY
U.UserID,
U.Position,
U.BranchDescription,
D.LastDayOfQuarter
Notes:
I adapted the date arithmetics - this assumes that you are using MySQL, as the syntax of the query suggests
You should really qualify all the columns in the query, by prefixing them with the alias of the table they belong to; this makes the query so much easier to understand. I gave a tried at it, you might need to review that.
All non-aggregated columns should appear in the group by clause (also see the comment from Eric); this is a a requirement in most databaseses, and good practice anywhere

SQL for looping the date and summarize data

I need to summarize by customer having specific product from 2018-1-1 until today if cdate is within activationdt and next_activationdt. When next_activationdt is empty that means it is still in use and not cancelled or changed.
For example 2018-1-1, Mary and Alan are using product A.
I could not figure out a good way of running all the dates together.
I need to run the code on SQL workbench pulling data from AWS Redshift database.
You need a way to generate the dates. Perhaps you have a calendar table, or you can use something like this:
with dates as (
select dateadd(day, row_number() over (), '2018-01-01'::date) as dte
from t
limit 365 -- assumes table has at least 365 rows
)
select d.dte,
sum( (product = 'A')::int ) as num_a,
sum( (product = 'B')::int ) as num_b,
sum( (product = 'C')::int ) as num_c,
sum( (product = 'D')::int ) as num_d
from dates d left join
t
on d.dte >= t.activationdate and
(d.dte < t.nextactivationdate or t.nextactivationdate is null)
group by d.dte
order by d.dte;

First date when certain condition was met

I'm trying to find a first date when a condition was met. So the logic is below:
use
[AdventureWorksDW2012]
go
;WITH sales AS (
select d.OrderDateKey,
SalesAmount = SUM(d.SalesAmount)
from [dbo].[FactInternetSales] d
group by d.OrderDateKey
having SUM(d.SalesAmount)>10000
)
select FirstOrderDateKey = MIN(OrderDateKey)
from sales
The only problem is that in my data is too complex and too huge to calculate value for each date and then choose the min date when the condition is met. Is there any quick way of finding first date when Internet sales amount exceeded 10000? Is there some kind of loop required?
You can do it in single statement also. The performance can be improved with this if you have proper index on orderdatekey.
select MIN(s.OrderDateKey) as FirstOrderDateKey
from [dbo].[FactInternetSales] d
group by d.OrderDateKey
having SUM(d.SalesAmount)>10000
SELECT TOP 1
d.OrderDateKey
,S.RunningTotal
FROM [dbo].[FactInternetSales] d
CROSS APPLY ( SELECT SUM(SalesAmount) AS RunningTotal
FROM [dbo].[FactInternetSales]
WHERE OrderDateKey <= d.OrderDateKey
) S
WHERE S.RunningTotal < -- Condition
ORDER BY d.OrderDateKey DESC

sql db2 select records from either table

I have an order file, with order id and ship date. Orders can only be shipped monday - friday. This means there are no records selected for Saturday and Sunday.
I use the same order file to get all order dates, with date in the same format (yyyymmdd).
i want to select a count of all the records from the order file based on order date... and (i believe) full outer join (or maybe right join?) the date file... because i would like to see
20120330 293
20120331 0
20120401 0
20120402 920
20120403 430
20120404 827
etc...
however, my sql statement is still not returning a zero record for the 31st and 1st.
with DatesTable as (
select ohordt "Date" from kivalib.orhdrpf
where ohordt between 20120315 and 20120406
group by ohordt order by ohordt
)
SELECT ohscdt, count(OHTXN#) "Count"
FROM KIVALIB.ORHDRPF full outer join DatesTable dts on dts."Date" = ohordt
--/*order status = filled & order type = 1 & date between (some fill date range)*/
WHERE OHSTAT = 'F' AND OHTYP = 1 and ohscdt between 20120401 and 20120406
GROUP BY ohscdt ORDER BY ohscdt
any ideas what i'm doing wrong?
thanks!
It's because there is no data for those days, they do not show up as rows. You can use a recursive CTE to build a contiguous list of dates between two values that the query can join on:
It will look something like:
WITH dates (val) AS (
SELECT CAST('2012-04-01' AS DATE)
FROM SYSIBM.SYSDUMMY1
UNION ALL
SELECT Val + 1 DAYS
FROM dates
WHERE Val < CAST('2012-04-06' AS DATE)
)
SELECT d.val AS "Date", o.ohscdt, COALESCE(COUNT(o.ohtxn#), 0) AS "Count"
FROM dates AS d
LEFT JOIN KIVALIB.ORDHRPF AS o
ON o.ohordt = TO_CHAR(d.val, 'YYYYMMDD')
WHERE o.ohstat = 'F'
AND o.ohtyp = 1