How to use analytical and grouping functions in one query in SQL Developer - function

How can I use analytical and grouping function in one query? I see error "ORA-00979:-not a GROUP BY expression". Below my query:
Select
date1,
TO_CHAR(date1, 'IW') as NO_OF_WEEK,
extract(month from date1) as NO_OF_MONTH,
count(sales_pln) as DAILY_NUMBER_OF_ORDERS,
round(sales_pln,2) as SALES1,
round(sum(sales_pln) OVER(partition by date1),2) as DAILY_SUM,
round(sum(sales_pln) OVER(partition by TO_CHAR(date1, 'IW')),2) as WEEKLY_SUM,
round(sum(sales_pln) OVER(partition by extract(month from date1)),2) as MONTHLY_SUM
from TABLE1
where date1 > date '2022-12-31'
group by 
date1,
TO_CHAR(date1, 'IW'),
extract(month from date1);
Thank you in advance for all help :-)

Related

Get growth rate per month from transaction table

I have a transaction history like this:
date
revenue
balance
2021-05-03
0
1000
2021-05-21
500
1500
2021-05-23
-250
1250
2021-06-02
-500
750
and I would like to get a result like this:
date
growth
2021-5
0.25
2021-6
-0.4
The formula is:
balance (end of month) - balance (start of month) / balance (start of month)
e.g.: 1250-1000/1000=0.25
and: 750-1250/1250=-0.4
I would very much appreciate a hint for a MYSQL query that is as simple as possible.
You need to pull in the balance from the previous month, if it exists. For that, you can combine lag() with conditional aggregation:
select year(date), month(date),
(-1 +
(max(case when seqnum_desc = 1 then balance end) /
max(case when seqnum_asc = 1 then coalesce(prev_balance, balance) end)
)
) as growth
from (select t.*,
row_number() over (partition by year(date), month(date) order by date) as seqnum_asc,
row_number() over (partition by year(date), month(date) order by date desc) as seqnum_desc,
lag(balance) over (order by date) as prev_balance
from t
) t
group by year(date), month(date);
Here is a db<>fiddle.
#MBauerDC ... Thank You. In my case this was the right direction as I work with MYSQL 5.7. However, a few changes were still necessary to get to the final result:
SELECT
t0.month,
(t2.balance - (t1.balance - t1.revenue)) / (t1.balance - t1.revenue) AS growth
FROM
(SELECT
DATE_FORMAT(date, '%Y-%m') AS 'month',
MIN(date) AS 'min_date',
MAX(date) AS 'max_date'
FROM
t
GROUP BY month) AS t0
JOIN
t AS t1 ON (t1.date = min_date)
JOIN
t AS t2 ON (t2.date = max_date)
The logic of this calculation isn't consistent by your example.
Let's take "start of month" and "end of month" as the record with the first date in the month and the record with the last date in the month.
By this calculation, you do arrive at "0.25" for month 05, but for month 06, there is only one record, so its both the first and last record of the month, and the calculation is (750-750)/750, which is zero.
In your example calculation, you take the end of the last month to calculate the value for month 06, but take the end and start of month 05 for the calculation of the growth in month 05. These are two different calculations! You'll have to decide which one to use.
If you want to use the first and last record in a given month (as you do for month 05), you can use this:
SELECT
`t0`.`month`,
IF(`t1`.balance = 0,
NULL,
((`t2`.balance - `t1`.balance) / `t1`.balance)) AS `growth`
FROM
(SELECT
DATE_FORMAT(`date`, '%Y-%m') AS 'month',
MIN(`date`) AS 'min_date',
MAX(`date`) AS 'max_date'
FROM
`your_table`
GROUP BY MONTH(`date`)) AS `t0`
JOIN
`your_table` AS `t1` ON (`t1`.date = `min_date`)
JOIN
`your_table` AS `t2` ON (`t2`.date = `max_date`);
Note the "IF" because you have to guard against dividing by zero - a growth from zero to any positive value is always "infinity percent", which makes no sense to use - so you have to know how the things you're trying to build should work in these cases.
Provided that the DB version is 8.0, then you can use analytic functions as in the following query
SELECT month,
(bal_end - COALESCE(LAG(bal_end) OVER(ORDER BY month), bal_start)) /
COALESCE(LAG(bal_end) OVER(ORDER BY month), bal_start) AS growth
FROM (SELECT month,
MAX(CASE
WHEN m_end = 1 THEN
sum_balance
END) AS bal_end,
MAX(CASE
WHEN m_start = 1 THEN
sum_balance
END) bal_start
FROM (SELECT month,
SUM(COALESCE(CASE
WHEN bal_start = 1 THEN
balance
END,
0) + COALESCE(revenue, 0)) OVER(ORDER BY date) AS sum_balance,
m_end,
m_start,
date
FROM (SELECT DATE_FORMAT(date, '%Y-%m') AS month,
t.*,
ROW_NUMBER() OVER(PARTITION BY DATE_FORMAT(date, '%Y-%m') ORDER BY date) AS m_start,
ROW_NUMBER() OVER(PARTITION BY DATE_FORMAT(date, '%Y-%m') ORDER BY date DESC) AS m_end,
ROW_NUMBER() OVER(ORDER BY date) AS bal_start
FROM t) AS t0) AS t1
GROUP BY month) AS t2;
month
growth
2021-05
0.2500
2021-06
-0.4000
Demo

