How to select working time by calling time, group id? - mysql

I need to select working time - SUM(users_worktime.length) as working_time) when users was calling by the calling time and users group id. How to do that?
Here is my select:
SELECT
users_groups.name as name,
COUNT(DISTINCT calls.id) as calls,
SUM(calls.status = 'ended') as answers,
COUNT(DISTINCT orders.id) as deals,
ROUND(COUNT(DISTINCT orders.id) * 100 / SUM(calls.status = 'ended'),2) as rate,
SUM(case when calls.status = 'ended' then calls.call_length else 0 end) as talking_time,
//SUM(users_worktime.length) as working_time
FROM
users_groups
LEFT JOIN users ON users.group_id = users_groups.id
LEFT JOIN calls ON (calls.user_id = users.id AND calls.created_at >= '2015-12-30 00:00:00' AND calls.created_at <= '2015-12-31 23:59:59')
LEFT JOIN orders ON orders.id = calls.order_id AND orders.status = 'finished'
WHERE 1
AND users.group_id = 1
GROUP BY
users_groups.id
I need result like this:
| name | calls | answers | deals | rate | talking_time| working_time |
------------------------------------------------------------------------
| Group | 4 | 3 | 2 | 75 % | 180 | 355.00 |
And here are my data tables:
users_worktime:
| id | user_id | length | start |
-----------------------------------------------
| 1 | 2 | 130 | 2015-12-30 07:53:38 |
| 2 | 8 | 55 | 2015-12-30 12:53:38 |
| 3 | 8 | 170 | 2015-12-31 22:53:38 |
users:
| id | username | group_id |
----------------------------
| 2 | Thomas | 1 |
| 8 | Haroldas | 1 |
groups:
| id | name |
-------------
| 1 | Group |
calls:
| id | user_id | order_id | status | call_length | created_at |
-------------------------------------------------------------------------
| 1 | 2 | 3 | ended | 35 | 2015-12-30 07:53:38 |
| 2 | 8 | 4 | ended | 100 | 2015-12-31 12:53:38 |
| 3 | 8 | NULL | started | 15 | 2015-12-31 14:53:38 |
| 4 | 8 | NULL | ended | 45 | 2015-12-31 20:53:38 |
orders:
| id | user_id | call_id | start |
-----------------------------------------------
| 3 | 2 |1 | 2015-12-30 07:53:38 |
| 4 | 8 |2 | 2015-12-31 12:53:38 |
Thank you
EDIT:
I trying with subquery like this but is not correct because SUM is only one user of group
SELECT
users_groups.name as name,
COUNT(DISTINCT calls.id) as calls,
SUM(calls.status = 'ended') as answers,
COUNT(DISTINCT orders.id) as deals,
ROUND(COUNT(DISTINCT orders.id) * 100 / SUM(calls.status = 'ended'),2) as rate,
SUM(case when calls.status = 'ended' then calls.call_length else 0 end) as talking_time,
(SELECT SUM(users_worktime.length) FROM users_worktime WHERE users_worktime.user_id = users.id AND users_worktime.start >= '2015-12-30 00:00:00' AND users_worktime.start <= '2015-12-31 23:59:59') as working_time
FROM
users_groups
LEFT JOIN users ON users.group_id = users_groups.id
LEFT JOIN calls ON (calls.user_id = users.id AND calls.created_at >= '2015-12-30 00:00:00' AND calls.created_at <= '2015-12-31 23:59:59')
LEFT JOIN orders ON orders.id = calls.order_id AND orders.status = 'finished'
WHERE 1
AND users.group_id = 1
GROUP BY
users_groups.id

Related

MySQL query with sum fields from other table, with a twist

