I have a query which does not output the result I want. I spent several days on this, but I did not find a solution.
My Code
SELECT *, IFNULL(type, 'N/A') AS groupField, IFNULL(type, 'N/A') AS displayField, `pmonth`, `pyear`, count(prj.code) as projects_num
FROM `tblProjecats` `prj`
LEFT JOIN ( select cda, pyear, pmonth, sum(venituri) venituri, sum(cheltuieli) cheltuieli, sum(if(cont3 between 500 and 550,cheltuieli,0)) cheltuieliD, sum(if(cont3 > 550,cheltuieli,0)) cheltuieliI from buffer_cda group by pyear, pmonth, cda ) cda ON `cda`.`cda`=`prj`.`code`
-- more simple left jons --
WHERE (concat(pyear,lpad(pmonth,2,0)) BETWEEN 201811 AND 201911)
GROUP BY `type`, concat_ws('', `cda`.`pyear`, lpad(cda.pmonth, 2, '0'))
ORDER BY `type` ASC, `pyear` ASC, `pmonth` ASC, `cda` ASC
Table tblProjects
code type
66666 N/A
66667 Custom
66668 Simple
66669 Simple
66610 N/A
66611 Custom
66612 N/A
Table buffer_cda (with project Type joined for easier visualization)
cda pmonth pyear type cheltuieliD
66666 1 2019 N/A 1
66667 1 2019 Custom 10
66668 1 2019 Simple 100
66669 12 2018 Simple 200
66610 12 2018 N/A 2
66611 12 2018 Custom 20
66612 12 2018 N/A 3
66612 12 2018 N/A 4
Expected result
Type pmonth pyear count_prj cheltuieliD
N/A 12 2018 2 9
N/A 1 2019 1 1
Custom 12 2018 1 20
Custom 1 2019 1 10
Simple 12 2018 1 200
Simple 1 2019 1 100
Actual result
Type pmonth pyear count_prj cheltuieliD
N/A 12 2018 2 7
N/A 1 2019 8 1
Custom 12 2018 1 20
Custom 1 2019 3 10
Simple 12 2018 1 200
Simple 1 2019 1 100
I am doing something wrong with groupings but I do not know what. In other cases I have a duplicate Type results with cheltuieliD= 2 and cheltuieliD= 7 (instead of one type with cheltuieliD=9)
Next step on this would be calculating the cheltuieliD indicator on each project type, on Period Groups (month/Year) and then a grand total. Is this possible with a single query?
Chnge your groupby sequence with cda, pyear, pmonth...
If you just want to return project count every month then remove cda frim groupby clause...
If you could explain what logical operation you want to perform we can help.
Your data does not seem to be representative of your problem but given the sample data provided I would code like this
drop table if exists tblprojects,buffer_cda;
create table tblProjects
(code int, type varchar(20));
insert into tblprojects values
(66666 , 'N/A'),
(66667 , 'Custom'),
(66668 , 'Simple'),
(66669 , 'Simple'),
(66610 , 'N/A'),
(66611 , 'Custom'),
(66612 , 'N/A');
create Table buffer_cda
(cda int,pmonth int, pyear int, cheltuieliD int);
insert into buffer_cda values
(66666 , 1 ,2019 , 1),
(66667 , 1 ,2019 , 10),
(66668 , 1 ,2019 , 100),
(66669 , 12 ,2018 , 200),
(66610 , 12 ,2018 , 2),
(66611 , 12 ,2018 , 20),
(66612 , 12 ,2018 , 3),
(66612 , 12 ,2018 , 4);
select type,pmonth,pyear,count(type),sum(cheltuieliD)
from tblprojects tp
join buffer_cda bc on bc.cda = tp.code
group by type,pyear,pmonth;
+--------+--------+-------+-------------+------------------+
| type | pmonth | pyear | count(type) | sum(cheltuieliD) |
+--------+--------+-------+-------------+------------------+
| Custom | 12 | 2018 | 1 | 20 |
| Custom | 1 | 2019 | 1 | 10 |
| N/A | 12 | 2018 | 3 | 9 |
| N/A | 1 | 2019 | 1 | 1 |
| Simple | 12 | 2018 | 1 | 200 |
| Simple | 1 | 2019 | 1 | 100 |
+--------+--------+-------+-------------+------------------+
I found the correct way!
Thank you all for your responses!
I had to swap joining buffer_cda on projects to projects joining to buffer_cda.
More on, I had to eliminate other joins.
Bellow my answer, written for codeigniter!
$this->db->select("*,IFNULL(".$groupField.", 'N/A') AS groupField, IFNULL(".$displayField.", 'N/A') AS displayField, luna,anul,
count(DISTINCT prj.code) as projects_num,
sum(if(cont3 between 500 and 550,cheltuieli,0)) AS cheltuieliD,
sum(if(cont3 > 550,cheltuieli,0)) cheltuieliI,
sum(if(cont3 between 500 and 550,cheltuieli,0))/count(DISTINCT prj.code) AS cheltuieliDAvg,
sum(if(cont3 > 550,cheltuieli,0))/count(DISTINCT prj.code) cheltuieliIAvg
");
$this->db->from('buffer_cda cda');
$this->db->join('(
select * from tblProjects ) prj', 'cda.cda=prj.code', 'LEFT');
I also grouped by type, pyear, pmonth.
Related
i have a normal turnover table with 3 columns "customer" , "year", "amount"
(for example)
customer
year
amount
anton
2020
$5
paul
2019
$12
anton
2021
$5
paul
2019
$10
felicia
2021
$5
anton
2019
$12
felipe
2019
$12
and i have the following mysql query
SELECT `customer` , SUM(`amount`) as summ FROM `customer`.`accountsales` WHERE `amount`> 0 GROUP BY `customer` ORDER BY summ DESC ;
This transaction gives me a nice Paretto table with the sales of each customer in descending order
name
sales all years
customer1
sum of all transactions of customer1
customer2
sum of all transactions of customer2
customer3
sum of all transactions of customer3
so far so good, i want to go one step further and i want to create the following table
Name
Sales all years
Sales 2021
Sales2020
Sales2019
customer1
sum1
sum2021 from customer1
sum2020 from customer1
sum2019 from customer1
customer2
sum2
sum2021 from customer2
sum2020 from customer2
sum2019 from customer2
customer3
sum3
sum2021 from customer3
sum2020 from customer3
sum2019 from customer3
but i want to do it in only one transaction, because the initial table is very huge.
can someone give a hint ?
p.S. feel free to edit the title since I am not very inspired today
You can try to use condition aggregate function
Query 1:
SELECT `customer` ,
SUM(`amount`) 'Sales all years',
SUM(CASE WHEN year = 2021 THEN `amount` ELSE 0 END) Sales2021,
SUM(CASE WHEN year = 2020 THEN `amount` ELSE 0 END) Sales2020,
SUM(CASE WHEN year = 2019 THEN `amount` ELSE 0 END) Sales2019
FROM accountsales
GROUP BY `customer`
Results:
| customer | Sales all years | Sales2021 | Sales2020 | Sales2019 |
|----------|-----------------|-----------|-----------|-----------|
| anton | 22 | 5 | 5 | 12 |
| felicia | 5 | 5 | 0 | 0 |
| felipe | 12 | 0 | 0 | 12 |
| paul | 22 | 0 | 0 | 22 |
I have a table like this
userID time NoOfVisits
1 2014 50
2 2015 60
3 2016 70
4 2017 80
5 2018 90
6 2019 100
I need to write a sql query which will print time and average of past 3 years NoOfVisits for a particular site.
output should be as
userID time NoOfVisits
1 2014 50.0000
2 2015 55.0000
3 2016 60.0000
4 2017 70.0000
5 2018 80.0000
6 2019 90.0000
Explanation :
For user Id 6 (80+90+100)/3=90.0000
Please help me to solve this problem.
You can use a cumulative average, available in MySQL 8+:
select t.*,
avg(visits) over (order by time rows between 2 preceding and current row) as avg_visits_3
from t;
Assuming there are no gaps between the years (like your sample data), you can self join the table and group by userid, time to get the average:
select
t.userid, t.time, avg(tt.noofvisits) NoOfVisits
from tablename t inner join tablename tt
on tt.time between t.time - 2 and t.time
group by t.userid, t.time
See the demo.
Results:
| userid | time | NoOfVisits |
| ------ | ---- | ---------- |
| 1 | 2014 | 50 |
| 2 | 2015 | 55 |
| 3 | 2016 | 60 |
| 4 | 2017 | 70 |
| 5 | 2018 | 80 |
| 6 | 2019 | 90 |
employee makes entry in the following table when starting new task
from home or office
[tablename=CHECK]
c_id c_sdate c_emp c_task
-------------------------------------------------
1 2013-05-01 01:01:00 1 26 //date 01 from home-----
2 2013-05-01 08:11:00 1 27 //date 01 from office--- Present
3 2013-05-02 03:41:00 1 28 //date 02 from home---
4 2013-05-02 09:12:00 1 29 //date 02 from office-
5 2013-05-02 22:32:00 1 30 //date 02 from home---Present
6 2013-05-03 01:43:00 1 31 //date 03 from home
7 2013-06-03 23:25:00 1 32 //date 03 from home----------Homework
8 2013-06-03 02:15:00 2 33 //other employee
an employe will be considered as present if there 1 or many records where time between 8am and 8pm
an employe will be considered as workedFromHome if there 1 or many records where time NOT between 8am and 8pm, and not present on that day
note: do not count a day as workedFromHome if there is any record time between 8am and 8pm (means workedFromHome is only counted if he is not resent on that day)
I want to display monthly report of a employee eg. c_emp=1 for month eg. 5
like this in 1 query
c_emp presentCount HW_Count
1 3 1
or separatly query 1
c_emp presentCount
1 3
and query 2
c_emp HW_Count
1 1
I have tried for counting present working fine
select count(distinct(date_format(c_sdate,'%e'))) as count
from ita_check
where date_format(c_sdate,'%m')=5
and c_emp=1
and date_format(c_sdate,'%H%i')>=800
and date_format(c_sdate,'%H%i')<=2000
and for counting fromHome giving wrong count
select count(distinct(date_format(c_sdate,'%e'))) as count
from ita_check
where date_format(c_sdate,'%m')=5
and c_eid=1
and c_id not in (
select c_id
from ita_check
where date_format(c_sdate,'%m')=5
and c_eid=1
and (date_format(c_sdate,'%H%i')<=800 or date_format(c_sdate,'%H%i')>=2000)
)
and date_format(c_sdate,'%H%i')<800
or date_format(c_sdate,'%H%i')>2000
in above query for counting Working
the sub query returns 1 and 2
while the outer eliminate c_id=2 but not c_id=1
Try this query
SELECT c_emp,
sum(if(cnt>=1,1,0)) as Office,
count(*)-sum(if(cnt>=1,1,0)) as WFH from (
select c_emp, Date(c_sdate),
sum(if(c_sdate BETWEEN Date(c_sdate) + interval 8 hour
AND Date(c_sdate) + interval 20 hour, 1, 0)) as cnt
from table1
group by c_emp, Date(c_sdate)) tmp
group by c_emp
SQL FIDDLE:
| C_EMP | OFFICE | WFH |
------------------------
| 1 | 2 | 2 |
| 2 | 0 | 1 |
For monthly report
SELECT c_emp, date_format(c_date, '%c %Y') as Mnth,
sum(if(cnt>=1,1,0)) as Office,
count(*)-sum(if(cnt>=1,1,0)) as WFH from (
select c_emp, Date(c_sdate) as c_date,
sum(if(c_sdate BETWEEN Date(c_sdate) + interval 8 hour
AND Date(c_sdate) + interval 20 hour, 1, 0)) as cnt
from table1
group by c_emp, Date(c_sdate)) tmp
group by c_emp,Mnth
SQL FIDDLE:
| C_EMP | MNTH | OFFICE | WFH |
---------------------------------
| 1 | 5 2013 | 2 | 1 |
| 1 | 6 2013 | 0 | 1 |
| 2 | 6 2013 | 0 | 1 |
I want to calculate total hrs spend by an employee between 09:00am and 18:00pm.
My database look like this.
How can I do this??
AttendanceId EmpId CheckTime CheckType
-------------------------------------------------------------------------
3 5 2013-01-03 09:00:15.000 1 (Login)
4 5 2013-01-03 11:00:00.000 2 (Logout)
5 5 2013-01-03 11:30:00.000 1
6 5 2013-01-03 13:00:00.000 2
7 5 2013-01-03 13:30:00.000 1
8 5 2013-01-03 16:00:00.000 2
9 5 2013-01-03 16:30:00.000 1
10 5 2013-01-03 18:00:00.000 2
Since your Login/Logout values are in the same column, this might be easier to PIVOT the login/logout times first then get the datediff to determine the total amount of time an employee is present.
The PIVOT portion of the query is this:
select empid, [1], [2]
from
(
select empid, checktime, checktype,
row_number() over(partition by empid, checktype order by checktime) rn
from yourtable
) src
pivot
(
max(checktime)
for checktype in ([1], [2])
) piv
See SQL Fiddle with Demo
The result of this is:
| EMPID | 1 | 2 |
---------------------------------------------------------------------------
| 5 | January, 03 2013 09:00:15+0000 | January, 03 2013 11:00:00+0000 |
| 5 | January, 03 2013 11:30:00+0000 | January, 03 2013 13:00:00+0000 |
| 5 | January, 03 2013 13:30:00+0000 | January, 03 2013 16:00:00+0000 |
| 5 | January, 03 2013 16:30:00+0000 | January, 03 2013 18:00:00+0000 |
Once the data is in this structure, you can easily get the difference in the time by applying the DateDiff() function.
The final query to generate the amount of time an employee is logged in is:
select empid, sum(SecondsDiff) / 3600 as TotalHours
from
(
select empid, datediff(ss, [1], [2]) SecondsDiff
from
(
select empid, checktime, checktype,
row_number() over(partition by empid, checktype order by checktime) rn
from yourtable
) src
pivot
(
max(checktime)
for checktype in ([1], [2])
) piv
) src
group by empid
See SQL Fiddle with Demo
And the result is:
| EMPID | TOTALHOURS |
----------------------
| 5 | 7 |
To get the difference between two dates, you use the DATEDIFF function:
http://msdn.microsoft.com/en-us/library/ms189794.aspx
I think you'd need to do this row-by-row, though. Because of the structure of your table, you can't just do a simple query.
I have a MYSQL table like this:
id | userid | score | datestamp |
-----------------------------------------------------
1 | 1 | 5 | 2012-12-06 03:55:16
2 | 2 | 0,5 | 2012-12-06 04:25:21
3 | 1 | 7 | 2012-12-06 04:35:33
4 | 3 | 12 | 2012-12-06 04:55:45
5 | 2 | 22 | 2012-12-06 05:25:11
6 | 1 | 16,5 | 2012-12-06 05:55:21
7 | 1 | 19 | 2012-12-06 13:55:16
8 | 2 | 8,5 | 2012-12-07 06:27:16
9 | 2 | 7,5 | 2012-12-07 08:33:16
10 | 1 | 10 | 2012-12-07 09:25:19
11 | 1 | 6,5 | 2012-12-07 13:33:16
12 | 3 | 6 | 2012-12-07 15:45:44
13 | 2 | 4 | 2012-12-07 16:05:16
14 | 2 | 34 | 2012-12-07 18:33:55
15 | 2 | 22 | 2012-12-07 18:42:11
I would like to display user scores like this:
if a user on a certain day has more than 3 scores it would get only highest 3, repeat that for every day for this user and then add all days together. I want to display this sum for every user.
EDIT:
So in the example above for user 1 on 06.12. I would add top 3 scores together and ignore 4th score, then add to that number top 3 from the next day and so on. I need that number for every user.
EDIT 2:
Expected output is:
userid | score
--------------------
1 | 59 //19 + 16.5 + 7 (06.12.) + 10 + 6.5 (07.12.)
2 | 87 //22 + 0.5 (06.12.) + 34 + 22 + 8.5 (07.12.)
3 | 18 //12 (06.12.) + 6 (07.12.)
I hope this is more clear :)
I would really appreciate the help because I am stuck.
Please take a look at the following code, if your answer to my comment is yes :) Since your data all in 2012, and month of november, I took day.
SQLFIDDLE sample
Query:
select y.id, y.userid, y.score, y.datestamp
from (select id, userid, score, datestamp
from scores
group by day(datestamp)) as y
where (select count(*)
from (select id, userid, score, datestamp
from scores group by day(datestamp)) as x
where y.score >= x.score
and y.userid = x.userid
) =1 -- Top 3rd, 2nd, 1st
order by y.score desc
;
Results:
ID USERID SCORE DATESTAMP
8 2 8.5 December, 07 2012 00:00:00+0000
20 3 6 December, 08 2012 00:00:00+0000
1 1 5 December, 06 2012 00:00:00+0000
Based on your latter updates to question.
If you need some per user by year/month/day and then find highest, you may simply add aggregation function like sum to the above query. I am reapeating myself, since your sample data is for just one year, there's no point group by year or month. That's why I took day.
select y.id, y.userid, y.score, y.datestamp
from (select id, userid, sum(score) as score,
datestamp
from scores
group by userid, day(datestamp)) as y
where (select count(*)
from (select id, userid, sum(score) as score
, datestamp
from scores
group by userid, day(datestamp)) as x
where y.score >= x.score
and y.userid = x.userid
) =1 -- Top 3rd, 2nd, 1st
order by y.score desc
;
Results based on sum:
ID USERID SCORE DATESTAMP
1 1 47.5 December, 06 2012 00:00:00+0000
8 2 16 December, 07 2012 00:00:00+0000
20 3 6 December, 08 2012 00:00:00+0000
UPDATED WITH NEW SOURCE DATA SAMPLE
Simon, please take a look at my own sample. As your data was changing, I used mine.
Here is the reference. I have used pure ansi style without any over partition or dense_rank.
Also note the data I used are getting top 2 not top 3 scores. You can change is accordingly.
Guess what, the answer is 10 times simpler than the first impression your first data gave....
SQLFIDDLE
Query to 1:
-- for top 2 sum by user by each day
SELECT userid, sum(Score), datestamp
FROM scores t1
where 2 >=
(SELECT count(*)
from scores t2
where t1.score <= t2.score
and t1.userid = t2.userid
and day(t1.datestamp) = day(t2.datestamp)
order by t2.score desc)
group by userid, datestamp
;
Results for query 1:
USERID SUM(SCORE) DATESTAMP
1 70 December, 06 2012 00:00:00+0000
1 30 December, 07 2012 00:00:00+0000
2 22 December, 06 2012 00:00:00+0000
2 25 December, 07 2012 00:00:00+0000
3 30 December, 06 2012 00:00:00+0000
3 30 December, 07 2012 00:00:00+0000
Final Query:
-- for all two days top 2 sum by user
SELECT userid, sum(Score)
FROM scores t1
where 2 >=
(SELECT count(*)
from scores t2
where t1.score <= t2.score
and t1.userid = t2.userid
and day(t1.datestamp) = day(t2.datestamp)
order by t2.score desc)
group by userid
;
Final Results:
USERID SUM(SCORE)
1 100
2 47
3 60
Here goes a snapshot of direct calculations of data I used.
SELECT
*
FROM
table1
LEFT JOIN
(SELECT * FROM table1 ORDER BY score LIMIT 3) as lr on DATE(lr.datestamp) = DATE(table1.datastamp)
GROUP BY
datestamp