Avg function not returning proper value

I expect this query to give me the avg value from daily active users up to date and grouped by month (from Oct to December). But the result is 164K aprox when it should be 128K. Why avg is not working? Avg should be SUM of values / number of current month days up to today.
SELECT sq.month_year AS 'month_year', AVG(number)
FROM
(
SELECT CONCAT(MONTHNAME(date), "-", YEAR(DATE)) AS 'month_year', count(distinct id_user) AS number
FROM table1
WHERE date between '2020-10-01' and '2020-12-31 23:59:59'
GROUP BY EXTRACT(year_month FROM date)
) sq
GROUP BY 1
Ok guys thanks for your help. The problem was that on the subquery I was pulling the info by month and not by day. So I should pull the info by day there and group by month in the outer query. This finally worked:
SELECT sq.day_month, AVG(number)
FROM (SELECT date(date) AS day_month,
count(distinct id_user) AS number
FROM table_1
WHERE date >= '2020-10-01' AND
date < '2021-01-01'
GROUP BY 1
) sq
GROUP BY EXTRACT(year_month FROM day_month)
Do not use single quotes for column aliases!
SELECT sq.month_year, AVG(number)
FROM (SELECT CONCAT(MONTHNAME(date), '-', YEAR(DATE)) AS month_year,
count(distinct id_user) AS number
FROM table1
WHERE date >= '2020-10-01' AND
date < '2021-01-01'
GROUP BY month_year
) sq
GROUP BY 1;
Note the fixes to the query:
The GROUP BY uses the same columns as the SELECT. Your query should return an error (although it works in older versions of MySQL).
The date comparisons have been simplified.
No single quotes on column aliases.
Note that the outer query is not needed. I assume it is there just to illustrate the issue you are having.

Average timediff of 2nd and 3rd datetimes for a group

I need to find the average time in days between a customer's second order and third order
I know that I need to use the timestampdiff but am quite at a loss for how to select the second and third dates and need some sort of nest.
SELECT CustomerID,
OrderDate,
diff,
avg(timestampdiff(day, start_date, end_date)) AS average_days
FROM () o3
WHERE date3, date2
ORDER BY CustomerID, OrderDate;
Table
To achieve your desired result, you first need to calculate ROW_NUMBER from your data PARTITION BY CustmerId. Then keep rows only with RowNumber IN (2,3) and then get the DateDiff between two days. The following query will help getting your desired results-
SELECT CustomerID,datediff(MAX(OrderDate),MIN(OrderDate))
FROM
(
SELECT *,
#row_num :=IF(#prev_value = concat_ws('',CsutomerID),#row_num+1,1)AS RowNumber
, #prev_value := concat_ws('',CsutomerID)
FROM your_table A
ORDER BY CustomerID,OrderDate
)B
WHERE B.RowNumber IN (2,3)
GROUP BY CustomerID;