Sorry for the vague title, but I don't know how to word this type of problem better. Here is a simple example to explain it. I have to tables: OrderItemList and OrderHistoryLog.
OrderItemList:
|------------------------------|
| OrderNo | ItemNo | Loc | Qty |
|------------------------------|
| 100 | A | 1 | 1 |
| 101 | A | 1 | 2 |
| 102 | A | 1 | 1 |
| 103 | A | 2 | 1 |
| 104 | A | 2 | 1 |
OrderHistoryLog:
|------------------------------|
| OrderNo | ItemNo | Loc | Qty |
|------------------------------|
| 50 | A | 1 | 5 |
| 51 | A | 1 | 2 |
| 100 | A | 1 | 1 |
| 102 | A | 1 | 3 |
| 103 | A | 2 | 1 |
I need to show the records in the OrderItemList along with a LocHistQty field, which is the sum(Qty) from the OrderHistoryLog table for a given Item and Location, but only for the orders that are present in the OrderItemList.
For the above example, the result should be:
Result:
|------------------------------------------------------
| OrderNo | ItemNo | Loc | Qty | HistQty | LocHistQty |
|------------------------------|-----------------------
| 100 | A | 1 | 1 | 1 | 4 |
| 101 | A | 1 | 2 | 0 | 4 |
| 102 | A | 1 | 1 | 3 | 4 |
| 103 | A | 2 | 1 | 1 | 1 |
| 104 | A | 2 | 1 | 0 | 1 |
It is the last field, LocHistQty that I could use some help with. Here is what I started with (does not work):
select OI.OrderNo, OI.ItemNo, OI.Loc, OI.Qty, IFNULL(OL.Qty, 0) as HistQty, OL2.LocHistQty
from OrderItemList OI
left join OrderItemLog OL on OL.OrderNo = OI.OrderNo and OL.ItemNo = OI.ItemNo
join
(
select ItemNo, Loc, sum(qty) as LocHistQty
from zOrderItemLog
group by ItemNo, Loc
) as OL2
on OL2.ItemNo = OI.ItemNo and OL2.Loc = OI.Loc
order by OrderNo
The issue is with the above SQL is that LocHistQty contains the summary of the Qty for all orders (=11 for Loc 1 and 1 for Loc 2), not only the ones in OrderItemList.
Lastly, the real data is voluminous and query performance is important.
Help would be much appreciated.
The subquery can join with OrderItemList to restrict the order numbers that it sums.
select OI.OrderNo, OI.ItemNo, OI.Loc, OI.Qty, IFNULL(OL.Qty, 0) as HistQty, OL2.LocHistQty
from OrderItemList OI
left join OrderItemLog OL on OL.OrderNo = OI.OrderNo and OL.ItemNo = OI.ItemNo
join
(
select OL.ItemNo, OL.Loc, sum(OL.qty) as LocHistQty
from OrderItemLog AS OL
JOIN OrderItemList AS OI ON OL.OrderNo = OI.OrderNo
group by OL.ItemNo, OL.Loc
) as OL2
on OL2.ItemNo = OI.ItemNo and OL2.Loc = OI.Loc
order by OrderNo
DEMO
Option 1
SELECT
OrderNo,
ItemNo,
Loc,
Qty,
(SELECT
Qty
FROM
OrderHistoryLog AS A
WHERE
A.OrderNo = B.OrderNo AND A.Loc = B.Loc) AS HistQty,
(SELECT
SUM(Qty)
FROM
OrderHistoryLog AS D
WHERE
D.OrderNo = B.OrderNo AND D.Loc = B.Loc) AS LocHistQty
FROM
OrderItemList AS B;
Option 2
SELECT
B.OrderNo,
B.ItemNo,
B.Loc,
B.Qty,
C.Qty AS HistQty,
(SELECT
SUM(Qty)
FROM
OrderHistoryLog AS A
WHERE
A.OrderNo = B.OrderNo AND A.Loc = B.Loc) AS LocHistQty
FROM
OrderItemList AS B,
OrderHistoryLog AS C
WHERE
C.OrderNo = B.OrderNo AND C.Loc = B.Loc;

MySQL merge columns from multiple tables

