Selecting totals as three fields from same table as one query? - mysql

I have a table with various orders in it:
ID | Date | etc...
1 | 2013-01-01 | etc
2 | 2013-02-01 | etc
3 | 2013-03-01 | etc
4 | 2013-04-01 | etc
5 | 2013-05-01 | etc
6 | 2013-06-01 | etc
7 | 2013-06-01 | etc
8 | 2013-03-01 | etc
9 | 2013-04-01 | etc
10 | 2013-05-01 | etc
I want a query that ends wit the result:
overallTotal | totalThisMonth | totalLastMonth
10 | 2 | 1
But I want to do this in one query! I am trying to find a way to use subqueries to do this. SO far I have:
SELECT * from (
SELECT count(*) as overallTotal from ORDERS
)
How can I combine this with other subqueries so I can get the totals in one query?
UPDATE
Original question was for MySQL, but I need it for Firebird now.

With conditional sums you can do it (MySQL syntax):
select
count(*) as overallTotal,
sum(if(Month(Date)+12*Year(Date)=Month(GetDate())+12*Year(GetDate()), 1, 0))
as totalThisMonth
sum(if(Month(Date)+12*Year(Date)=Month(GetDate())+12*Year(GetDate())-1, 1, 0))
as totalThisMonth;
from mytable
Use the month+12*year formula to avoid the problem with year change.
UPDATE
With Firebird the same applies, you only have to replace Month(x) with EXTRACT (MONTH FROM x), Year(x) with EXTRACT (YEAR FROM x) and Getdate() with CURRENT_TIME. This will look ugly, so I won't put it here, but you could easily do it yourself.

Posubly use SUM of the result of IF statements, with the IF checking that it is a relevant date.
SELECT COUNT(*),
SUM(IF(YEAR(Date) = YEAR(CURDATE()) AND MONTH(Date) = MONTH(CURDATE()), 1, 0))
SUM(IF((YEAR(Date) = YEAR(CURDATE()) AND MONTH(Date) = MONTH(CURDATE()) - 1) OR (YEAR(Date) = YEAR(CURDATE()) - 1 AND MONTH(Date) = 12 AND MONTH(CURDATE()) = 1), 1, 0))
from ORDERS
Complexity is caused by coping with a change of year when looking for the previous month

I see you are already using sub queries, so why not do something like the following,
SELECT
(SELECT count(*) from ORDERS) as overallTotal,
(SELECT count(*) from ORDERS where Date between ... and ...) as totalThisMonth,
(SELECT count(*) from ORDERS where Date between ... and ...) as overallTotal

SELECT
(SELECT COUNT(*) FROM C AS total) AS T,
(SELECT COUNT(*) AS thisMonth FROM C WHERE MONTH(d) = 6) AS A,
(SELECT COUNT(*) AS lastMonth FROM C WHERE MONTH(d) = 5) AS B;
Please notice "month" are "hard coded" -- but it shouldn't be too difficult to extract the "current month" at application level. If you really need to, you could do it at SQL level with something like that:
SELECT
(SELECT COUNT(*) FROM C AS total) AS T,
(SELECT COUNT(*) AS thisMonth FROM C WHERE MONTH(d) = MONTH(NOW()) ) AS A,
(SELECT COUNT(*) AS lastMonth FROM C WHERE MONTH(d) = (MONTH(NOW())+11)%12) ) AS B;

Related

mysql using sum for a column to get total amount for each group

