Sort a Grouped query - sql-server-2008

I am using the following query to display a list of months for which my database has data.
SELECT DATENAME(MONTH, EVENT_DATE) + ' ' + DATENAME(YEAR, EVENT_DATE) AS MonthYear
FROM [ODA].[dbo].[REPORT_CASE_EXT]
GROUP BY DATENAME(MONTH, EVENT_DATE) + ' ' + DATENAME(YEAR, EVENT_DATE)
If I add the following sort to the query, I get an error because they don't appear in the groupby
SORT BY DATENAME(YEAR, EVENT_DATE), DATENAME(MONTH, EVENT_DATE)
Is there a way to accomplish this sort?

It would "work" if you would use the same in the ORDER BY as in the GROUP BY.
ORDER BY
DATENAME(MONTH, EVENT_DATE) + ' ' + DATENAME(YEAR, EVENT_DATE)
But you need to add more informations to the GROUP BY to be able to order in chronological order instead of alphabetically(otherwise February is before January):
GROUP BY
datename(YEAR, EVENT_DATE),
datename(MONTH, EVENT_DATE),
datepart(YEAR, EVENT_DATE),
datepart(MONTH, EVENT_DATE)
ORDER BY
datepart(YEAR, EVENT_DATE),
datepart(MONTH, EVENT_DATE)

Related

how to display individual resolvers within a team on a monthly basis

I've currently got an SQL view that shows how many members of each team have made a resolution on each day since April 2017 using the below code. How would I amend this so I could do this by month rather than on a daily basis?
SELECT CAST(stat_datetimeresolved as date) as DateResolved, ResolvedByTeam, COUNT(DISTINCT resolvedby) as ResolvedByCnt
FROM [dbo].[Incident]
WHERE Stat_DateTimeResolved >= '20170401'
GROUP BY CAST(Stat_DateTimeResolved as DATE), ResolvedByTeam
ORDER BY CAST(stat_datetimeresolved as DATE) asc, ResolvedByTeam
You can format the date to be shown as year and month only:
select format(getdate(), 'yyyyMM')
And then group by this value. So your query should become something like this:
SELECT FORMAT(stat_datetimeresolved, 'yyyyMM') as MonthResolved, ResolvedByTeam, COUNT(DISTINCT resolvedby) as ResolvedByCnt
FROM [dbo].[Incident]
WHERE Stat_DateTimeResolved >= '20170401'
GROUP BY FORMAT(stat_datetimeresolved, 'yyyyMM'), ResolvedByTeam
ORDER BY MonthResolved asc, ResolvedByTeam
UPDATE: For pre-2012 versions, the year-month part can be constructed using DATEPART function. In this case the query could look like this:
SELECT CAST(DATEPART(year, stat_datetimeresolved) as varchar(4)) + '-' + RIGHT('0' + CAST(DATEPART(month, stat_datetimeresolved) as varchar(2)), 2) as MonthResolved, ResolvedByTeam, COUNT(DISTINCT resolvedby) as ResolvedByCnt
FROM [dbo].[Incident]
WHERE stat_datetimeresolved >= '20170401'
GROUP BY CAST(DATEPART(year, stat_datetimeresolved) as varchar(4)) + '-' + RIGHT('0' + CAST(DATEPART(month, stat_datetimeresolved) as varchar(2)), 2), ResolvedByTeam
ORDER BY MonthResolved asc, ResolvedByTeam
or
SELECT convert(char(6), Stat_DateTimeResolved , 112) as MonthResolved, ResolvedByTeam, COUNT(DISTINCT resolvedby) as ResolvedByCnt
FROM [dbo].[Incident]
WHERE Stat_DateTimeResolved >= '20170401'
GROUP BY convert(char(6), Stat_DateTimeResolved , 112), ResolvedByTeam
ORDER BY MonthResolved asc, ResolvedByTeam

count not working inside group_concat Mysql