i'm trying to select some data in the following way:
field:
+----------+------------+-----------+
| id | room_id | server_id |
+----------+------------+-----------+
| 1 | 34 | 0 |
| 2 | 34 | 0 |
| 3 | 35 | 1 |
+----------+------------+-----------+
user_position:
+----------+------------+-----------+
| user_id | server_id | position |
+----------+------------+-----------+
| 11 | 0 | 2 |
| 17 | 1 | 25 |
| 19 | 0 | 28 |
+----------+------------+-----------+
room:
+----------+------------+-----------+
| id | server_id | background|
+----------+------------+-----------+
| 34 | 0 | #d91a1a |
| 35 | 1 | #f81b2a |
| 36 | 0 | #191b4a |
+----------+------------+-----------+
RESULT:
(I hope I didn't mess it up)
+----------+------------+-----------+------------+------------+
| id | server_id | background| room_id | user_id |
+----------+------------+-----------+------------+------------+
| 1 | 0 | #d91a1a | 34 | null |
| 2 | 0 | #d91a1a | 34 | 11 |
| 3 | 1 | #f81b2a | 35 | null |
| 25 | 1 | null | null | 17 |
| 28 | 0 | null | null | 19 |
+----------+------------+-----------+------------+------------+
Unfortunately i couldn't write the right query to achieve this result. The best I could get was that the field.id, user_position.position and field.room_id, user_position.room_id columns were separated. I have no idea how to merge them together.
Can somebody help me?
UPDATE
OK, so after some trying I got this:
SELECT field.id, field.server_id, field.room_id, null AS user_id, room.background
FROM field
LEFT JOIN room ON room.id = field.room_id
WHERE field.server_id = 0
UNION
SELECT user_position.position, user_position.server_id, null, user_position.user_id, room.background
FROM user_position
LEFT JOIN room ON room.id = (SELECT field.room_id FROM field WHERE field.id = user_position.position)
WHERE user_position.server_id = 0
Now it is working I just want to ask if there isn't a better way to achieve the same result. Or do you think this query is good enough?
With the given set of data, you may try below -
SELECT COALESCE(F.id, UP2.position) id
,COALESCE(F.server_id, UP2.server_id) server_id
,UP2.background
,F.room_id
,UP2.user_id
FROM field F
LEFT OUTER JOIN (SELECT UP.server_id, R.background, UP.position, UP.user_id
FROM (SELECT server_id, position, user_id, ROW_NUMBER()OVER(PARTITION BY server_id ORDER BY user_id) rn
FROM user_position) UP
JOIN (SELECT server_id, background, ROW_NUMBER()OVER(PARTITION BY server_id ORDER BY id) rn
FROM room) R ON UP.rn = R.rn
AND UP.server_id = R.server_id) UP2 ON F.id = UP2.position
UNION
SELECT COALESCE(F.id, UP3.position) id
,COALESCE(F.server_id, UP3.server_id) server_id
,UP3.background
,F.room_id
,UP3.user_id
FROM field F
RIGHT OUTER JOIN (SELECT UP.server_id, R.background, UP.position, UP.user_id
FROM (SELECT server_id, position, user_id, ROW_NUMBER()OVER(PARTITION BY server_id ORDER BY user_id) rn
FROM user_position) UP
JOIN (SELECT server_id, background, ROW_NUMBER()OVER(PARTITION BY server_id ORDER BY id) rn
FROM room) R ON UP.rn = R.rn
AND UP.server_id = R.server_id) UP3 ON F.id = UP3.position
ORDER BY id
Here is the demo.

How to combine table records with some condition in SQL table?

I have six tables in the database, all tables are relative to each other and want to show records in one table.
Following are my tables:
1) mls_stores
*----------------------------*
| store_id | store_title |
*----------------------------*
| 1001 | ajmar-jaipur |
| 1002 | dwarka-delhi |
*----------------------------*
2) mls_category
*-------------------------------------------*
| cat_no | store_id | cat_value | cat_type |
*-------------------------------------------*
| 20 | 1001 | 1 | running |
| 21 | 1001 | 4 | cycling |
| 22 | 1002 | 1 | running |
| 23 | 1002 | 2 | swmining |
*-------------------------------------------*
3) mls_points_matrix
*----------------------------------------*
| store_id | value_per_point | maxpoint |
*----------------------------------------*
| 1001 | 1 | 10 |
| 1001 | 2 | 20 |
| 1002 | 1 | 20 |
| 1002 | 4 | 30 |
*----------------------------------------*
4) mls_user
*--------------------------*
| id | store_id | name |
*--------------------------*
| 1 | 1001 | sandeep |
| 2 | 1001 | jagveer |
| 3 | 1002 | gagan |
*--------------------------*
5) bonus_points
*---------------------------------------------------*
| user_id | store_id | bonus_points | bonus_type |
*---------------------------------------------------*
| 1 | 1001 | 10 | fixed |
| 3 | 1002 | 2 | % |
*---------------------------------------------------*
6) mls_entry
*-------------------------------------------------------*
| user_id | store_id | category | distance | status |
*-------------------------------------------------------*
| 1 | 1001 | 20 | 10 | approved |
| 1 | 1001 | 21 | 40 | approved |
| 1 | 1001 | 20 | 5 | reject |
| 2 | 1001 | 21 | 40 | approved |
| 3 | 1002 | 22 | 10 | approved |
| 3 | 1002 | 23 | 20 | approved |
*-------------------------------------------------------*
Now I want output as below:
*-----------------------------------------------------------------------------------*
| Name | Entries | Points Earned | Bonus Points | Total Points | Total Amount |
*-----------------------------------------------------------------------------------*
| Sandeep | running(1) | 20 | 10 | 30 | 60 |
| | cycling(1) | | | | |
*-----------------------------------------------------------------------------------*
| Jagveer | cycling(1) | 10 | 0 | 10 | 10 |
*-----------------------------------------------------------------------------------*
I am using following code:
SELECT
u.name,
ROUND(COALESCE(t1.points, 0)) AS points,
ROUND(COALESCE(b.bonus_points, 0)) AS bonus_points,
ROUND(COALESCE(t1.points, 0) + COALESCE(b.bonus_points, 0)) AS total_points
FROM mls_user u
LEFT JOIN
(
SELECT e.user_id, e.status, SUM(e.distance / c.cat_value) AS points
FROM mls_entry e
INNER JOIN mls_category c
ON e.store_id = c.store_id AND e.category = c.cat_no
GROUP BY e.user_id
HAVING e.status='approved'
) t1
ON u.id = t1.user_id
LEFT JOIN bonus_points b
ON u.id = b.user_id
WHERE u.store_id = '1001'
ORDER BY
total_points DESC
This SQL query giving me Point earned, bonus points and total points, But I am not able to find Entries And Total Amount and it is giving me wrong Point calculation for Sandeep, As per data one entry is rejected. so it should be 20, not 25.
My total amount will be for Sandeep 30X2(it is coming from point matrix) = 60
same like for jagveer, the total amount for jagveer 10X1 = 10.
I have created tables in DEMO
Try below :
SELECT
u.name,
ROUND(COALESCE(t1.points, 0)) AS points,
ROUND(COALESCE(b.bonus_points, 0)) AS bonus_points,
ROUND(COALESCE(t1.points, 0) + COALESCE(b.bonus_points, 0)) AS total_points,
ROUND(COALESCE(t1.points, 0) + COALESCE(b.bonus_points, 0)) * t1.countId as total_amount,
group_concat(t1.EntriesConcat) as Entries
FROM mls_user u
LEFT JOIN
(
SELECT e.user_id, e.status, SUM(e.distance / c.cat_value) AS points,
concat(c.cat_type, '(',count(e.user_id), ')' ) as EntriesConcat,
count(e.user_id) as countId -- it returns count of records according to group by part
FROM mls_entry e
INNER JOIN mls_category c
ON e.store_id = c.store_id AND e.category = c.cat_no
-- remove HAVING and use WHERE clause
WHERE e.status='approved'
GROUP BY e.user_id
) t1 ON u.id = t1.user_id
LEFT JOIN bonus_points b ON u.id = b.user_id
WHERE u.store_id = '1001'
ORDER BY total_points DESC
group_concat of mysql is useful to concatinating values on Group By

