I have a db with logs of TSTAMP USERNAME SUBCR_TYPE and BALANCE.
I want to know how many users had at each past end of month a positive BALANCE by SUBSCR_TYPE
The resulting table should look like this
silver|gold|platinum
2011-09 34|56 |109
2011-10 23|43 |67
2011-11 33|56 |45
.
.
.
I have tried this with obviously wrong results
SET #ts = unix_timestamp(LAST_DAY('2011-09-01'));
SELECT COUNT(DISTINCT USERNAME) AS 'silver'
FROM accLog_table
WHERE BALANCE>=1
AND SUBSCR_TYPE = 'silver'
AND TSTAMP<#ts
how can I do this correctly?
I happy to post a solution that worked for me
SET #ts = unix_timestamp(LAST_DAY('2011-09-01'));
SET #subcr = 'silver';
INSERT
INTO monthlyLiveAccess (
timePeriod,
silver
)
SELECT DATE_FORMAT(FROM_UNIXTIME(#ts), "%Y-%m") AS timePeriod,
COUNT(*) AS silver FROM (
SELECT t.* from(
SELECT DATE_FORMAT(FROM_UNIXTIME(DATE_TIME), "%Y-%m %H:%i:%s") AS "timePeriod",
USERNAME, BALANCE
from accLog_table
WHERE N_BALANCE>1
AND DATE_TIME<#ts
AND SUBSCR='silver'
ORDER BY timePeriod desc) as t
GROUP BY USERNAME) AS t1
ON DUPLICATE KEY UPDATE silver = VALUES(silver);
Related
I need to get users visits duration for each day in MySQL.
I have table like:
user_id,date,time_start, time_end
1, 2018-09-01, 09:00:00, 12:30:00
2, 2018-09-01, 13:00:00, 15:10:00
1, 2018-09-03, 09:30:00, 12:30:00
2, 2018-09-03, 13:00:00, 15:10:00
and need to get:
user_id,2018-09-01_duration,2018-09-03_duration
1,03:30:00,03:00:00
2,02:10:00,02:10:00
So columns need to be dynamic as some dates can be missed (2018-09-02).
Is it possible to do with one query without explicit joins per each day (as some days can be null)?
Update #1
Yes, I can generate columns in application side, But I still have terrible query like
SELECT user_id, d1.dt AS "2018-08-01_duration", d2.dt AS "2018-08-03_duration"...
FROM (SELECT
user_id,
time_format(TIMEDIFF(TIMEDIFF(time_out,time_in),time_norm),"%H:%i") AS dt
FROM visits
WHERE date = "2018-09-01") d1
LEFT JOIN(
SELECT
user_id,
time_format(TIMEDIFF(TIMEDIFF(time_out,time_in),time_norm),"%H:%i") AS dt
FROM visits
WHERE date = "2018-09-03") d3
ON users.id = d3.user_id...
Update #2
Yes, data like
select user_id, date, SEC_TO_TIME(SUM(TIME_TO_SEC(time_out) - TIME_TO_SEC(time_in))) as total
from visits
group by user_id, date;
is correct, but in this case data for users goes consistently. And I hope there's the way when I have rows with users and columns with dates (like in example above)
Try something like this:
select user_id, date, sum(time_end - time_start)
from table
group by user_id, date;
You will need to do some tweaking, as you didn't mention the RDBMS provider, but it should give you a clear idea on how to do it.
There's no dynamic way to use pivotting in MySQL but you might use the following for your case :
create table t(user_id int, time_start timestamp, time_end timestamp);
insert into t values(1,'2018-09-01 09:00:00', '2018-09-01 12:30:00');
insert into t values(2,'2018-09-01 13:00:00', '2018-09-01 15:10:00');
insert into t values(1,'2018-09-03 09:30:00', '2018-09-03 12:30:00');
insert into t values(2,'2018-09-03 13:00:00', '2018-09-03 15:10:00');
select min(q.user_id) as user_id,
min(CASE WHEN (q.date='2018-09-01') THEN q.time_diff END) as '2018-09-01_duration',
min(CASE WHEN (q.date='2018-09-03') THEN q.time_diff END) as '2018-09-03_duration'
from
(
select user_id, date(time_start) date,
concat(concat(lpad(hour(timediff(time_start, time_end)),2,'0'),':'),
concat(lpad(minute(timediff(time_start, time_end)),2,'0'),':'),
lpad(second(timediff(time_start, time_end)),2,'0')) as time_diff
from t
) q
group by user_id;
If you know the dates that you want in the result set, you don't need a dynamic query. You can just use conditional aggregation:
select user_id,
SEC_TO_TIME(SUM(CASE WHEN date = '2018-09-01' THEN TIME_TO_SEC(time_out) - TIME_TO_SEC(time_in))) as total_20180901,
SEC_TO_TIME(SUM(CASE WHEN date = '2018-09-02' THEN TIME_TO_SEC(time_out) - TIME_TO_SEC(time_in))) as total_20180902,
SEC_TO_TIME(SUM(CASE WHEN date = '2018-09-03' THEN TIME_TO_SEC(time_out) - TIME_TO_SEC(time_in))) as total_20180903
from visits
group by user_id;
You only need dynamic SQL if you don't know the dates you want in the result set. In that case, I would suggest following the same structure with the dates that you do want.
By the query you can solve your problem. the query is dynamic and you can improve it.
i use TSQL for the query, you can use the idea in MySQL.
declare
#columns as nvarchar(max),
#query as nvarchar(max)
select
#columns =
stuff
((
select
distinct
',' + quotename([date])
from
table_test
for xml path(''), type
).value('.', 'nvarchar(max)'), 1, 1, '')
--select #columns
set #query =
'with
cte_result
as
(
select
[user_id] ,
[date] ,
time_start ,
time_end ,
datediff(minute, time_start, time_end) as duration
from
table_test
)
select
[user_id], ' + #columns + '
from
(
select
[user_id] ,
[date] ,
duration
from
cte_result
)
sourceTable
pivot
(
sum(duration)
for [date] in (' + #columns + ')
)
pivotTable'
execute(#query)
I have a simple group by query:
SELECT timestamp, COUNT(users)
FROM my_table
GROUP BY users
How do I add a sum_each_day column that will sum the users count of each row and will aggregate it forward to the next row and so on
The output should be like this:
timestamp | users | sum_each_day
2015-11-27 1 1
2015-11-28 5 6
2015-11-29 3 9
2015-11-30 7 16
Thanks in advance
You could use a sub-query, like this:
SELECT timestamp,
num_users,
(SELECT COUNT(users)
FROM my_table
WHERE timestamp <= main.timestamp) sum_users
FROM (
SELECT timestamp,
COUNT(users) num_users
FROM my_table
GROUP BY timestamp
) main
If you really need this in mysql it'll cost some performance but i believe a sub query with a count will solve it:
SELECT t1.timestamp, count (), select count () from my_table t2 where t2.timestamp <= t1.timestamp From my_table t1 Group by users
If you display this data through a scripting language like PHP it would be easier to keep a counter and display the aggregate per row.
I would do this using variable:
SET #total := 0;
SELECT timestamp, DayCount, (#total := #total + DayCount) AS Total
FROM
(SELECT timestamp, COUNT(users) AS DayCount
FROM my_table
GROUP BY timestamp) AS t1
Fiddler: I am not using your table structure here, but you can get idea
If I understand correclty, this will work:
set #c=0;
SELECT `timestamp`,sum(`users`),(select #c:=#c+sum(`users`))
FROM `my_table`
group by `timestamp`;
id staff_ID STAFFNAME CARDTIME
39618 1203024 BARAYUGA M. 2014-02-03 08:44:02
39618 1203024 BARAYUGA M. 2014-02-03 12:20:02
39618 1203024 BARAYUGA M. 2014-02-03 12:50:49
39618 1203024 BARAYUGA M. 2014-02-03 17:33:44
39622 1203056 LEONES M. 2014-02-03 12:00:21
39622 1203056 LEONES M. 2014-02-03 12:23:19
39622 1203056 LEONES M. 2014-02-03 13:22:33
39622 1203056 LEONES M. 2014-02-03 15:30:11
Above is my table tbl_staff in my database, is there a way that I can get the total break hours of each employees? using Mysql query only.
Here is my sample query that I am using right now.
SELECT
DATE,
STAFFNAME,
LOGIN, LOGOUT,
SUCCESSFUL,
TIME,
NUMBEROFTIME,
FIND_IN_SET(LOGIN,TIME),
FIND_IN_SET(LOGOUT,TIME)
FROM
(
SELECT
DATE( CARDTIME ) AS DATE,
STAFFNAME,
MIN( CARDTIME ) AS LOGIN,
MAX( cardtime ) AS LOGOUT,
CASE
WHEN COUNT( CARDTIME ) %2 =0 THEN 1
ELSE 0
END AS 'SUCCESSFUL',
GROUP_CONCAT(DISTINCT(CARDTIME) ORDER BY (CARDTIME) ) AS TIME,
COUNT(CARDTIME) as NUMBEROFTIME
FROM tbl_staff
GROUP BY STAFFNAME, DATE( CARDTIME )
) AS x
I already research how to get the break time but the example data is different from mine where there is no LOGIN and LOGOUT.
Thanks in advance for your help.
MySQL allows you to write a query like this:
SELECT
id, staff_ID, STAFFNAME,
timediff(t3,t2) AS Break
FROM (
SELECT
id, staff_ID, STAFFNAME,
DATE(CARDTIME) as carddate,
SUBSTRING_INDEX(
SUBSTRING_INDEX(
GROUP_CONCAT(CARDTIME order by CARDTIME),
',',
3),
',',
-1) t3,
SUBSTRING_INDEX(
SUBSTRING_INDEX(
GROUP_CONCAT(CARDTIME order by CARDTIME),
',',
2),
',',
-1) t2
FROM
tablename
GROUP BY
id, staff_ID, STAFFNAME,
DATE(CARDTIME)
) s
it's not too optimized and not SQL standard, and you should also be sure that there are four cardtimes every day. But it should return the result that you need.
Please see fiddle here.
Edit
If employes can have less or more than 4 cardtime entries, you should consider using this query:
SELECT *
FROM (
SELECT
id,
staff_ID,
STAFFNAME,
DATE(CARDTIME) AS card_day,
timediff(next_CARDTIME,CARDTIME) As t_diff,
CASE WHEN
CASE WHEN next_CARDTIME IS NULL THEN #n:=-1 ELSE #n:=#n+1 END MOD 2 = 0
THEN 'Work' ELSE 'Break'
END AS type
FROM (
SELECT
t1.id,
t1.staff_ID,
t1.STAFFNAME,
t1.CARDTIME,
MIN(t2.CARDTIME) next_CARDTIME
FROM
tablename t1 LEFT JOIN tablename t2
ON (t1.id, t1.staff_ID) = (t2.id, t2.staff_ID)
AND DATE(t1.cardtime)=DATE(t2.cardtime)
AND t1.cardtime<t2.cardtime
GROUP BY
t1.id, t1.staff_ID, t1.STAFFNAME, t1.CARDTIME
ORDER BY
t1.id, t1.staff_ID, t1.STAFFNAME, t1.CARDTIME
) s, (SELECT #n:=-1) r
) s
WHERE t_diff IS NOT NULL
of course if number of cardtime entries is odd, last entry of the day will be a break. Have a look at this fiddle.
I have two SELECT statements that give the values "TotalSales" and "VendorPay_Com". I want to be able to subtract VendorPay_Com from TotalSales within the one MySQL statement to get the value "Outstanding_Funds" but I haven't found a reliable way to do so.
These are my two statements:
Query 1:
SELECT SUM(Price) AS TotalSales
FROM PROPERTY
WHERE Status = 'Sold';
Query 2:
SELECT SUM(AC.Amount) AS VendorPay_Comm
FROM (
SELECT Amount FROM lawyer_pays_vendor
UNION ALL
SELECT CommissionEarned AS `Amount` FROM COMMISSION AS C WHERE C.`status` = 'Paid') AS AC
Any help on this matter would be greatly appreciated :)
You can do it as follows :
select (select ...) - (select ...)
In your example, simply :
select
(
SELECT SUM(Price) AS TotalSales
FROM PROPERTY
WHERE Status = 'Sold'
)
-
(
SELECT SUM(AC.Amount) AS VendorPay_Comm
FROM (
SELECT Amount FROM lawyer_pays_vendor
UNION ALL
SELECT CommissionEarned AS `Amount` FROM COMMISSION AS C WHERE C.`status` = 'Paid') AS AC
) AS Outstanding_Funds
Try
SELECT TotalSales-VendorPay_Comm AS Outstanding_Funds
FROM
(SELECT SUM(Price) AS TotalSales
FROM PROPERTY
WHERE Status = 'Sold') t1,
(SELECT SUM(Amount) AS VendorPay_Comm
FROM (SELECT Amount FROM lawyer_pays_vendor
UNION ALL
SELECT CommissionEarned AS Amount
FROM COMMISSION
WHERE Status = 'Paid') t0) t2
Here is sqlfiddle
I'm trying to calculate a percentage in my SQL query.
This is what I have right now:
SELECT
DATE(processed_at) AS day,
(
SELECT
COUNT(*) FROM return_items
WHERE return_id IN (SELECT id FROM returns WHERE DATE(processed_at) = day)
) as products_returned,
COUNT(*) as return_count,
(
SELECT
COUNT(*) as co_returns
FROM returns
WHERE return_method = 'mondial_relais'
AND DATE(processed_at) = day
) as return_rate_mr
FROM returns
WHERE MONTH(processed_at) = 10
AND YEAR(processed_at) = 2011
GROUP BY day;
Basically I need the return_rate_mr to be a percentage value.
I tried doing something like return_rate_mr * 100 / return_count as perc_value but this doesn't work. (I don't actually need the current return_rate_mr value, just the percentage.
Any ideas?
Assuming your original query returns the desired results, you can wrap it as a subquery:
SELECT
day,
return_rate_mr * 100 / return_count as perc_value,
... any other columns ...
FROM
( ... your original query here ...) as myalias;
Basically, the subquery creates a result set where the columns are renamed. Then, the outer query is free to use those new column names.
Are you looking for this?
SELECT
`day`,
`products_returned`,
(`return_rate_mr` * 100) / `return_count` AS `percentage`
FROM (
SELECT
DATE(processed_at) AS day,
(
SELECT
COUNT(*) FROM return_items
WHERE return_id IN (SELECT id FROM returns WHERE DATE(processed_at) = day)
) as products_returned,
COUNT(*) as return_count,
(
SELECT
COUNT(*) as co_returns
FROM returns
WHERE return_method = 'mondial_relais'
AND DATE(processed_at) = day
) as return_rate_mr
FROM returns
WHERE MONTH(processed_at) = 10
AND YEAR(processed_at) = 2011
GROUP BY day) AS `ss`
Did you try something like:
SELECT (`return_rate_mr` * 100 ) / `return_count` as "yourValue", OthersFields
FROM SELECT
DATE(processed_at) AS day,
(
SELECT
COUNT(*) FROM return_items
WHERE return_id IN (SELECT id FROM returns WHERE DATE(processed_at) = day)
) as products_returned,
COUNT(*) as return_count,
(
SELECT
COUNT(*) as co_returns
FROM returns
WHERE return_method = 'mondial_relais'
AND DATE(processed_at) = day
) as return_rate_mr
FROM returns
WHERE MONTH(processed_at) = 10
AND YEAR(processed_at) = 2011
GROUP BY day;
Hope this helps
Try:
SELECT DATE(processed_at) AS day,
count(distinct id) as products_returned,
COUNT(*) as return_count,
100* sum(case return_method when 'mondial_relais' then 1 end) / COUNT(*)
as return_perc_mr
FROM returns
WHERE MONTH(processed_at) = 10
AND YEAR(processed_at) = 2011
GROUP BY day;
I suspect that products_returned should be counting distinct item_id values (or something similar), but this should duplicate the logic in the original query.