I am trying to concat count of id and 1 inside group_concat. Below is my query.
MYSQL:
SELECT
months.name AS NAME,
CONCAT(
'[',
GROUP_CONCAT(
CONCAT(
'[\"',
COUNT(p_c_n_details.JPN_ID),
'\",',
1,
']'
)
),
']'
) AS DATA
FROM
p_c_n_details
INNER JOIN in_e_s_s__p_c_ns RIGHT OUTER JOIN months ON months.id =
MONTH(p_c_n_details.created_at) AND p_c_n_details.type IN('Process Change',
'Design Change') AND p_c_n_details.JPN_ID =
in_e_s_s__p_c_ns.juniperinternalpcnid AND p_c_n_details.created_at >=
last_day(NOW()) + INTERVAL 1 DAY - INTERVAL 3 MONTH
WHERE
months.name IN(
MONTHNAME(
DATE_SUB(CURDATE(), INTERVAL 0 MONTH)),
MONTHNAME(
DATE_SUB(CURDATE(), INTERVAL 1 MONTH)),
MONTHNAME(
DATE_SUB(CURDATE(), INTERVAL 2 MONTH))
)
GROUP BY
months.id
Expected Output:
Name | DATA
-------------------------
July [20,1]
August [33,1]
Table months contains month names.
But I am getting error #1111 - Invalid use of group function. I tried this link
mysql group_concat with a count inside?
but I am facing error when using in my query.
This is a guess.
Try:
SELECT months.name AS NAME,
CONCAT('{',COUNT(p_c_n_details.JPN_ID),',1}' AS DATA
in place of the SELECT you now have.
Your example result doesn't show a need for GROUP_CONCAT().

Count number of occurrences of a date range

I am working on mysql select query with the ultimate goal of getting an integer based on the count of a subset from a pre-defined date range.
Scenario has a few constraints I can't seem to navigate in a clean way. For the sake of being concise, lets make an assumption that I am working with data for a membership organization. The basics of my situation include:
A user joins on a date (later referenced as 'join_date' and stored in my database in a datefield in format 'yyyy-mm-dd')
My calendar is based on two periods: a Fall Semester and a Spring Semester (names may be a little misleading). Within the calendar year, the Spring semester is defined as February 16 through September 15, while the Fall semester is defined as September 16 though February 15 (into the next year).
When a user joins, for the purposes of counting for this query, the next semester is considered the first semester. (i.e. joining on January 5 (Fall) means that users first semester will be Spring of that year, while joining on September 20 (Fall) means that users first semester will be Spring of the next year.
Back to my query needs. Relative to the next semester from the current date, I need to calculate the number of Fall + Spring semesters that remain before the eighth semester (including that eighth semester). i.e., If I join on April 1, 2016, my first semester would be Fall 2016, and my eighth would be Spring 2020. As of the current date (today = 2017-07-17), I need to get a count of 6 returned, which would represent Fall 2017, Spring 2018, Fall 2018, Spring 2019, Fall 2019, and Spring 2020.
A few other examples:
Join 2016-01-01, 1st semester is Spring 2016, 8th semester is Fall 2019, remaining count needed to return is 5.
Join 2016-10-01, 1st semester is Spring 2017, 8th semester is Fall 2020, remaining count needed to return is 7.
I have been using CASE statements to help break this down, but I am a novice and do not know if this is the best approach. It is strongly preferred to do this dynamically and not have to to use a staging table or similar, in which case it would be easy. My current CASE statements, which seem to work include:
CASE
WHEN DATE_FORMAT( join_date, '%m/%d' ) BETWEEN '01/01' AND '02/14' THEN CONCAT( 'Spring ', YEAR ( join_date) )
WHEN DATE_FORMAT( join_date, '%m/%d' ) BETWEEN '02/15' AND '09/14' THEN CONCAT( 'Fall ', YEAR ( join_date) )
WHEN DATE_FORMAT( join_date, '%m/%d' ) BETWEEN '09/15' AND '12/31' THEN CONCAT( 'Spring ', YEAR ( join_date) + 1 )
END AS '1st semester'
CASE
WHEN DATE_FORMAT( join_date, '%m/%d' ) BETWEEN '01/01' AND '02/14' THEN CONCAT( 'Fall ', YEAR ( join_date) + 3 )
WHEN DATE_FORMAT( join_date, '%m/%d' ) BETWEEN '02/15' AND '09/14' THEN CONCAT( 'Spring ', YEAR ( join_date) + 4 )
WHEN DATE_FORMAT( join_date, '%m/%d' ) BETWEEN '09/15' AND '12/31' THEN CONCAT( 'Fall ', YEAR ( join_date ) + 4 )
END AS '8th semester'
CASE
WHEN DATE_FORMAT( CURDATE( ), '%m/%d' ) BETWEEN '01/01' AND '02/14' THEN CONCAT( 'Fall ', YEAR ( CURDATE( ) ) - 1 )
WHEN DATE_FORMAT( CURDATE( ), '%m/%d' ) BETWEEN '02/15' AND '09/14' THEN CONCAT( 'Spring ', YEAR ( CURDATE( ) ) )
WHEN DATE_FORMAT( CURDATE( ), '%m/%d' ) BETWEEN '09/15' AND '12/31' THEN CONCAT( 'Fall ', YEAR ( CURDATE( ) ) )
END AS 'current semester'
CASE
WHEN DATE_FORMAT( CURDATE( ), '%m/%d' ) BETWEEN '01/01' AND '02/14' THEN CONCAT( 'Spring ', YEAR ( CURDATE( ) ) )
WHEN DATE_FORMAT( CURDATE( ), '%m/%d' ) BETWEEN '02/15' AND '09/14' THEN CONCAT( 'Fall ', YEAR ( CURDATE( ) ) )
WHEN DATE_FORMAT( CURDATE( ), '%m/%d' ) BETWEEN '09/15' AND '12/31' THEN CONCAT( 'Spring ', YEAR ( CURDATE( ) ) + 1 )
END AS 'next semester'
Ultimately, I need find the count of the 'remaining semesters'
Any help or guidance would be greatly appreciated. You can find a sample of a few user examples at http://sqlfiddle.com/#!9/01319.
The expected result for these examples would be to get 2 for user 3060457, 5 for user 3060458, 6 for user 3060459, and 7 for user 3060460.

MYSQL Query group by custom Month date?

I wrote a query that returns monthly sales.
SELECT
count(O.orderid) as Number_of_Orders,
concat (MonthName(FROM_UNIXTIME(O.`date`)),' - ',year(FROM_UNIXTIME(O.date))) as Ordered_Month,
sum(O.total) as TotalAmount,
Month(FROM_UNIXTIME(O.`date`)) as Month_of_Year,
year(FROM_UNIXTIME(O.date)) as Sale_Year
FROM orders O
group by Month_of_Year, Sale_Year
order by Sale_Year DESC,Month_of_Year DESC
I would like to make it group for a custom date like
instead of 1st to 1st, it should group for 10th -10th of every month.
Not sure how to group it that way!
because you are dealing with a time "shift", you'll have to do that math in your equation to "fake it out". Something like
SELECT
count(O.orderid) as Number_of_Orders,
concat(
MonthName( Date_Sub( FROM_UNIXTIME(O.`date`), INTERVAL 10 DAY )),
' - ',
Year( Date_Sub( FROM_UNIXTIME(O.date), INTERVAL 10 DAY) )
) as Ordered_Month,
sum(O.total) as TotalAmount,
Month( Date_Sub( FROM_UNIXTIME(O.`date`), INTERVAL 10 DAY )) as Month_of_Year,
Year( Date_Sub( FROM_UNIXTIME(O.date), INTERVAL 10 DAY )) as Sale_Year
FROM
orders O
group by
Month_of_Year,
Sale_Year
order by
Sale_Year DESC,
Month_of_Year DESC
So, in essence, you are taking the dates ex: March 11-31 + April 1-10 and subtracting "10 days" from them... so for the query, they will look like March 1-31, and April 11-30 will appear like April 1-20 + May, etc for rest of each year...
Not tested.
group by Month_of_Year, ceil(day(o.`date`)/10), Sale_Year
This is a better idea in order to avoid having 4 groups but just 3
select
month(my_date) as your_month,
year(my_date) as your_year,
case
when day(my_date) <= 10 then 1
when day(my_date) between 11 and 20 then 2
else 3 end as decade,
count(*) as total
from table
group by
your_month,your_year,decade
Adapt it to your needs.

calculate difference of two calculated fields

I have this query, which basically gets the average spend of a customer over the last year, and 3 months:
SELECT SQL_CALC_FOUND_ROWS
customer_id,
customer_name,
AVG(IF(
DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 1 YEAR, "%Y-%m-01"),
spend_amount,
NULL
)) AS 1_year_average_spend,
AVG(IF(
DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 3 MONTH, "%Y-%m-01"),
spend_amount,
NULL
)) AS 3_month_average_spend
FROM customer_spends
GROUP BY customer__id
But I also need to get the percent difference of the spend averages:
E.g. (pseudo code)
if (1_year_average_spend = 0)
change = N/A
else
change = 3_month_average_spend / 1_year_average_spend - 1
How can, or what do you recommend I do to implement this?
The only way I can think of is horrible:
IF(
AVG(IF(
DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 1 YEAR, "%Y-%m-01"),
`spend_amount`,
NULL
)) > 0,
AVG(IF(
DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 3 MONTH, "%Y-%m-01"),
spend_amount,
NULL
)) / AVG(IF(
DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 1 YEAR, "%Y-%m-01"),
`spend_amount`,
NULL
)) - 1,
"N/A"
) AS 3_month_performance
If you are happy to use MySQL specific code, you can use User-Defined Variables like this (simplified version):
SELECT #avg1 := ROUND((1 + 2 + 3) / 3, 2) AS avg1,
#avg2 := ROUND((4 + 5 + 6) / 3, 2) AS avg2,
IF( #avg1, ROUND(#avg2 / #avg1 - 1, 2), NULL ) AS result;
+------+------+--------+
| avg1 | avg2 | result |
+------+------+--------+
| 2.00 | 5.00 | 1.50 |
+------+------+--------+
That would become:
SELECT SQL_CALC_FOUND_ROWS
customer_id,
customer_name,
#1_year_average_spend := AVG(IF(
DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 1 YEAR, "%Y-%m-01"),
spend_amount,
NULL
)) AS 1_year_average_spend,
#3_month_average_spend := AVG(IF(
DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 3 MONTH, "%Y-%m-01"),
spend_amount,
NULL
)) AS 3_month_average_spend,
IF( #1_year_average_spend,
#3_month_average_spend / #1_year_average_spend - 1,
NULL
) AS diff
FROM customer_spends
GROUP BY customer__id
Note 1: I've used diff as the column name for the difference, as change is a reserved word, so may end up causing problems.
Note 2: You need to be aware of the following caveats from the docs, as they may affect your result:
Assignment of decimal and real values does not preserve the precision
or scale of the value.
And:
As a general rule, you should never assign a value to a user variable
and read the value within the same statement. You might get the
results you expect, but this is not guaranteed. The order of
evaluation for expressions involving user variables is undefined and
may change based on the elements contained within a given statement.
In SELECT #a, #a:=#a+1, ..., you might think that MySQL will evaluate
#a first and then do an assignment second. However, changing the
statement (for example, by adding a GROUP BY, HAVING, or ORDER BY
clause) may cause MySQL to select an execution plan with a different
order of evaluation.
So use with caution, and proper testing!
Would NULL do as the N/A value? If so, you could apply NULLIF() to the denominator like this:
a / NULLIF(b, 0) - 1
NULLIF returns NULL if its first argument is equal to the second argument. And if an operand is NULL, the entire expression evaluates to NULL.
Like #Bohemian, I, too, suggest using a subselect. Here's the complete query:
SELECT SQL_CALC_FOUND_ROWS
customer_id,
customer_name,
1_year_average_spend,
3_month_average_spend,
3_month_average_spend / NULLIF(1_year_average_spend, 0) - 1 AS change
FROM (
SELECT
customer_id,
customer_name,
AVG(IF(
DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 1 YEAR, "%Y-%m-01"),
spend_amount,
NULL
)) AS 1_year_average_spend,
AVG(IF(
DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 3 MONTH, "%Y-%m-01"),
spend_amount,
NULL
)) AS 3_month_average_spend
FROM customer_spends
GROUP BY customer__id
) s
Use an inner select (it's like a temporary view) and select from that. This should work:
SELECT
customer_id,
customer_name,
1_year_average_spend,
3_month_average_spend,
if (1_year_average_spend = 0, "N/A", (3_month_average_spend / 1_year_average_spend) - 1) AS 3_month_performance
FROM (SELECT
customer_id,
customer_name,
AVG(IF(DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 1 YEAR, "%Y-%m-01"), spend_amount, NULL)) AS 1_year_average_spend,
AVG(IF(DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 3 MONTH, "%Y-%m-01"), spend_amount, NULL)) AS 3_month_average_spend)
FROM customer_spends
GROUP BY customer_id, customer_name ) x
Getting rid of IF(), DATE() and CONCAT() function calls. Your query, as it is now, has to scan the whole customer_spends table and check those complex conditions for ALL rows, even if they are 10 years old data.
This will also use an index of (year_of_spend, month_of_spend) or (customer_id, year_of_spend, month_of_spend) to speed up the query:
SELECT c.customer_id
, c.customer_name
, 1_year_average_spend
, 3_month_average_spend
, CASE WHEN 1_year_average_spend = 0
THEN 'N/A'
ELSE (3_month_average_spend / 1_year_average_spend) - 1
END AS percent_difference
FROM
customer AS c
JOIN
( SELECT customer_id
, AVG(spend_amount) AS 1_year_average_spend
FROM customer_spends
WHERE (year_of_spend, month_of_spend) >=
( YEAR(CUR_DATE() - INTERVAL 1 YEAR)
, MONTH(CUR_DATE() - INTERVAL 1 YEAR)
)
GROUP BY customer_id
) AS grp1year
ON grp1year.customer_id = c.customer_id
LEFT JOIN
( SELECT customer_id
, AVG(spend_amount) AS 3_month_average_spend
FROM customer_spends
WHERE (year_of_spend, month_of_spend) >=
( YEAR(CUR_DATE() - INTERVAL 3 MONTH)
, MONTH(CUR_DATE() - INTERVAL 3 MONTH)
)
GROUP BY customer_id
) AS grp3month
ON grp3month.customer_id = c.customer_id