This is data in table orders.
Does anyone can help me to do the statistics work?
-------------------------------
id | order_amount | createtime
-------------------------------
1 | 10 | 1513605522
2 | 20 | 1513605523
3 | 30 | 1513605524
4 | 40 | 1513605525
-------------------------------
This is the output what I need
-------------------------------
total_income | createtime
-------------------------------
10 | 1513605522
30 | 1513605523
60 | 1513605524
100 | 1513605525
-------------------------------
this is my sql statement, it doesn't give me what i want.
select sum(order_amount) as total_income from order group by create time;
BTW. Is it possible to produce the output....
Any help means a lot to me. Many thanks.
You can use:
set #amt := 0;
select #amt := #amt + order_amount as total_income, createtime
from order
order by createtime asc;
In MySQL, you can do this using a correlated subquery:
select o.*,
(select sum(o2.order_amount)
from orders o2
where o2.createtime <= o.createtime
) as running_amount
from orders o;
Another option -- using non-standard SQL -- is to use variables:
select o.*, (#s := #s + o.order_amount) as running_amount
from (select o.*
from orders o
order by createtime
) o cross join
(select #s := 0) params;
Note that the subquery is only needed in the more recent versions of MySQL.
Actually, MySQL 8.0 finally supports window functions, so it can be done using standard SQL in that version:
select o.*, sum(o.order_amount) over (order by o.createtime) as running_amount
from orders o;

If a value occurs for the first time mark 1 else 0, and count those in group

My data looks like this:
CreateTime | mobile
-----------+--------
2017/01/01 | 111
2017/01/01 | 222
2017/01/05 | 111
2017/01/08 | 333
2017/03/09 | 111
What I am trying to do is to add a variable if it is the first time that this mobile number occured:
CreateTime | mobile | FirstTime
-----------+--------+----------
2017/01/01 | 111 | 1
2017/01/01 | 222 | 1
2017/01/05 | 111 | 0
2017/01/08 | 333 | 1
2017/03/09 | 111 | 0
2017/03/15 | 222 | 0
2017/03/18 | 444 | 1
Basically we need to add a "true/false" column if it is the first time (based on createtime (and some other fields) which may or may not be sorted) that this specific mobile number occurred.
Ideally, this adjusted table will then be able to give me the following results when queried:
Select Month(createtime) as month,
count(mobile) as received,
sum(Firsttime) as Firsttimers
from ABC
Group by month(createtime)
Result:
Month | Received | FirstTimers
--------+----------+------------
2017/01 | 4 | 3
2017/03 | 3 | 1
If I can get to the RESULTS without needing to create the additional step, then that will be even better.
I do however need the query to run fast hence my thinking of creating the middle table perhaps but I stand corrected.
This is my current code and it works but it is not as fast as I'd like nor is it elegant.
SELECT Month(InF1.createtime) as 'Month',
Count(InF1.GUID) AS Received,
Sum(coalesce(Unique_lead,1)) As FirstTimers
FROM MYDATA_TABLE as InF1
Left Join
( SELECT createtime, mobile, GUID, 0 as Unique_lead
FROM MYDATA_TABLE as InF2
WHERE createtime = (SELECT min(createtime)
FROM MYDATA_TABLE as InF3
WHERE InF2.mobile=InF3.mobile
)
) as InF_unique
On Inf1.GUID = InF_unique.GUID
group by month(createtime)
(appologies if the question is incorrectly posted, it is my first post)
You could use sub query to get the first date per mobile, outer join it on the actual mobile date, and count matches. Make sure to count distinct mobile numbers to not double count the same number when it occurs with the same date twice:
select substr(createtime, 1, 7) month,
count(*) received,
count(distinct grp.mobile) firsttimers
from abc
left join (
select mobile,
min(createtime) firsttime
from abc
group by mobile
) grp
on abc.mobile = grp.mobile
and abc.createtime = grp.firsttime
group by month
Here is an alternative using variables, which can give you a row number:
select substr(createtime, 1, 7) month,
count(*) received,
sum(rn = 1) firsttimers
from (
select createtime,
#rn := if(#mob = mobile, #rn + 1, 1) rn,
#mob := mobile mobile
from (select * from abc order by mobile, createtime) ordered,
(select #rn := 1, #mob := null) init
order by mobile, createtime
) numbered
group by month;
NB: If you have MySql 8+, then use window functions.

SQL: Get the most frequent value for each group

Lets say that I have a table ( MS-ACCESS / MYSQL ) with two columns ( Time 'hh:mm:ss' , Value ) and i want to get most frequent value for each group of row.
for example i have
Time | Value
4:35:49 | 122
4:35:49 | 122
4:35:50 | 121
4:35:50 | 121
4:35:50 | 111
4:35:51 | 122
4:35:51 | 111
4:35:51 | 111
4:35:51 | 132
4:35:51 | 132
And i want to get most frequent value of each Time
Time | Value
4:35:49 | 122
4:35:50 | 121
4:35:51 | 132
Thanks in advance
Remark
I need to get the same result of this Excel solution : Get the most frequent value for each group
** MY SQL Solution **
I found a solution(Source) that works fine with mysql but i can't get it to work in ms-access:
select cnt1.`Time`,MAX(cnt1.`Value`)
from (select COUNT(*) as total, `Time`,`Value`
from `my_table`
group by `Time`,`Value`) cnt1,
(select MAX(total) as maxtotal from (select COUNT(*) as total,
`Time`,`Value` from `my_table` group by `Time`,`Value`) cnt3 ) cnt2
where cnt1.total = cnt2.maxtotal GROUP BY cnt1.`Time`
Consider an INNER JOIN to match the two derived table subqueries rather than a list of subquery select statements matched with WHERE clause. This has been tested in MS Access:
SELECT MaxCountSub.`Time`, CountSub.`Value`
FROM
(SELECT myTable.`Time`, myTable.`Value`, Count(myTable.`Value`) AS CountOfValue
FROM myTable
GROUP BY myTable.`Time`, myTable.`Value`) As CountSub
INNER JOIN
(SELECT dT.`Time`, Max(CountOfValue) As MaxCountOfValue
FROM
(SELECT myTable.`Time`, myTable.`Value`, Count(myTable.`Value`) AS CountOfValue
FROM myTable
GROUP BY myTable.`Time`, myTable.`Value`) As dT
GROUP BY dT.`Time`) As MaxCountSub
ON CountSub.`Time` = MaxCountSub.`Time`
AND CountSub.CountOfValue = MaxCountSub.MaxCountOfValue
you can do this by query like this:
select time, value
from (select value, time from your_table
group by value , time
order by count(time) desc
) temp where temp.value = value
group by value

How to find multiple records(attendance) of same date with alias of same table in MySql

I have a table named rjs_attendance with following four column
_________________________________________________
|attenedance_id | admin_id | note | created_date|
-------------------------------------------------
A user can make attendance several times in a day. Odd entry is assumed as sign in and even entry is assumed as sign out.
The output I need looks something like this.
_______________________________________________________________________________
|admin_id | time_in | time_in_note | time_out | time_out_note | date |
-------------------------------------------------------------------------------
|1 |10:00 | none | 11:00 | none | 2015-12-24|
-------------------------------------------------------------------------------
|1 |11:30 |none |12:15 |none | 2015-12-24|
-------------------------------------------------------------------------------
I'm not able to fetch all record of the same date, but I'm able to fecth one record of the same date.
The query I have run is as follows:
SELECT
`atd_in`.`admin_id` AS `admin_id`,
CAST(MIN(`atd_in`.`created_date`) AS TIME) AS `time_in`,
`atd_in`.`note` AS `time_in_note`,
CAST(MAX(`atd_out`.`created_date`) AS TIME) AS `time_out`,
`atd_out`.`note` AS `time_out_note`,
CAST(`atd_in`.`created_date` AS DATE) AS `date_on`
FROM
`zf2`.`rjs_attendance` `atd_in`
LEFT JOIN `zf2`.`rjs_attendance` `atd_out`
ON
`atd_in`.`admin_id` = `atd_out`.`admin_id`
AND CAST(`atd_in`.`created_date` AS DATE) = CAST(`atd_out`.`created_date` AS DATE)
AND `atd_in`.`attendance_id` <> `atd_out`.`attendance_id`
GROUP BY
CAST(`atd_in`.`created_date` AS DATE), `atd_in`.`admin_id`
Any help is greatly appreciated.
Try this:
SELECT A.admin_id,
MAX(IF(A.ID % 2 = 1, CAST(A.created_date AS TIME), NULL)) AS time_in,
MAX(IF(A.ID % 2 = 1, note, NULL)) AS time_in_note,
MAX(IF(A.ID % 2 = 0, CAST(A.created_date AS TIME), NULL)) AS time_out,
MAX(IF(A.ID % 2 = 0, note, NULL)) AS time_out_note,
CAST(A.created_date AS DATE) AS date_on
FROM (SELECT IF(#adminId=#adminId:=A.admin_id, #id:=#id+1, #id:=1) AS ID,
A.admin_id, A.note, A.created_date
FROM zf2.rjs_attendance A, (SELECT #id:=1, #adminId:=0) AS B
ORDER BY A.admin_id, A.attendance_id
) AS A
GROUP BY A.admin_id, CAST(A.created_date AS DATE), CEILING(A.ID / 2);
sqlfiddle
before seeing any code - if you assume odd and even as entry and exit => join on this condition -
on A.id +1 = B.id
or
`atd_in`.`attendance_id` +1 = `atd_out`.`attendance_id`
you can even leave it left joined for non exit entries
(ids should be numerical of course)
if your records are not sorted this way, just reissue the ids column after sorting with order by and make new id column

Calculate percent increase/decrease from previous row value

I have a table that looks something like this:
|date_start | date_end |amount |
+------------+-------------+-------+
|2015-02-23 | 2015-03-01 |50 |
|2015-03-02 | 2015-03-08 |50 |
|2015-03-09 | 2015-03-15 |100 |
|2015-03-16 | 2015-03-22 |800 |
|2015-03-23 | 2015-03-29 |50 |
and I'd like to work out the percent increase/decrease for column amount, from the previous date. For example the result would be something like this,
|date_start | date_end |amount | perc_change |
+------------+-------------+-------+-------------+
|2015-02-23 | 2015-03-01 |50 |
|2015-03-02 | 2015-03-08 |50 | 0
|2015-03-09 | 2015-03-15 |100 | 50
|2015-03-16 | 2015-03-22 |800 | 700
|2015-03-23 | 2015-03-29 |50 | -750
I've searched and racked my brain for a couple of days now. Usually, I simply do this using server side code but now I need to contain it all within the query.
Try this:
SELECT t.*,
amount - (SELECT amount FROM transactions prev WHERE prev.date_end < t.date_start ORDER BY date_start DESC LIMIT 1) AS changes
FROM transactions t
If we assume that the previous row always ends exactly one day before the current begins (as in your sample data), then you can use a join. The percentage increase would be:
select t.*,
100 * (t.amount - tprev.amount) / tprev.amount
from atable t left join
atable tprev
on tprev.date_end = t.date_start - interval 1 day;
However, your results seem to just have the difference, which is easier to calculate:
select t.*,
(t.amount - tprev.amount) as diff
from atable t left join
atable tprev
on tprev.date_end = t.date_start - interval 1 day;
you can use window function it would be easier
select date_start,
date_end,
amount,
LAG(amount, 1, 0) OVER (ORDER BY date_start) as previous_amount,
amount - LAG(amount, 1, 0) OVER (ORDER BY date_start) as amount_diff,
((amount - LAG(amount, 1, 0) OVER (ORDER BY date_start)) / (amount - LAG(amount, 1, 0) OVER (ORDER BY date_start))) * 100 amount_diff_percentage
from your_table_name
I started by joining each row in the table with the one that comes after it, like this:
SELECT m.date_start AS mStart, mt.date_start AS mtStart
FROM myTable m
JOIN myTable mT ON m.date_start < mt.date_start AND mt.date_start = (SELECT MIN(date_start) FROM myTable WHERE date_start > m.date_start);
This will join the tables so that each row can be seen with the following date if there is one. If there's not, the date is not returned.
Once you have those values, you can adjust the SELECT query to show you the percent change from the date before, like this:
SELECT
mT.date_start AS secondDate,
mT.amount - m.amount AS percentChange
FROM myTable m
JOIN myTable mT ON m.date_start < mT.date_start AND mt.date_start = (SELECT MIN(date_start) FROM myTable WHERE date_start > m.date_start);
I would like to make a note here that while you say 'percent difference' in your question, your expected results have nothing to do with percentage, but just difference in value. If you need to calculate this a different way, you can just adjust the select query above to meet your needs.
The last thing you will have to do is join this back to your original table to see all of the values together. This has to be done using a left join, in order for the first date of the table to be seen. Here is the final query:
SELECT m.date_start, m.date_end, m.amount, tmp.percentChange
FROM myTable m
LEFT JOIN(
SELECT
mT.date_start AS secondDate,
mT.amount - m.amount AS percentChange
FROM myTable m
JOIN myTable mT ON m.date_start < mT.date_start AND mt.date_start = (SELECT MIN(date_start) FROM myTable WHERE date_start > m.date_start)
) tmp ON tmp.secondDate = m.date_start;
And here is an SQL Fiddle example.
It looks something like this, i dont have environment to test this select but i think it should work
SELECT t1.date_start, t1.date_end, t1.amount, (t1.amount - t2.amount) as perc_change
FROM table1 t1
JOIN table1 t2 ON t1.id = t2.id + 1 // here your join to
//get next row and calculate this perc_change field
You case use the mysql var system :
SELECT date_start, date_end, IF(#last IS NOT NULL,ammount - #last , '' ) as perc_change, #last := amount as amount
From table
ORDER BY date_start;
the var #last is set at each passage, so column order between perc_change and amount is important