SQL command advice

How would I make the shipdate year greater than 2013 in sql. Here is the code I have so far:
SELECT shipdate, COUNT(*)
FROM dw.orderline
GROUP BY shipdate
ORDER BY 1;
Something like this will do the trick:
SELECT shipdate, COUNT(*) AS SHIP_COUNT
FROM dw.orderline
WHERE shipdate >= '01/01/2013'
GROUP BY shipdate;
EDIT:
As per your comments:
SELECT YEAR(shipdate), COUNT(*) AS SHIP_COUNT
FROM dw.orderline
WHERE YEAR(shipdate) > 2013
GROUP BY shipdate
EDIT 2:
For an Oracle based SQL:
SELECT EXTRACT(year FROM shipdate), COUNT(*) AS SHIP_COUNT
FROM dw.orderline
WHERE EXTRACT(year FROM shipdate) > 2013
GROUP BY shipdate
See working example on SQL Fiddle here
Should it not be
SELECT
shipdate, COUNT(*)
FROM
dw.orderline
WHERE
Year(shipdate)>2013
GROUP BY
shipdate
Every date with year 2013 should be excluded as well. Or did I interpreted the question incorrect?
Correct Query:
SELECT shipdate, COUNT(*) ships_counter
FROM dw.orderline
WHERE shipdate > '01/01/2013'
GROUP BY shipdate;

MySQL select rows with date like

In MySQL I have this query
SELECT DISTINCT date, descr FROM book ORDER BY date
Date is in format yyyy-mm-dd
I want to select only the the books from January 2012. I have tried to use like but that does not work.
Any ideas?
Using DATE_FORMAT function
SELECT DISTINCT date, descr FROM book
WHERE DATE_FORMAT(date, '%Y %m') = DATE_FORMAT('2012-01-01', '%Y %m')
ORDER BY date
Or using MONTH and YEAR functions
SELECT DISTINCT date, descr FROM book
WHERE Month(date) = Month('2012-01-01')
AND Year(date) = Year('2012-01-01')
ORDER BY date;
Or using BETWEEN functions
SELECT DISTINCT date, descr FROM book
WHERE date BETWEEN '2012-01-01'
AND '2012-01-31'
ORDER BY date;
Or using <= and >= operators
SELECT DISTINCT date, descr FROM book
WHERE date >= '2012-01-01'
AND date <= '2012-01-31'
ORDER BY date;
See this SQLFiddle
You can use >= and <= operators here. Check the below code:
SELECT *
FROM book
WHERE date >= '2012-01-01' AND date <= '2012-01-31'
Try this:
SELECT DISTINCT date, descr FROM book WHERE YEAR(date) = '2012' and MONTH(date) = '1'
This works if your "date"-column is a MySQL date field.
If you are adamant that you want to use the LIKE syntax, you can convert the date to CHAR first:
SELECT DISTINCT date, descr FROM book WHERE CAST(date AS char) LIKE '2012-01%' ORDER BY date;
Using like also works. With #hims056 fiddle, you can test it:
SELECT DISTINCT ID, date FROM book
WHERE date LIKE '2012-01%'
ORDER BY date;
However, it's not usual to use a like for date filtering, for me it's more natural to use >= and <= , or between. Also, there's a performance benefit.
SELECT DISTINCT date, descr
FROM book
WHERE YEAR = DATE(NOW()) AND MONTH(date) = '1'
This will give you this years books