This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Calculate a running total in MySQL
I'm monitoring the number of users created since 2011 in an application by month for a chart using MySQL and PHP. As a part of the query I would also like to include a running total.
SELECT
DATE_FORMAT(created,'%Y%m%d') as 'Date',
COUNT(item_id) as 'NewUsers'
FROM AP_user
WHERE YEAR(created) > 2011
AND user_groups = '63655'
AND user_active = 1
AND userID NOT IN $excludedUsers
GROUP BY MONTH(created) ASC
I'm able to return the "users by month" but how do I include a running total as a part of this query?
Unfortunately, MySQL doesn't provide analytic functions, like Oracle and SQL Server do.
One way to get a "running total" is to make use of a user variable, something like this:
SELECT t.Date
, t.NewUsers
, #rt := #rt + t.NewUsers AS `Running Total`
FROM (SELECT #rt := 0) i
JOIN (
SELECT DATE_FORMAT(created,'%Y%m%d') AS `Date`
, COUNT(item_id) as `NewUsers`
FROM AP_user
WHERE YEAR(created) > 2011
AND user_groups = '63655'
AND user_active = 1
AND userID NOT IN $excludedUsers
GROUP BY DATE_FORMAT(created,'%Y-%m')
ORDER BY DATE_FORMAT(created,'%Y-%m') ASC
) t
NOTE: The behavior of memory variables like used above is not guaranteed in this context. But if we are careful with the query, we can get predictable, repeatable results in SELECT statements. The behavior of memory variables may change in a future release, rendering this approach unworkable.
NOTE: I basically wrapped your query in parentheses, and gave it an alias as an inline view (what MySQL calls a "derived table"). I made a few changes to your query, your GROUP BY has potential to group January 2012 together with January from 2013, I changed that. I also added an ORDER BY clause.
Related
We updated our Mysql Driver to
Database Name MySQL
Database Version 5.6.10-log
Driver Name MySQL-AB JDBC Driver
We were using an older version but no one knows what that version was as that machine is DEAD.
The query below is running within our railo web site. The MySQL server does not like the # but I have no idea how to re-write as MySQL is not my thing and this was code written many many moons ago.
set #row = 0;
select nf.nid, #row:=#row+1 as ranking from financial nf
where nf.year = (select distinct year from financial where type = 'Total income' Order by year DESC LIMIT 1)
and nf.type in ('Total Spend','Total budget (Spend)')
and nf.nid in (select ft_no from n where ft_type in (1))
and nf.value > 0
order by nf.value desc
If anyone knows Mysql better than me (most people) please help me solve this issue. I am sure you will find more issues with the query so any help is welcomed.
Thanks in advance
Andrea
Sometimes there is a problem running multiple queries in a single query. If that is the issue, you can solve this by setting the initial value in the query:
select nf.nid, (#row := #row+1) as ranking
from financial nf cross join
(select #row := 0) params
where nf.year = (select max(year)
from financial
where type = 'Total income'
) and
nf.type in ('Total Spend', 'Total budget (Spend)') and
nf.nid in (select ft_no from n where ft_type in (1)) and
nf.value > 0
order by nf.value desc;
I simplified the subquery for year as well.
How can I simplify this query? Can this query be simplified? I tried some
joins but the results were not the same as this query below. Please give me
some insights.
SELECT trafficbywebsite.`adwordsCampaignID`,
trafficbywebsite.adwordsAdGroupID, trafficbywebsite.adPlacementDomain,
trafficbywebsite.counts traffic, convertedtrafficbywebsite.counts
convertedclicks
FROM
(
SELECT `adwordsAdGroupID`, `adPlacementDomain`, COUNT(*) counts
FROM
(
SELECT GA_entrances.*
FROM
GA_entrances,
GA_conversions
WHERE
GA_entrances.clientId=GA_conversions.clientId
AND (eventLabel='myurl' OR eventLabel='myotherurl')
AND YEAR(GA_entrances.timestamp)>=2016
AND MONTH(GA_entrances.timestamp)>=6
AND YEAR(GA_conversions.timestamp)>=2016
AND MONTH(GA_conversions.timestamp)>=6
GROUP BY GA_entrances.clientId
) clickers
GROUP BY `adwordsAdGroupID`, `adPlacementDomain`
) convertedtrafficbywebsite
,(
SELECT `adwordsCampaignID`, `adwordsAdGroupID`, adPlacementDomain,
COUNT(*) counts
FROM
GA_entrances
WHERE
YEAR(timestamp)>=2016
AND MONTH(timestamp)>=6
GROUP BY `adwordsAdGroupID`, `adPlacementDomain`
) trafficbywebsite
WHERE
convertedtrafficbywebsite.counts>=(trafficbywebsite.counts/10)
ORDER BY traffic DESC
Without sample data it is difficult to be certain but it appears unlikely you can remove one of the subqueries. What you can do however is improve the way you flter for the dates. The thing to avoid is using functions on data to suit your filter criteria. For example you want data from 2016-06-01 onward, that is a single date, yet you are amending every row of data to match to a year and a month.
AND YEAR(GA_entrances.timestamp) >= 2016
AND MONTH(GA_entrances.timestamp) >= 6
AND YEAR(GA_conversions.timestamp) >= 2016
AND MONTH(GA_conversions.timestamp) >= 6
;
There is no need for all those functions, just compare to a single date:
AND GA_entrances.timestamp) >= '2016-06-01'
AND GA_conversions.timestamp >= '2016-06-01'
;
The other thing to avoid is using commas as a way to join tables. ANSI standard syntax for this 25+ years old. This is the antique way of joining:
FROM GA_entrances, GA_conversions
WHERE GA_entrances.clientId = GA_conversions.clientId
This is considered best practice:
GA_entrances.*
FROM GA_entrances
INNER JOIN GA_conversions ON GA_entrances.clientId = GA_conversions.clientId
The question I am working on is as follows:
What is the difference in the amount received for each month of 2004 compared to 2003?
This is what I have so far,
SELECT #2003 = (SELECT sum(amount) FROM Payments, Orders
WHERE YEAR(orderDate) = 2003
AND Payments.customerNumber = Orders.customerNumber
GROUP BY MONTH(orderDate));
SELECT #2004 = (SELECT sum(amount) FROM Payments, Orders
WHERE YEAR(orderDate) = 2004
AND Payments.customerNumber = Orders.customerNumber
GROUP BY MONTH(orderDate));
SELECT MONTH(orderDate), (#2004 - #2003) AS Diff
FROM Payments, Orders
WHERE Orders.customerNumber = Payments.customerNumber
Group By MONTH(orderDate);
In the output I am getting the months but for Diff I am getting NULL please help. Thanks
I cannot test this because I don't have your tables, but try something like this:
SELECT a.orderMonth, (a.orderTotal - b.orderTotal ) AS Diff
FROM
(SELECT MONTH(orderDate) as orderMonth,sum(amount) as orderTotal
FROM Payments, Orders
WHERE YEAR(orderDate) = 2004
AND Payments.customerNumber = Orders.customerNumber
GROUP BY MONTH(orderDate)) as a,
(SELECT MONTH(orderDate) as orderMonth,sum(amount) as orderTotal FROM Payments, Orders
WHERE YEAR(orderDate) = 2003
AND Payments.customerNumber = Orders.customerNumber
GROUP BY MONTH(orderDate)) as b
WHERE a.orderMonth=b.orderMonth
Q: How do I subtract two declared variables in MySQL.
A: You'd first have to DECLARE them. In the context of a MySQL stored program. But those variable names wouldn't begin with an at sign character. Variable names that start with an at sign # character are user-defined variables. And there is no DECLARE statement for them, we can't declare them to be a particular type.
To subtract them within a SQL statement
SELECT #foo - #bar AS diff
Note that MySQL user-defined variables are scalar values.
Assignment of a value to a user-defined variable in a SELECT statement is done with the Pascal style assignment operator :=. In an expression in a SELECT statement, the equals sign is an equality comparison operator.
As a simple example of how to assign a value in a SQL SELECT statement
SELECT #foo := '123.45' ;
In the OP queries, there's no assignment being done. The equals sign is a comparison, of the scalar value to the return from a subquery. Are those first statements actually running without throwing an error?
User-defined variables are probably not necessary to solve this problem.
You want to return how many rows? Sounds like you want one for each month. We'll assume that by "year" we're referring to a calendar year, as in January through December. (We might want to check that assumption. Just so we don't find out way too late, that what was meant was the "fiscal year", running from July through June, or something.)
How can we get a list of months? Looks like you've got a start. We can use a GROUP BY or a DISTINCT.
The question was... "What is the difference in the amount received ... "
So, we want amount received. Would that be the amount of payments we received? Or the amount of orders that we received? (Are we taking orders and receiving payments? Or are we placing orders and making payments?)
When I think of "amount received", I'm thinking in terms of income.
Given the only two tables that we see, I'm thinking we're filling orders and receiving payments. (I probably want to check that, so when I'm done, I'm not told... "oh, we meant the number of orders we received" and/or "the payments table is the payments we made, the 'amount we received' is in some other table"
We're going to assume that there's a column that identifies the "date" that a payment was received, and that the datatype of that column is DATE (or DATETIME or TIMESTAMP), some type that we can reliably determine what "month" a payment was received in.
To get a list of months that we received payments in, in 2003...
SELECT MONTH(p.payment_received_date)
FROM payment_received p
WHERE p.payment_received_date >= '2003-01-01'
AND p.payment_received_date < '2004-01-01'
GROUP BY MONTH(p.payment_received_date)
ORDER BY MONTH(p.payment_received_date)
That should get us twelve rows. Unless we didn't receive any payments in a given month. Then we might only get 11 rows. Or 10. Or, if we didn't receive any payments in all of 2003, we won't get any rows back.
For performance, we want to have our predicates (conditions in the WHERE clause0 reference bare columns. With an appropriate index available, MySQL will make effective use of an index range scan operation. If we wrap the columns in a function, e.g.
WHERE YEAR(p.payment_received_date) = 2003
With that, we will be forcing MySQL to evaluate that function on every flipping row in the table, and then compare the return from the function to the literal. We prefer not do do that, and reference bare columns in predicates (conditions in the WHERE clause).
We could repeat the same query to get the payments received in 2004. All we need to do is change the date literals.
Or, we could get all the rows in 2003 and 2004 all together, and collapse that into a list of distinct months.
We can use conditional aggregation. Since we're using calendar years, I'll use the YEAR() shortcut (rather than a range check). Here, we're not as concerned with using a bare column inside the expression.
SELECT MONTH(p.payment_received_date) AS `mm`
, MAX(MONTHNAME(p.payment_received_date)) AS `month`
, SUM(IF(YEAR(p.payment_received_date)=2004,p.payment_amount,0)) AS `2004_month_total`
, SUM(IF(YEAR(p.payment_received_date)=2003,p.payment_amount,0)) AS `2003_month_total`
, SUM(IF(YEAR(p.payment_received_date)=2004,p.payment_amount,0))
- SUM(IF(YEAR(p.payment_received_date)=2003,p.payment_amount,0)) AS `2004_2003_diff`
FROM payment_received p
WHERE p.payment_received_date >= '2003-01-01'
AND p.payment_received_date < '2005-01-01'
GROUP
BY MONTH(p.payment_received_date)
ORDER
BY MONTH(p.payment_received_date)
If this is a homework problem, I strongly recommend you work on this problem yourself. There are other query patterns that will return an equivalent result.
I think this is the problem:
In #2003 and #2004, you select only the sum. And even if you group by the month you still select one column i.e. each row does not say what month it is select for. So when you try to subtract SQL asks which row in #2003 should be subtracted from #2004.
So I think the solution is to select the month with the sum and do the subtract later based on the month.
I am currently working on a MySQL db with MySQL Workbench.
My objective is to retrieve the signups from a database to establish my company's KPIs on an Excel spreadsheet.
I wrote some sql queries that worked but I want to set up a very complete one in order to avoid using xxx different queries.
To get the signups for each month (based on 'created_at'), this makes the job:
SELECT year(u.created_at) year, monthname(u.created_at) month, COUNT(DISTINCT u.id) as 'New shoppers signups'
FROM users u
GROUP BY year, month
ORDER BY u.created_at
But I also wanted to have the total of previous signups for each month
Jan : 12
Feb : 14 (12 + 2 new signups)
March : 22 (14 + 8 new signups)
...
Where I get the sum of all the previous signups
I was thinking about something like:
DECLARE #month = '2012-01-01' //startdate
WHILE #month < curdate()
BEGIN
SELECT count(distinct u.id)
WHERE u.created_at < #month
dateadd(month, 1, #month) // incrementing to next month
END
But neither the while loop, the declare, set, or date function do work on MySQL Workbench.
I heard I have to declare procedures but I didn't have any more success...
I know I could use excel to get the result, but I want to improve my use of SQL and make this a very clear work.
You are actually close to the answer. Take your results and make that an inner query. Then that is basis of an outer query using MySQL variables to accumulate for each row.
select
pq.yearAdded,
pq.monthAdded,
pq.NewShoppers as 'New shoppers signups',
#runBal := #runBal + pq.NewShoppers as TotalNewShoppers
from
( SELECT
year(u.created_at) yearAdded,
monthname(u.created_at) monthAdded,
COUNT(DISTINCT u.id) as NewShoppers
from
users u
GROUP BY
year(u.created_at),
monthname(u.created_at)
ORDER BY
year(u.created_at),
monthname(u.created_at) ) pq,
( select #runBal := 0 ) sqlvars
I would just suggest having column names stay away from possible reserved words, such as Year, Month and other standard SQL commands and function names... otherwise you typically need to add tick-marks around the column names
I have a Data as follows:
Order_id Created_on Comment
1 20-07-2015 18:35 Order Placed by User
1 20-07-2015 18:45 Order Reviewed by the Agent
1 20-07-2015 18:50 Order Dispatched
2 20-07-2015 18:36 Order Placed by User
And I am trying to find the difference between the
first and second Date
Second and third Date for each Order. How Do i Obtain this through a SQL query?
SQL is about horizontal relations - vertical relations do not exist. To a relational database they're just 2 rows, stored somewhere on a disk, and until you apply ordering to a result set the 'first and second' are just 2 randomly picked rows.
In specific cases it's possible to calculate the time difference within SQL, but rarely a good idea for performance reason, as it requires costly self-joins or subqueries. Just selecting the right data in the right order and then calculating the differences during postprocessing in C#/PHP/whatever is far more practical and faster.
I think you can use a query like this:
SELECT t1.Order_id, t1.Created_on, TIMEDIFF(mi, t1.Created_on, COALESCE(MIN(t2.Created_on), t1.Created_on)) AS toNextTime
FROM yourTable t1
LEFT JOIN yourTable t2 ON t1.Order_id = t2.Order_id AND t1.Created_on < t2.Created_on
GROUP BY t1.Order_id, t1.Created_on
Posting this even though another answer has been accepted already - and I don't disagree with the accepted answer - but there is in fact a fairly neat way to do this with mySQL variables.
This query will give you the time between stages in minutes - it can't be expressed as a datetime as it's an interval between two dates:
SELECT
Order_id,
Created_on,
Comment,
if (#last_id = Order_id, TIMESTAMPDIFF(MINUTE, #last_date, Created_on), 0) as StageMins,
#last_id := Order_id,
#last_date := Created_on
FROM tblData
ORDER BY Order_id, Created_on;
SQL Fiddle here: http://sqlfiddle.com/#!9/6ffdd/10
Info on mySQL TIMESTAMPDIFF function here: https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_timestampdiff