SQL month to day average (auto) - sql-server-2014

How to count the day for average use in SQL?
for example, I can use today()-20171001 to count it is 22 for average use in excel which it is automatic.
I need count the average value of the computer in Oct-2017 to day.
table: computer data
date | property | computer code | value
---------- -------- ----------- -------
20160131 | companyA | 256584 | 1,000
20160131 | companyB | 987451 | 1,200
...
20171022 | companyA | 157489 | 1,600
20171022 | companyA | 589741 | 1,400
20171022 | comapnyA | 547182 | 2,750
sql:
select property
,sum (case when property = 'companyA' then value end) / ( the code I have asked - the answer should be 22, 20171023-20171001) as avgvalue_computer
from computer table
group by property
order by property
the result I wanted
property | avgvalue_computer
companyA | 8,500 <- 2017-Oct-01 to 2017-Oct-23 (today)
companyB | 16,800 <- 2017-Oct-01 to 2017-Oct-23 (today)

DECLARE #Table TABLE (Date DATE, Property VARCHAR(20), PropertyCode INT, [Value] FLOAT)
INSERT INTO #Table VALUES
('20161031','cA', 256584, 1000)
SELECT Property CompanyName, MTD.Value, MTD.DateFrom, MTD.DateTo
FROM #Table T1
CROSS APPLY (SELECT AVG(VALUE) Value, DATEADD (D,
DATEPART(DAY, GETDATE()) *-1,
GETDATE()) DateFrom, GETDATE() DateTo
FROM #Table T2
WHERE T2.PropertyCode = T1.PropertyCode
AND T2.Date BETWEEN (DATEADD (D,
DATEPART(DAY, GETDATE()) *-1,
GETDATE()))
AND GETDATE()
) MTD

select property
,sum (case when property = 'companyA' then value end) / datediff(day, '20171001', getdate())) as avgvalue_computer
from computer table
group by property
order by property

Related

number of visitors gained and lost comparing with previous day - sql

CREATE TABLE visitors (
Date DATE,
visitor VARCHAR(20));
INSERT INTO visitors VALUES
("2019-10-01", "v1"),
("2019-10-01", "v2"),
("2019-10-01", "v3"),
("2019-10-02", "v2"),
("2019-10-03", "v2"),
("2019-10-03", "v4"),
("2019-10-03", "v5");
please find Find number_of_visitors_per_date, number_of_visitors gained comparing with previous day, and number_of_visitors lost comparing with previous day. The expected results for the above table should be:
Date | number_of_visitors | number_of_visitors_gained | number_of_visitors_lost
2019-10-01 | 3 | 3 | 0
2019-10-02 | 1 | 0 | 2
2019-10-03 | 3 | 2 | 0
Obviously, the most challenging part is how to get the last two columns. Note, since there is no previous day for the first day, the number_of_visitors_gained is the total number of visitors in the first day, and the number_of_visitors_lost is 0.
If you RDBMS supports window functions, you can aggregate, and then use lag():
select
date,
number_of_visitors,
case
when lag(number_of_visitors) over(order by date) is null
then number_of_visitors
when lag(number_of_visitors) over(order by date) < number_of_visitors
then number_of_visitors - lag(number_of_visitors) over(order by date)
else 0
end number_of_visitors_gained,
case when lag(number_of_visitors) over(order by date) > number_of_visitors
then lag(number_of_visitors) over(order by date) - number_of_visitors
else 0
end number_of_visitors_lost
from (
select date, count(*) number_of_visitors
from visitors
group by date
) t
order by date
Demo on DB Fiddle:
date | number_of_visitors | number_of_visitors_gained | number_of_visitors_lost
:--------- | -----------------: | ------------------------: | ----------------------:
2019-10-01 | 3 | 3 | 0
2019-10-02 | 1 | 0 | 2
2019-10-03 | 3 | 2 | 0
You are looking for aggregation using lead() and lag() window functions:
select date, count(*) as visitors_on_day,
sum(case when prev_vdate is null or prev_vdate <> date - interval 1 day
then 1 else 0
end) as gained_visitors,
sum(case when next_vdate <> date + interval 1 day
then 1 else 0
end) as lost_visitors
from (select v.*,
lag(date) over (partition by visitor) as prev_vdate,
lead(date) over (partition by visitor) as next_vdate
from visitors v
) t
group by date
order by date;

SQL: Grouped average-if / case SELECT statement

