MySql order by date after DATE_FORMAT - mysql

I have two tables, fbpost and fbalbum, which each have a DATETIME column called createdTime. I'm finding the number of albums per month, and the number of posts per month, and adding them together.
Here's my query(which works as described):
SELECT createdTime, itemCount FROM
(SELECT DATE_FORMAT(createdTime, '%m-%Y') AS createdTime, COUNT(*) AS itemCount FROM fbpost WHERE page_id =2
GROUP BY YEAR(createdTime), MONTH(createdTime)
UNION ALL
SELECT DATE_FORMAT(createdTime, '%m-%Y') AS createdTime, COUNT(*) AS itemCount FROM fbalbum WHERE page_id=2
GROUP BY YEAR(createdTime), MONTH(createdTime)) AS foo
GROUP BY createdTime
This gives the results:
01-2009 | 173
01-2010 | 21
01-2011 | 521
01-2012 | 776
02-2009 | 117
02-2010 | 158
02-2011 | 678
...
But I would like the results to be ordered like this:
01-2009 | 173
02-2009 | 56
03-2009 | 543
04-2009 | 211
05-2009 | 723
06-2009 | 55
07-2009 | 521
...
How can I achieve this?
Note: DATE_FORMAT() gives a string, not a DATETIME, so you can't sort by date. But, if I take out the DATE_FORMAT() in the two nested select statements, I get 2 rows for most months, since that would leave the day. Though there would be only one row per month for each nested select, they day would usually differ, since the last item in a month may be on any day.

Don't use DATE_FORMAT until your outer query:
SELECT DATE_FORMAT(a.createdTime, '%m-%Y') AS createdTime, SUM(a.itemCount) AS itemCount
FROM
(SELECT DATE(createdTime) AS createdTime, COUNT(*) AS itemCount
FROM fbpost WHERE page_id = 2
GROUP BY DATE(createdTime)
UNION ALL
SELECT DATE(createdTime) AS createdTime, COUNT(*) AS itemCount
FROM fbalbum WHERE page_id = 2
GROUP BY DATE(createdTime)) a
GROUP BY YEAR(a.createdTime), MONTH(a.createdTime)
See it in action *
*Demo does not have page_id

Related

Get Percentage of Last X entries in MySQL