MySql aggregate by different initial values on other column

I have a table structure as follows
| user_id | value | date |
|---------|-------|------------|
| 1 | 5 | 2017-09-01 |
| 2 | 6 | 2017-09-01 |
| 1 | 1 | 2017-09-02 |
| 1 | 2 | 2017-09-03 |
| 2 | 9 | 2017-09-02 |
| 1 | 3 | 2017-09-04 |
| 2 | 5 | 2017-09-04 |
| 2 | 5 | 2017-09-05 |
| 1 | 1 | 2017-09-05 |
| 1 | 5 | 2017-09-06 |
| 1 | 6 | 2017-09-07 |
| 1 | 3 | 2017-09-08 |
| 1 | 4 | 2017-09-09 |
| 2 | 6 | 2017-09-06 |
| 1 | 1 | 2017-09-10 |
I have another table where initial cutoff date for user_ids are given like
| user_id | date |
|---------|------------|
| 1 | 2017-09-04 |
| 2 | 2017-09-05 |
The final cutoff date for all the user is 2017-09-08
I want to get the sum of values aggregated by user_id
What I have tried is
SELECT user_id, SUM(value) as Total
FROM table
WHERE date >= $DATE and date <= '2017-09-08'
GROUP BY user_id
I am stuck how should I deal with $DATE as it is variable for each user
The solution in the case should be
| user_id | Total |
|---------|------------|
| 1 | 18 |
| 2 | 11 |
Say you have table names as users and cutoff. cutoff having cutoff date for each user. Try this and let me know if it works or not.
select u.user_id,sum(u.value) Total
from users u join cutoff c on u.user_id=c.user_id and (u.date>=c.date and u.date<='2017-09-08')
group by u.user_id
SELECT x.user_id, SUM(x.value) as Total
FROM table x
WHERE x.date >= (select date from table2 where user_id = x.user_id)
and x.date <= '2017-09-08'
GROUP BY x.user_id
Using where exists
SELECT a.user_id, SUM(a.value) as Total
FROM table1 a
WHERE EXISTS (
SELECT 1
FROM table2 b
WHERE a.user_id = b.user_id
AND a.date >= b.date and a.date <= '2017-09-08'
)
GROUP BY user_id
DEMO
OR using conditional aggregation
SELECT a.user_id,
SUM(CASE WHEN a.date >= b.date and a.date <= '2017-09-08'
THEN a.value
ELSE 0
END) as Total
FROM table1 a
JOIN table2 b ON a.user_id = b.user_id
GROUP BY user_id
DEMO