I have a database that looks like this SQL Fiddle: http://sqlfiddle.com/#!9/aa02e/1
CREATE TABLE Table1
(`Store` varchar(1), `Date` date, `Product` varchar(2), `Weekday` int, `Month` int, `Revenue` float)
;
INSERT INTO Table1
(`Store`, `Date`, `Product`, `Weekday`, `Month`, `Revenue`)
VALUES
('a', '20160101', 'aa', 5, 1, 1.5),
('a', '20160101', 'bb', 5, 1, 4),
('a', '20160101', 'cc', 5, 1, 3.5),
('a', '20160108', 'dd', 5, 1, 2.5),
('a', '20160108', 'ee', 5, 1, 5),
('b', '20160204', 'aa', 4, 2, 9.5),
('b', '20160204', 'bb', 4, 2, 4),
('b', '20160204', 'cc', 4, 2, 3),
('b', '20160211', 'dd', 4, 2, 1.5),
('b', '20160211', 'ee', 4, 2, 2.5)
;
SELECT * FROM table1;
+-------+------------+---------+---------+-------+---------+
| Store | Date | Product | Weekday | Month | Revenue |
+-------+------------+---------+---------+-------+---------+
| a | 2016-01-01 | aa | 5 | 1 | 1.5 |
| a | 2016-01-01 | bb | 5 | 1 | 4 |
| a | 2016-01-01 | cc | 5 | 1 | 3.5 |
| a | 2016-01-08 | dd | 5 | 1 | 2.5 |
| a | 2016-01-08 | ee | 5 | 1 | 5 |
| b | 2016-02-04 | aa | 4 | 2 | 9.5 |
| b | 2016-02-04 | bb | 4 | 2 | 4 |
| b | 2016-02-04 | cc | 4 | 2 | 3 |
| b | 2016-02-11 | dd | 4 | 2 | 1.5 |
| b | 2016-02-11 | ee | 4 | 2 | 2.5 |
+-------+------------+---------+---------+-------+---------+
It shows revenue data for stores incl. products, date and the respective day/month.
I want to select the following:
Store
Monthly revenue totals (i.e. what is the total revenue for store a in Jan?)
Weekday revenue averages (i.e. what is the avg revenue for store a on Thu?)
The first and second bullet are straightforward, but I'm having problems with the last one.
Currently, it takes the average over all products and all dates (assuming the weekday matches). What I need are the following steps:
Sum up all revenues for a store and a particular date (e.g. for store b: 9.5+4+3=16.5 for Feb 4th, and 1.5+2.5=4 for Feb 11th) if that date has the same weekday (here Thursday)
Take the average of the two values (e.g. avg(16.5,4)=10.25)
How can I accomplish that?
Thank you
Here is the query:
SELECT
Store,
SUM(CASE WHEN Month = 1 THEN Revenue ELSE NULL END) AS REVENUE_JAN,
SUM(CASE WHEN Month = 2 THEN Revenue ELSE NULL END) AS REVENUE_FEB,
AVG(CASE WHEN Weekday = 4 THEN Revenue ELSE NULL END) AS REVENUE_THU,
AVG(CASE WHEN Weekday = 5 THEN Revenue ELSE NULL END) AS REVENUE_FRI
FROM Table1
GROUP BY
Store
;
The weekday average is tricky. Your query is getting the average "order size" per weekday. But you want the total revenue.
One method is to first aggregate by weekday, but that is a bit of a mess. Instead, you can use this trick of calculating the average by dividing the total revenue by the number of days:
SELECT Store,
SUM(CASE WHEN Month = 1 THEN Revenue ELSE NULL END) AS REVENUE_JAN,
SUM(CASE WHEN Month = 2 THEN Revenue ELSE NULL END) AS REVENUE_FEB,
(SUM(CASE WHEN Weekday = 4 THEN Revenue END) /
COUNT(DISTINCT CASE WHEN Weekday = 4 THEN Date END)
) AS REVENUE_THU,
(SUM(CASE WHEN Weekday = 5 THEN Revenue END) /
COUNT(DISTINCT CASE WHEN Weekday = 5 THEN Date END)
) AS REVENUE_FRI
FROM Table1
GROUP BY Store;
SELECT
t1.store,
SUM(CASE WHEN Month = 1 THEN Revenue ELSE NULL END) AS REVENUE_JAN,
SUM(CASE WHEN Month = 2 THEN Revenue ELSE NULL END) AS REVENUE_FEB,
daily.REVENUE_THU,
daily.REVENUE_FRI
FROM Table1 t1
JOIN (
SELECT
Store,
weekday,
avg(CASE WHEN weekday = 4 THEN sum_rev END) as REVENUE_THU,
avg(CASE WHEN weekday = 5 THEN sum_rev END) as REVENUE_FRI
FROM (
SELECT
Store, date, weekday,
SUM(revenue) AS sum_rev
FROM Table1
GROUP BY
Store, date, weekday
) AS foo
GROUP BY Store, weekday
) AS daily ON daily.store = t1.store
GROUP BY
t1.store
How about this solution it return average for chosen day of chosen store
CREATE PROCEDURE sumForDayStore(IN vday INTEGER, IN vStore VARCHAR(50))
BEGIN
DECLARE totalDays INTEGER;
DECLARE totalRevenu INTEGER;
SET totalDays = (SELECT count(*) FROM Table1 WHERE WeekDay = vDay AND store = vStore);
SET totalRevenu = (SELECT sum(Revenue) FROM Table1 WHERE WeekDay = vDay AND store = vStore);
SELECT totalRevenu/totalDays;
END;
CALL sumForDayStore(5,'a');
How about this one:
SELECT mnth.Store, REVENUE_JAN, REVENUE_FEB, avg(rthu) REVENUE_THU, avg(rfri) REVENUE_FRI
FROM
(Select Store, sum(case when Month = 1 then Revenue else NULL END) REVENUE_JAN,
sum(case when Month = 2 then Revenue else NULL END) REVENUE_FEB
From Table1 group by Store) as mnth
join
(Select Store, sum(case when Weekday = 4 then Revenue end) rThu,
sum(case when Weekday = 5 then Revenue end) rFri from Table1 group by Store, Date) as dys
on mnth.Store = dys.Store
group by mnth.Store, REVENUE_JAN, REVENUE_FEB
I compared the performance of this with the query in the first answer and it shows better performance according to SQL server execution plan (1.6 times faster). Maybe this would be helpful on a larger data set.