I have 2 tables in MySQL(InnoDB). The first is an employee table. The other table is the expense table. For simplicity, the employee table contains just id and first_name. The expense table contains id, employee_id(foreign key), amount_spent, budget, and created_time. What I would like is a query that returns the percentage of their budget spent for the most recent X number of expense they've registered.
So given the employee table:
| id | first_name
-------------------
1 alice
2 bob
3 mike
4 sally
and the expense table:
| id | employee_id | amount_spent | budget | created_time
----------------------------------------------------------
1 1 10 100 10/18
2 1 50 100 10/19
3 1 0 40 10/20
4 2 5 20 10/22
5 2 10 70 10/23
6 2 75 100 10/24
7 3 50 50 10/25
The query for the last 3 trips would return
|employee_id| first_name | percentage_spent |
--------------------------------------------
1 alice .2500 <----------(60/240)
2 bob .4736 <----------(90/190)
3 mike 1.000 <----------(50/50)
The query for the last 2 trips would return
|employee_id| first_name | percentage_spent |
--------------------------------------------
1 alice .3571 <----------(50/140)
2 bob .5000 <----------(85/170)
3 mike 1.000 <----------(50/50)
It would be nice if the query, as noted above, did not return any employees who have not registered any expenses (sally). Thanks in advance!!
I'll advise you to convert datatype of created_time as DATETIME in order to get accurate results.
As of now, I've assumed that most recent id indicates most recent spents as it's what sample data suggests.
Following query should work (didn't tested though):
select t2.employee_id,t1.first_name,
sum(t2.amount_spent)/sum(t2.budget) as percentage_spent
from employee t1
inner join
(select temp.* from
(select e.*,#num := if(#type = employee_id, #num + 1, 1) as row_number,
#type := employee_id as dummy
from expense e
order by employee_id,id desc) temp where temp.row_number <= 3 //write value of **n** here.
) t2
on t1.id = t2.employee_id
group by t2.employee_id
;
Click here for DEMO
Feel free to ask doubt(s), if you've any.
Hope it helps!
If you are using mysql 8.0.2 and higher you might use window function for it.
SELECT employee_id, first_name, sliding_sum_spent/sliding_sum_budget
FROM
(
SELECT employee_id, first_name,
SUM(amount_spent) OVER (PARTITION BY employee_id
ORDER BY created_time
RANGE BETWEEN 3 PRECEDING AND 0 FOLLOWING) AS sliding_sum_spent,
SUM(budget) OVER (PARTITION BY employee_id
ORDER BY created_time
RANGE BETWEEN 3 PRECEDING AND 0 FOLLOWING) AS sliding_sum_budget,
COUNT(*) OVER (PARTITION BY employee_id
ORDER BY created_time DESC) rn
FROM expense
JOIN employee On expense.employee_id = employee.id
) t
WHERE t.rn = 1
As mentioned by Harshil, order of row according to the created_time may be a problem, therefore, it would be better to use date date type.

How to sum up records from starting month to current per month

I've searched for this topic but all I got was questions about grouping results by month. I need to retrieve rows grouped by month with summed up cost from start date to this whole month
Here is an example table
Date | Val
----------- | -----
2017-01-20 | 10
----------- | -----
2017-02-15 | 5
----------- | -----
2017-02-24 | 15
----------- | -----
2017-03-14 | 20
I need to get following output (date format is not the case):
2017-01-20 | 10
2017-02-24 | 30
2017-03-14 | 50
When I run
SELECT SUM(`val`) as `sum`, DATE(`date`) as `date` FROM table
AND `date` BETWEEN :startDate
AND :endDate GROUP BY year(`date`), month(`date`)
I got sum per month of course.
Nothing comes to my mind how to put in nicely in one query to achieve my desired effect, probably W will need to do some nested queries but maybe You know some better solution.
Something like this should work (untestet). You could also solve this by using subqueries, but i guess that would be more costly. In case you want to sort the result by the total value the subquery variant might be faster.
SET #total:=0;
SELECT
(#total := #total + q.sum) AS total, q.date
FROM
(SELECT SUM(`val`) as `sum`, DATE(`date`) as `date` FROM table
AND `date` BETWEEN :startDate
AND :endDate GROUP BY year(`date`), month(`date`)) AS q
You can use DATE_FORMAT function to both, format your query and group by.
DATE_FORMAT(date,format)
Formats the date value according to the format string.
SELECT Date, #total := #total + val as total
FROM
(select #total := 0) x,
(select Sum(Val) as Val, DATE_FORMAT(Date, '%m-%Y') as Date
FROM st where Date >= '2017-01-01' and Date <= '2017-12-31'
GROUP BY DATE_FORMAT(Date, '%m-%Y')) y
;
+---------+-------+
| Date | total |
+---------+-------+
| 01-2017 | 10 |
+---------+-------+
| 02-2017 | 30 |
+---------+-------+
| 03-2017 | 50 |
+---------+-------+
Can check it here: http://rextester.com/FOQO81166
Try this.
I use yearmonth as an integer (the year of the date multiplied by 100 plus the month of the date) . If you want to re-format, your call, but integers are always a bit faster.
It's the complete scenario, including input data.
CREATE TABLE tab (
dt DATE
, qty INT
);
INSERT INTO tab(dt,qty) VALUES( '2017-01-20',10);
INSERT INTO tab(dt,qty) VALUES( '2017-02-15', 5);
INSERT INTO tab(dt,qty) VALUES( '2017-02-24',15);
INSERT INTO tab(dt,qty) VALUES( '2017-03-14',20);
SELECT
yearmonths.yearmonth
, SUM(by_month.month_qty) AS running_qty
FROM (
SELECT DISTINCT
YEAR(dt) * 100 + MONTH(dt) AS yearmonth
FROM tab
) yearmonths
INNER JOIN (
SELECT
YEAR(dt) * 100 + MONTH(dt) AS yearmonth
, SUM(qty) AS month_qty
FROM tab
GROUP BY YEAR(dt) * 100 + MONTH(dt)
) by_month
ON yearmonths.yearmonth >= by_month.yearmonth
GROUP BY yearmonths.yearmonth
ORDER BY 1;
;
yearmonth|running_qty
201,701| 10.0
201,702| 30.0
201,703| 50.0
select succeeded; 3 rows fetched
Need explanations?
My solution has the advantage over the others that it will be re-usable without change when you move it to a more modern database - and you can convert it to using analytic functions when you have time.
Marco the Sane

mysql get last N records with MAX(date)

So I have following data in a product_rate_history table -
I want to select last N records ( eg 7 records ) informing rate change history of given product. If product rate is changed more than one time a day, then query should select most recent rate change for that day.
So from above table I want output like following for product id 16-
+-----------+-------------------------+------------------------+
| product_id | previous_rate | date |
+----------------+--------------------+------------------------|
| 16 | 2400 | 2016-04-30 23:05:35 |
| 16 | 4500 | 2016-04-29 11:02:42 |
+----------------+--------------------+------------------------+
I have tried following query but it returns only one row having last update rate only-
SELECT * FROM `product_rate_history` prh
INNER JOIN (SELECT max(created_on) as max FROM `product_rate_history` GROUP BY Date(created_on)) prh2
ON prh.created_on = prh2.max
WHERE prh.product_id = 16
GROUP BY DATE(prh.created_on)
ORDER BY prh.created_on DESC;
First, you do not need an aggregation in the outer query.
Second, you need to repeat the WHERE clause in the subquery (for the method you are using):
SELECT prh.*
FROM product_rate_history prh INNER JOIN
(SELECT max(created_on) as maxco
FROM product_rate_history
WHERE prh.product_id = 16
GROUP BY Date(created_on)
) prh2
ON prh.created_on = prh2.maxco
WHERE prh.product_id = 16
ORDER BY prh.created_on DESC;

Select distinct ID's

We would like an SQL statement that lists the number of times a unique IP/uniqueID pair has visited on any unique date ordered by the maximum numbers of times that the UniqueID/IP pair has visited.
Here is the table structure:
Column Type
------------------------------
Date Timestamp
NumberofUsers smallint
ipaddress varchar(16)
location varchar(2)
Count bigint(20)
Here is the sql we have been trying:
SELECT
LicenseID,
MAX(Date) AS LatestAccess,
COUNT(DISTINCT Location) AS DifferentCountries,
COUNT(DISTINCT IPAddress) AS DistinctIPCount,
COUNT(DISTINCT Date,IPAddress) AS DistinctDate
FROM
LicenseHistory
WHERE
(LicenseID<>30002)
GROUP BY
LicenseID
ORDER BY
DistinctDate DESC
Here is some sample date from the table in CSV format:
2009-10-08 10:37,30002,8,24.108.64.80,CA,2399
2009-05-27 16:57,24508,50,24.108.64.80,CA,645
2008-11-06 12:04,30,100,24.108.64.80,CA,282
2008-02-04 10:51,24508,30,24.69.19.207,CA,62
2009-10-08 14:52,13136,5,24.108.64.80,CA,285
2013-05-13 13:10,718,10,66.251.68.106,US,23860
2008-02-12 11:10,30002,8,24.69.19.207,CA,36
2008-04-09 17:49,18504,10,70.90.32.57,US,121
2007-07-26 13:38,30002,8,76.226.201.191,US,2
2009-12-03 22:35,30002,8,196.25.255.214,ZA,14
2013-05-13 6:49,20341,4,66.232.201.125,US,2676
2007-07-28 23:57,30002,8,75.81.107.238,US,1
2007-07-29 10:39,30002,8,70.63.54.162,US,1
2007-07-30 3:53,30002,8,121.210.199.31,AU,4
2007-07-30 5:11,30002,8,41.207.67.10,KE,2
Here is some sample results (not correct yet, last column should not match second to last):
uniqueID LatestAccess DifferentCountries DistinctIPCount DistinctDate
--------------------------------------------------------------------------------
20677 2013-05-13 18:20:15 4 162 162
27749 2013-05-14 05:30:59 7 155 155
459 2013-05-13 11:12:47 2 143 143
24965 2013-05-14 13:44:56 6 123 123
25226 2013-05-06 16:11:56 3 104 104
20370 2013-05-14 05:54:04 4 100 100
The problem I think is in the "COUNT(DISTINCT Date,IPAddress) AS DistinctDate" piece.
You need a COUNT DISTINCT. Here's a guess because there's no table structure provided:
SELECT
VisitDate,
COUNT(DISTINCT IPAddress, UniqueID) AS UniqueVisits
FROM MyTable
GROUP BY VisitDate
ORDER BY UniqueVisits DESC
Or if your visit date is a datetime or timestamp, cut out the time part with the DATE function (note the changes on the second and fifth lines):
SELECT
DATE(VisitDate),
COUNT(DISTINCT IPAddress, UniqueID) AS UniqueVisits
FROM MyTable
GROUP BY DATE(VisitDate)
ORDER BY UniqueVisits DESC
Your date format has a time in it. So, I think all the dates are unique. Try this:
SELECT
LicenseID,
MAX(Date) AS LatestAccess,
COUNT(DISTINCT Location) AS DifferentCountries,
COUNT(DISTINCT IPAddress) AS DistinctIPCount,
COUNT(DISTINCT date(Date), IPAddress) AS DistinctDate
FROM
LicenseHistory
WHERE
(LicenseID<>30002)
GROUP BY
LicenseID
ORDER BY
DistinctDate DESC

How to count all the results and count specific in the same query - MySql

I need to count all FirstExtracted for a specific date, and I need to count all LastExtracted for the same date. So, for today, I would need all the FirstExtracted and LastExtractedthat equal 2012-10-24.
Here is what I have so far but it doesnt bring up LastExtracted. It outputs LastExtracted as same count as FirstExtracted:
(SELECT LastExtracted,FirstExtracted,
COUNT(FirstExtracted) AS FirstCount,
COUNT(LastExtracted) AS LastCount,
DATE_FORMAT(`LastExtracted`,'%Y-%m-%d') AS Lastdate,
DATE_FORMAT(`FirstExtracted`,'%Y-%m-%d') AS Firstdate
FROM results
WHERE DATE_FORMAT(`FirstExtracted`,'%Y-%m-%d') = DATE_FORMAT(`LastExtracted`,'%Y-%m-%d'))
UNION ALL
(SELECT LastExtracted,FirstExtracted,
COUNT(FirstExtracted) AS FirstCount,
COUNT(LastExtracted) AS LastCount,
DATE_FORMAT(`LastExtracted`,'%Y-%m-%d') AS Lastdate,
DATE_FORMAT(`FirstExtracted`,'%Y-%m-%d') AS Firstdate
FROM results
WHERE DATE_FORMAT(`FirstExtracted`,'%Y-%m-%d') = DATE_FORMAT(`LastExtracted`,'%Y-%m-%d') GROUP BY Firstdate)
ORDER BY Firstdate DESC
LIMIT 20
Maybe I should use inner join?
UPDATE:
so using your query i made some changes to make it do something else for me now. if you look at this page i put the query up
semesterold.com/code2.html
I want to count all titles and GROUP BY artist. it would be an array. then i want the sub queries to count by searchtype that match by the artists. so if the db has akon, rihanna, chris brown. i want it to count how many titles each artist has, say akon has 100. then i want to display the number of titles and then count how many of those 100 titles are google, bing, site specific for akon and etc.
I would use the union to get the two results (FirstExtracted and LastExtracted) separately and then merge them using a subquery.
SELECT
sub.date, sum(sub.FirstCount) AS FirstCount,
sum(sub.LastCount) AS LastCount
FROM (
SELECT
DATE_FORMAT(`FirstExtracted`,'%Y-%m-%d') AS date,
COUNT(FirstExtracted) AS FirstCount, 0 AS LastCount
FROM results
GROUP BY date
UNION ALL
SELECT
DATE_FORMAT(`LastExtracted`,'%Y-%m-%d'),
0, COUNT(LastExtracted)
FROM results
GROUP BY date
) AS sub
GROUP BY sub.date
Say you have 220 entries extracted today for the first time and 292 that were most recently extracted today. Among entries for other dates, this would give you:
+------------+------------+-----------+
| date | FirstCount | LastCount |
+------------+------------+-----------+
| 2012-10-24 | 220 | 292 |
+------------+------------+-----------+
Update The UNION alone will give you the following results. Notice the zero placeholders.
+------------+------------+-----------+
| date | FirstCount | LastCount |
+------------+------------+-----------+
| 2012-10-24 | 220 | 0 |
+------------+------------+-----------+
| 2012-10-24 | 0 | 292 |
+------------+------------+-----------+
I suggest adding DISTINCT in your query:
(SELECT LastExtracted,FirstExtracted,
COUNT(DISTINCT FirstExtracted) AS FirstCount,
COUNT(DISTINCT LastExtracted) AS LastCount,
DATE_FORMAT(`LastExtracted`,'%Y-%m-%d') AS Lastdate,
DATE_FORMAT(`FirstExtracted`,'%Y-%m-%d') AS Firstdate
FROM results
WHERE DATE_FORMAT(`FirstExtracted`,'%Y-%m-%d') = DATE_FORMAT(`LastExtracted`,'%Y-%m-%d'))
UNION ALL
(SELECT LastExtracted,FirstExtracted,
COUNT(DISTINCT FirstExtracted) AS FirstCount,
COUNT(DISTINCT LastExtracted) AS LastCount,
DATE_FORMAT(`LastExtracted`,'%Y-%m-%d') AS Lastdate,
DATE_FORMAT(`FirstExtracted`,'%Y-%m-%d') AS Firstdate
FROM results
WHERE DATE_FORMAT(`FirstExtracted`,'%Y-%m-%d') = DATE_FORMAT(`LastExtracted`,'%Y-%m-%d') GROUP BY Firstdate)
ORDER BY Firstdate DESC
LIMIT 20