Get minimum from result with GROUP BY in MySQL

I have table it store hierarchy data in MySQL this table store stable relation but if each user less than 1000 buy removed and user User a lower level replace this is my code and work fine, after GROUP BY it contain all ancestor of descendant with compare then COUNT(*) AS level count level each user. This I have SQL code to compress data According to minimum buy for each user
+-------------+---------------+-------------+
| ancestor_id | descendant_id | path_length |
+-------------+---------------+-------------+
| 1 | 1 | 0 |
| 1 | 2 | 1 |
| 1 | 3 | 1 |
| 1 | 4 | 2 |
| 1 | 5 | 3 |
| 1 | 6 | 4 |
| 2 | 2 | 0 |
| 2 | 4 | 1 |
| 2 | 5 | 2 |
| 2 | 6 | 3 |
| 3 | 3 | 0 |
| 4 | 4 | 0 |
| 4 | 5 | 1 |
| 4 | 6 | 2 |
| 5 | 5 | 0 |
| 5 | 6 | 1 |
| 6 | 6 | 0 |
+-------------+---------------+-------------+
This is table buy
+--------+--------+
| userid | amount |
+--------+--------+
| 2 | 2000 |
| 4 | 6000 |
| 6 | 7000 |
| 1 | 7000 |
SQL code
SELECT a.*
FROM
( SELECT userid
FROM webineh_user_buys
GROUP BY userid
HAVING SUM(amount) >= 1000
) AS buys_d
JOIN
webineh_prefix_nodes_paths AS a
ON a.descendant_id = buys_d.userid
JOIN
(
SELECT userid
FROM webineh_user_buys
GROUP BY userid
HAVING SUM(amount) >= 1000
) AS buys_a on (a.ancestor_id = buys_a.userid )
JOIN
( SELECT descendant_id
, MAX(path_length) path_length
FROM webineh_prefix_nodes_paths
where a.ancestor_id = ancestor_id
GROUP
BY descendant_id
) b
ON b.descendant_id = a.descendant_id
AND b.path_length = a.path_length
GROUP BY a.descendant_id, a.ancestor_id
I need get max path_length where ancestor_id have At least 1000 amount buy but have error in where in subquery where a.ancestor_id = ancestor_id error code
1054 - Unknown column 'a.ancestor_id' in 'where clause'
I add SQLFidle demo.
You could use this query:
select m.userid as descendant,
p.ancestor_id,
p.path_length
from (
select b1.userid,
min(case when b2.amount >= 1000
then p.path_length
end) as path_length
from (select userid, sum(amount) amount
from webineh_user_buys
group by userid
having sum(amount) >= 1000
) as b1
left join webineh_prefix_nodes_paths p
on p.descendant_id = b1.userid
and p.path_length > 0
left join (select userid, sum(amount) amount
from webineh_user_buys
group by userid) as b2
on p.ancestor_id = b2.userid
group by b1.userid
) as m
left join webineh_prefix_nodes_paths p
on p.descendant_id = m.userid
and p.path_length = m.path_length
order by m.userid
Output for sample data in the question:
| userid | ancestor_id | path_length |
|--------|-------------|-------------|
| 1 | (null) | (null) |
| 2 | 1 | 1 |
| 4 | 2 | 1 |
| 6 | 4 | 2 |
SQL fiddle