MySQL - 2 sums and 2 WHERE statements from one table into 2 columns

maybe this is an easy answer for someone but I searched and searched and failed.
Here's what I have: 1 Table that contains the following:
id, int
year, int
month, int
day, int
revenue, float
sub_category, text
What I'm trying to get is 1 SQL statement that returns 1 table with 3 columns to hold the sum of the revenue per month and sub_category
-------------------------------
| Month | revcomp1 | revcomp2 |
-------------------------------
| 01 | sumcat1 | sumcat2 |
-------------------------------
| 02 | sumcat1 | sumcat2 |
-------------------------------
| .. | .. | .. |
Here's the SQL Statement I was running:
SELECT
month,
cast(sum(rev *-1) as decimal(10,2)) as REVCOMP1,
0 as REVCOMP2
FROM
tbl_data
WHERE
year='2014' and sub_category='comp1'
GROUP BY month, sub_category DESC
UNION
SELECT
month,
0 as REVCOMP1
cast(sum(rev *-1) as decimal(10,2)) as REVCOMP2,
FROM
tbl_data
WHERE
year='2014' and sub_category='comp2'
GROUP BY month, sub_category DESC
But this doesn't really return the sums of a given month in one line...
Any help is really appreciated,
Thanks
You can do this using conditional aggregation:
select month,
cast(sum(case when sub_cateogry = 'comp1' then rev *-1 end) as decimal(10,2)) as REVCOMP1,
cast(sum(case when sub_category = 'comp2' then rev *-1 end) as decimal(10,2)) as REVCOMP2
from tbl_data t
where year = '2014'
group by month;

Create a Summary View in MySQL by pivoting row into dynamic number of columns

