Hi everybody,
I have this visitors table:
ID | Date | Purpose
1 | 20/10/2016 | Purpose1
2 | 22/10/2016 | Purpose1
3 | 25/10/2016 | Purpose2
4 | 12/11/2016 | Purpose1
5 | 14/11/2016 | Purpose2
6 | 16/11/2016 | Purpose2
Currently I'm using this query:
select case
when date like '%/10/2016' then '10/2016'
when date like '%/11/2016' then '11/2016'
end as month, count(*) as total
from visitors
where
date like '%/10/2016'
or date like '%/11/2016'
GROUP by month
I can only get month and total column count from query above. How can I achieve this output?
Month | Total | Purpose1 | Purpose2
10/2016 | 3 | 2 | 1
11/2016 | 3 | 1 | 2
Thanks!
Consider the following...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,Date DATE NOT NULL
,Purpose VARCHAR(12) NOT NULL
);
INSERT INTO my_table VALUES
(1,'2016-10-20','Purpose1'),
(2,'2016-10-22','Purpose1'),
(3,'2016-10-25','Purpose2'),
(4,'2016-11-12','Purpose1'),
(5,'2016-11-14','Purpose2'),
(6,'2016-11-16','Purpose2');
SELECT DATE_FORMAT(date,'%Y-%m') month
, SUM(purpose = 'purpose1') purpose1
, SUM(purpose = 'purpose2') purpose2
, COUNT(*) total
FROM my_table
GROUP
BY month;
+---------+----------+----------+-------+
| month | purpose1 | purpose2 | total |
+---------+----------+----------+-------+
| 2016-10 | 2 | 1 | 3 |
| 2016-11 | 1 | 2 | 3 |
+---------+----------+----------+-------+
..or (and in my view, better, provided you have access to application code)...
SELECT DATE_FORMAT(date,'%Y-%m') month
, purpose
, COUNT(*) total
FROM my_table
GROUP
BY month
, purpose;
+---------+----------+-------+
| month | purpose | total |
+---------+----------+-------+
| 2016-10 | Purpose1 | 2 |
| 2016-10 | Purpose2 | 1 |
| 2016-11 | Purpose1 | 1 |
| 2016-11 | Purpose2 | 2 |
+---------+----------+-------+
Transposing tables isn't very fast. It is better to do so in some small program.
If you do a
select case
when date like '%/10/2016' then '10/2016'
when date like '%/11/2016' then '11/2016'
end as month, count(*) as total, Purpose
from visitors
where
date like '%/10/2016'
or date like '%/11/2016'
GROUP by month, Purpose
You'll have a good starting point.
You might need to add an ORDER BY clause (depending on your DBMS).
If (and only if) you only have two purposes in your table and the table isn't huge in size, you can create two views and join them.
Related
I have two tables i.e vehicle and vehicle_maintenance.
vehicle
-----------------------------------
| v_id | v_name | v_no |
-----------------------------------
| 1 | car1 | car123 |
-----------------------------------
| 2 | car2 | car456 |
-----------------------------------
vehicle_maintenance
-----------------------------------------------------------------------
| v_main_id | v_id | v_main_date | v_main_remainder |
-----------------------------------------------------------------------
| 1 | 1 | 2020/10/10 | 1 |
| 2 | 1 | 2020/10/20 | 2 |
| 3 | 2 | 2020/10/04 | 365 |
| 4 | 2 | 2020/10/15 | 5 |
-----------------------------------------------------------------------
I want to get each car maintenance details i.e car2 maintenance date is 2020/10/15 and i want to check next maintenance date based on v_main_remainder field. That means next maintenance date will be 2020/10/20 ( add 5 day to the maintenance date). I want to also calculate the no of days left from next maintenance date. Suppose today is 2020/10/10 then it will show 10 days left.
Here is my query
SELECT
v.v_id,
v.v_name,
v.v_no,
max(vm.v_main_date) as renewal_date,
datediff(
DATE_ADD(
max(vm.v_main_date), INTERVAL +vm.v_main_remainder day
),
now()
) as day_left
FROM vehicle as v, vehicle_maintenance as vm
GROUP BY v.v_id
But the problem is vm.v_main_remainder in date_add function taken from first row.
Here is the result
-----------------------------------------------------------------------
| v_id | v_name | v_no | renewal_date | day_left |
-----------------------------------------------------------------------
| 1 | car1 | car123 | 2020/10/20 | 11 |
-----------------------------------------------------------------------
| 2 | car2 | car456 | 2020/10/15 | 370 |
-----------------------------------------------------------------------
As a starter, your query is obviously missing a join condition between the two tables, so that's a cartesian product. This type of problem is much easier to spot when using explicit joins.
Then: you want to filter on the latest maintenance record per car, so aggregation is not appropriate.
One option uses window functions, available in MySQL 8.0:
select v.v_id, v.v_name, v.v_no, vm.v_main_date as renewal_date,
datediff(vm.v_main_date + interval vm.v_main_remainder day, current_date) as day_left
from vehicle as v
inner join (
select vm.*, row_number() over(partition by v_id order by v_main_date desc) rn
from vehicle_maintenance
) as vm on vm.v_id = v.v_id
where vm.rn = 1
Note that I changed now() to current_date, so datediff() works consistently on dates rather than datetimes.
I am currently struggling on how to aggregate my daily data in other time aggregations (weeks, months, quarters etc).
Here is how my raw data type looks like:
| date | traffic_type | visits |
|----------|--------------|---------|
| 20180101 | 1 | 1221650 |
| 20180101 | 2 | 411424 |
| 20180101 | 4 | 108407 |
| 20180101 | 5 | 298117 |
| 20180101 | 6 | 26806 |
| 20180101 | 7 | 12033 |
| 20180101 | 8 | 80368 |
| 20180101 | 9 | 69544 |
| 20180101 | 10 | 39919 |
| 20180101 | 11 | 26291 |
| 20180102 | 1 | 1218490 |
| 20180102 | 2 | 410965 |
| 20180102 | 4 | 108037 |
| 20180102 | 5 | 297727 |
| 20180102 | 6 | 26719 |
| 20180102 | 7 | 12019 |
| 20180102 | 8 | 80074 |
First, I would like to check the sum of visits regardless of traffic_type:
SELECT date, SUM(visits) as visits_per_day
FROM visits_tbl
GROUP BY date
Here is the outcome:
| ymd | visits_per_day |
|:--------:|:--------------:|
| 20180101 | 2294563 |
| 20180102 | 2289145 |
| 20180103 | 2300367 |
| 20180104 | 2310256 |
| 20180105 | 2368098 |
| 20180106 | 2372257 |
| 20180107 | 2373863 |
| 20180108 | 2364236 |
However, if I want to check the specific day which the visits_per_day was the highest for each time aggregation (eg.: Month), I am struggling to retrieve the right output.
Here is what I did:
SELECT
(date div 100) as y_month, MAX(visits_per_day) as max_visit_per_day
FROM
(SELECT date, SUM(visits) as visits_per_day
FROM visits_tbl
GROUP BY date) as t1
GROUP BY
y_month
And here is the output of my query:
| y_month | max_visit_per_day |
|:-------:|:-----------------:|
| 201801 | 2435845 |
| 201802 | 2519000 |
| 201803 | 2528097 |
| 201804 | 2550645 |
However, I cannot know what was the exact day where the visits_per_day was the highest.
Desired output:
| y_month | max_visit_per_day | ymd |
|:-------:|:-----------------:|:--------:|
| 201801 | 2435845 | 20180130 |
| 201802 | 2519000 | 20180220 |
| 201803 | 2528097 | 20180325 |
| 201804 | 2550645 | 20180406 |
ymd would represent the day in which the visits_per_day was the highest.
This logic would be used in a dashboard with the help of programming in order to automatically select the time aggregation.
Can someone please help me?
This is a job for the structured part of structured query language. That is, you will write some subqueries and treat them as tables.
You already know how to find the number of visits per day. Let's add the month for each day to that query (http://sqlfiddle.com/#!9/a8455e/13/0).
SELECT date DIV 100 as month, date,
SUM(visits) as visits
FROM visits_tbl
GROUP BY date
Next you need to find the largest number of daily visits in each month. (http://sqlfiddle.com/#!9/a8455e/12/0)
SELECT month, MAX(visits) max_daily_visits
FROM (
SELECT date DIV 100 as month, date,
SUM(visits) as visits
FROM visits_tbl
GROUP BY date
) dayvisits
GROUP BY month
Then, the trick is retrieving the date on which that maximum occurred in each month. That requires a join. Without common table expressions (which MySQL lacks) you need to repeat the first subquery. (http://sqlfiddle.com/#!9/a8455e/11/0)
SELECT detail.*
FROM (
SELECT month, MAX(visits) max_daily_visits
FROM (
SELECT date DIV 100 as month, date,
SUM(visits) as visits
FROM visits_tbl
GROUP BY date
) dayvisits
GROUP BY month
) maxvisits
JOIN (
SELECT date DIV 100 as month, date,
SUM(visits) as visits
FROM visits_tbl
GROUP BY date
) detail ON detail.visits = maxvisits.max_daily_visits
AND detail.month = maxvisits.month
The outline of this rather complex query helps explain it. Instead of that subquery, we'll use an imaginary table called dayvisits.
SELECT detail.*
FROM (
SELECT month, MAX(visits) max_daily_visits
FROM dayvisits
GROUP BY date DIV 100
) maxvisits
JOIN dayvisits detail ON detail.visits = maxvisits.max_daily_visits
AND detail.month = maxvisits.month
You're seeking an extreme value for each month in the subquery. (This is a fairly standard sort of SQL operation.) To do that you find that value with a MAX() ... GROUP BY query. Then you join that to the subquery itself to find the other values corresponding to the extreme value.
If you did have common table expressions, the query would look like this. YOu might consider adopting the MySQL fork called MariaDB, which has CTEs.
WITH dayvisits AS (
SELECT date DIV 100 as month, date,
SUM(visits) as visits
FROM visits_tbl
GROUP BY date
)
SELECT dayvisits.*
FROM (
SELECT month, MAX(visits) max_daily_visits
FROM dayvisits
GROUP BY month
) maxvisits
JOIN dayvisits ON dayvisits.visits = maxvisits.max_daily_visits
AND dayvisits.month = maxvisits.month
[Query Check on MSSQL] its quick and efficient.
select visit_sum_day_wise.date
, visit_sum_day_wise.Max_Visits
, visit_sum_day_wise.traffic_type
, LAST_VALUE(visit_sum_day_wise.visits) OVER(PARTITION BY
visit_sum_day_wise.date ORDER BY visit_sum_day_wise.date ROWS BETWEEN
UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS max_visit_per_day
from (
select visits_tbl.date , visits_tbl.visits , visits_tbl.traffic_type
,max(visits_tbl.visits ) OVER ( PARTITION BY visits_tbl.date ORDER
BY visits_tbl.date ROWS BETWEEN UNBOUNDED PRECEDING AND 0
PRECEDING) Max_visits
from visits_tbl
) as visit_sum_day_wise
where visit_sum_day_wise.visits = (select max(visits_B.visits ) from
visits_tbl visits_B where visits_B.Date = visit_sum_day_wise.date )
enter image description here
I have a database that store transaction logs, I would like to count all the logs for that day and group them based on prod_id
MySQL table structure:
Table name = products
+------+---------+------------+--------+
| ID | PROD_ID | DATE | PERSON |
+------+---------+------------+--------+
| 1 | 2 | 1400137633 | 1 |
| 2 | 2 | 1400137666 | 1 |
| 3 | 3 | 1400137125 | 2 |
| 4 | 4 | 1400137563 | 1 |
| 5 | 2 | 1400137425 | 2 |
| 6 | 3 | 1400137336 | 1 |
+------+---------+------------+--------+
MYSQL CODE:
$q = 'SELECT count(ID) as count
FROM PRODUCTS
WHERE PERSON ='.$db->qstr($person).'
AND DATE(FROM_UNIXTIME(DATE)) = DATE(NOW())';
so what I get is the number of items for the given date. Since the date is the same as all other entries. however I would like to group the items by prod_id, I tried GROUP BY PROD_ID but that did not give me what I want. I would like it to group if the PROD_ID is multiple and the date is the same display as one entry while still count the others
so here I should get an output ($Person = 1).... 2+2+2=1 +3 +4 so total should be 3
any suggestions?
Use DISTINCT with COUNT on PROD_ID.
Example:
SELECT count( distinct PROD_ID ) as count
FROM PRODUCTS
WHERE PERSON = 1 -- <---- change this with relevant variable
AND DATE( FROM_UNIXTIME (DATE ) ) = curdate();
And I suggest you to use Prepared Statement to bind values.
I am trying to do transformation on a table in Mysql. I can't figure out how to do it. Could anyone tell me how to do it? The input and output is given. I would like to know how it is done?
Input table
+-------------+------------+------------------+-------------------+
| Employee_ID | Start_Date | Termination_Date | Performance_Level |
+-------------+------------+------------------+-------------------+
| 1 | 1/1/2007 | 3/1/2007 | Low |
| 2 | 6/5/2004 | Null | Medium |
| 3 | 4/3/2003 | Null | High |
| 4 | 9/1/2002 | 4/15/2007 | Medium |
| 5 | 4/6/2007 | 11/1/2007 | Low |
| 6 | 7/1/2007 | Null | High |
| 7 | 3/2/2005 | 8/1/2007 | Low |
+-------------+------------+------------------+-------------------+
Ouput Table
+---------+-----------------------------------+-----------------+-------------------+----------------+
| Period | Total_Employees_at_end_of_quarter | High_Performers | Medium_Performers | Low_Performers |
+---------+-----------------------------------+-----------------+-------------------+----------------+
| Q1-2007 | 4 | 1 | 2 | 1 |
| Q2-2007 | 4 | 1 | 1 | 2 |
| Q3-2007 | 4 | 2 | 1 | 1 |
| Q4-2007 | 3 | 2 | 1 | 0 |
+---------+-----------------------------------+-----------------+-------------------+----------------+
This is what I tried
select * from emp
where date(sdate)< date'2007-04-01' and (date(tdate)> date'2007-03-31' or tdate is null);
select * from emp
where date(sdate)< date'2007-07-01' and (date(tdate)> date'2007-06-30' or tdate is null);
select * from emp
where date(sdate)< date'2007-010-01' and (date(tdate)> date'2007-09-30' or tdate is null);
select * from emp
where date(sdate)< date'2008-01-01' and (date(tdate)> date'2007-12-31' or tdate is null);
I have the individual queries but I want a single query which will give the outputs.
The approach taken below is to create a driver table for each quarter, with information about the year and quarter. This is then joined to the employee table, using a non-equijoin. Employees who start in or before the quarter and end after the quarter are active at the end of quarter.
It uses one trick for the date comparisons, which is to convert the year-quarter combination into a quarter count, by multiplying the year by 4 and adding the quarter. This is a convenience for simplifying the date comparisons.
select driver.qtryr, count(*) as TotalPerformers,
sum(Performance_level = 'High') as HighPerformers,
sum(Performance_level = 'Medium') as MediumPerformers,
sum(Performance_level = 'Low') as LowPerformers
from (select 2007 as yr, 1 as qtr, 'Q1-2007' as qtryr union all
select 2007 as yr, 2 as qtr, 'Q2-2007' as qtryr union all
select 2007 as yr, 3 as qtr, 'Q3-2007' as qtryr union all
select 2007 as yr, 4 as qtr, 'Q4-2007' as qtryr
) driver left outer join
Table1 emp
on year(emp.start_date)*4+quarter(emp.start_date) <= driver.yr*4+qtr and
(emp.termination_date is null or
year(emp.termination_date)*4+quarter(emp.termination_date) > driver.yr*4+qtr
)
group by driver.qtryr
sqlfiddle
try this
SELECT QUARTER('2008-04-01');
http://dev.mysql.com/doc/refman/5.6/en/date-and-time-functions.html#function_quarter
and CONCAT()
Using table below, How would get a column for 5 period moving average, 10 period moving average, 5 period exponential moving average.
+--------+------------+
| price | data_date |
+--------+------------+
| 122.29 | 2009-10-08 |
| 122.78 | 2009-10-07 |
| 121.35 | 2009-10-06 |
| 119.75 | 2009-10-05 |
| 119.02 | 2009-10-02 |
| 117.90 | 2009-10-01 |
| 119.61 | 2009-09-30 |
| 118.81 | 2009-09-29 |
| 119.33 | 2009-09-28 |
| 121.08 | 2009-09-25 |
+--------+------------+
The 5-row moving average in your example won't work. The LIMIT operator applies to the return set, not the rows being considered for the aggregates, so changing it makes no difference to the aggregate values.
SELECT AVG(a.price) FROM (SELECT price FROM t1 WHERE data_date <= ? ORDER BY data_date DESC LIMIT 5) AS a;
Replace ? with the date whose MA you need.
SELECT t1.data_date,
( SELECT SUM(t2.price) / COUNT(t2.price) as MA5 FROM mytable AS t2 WHERE DATEDIFF(t1.data_date, t2.data_date) BETWEEN 0 AND 6 )
FROM mytable AS t1 ORDER BY t1.data_date;
Change 6 to 13 for 10-day MA