I have a table in MySQL with the following fields:
id, company_name, year, state
There are multiple rows for the same customer and year, here is an example of the data:
id | company_name | year | state
----------------------------------------
1 | companyA | 2008 | 1
2 | companyB | 2009 | 2
3 | companyC | 2010 | 3
4 | companyB | 2009 | 1
5 | companyC | NULL | 3
I am trying to create a view from this table to show one company per row (i.e. GROUP BY pubco_name) where the state is the highest for a given year.
Here is an example of the view I am trying to create:
id | cuompany_name | NULL | 2008 | 2009 | 2010
--------------------------------------------------
1 | companyA | NULL | 1 | NULL | NULL
2 | companyB | NULL | 2 | NULL | NULL
3 | companyC | 3 | NULL | NULL | 3
There is a lot more data than this, but you can see what I am trying to accomplish.
I don't know how to select the max state for each year and group by pubco_name.
Here is the SQL I have thus far (I think we need to use CASE and/or sub-selects here):
SELECT
id,
company_name,
SUM(CASE WHEN year = 2008 THEN max(state) ELSE 0 END) AS 2008,
SUM(CASE WHEN year = 2009 THEN max(state) ELSE 0 END) AS 2009,
SUM(CASE WHEN year = 2010 THEN max(state) ELSE 0 END) AS 2010,
SUM(CASE WHEN year = 2011 THEN max(state) ELSE 0 END) AS 2011,
SUM(CASE WHEN year = 2012 THEN max(state) ELSE 0 END) AS 2012,
SUM(CASE WHEN year = 2013 THEN max(state) ELSE 0 END) AS 2013
FROM tbl
GROUP BY company_name
ORDER BY id DESC
Appreciate your help and thanks in advance.
You need to pivot the table but mysql does not have any such functionality of pivot
so we need to replicate its functionality
EDITED
Select
group_concat(
DISTINCT
if(year is null,
CONCAT('max(if (year is null, state, 0)) as ''NULL'' '),
CONCAT('max(if (year=''', year, ''', state, 0)) as ''',year, ''' '))
) into #sql from tbl join (SELECT #sql:='')a;
set #sql = concat('select company_name, ', #sql, 'from tbl group by company_name;');
PREPARE stmt FROM #sql;
EXECUTE stmt;
Result
| COMPANY_NAME | 2008 | 2009 | 2010 | NULL |
--------------------------------------------
| companyA | 1 | 0 | 0 | 0 |
| companyB | 0 | 2 | 0 | 0 |
| companyC | 0 | 0 | 3 | 3 |
SQL FIDDLE
There are 2 approaches to solve your problem
1. create case for each year, which is not possible in your case as we are dealing with year
2. generate the query dynamically so that we get proper columns as per your need.
I have given solution according to the second solution where I am generating the query and storing it in #sql variable. In the fiddle I have printed the contents of #sql before executing it.
select company_name, max(if (year='2008', state, 0)) as '2008' ,max(if (year='2009', state, 0)) as '2009' ,max(if (year='2010', state, 0)) as '2010' ,max(if (year is null, state, 0)) as 'NULL' from tbl group by company_name;
For more information regarding group_concat() go through the link
GROUP_CONCAT and
USER DEFINED VARIABLE
Hope this helps..
Please see the page linked in the answer to this question.
Note that when you do this, you must specify ahead of time how many columns you want in your output.
In response to the comment below, here is a simple/ basic implementation that reproduces the result table above (except for the ID column; having it makes no sense, as each row in the result can summarize more than one row in the input table)
SELECT
`company_name`,
NULLIF(SUM(CASE WHEN `t3`.`year` IS NULL THEN `t3`.`state` ELSE 0 END), 0) AS `null`,
NULLIF(SUM(CASE WHEN `t3`.`year` = 2008 THEN `t3`.`state` ELSE 0 END), 0) AS `2008`,
NULLIF(SUM(CASE WHEN `t3`.`year` = 2009 THEN `t3`.`state` ELSE 0 END), 0) AS `2009`,
NULLIF(SUM(CASE WHEN `t3`.`year` = 2010 THEN `t3`.`state` ELSE 0 END), 0) AS `2010`
FROM
(
SELECT
`t1`.`id`,
`t1`.`company_name`,
`t1`.`year`,
`t1`.`state`
FROM `tbl` `t1`
WHERE `t1`.`state` = (
SELECT MAX(`state`)
FROM `tbl` `t2`
WHERE `t2`.`company_name` = `t1`.`company_name`
AND (`t2`.`year` IS NULL AND `t1`.`year` IS NULL OR `t2`.`year` = `t1`.`year`)
)
) `t3`
GROUP BY `t3`.`company_name`;
This uses nested queries: the inner ones (with the t1 and t2 aliases) find the row with the maximum state for each year and company (and which will break unless you can be sure that this is unique!), and the outer one t3 does the pivot.
I would test this thoroughly to ensure performance is acceptable on real data.

Weekly report behalf of specific date field

I am using this query for weekly reporting but can not found a way like this
week_number | week_startdate | organization_1 | organization_2
---------------------------------------------------------------
1 | 2013-01--05 |count(date) like 4,24,etc_ | count(date) like 4,24,etc_
SQL:
SELECT WEEK(signed_date) AS week_name, signed_date AS Week_Starting,
YEAR(signed_date), WEEK(signed_date), COUNT(*)
FROM business
WHERE YEAR(signed_date) = YEAR(CURDATE())
GROUP BY CONCAT(YEAR(signed_date), '/', WEEK(signed_date))
ORDER BY YEAR(signed_date), WEEK(signed_date
SAMPLE DATA:
signed_date | organization_id
01-01-2013 | 1
02-01-2013 | 1
03-01-2013 | 2
In 1 week organization_1 have 2 signed & organization_2 has 1 signed.
You should use case within count or sum:
SELECT WEEK(signed_date) AS week_name, signed_date AS Week_Starting,
YEAR(signed_date), WEEK(signed_date),
SUM(CASE WHEN organization_id=1 THEN 1 ELSE 0 END) as organization_1,
SUM(CASE WHEN organization_id=2 THEN 1 ELSE 0 END) as organization_2
FROM business
WHERE YEAR(signed_date) = YEAR(CURDATE())
GROUP BY CONCAT(YEAR(signed_date), '/', WEEK(signed_date))
ORDER BY YEAR(signed_date), WEEK(signed_date);
http://sqlfiddle.com/#!2/587ad/3