I have two tables. One is a call history table which logs calls made (starttime, endtime, phone number, user, etc). The other is an orders table which logs order details (order number, customer info, orderdate, etc.). Orders are not always created when a call is created so there isnt a guaranteed ID to match them up. Right now, I'm interested in getting totals by day. When I try to run a a query to sum calls and join orders by day I get the following error:
The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET MAX_JOIN_SIZE=# if the SELECT is okay
This is the query I use:
SELECT
DATE_FORMAT(c.date_call_start,'%Y-%m-%d') as date,
COUNT(c.id) as calls,
COUNT(o.id) as orders
FROM tbl_calls c
LEFT OUTER JOIN tbl_orders o
ON DATE_FORMAT(c.date_call_start,'%Y-%m-%d') = DATE_FORMAT(o.created,'%Y-%m-%d')
WHERE c.campaign_id = 1
AND DATE_FORMAT(c.date_call_start,'%Y-%m-%d') = '2013-12-09'
GROUP BY DATE_FORMAT(c.date_call_start,'%Y-%m-%d')
Even when there are only a few calls for a particular day, it still shows the same error. So I'm pretty sure it my query that needs work.
I have also tried a sub query, but that doesn't rollup the totals from the subquery.
SELECT
DATE_FORMAT(c.date_call_start,'%Y-%m-%d') as date,
count(c.id) as calls,
(select count(DISTINCT o.id)
FROM tbl_orders o
WHERE DATE_FORMAT(o.created,'%Y-%m-%d') = DATE_FORMAT(c.date_call_start,'%Y-%m-%d')
) as orders
FROM tb_calls c
WHERE c.campaign_id = 1
AND DATE_FORMAT(c.date_call_start,'%Y-%m-%d') BETWEEN '2013-12-09' AND '2013-12-15'
GROUP BY DATE_FORMAT(c.date_call_start,'%Y-%m-%d')
WITH ROLLUP
Any thoughts on how I can get this query to work? Ultimately I'd like a result like below so I can do other calculations like % orders etc.
date | calls | orders
------------------------------------
2013-12-01 | 100| 10
2013-12-02 | 125| 20
NULL | 225| 30
UPDATED:
Based on the answer I did the following:
created call_date field with a date field (no datetime) to tbl_calls
created date_order field with a date format (not datetime) to tbl_orders
Updated each table and set the new fields to = date_format(the_date_time_stamp,'%Y-%m-%d') from the same table.
Also added an index to each of the new date fields.
That made the following query work:
SELECT
c.call_date as date,
COUNT(DISTINCT c.id) as calls,
COUNT(DISTINCT o.id) as orders,
ROUND((COUNT(DISTINCT o.id) / COUNT(DISTINCT c.id))*100,2) as conversion
FROM tbl_calls c
JOIN tbl_orders o
ON c.call_date = o.date_order
WHERE c.campaign_id = 1
AND c.call_date BETWEEN '2013-12-09' AND '2013-12-15'
GROUP BY c.call_date
WITH ROLLUP
Which gives me the following result and I can build off of this. Thanks to each of you who provided suggestions. I tried each. All make sense. However, since I ultimately had to create the additional date fields I chose the answer by
date | calls | orders| conversion
-------------------------------------------
2013-12-09 | 151 | 6 | 3.97
2013-12-10 | 164 | 2 | 1.22
2013-12-11 | 165 | 6 | 3.64
2013-12-12 | 189 | 1 | 0.53
2013-12-13 | 116 | 4 | 3.45
null | 785 | 19 | 2.42
First - try the results of EXPLAIN SELECT.... where ... is the rest of your select query above.
Since you're performing the join on two fields which have a function applied to them - I'm take a guess and say MySQL is performing two full table scans and using type all for the join. See this for an explanation of the EXPLAIN output.
DATE_FORMAT(c.date_call_start,'%Y-%m-%d') = DATE_FORMAT(o.created,'%Y-%m-%d')
You'll most likely want to create a separate field in each table that contains just the result of the DATE_FORMAT call. Then create an index for each of these new fields. Then join on these new indexed fields. MySQL should like that much better.
Presumably you want to count the calls and orders for each date. However, that is not what your query does, because it creates a cartesian product for all orders on a given date.
Instead, summarize the data first by date and then combine the results. This may be what you want:
select c.date, calls, orders
from (select DATE_FORMAT(c.date_call_start, '%Y-%m-%d') as date, count(*) as calls
from tbl_calls c
WHERE c.campaign_id = 1 and
DATE_FORMAT(c.date_call_start, '%Y-%m-%d') = '2013-12-09'
group by DATE_FORMAT(c.date_call_start, '%Y-%m-%d')
) c left outer join
(select DATE_FORMAT(o.created,'%Y-%m-%d') as date, count(*) as orders
from tbl_orders o
group by DATE_FORMAT(o.created, '%Y-%m-%d')
) o
on c.date = o.date;
If #Barmar 's suggestion does not work, then you may need to split the fields into DATE and TIME.
A different direction is to make two temp tables (giving you three queries:
CREATE TEMPORARY TABLE `tbl_calls_temp` SELECT * FROM tbl_calls c WHERE DATE(c.date_call_start) = '2013-12-09' AND c.campaign_id = 1
Then do the same restricting for the tbl_orders TABLE
CREATE TEMPORARY TABLE `tbl_orders_temp` SELECT * FROM tbl_orders o WHERE DATE(o.created) = '2013-12-09'
Finally query against the two temporary tables. Depending on how much data you get, you may want to add indexes to the temporary tables... but in all likelihood you are facing a full-join
SELECT
DATE_FORMAT(c.date_call_start,'%Y-%m-%d') as date,
COUNT(c.id) as calls,
COUNT(o.id) as orders
FROM tbl_calls_temp c
LEFT OUTER JOIN tbl_orders_temp o
ON DATE_FORMAT(c.date_call_start,'%Y-%m-%d') = DATE_FORMAT(o.created,'%Y-%m-%d')
GROUP BY DATE_FORMAT(c.date_call_start,'%Y-%m-%d')
And that should be much faster... assuming you have any indexes in your initial tables that can be queried.
Related
I am trying to find a way to add a country code to a database call record based on a phone number column. I have a table with countries and their dialling codes called countries. I can query all records and add the country code after but I need to be able to filter and paginate the results.
I am working with a system I don't have much control over so adding new columns to tables or rewriting large blocks of code isn't really an option. This is what I have to work with.
Countries Table.
id
name
dialling_code
1
Ireland
353
2
America
1
Call Record table.
id
startdatetime
enddatetime
route_id
phonenumber
duration_seconds
1
2014-12-18 18:51:12
2014-12-18 18:52:12
23
3538700000
60
2
2014-12-18 17:41:02
2014-12-18 17:43:02
43
18700000
120
Routes table.
id
number
enabled
23
1234567890
1
43
0987654321
1
I need to get sum values of duration, total unique phone numbers all grouped by route_id, route_number but now we need to group these results by country_id so we can group callers by country. I use the mysql query below to get sum values of duration, total unique phone numbers all grouped by route_id, route_number. This query was written by another developer a long time ago.
SELECT
phone_number,
route_number,
COUNT(callrecord_id) AS total_calls,
SUM(duration_sec) AS total_duration,
callrecord_join.route_id
FROM routes
RIGHT JOIN (
SELECT
DATE(a.startdatetime) AS call_date,
a.id AS callrecord_id,
a.route_id AS route_id,
a.phonenumber AS phone_number,
a.duration_seconds as duration_sec,
b.inboundnumber AS route_number,
FROM callrecord AS a
INNER JOIN routes AS b ON a.route_id = b.id
WHERE DATE_FORMAT(a.startdatetime, '%Y-%m-%d') >= '2014-12-18'
AND DATE_FORMAT(a.startdatetime, '%Y-%m-%d') <= '2014-12-18'
AND b.isenabled = 1
) AS callrecord_join ON routes.id = callrecord_join.route_id
GROUP BY route_id, route_number
LIMIT 10 offset 0;
I have everything up to adding a country_id in the right join table so I can group by the country_id.
I know I could loop through each country using php and get the results using a where clause, something like the below but I cannot paginate these results or filter them easily.
WHERE LEFT(a.phonenumber, strlen($dialling_code)) = $dialling_code
How can I use the countries table to add a column to the join table query with the country id so I can group by route_id, route_number and country_id? Something like the table below.
id
startdatetime
enddatetime
route_id
phonenumber
duration_seconds
country_id
1
2014-12-18 18:51:12
2014-12-18 18:52:12
23
3538700000
60
1
2
2014-12-18 17:41:02
2014-12-18 17:43:02
43
18700000
120
2
The RIGHT JOIN from routes to callrecord_join serves no purpose, as you already have the INNER JOIN between routes and callrecord in the sub-query, which is on the righthand side of the join.
You can use the join you have described -
JOIN countries c ON LEFT(a.phonenumber, LENGTH(c.dialling_code)) = c.dialling_code
but it will give the same result as:
JOIN countries c ON a.phonenumber LIKE CONCAT(c.dialling_code, '%')
which should be slightly less expensive.
You should test the join to countries to make sure none of your numbers in callrecord join to multiple countries. Some international dialling codes are ambiguous, so it depends on which list of dialling codes you are using.
SELECT a.*, COUNT(*), GROUP_CONCAT(c.dialling_code)
FROM callrecord a
JOIN country c ON a.phonenumber LIKE CONCAT(c.dialling_code, '%')
GROUP BY a.id
HAVING COUNT(*) > 1;
Obviously, you will need to batch the above query if your dataset is very large.
I hope I am not grossly over-simplifying things, but from what I understand of your question the query is just:
SELECT
r.id AS route_id,
r.number AS route_number,
c.name AS country_name,
SUM(a.duration_seconds) AS total_duration,
COUNT(a.id) AS total_calls,
COUNT(DISTINCT a.phonenumber) AS unique_numbers
FROM callrecord AS a
JOIN routes AS r ON a.route_id = r.id
JOIN countries c ON a.phonenumber LIKE CONCAT(c.dialling_code, '%')
WHERE a.startdatetime >= '2014-12-18'
AND a.startdatetime < '2014-12-19'
AND r.isenabled = 1
GROUP BY r.id, r.number, c.name
LIMIT 10 offset 0;
Please note the removal of DATE_FORMAT() from the startdatetime to make these criteria sargable, assuming a suitable index is available.
I want to Retrieve customer names, total orders (how many time they order the products) and the total amount they're spent in the lifetime. Run a single query WITHOUT Join, group by, having operators. Show only customers who have at least one order.
Here is my database
Customer- CustomerID| CustomerName SalesOrder- SalesOrderID | CustomerID | SaleTotal
100000 | John 1001 | 100000 | 2000
200000 | Jane 1002 | 100000 | 3000
300000 | Sean 1003 | 200000 | 5000
When I query
SELECT CustomerName,count(*) AS Total_Orders,sum(SaleTotal) AS SaleTotal
FROM Customer C,SalesOrderHeader SH WHERE C.CustomerID=SH.CustomerID;
It show only one row.
The answer that I want is
CustomerName | Total_Orders | SaleTotal
John 2 5000
Jane 1 5000
I just new on mysql.
So does anyone here know how to do this?
If you are to do this without joins and group by, then the simplest approach is to use correlated subqueries:
select *
from (
select
c.customerName,
(
select count(*)
from salesOrder so
where so.customerID = c.customerID
) totalOrders,
(
select sum(salesTotal)
from salesOrder so
where so.customerID = c.customerID
) saleTotal
from customer c
) t
where totalOrders > 0
Note that this query is clearly suboptimal - because it scans the salesOrder table twice, while a single scan would suffice. A better way to write this would be:
select c.customerName, count(*) totalOrders, sum(salesTotal) saleTotal
from customer c
inner join saleOrder so on so.customerID = c.customerID
group by c.customerID, c.customerName
There is no need for a having clause here - the inner join filters out customers that have no order already.
Use aggregation . . . and proper join syntax:
SELECT CustomerName, COUNT(*) AS Total_Orders, SUM(SaleTotal) AS SaleTotal
FROM Customer C JOIN
SalesOrderHeader SH
ON C.CustomerID = SH.CustomerID
GROUP BY CustomerName;
Your query would fail in almost any database -- including newer versions of MySQL. You have mixed aggregated columns and unaggregated columns in the SELECT. The unaggregated ones should be in a GROUP BY.
Never use commas in the FROM clause. Always use proper, explicit, standard, readable JOIN syntax.
You have to use below query. You cannot achieve it without join and group by
SELECT CustomerName,count(*) AS Total_Orders,sum(SaleTotal) AS SaleTotal
FROM Customer C,SalesOrderHeader SH WHERE C.CustomerID=SH.CustomerID
group by;
I want to get the number of tickets sold per payment method for each event. and i've the follow query:
SELECT count(distinct(a.performance_id)) as EventQuantity,
sum(a.admission_count) as TicketQuantity
FROM vw_PrecioTipoZona_Paid as a
WHERE 1 = 1
AND a.performance_id ='DED63133-A099-4949-AA57-13BBE9462BAF'
GROUP BY a.performance_id
and I get this result, which is OK:
EventQuantity TicketQuantity
1 203
But when join the table with other, the result wich is sum, in this case a.admission_count is multiplied by the number of records in the other table.
The query who has problem is this:
SELECT a.performance_id,
count(distinct(a.performance_id)) as EventQuantity,
sum(a.admission_count) as TicketQuantity,
b.payment_method as PaymentMethod
FROM vw_PrecioTipoZona_Paid as a inner join vw_Payment_UserByPerformance as b
on a.performance_id = b.performance_id
WHERE
1 = 1
and a.performance_id ='DED63133-A099-4949-AA57-13BBE9462BAF'
group by a.performance_id, b.payment_method
With this query i'm getting this result:
EventQuantity TicketQuantity PaymentMethod
1 10353 Cash
1 5887 Card
1 1624 MasterCardECommerce
1 812 VisaEcommece
And this result is wron, the result should be:
EventQuantity TicketQuantity PaymentMethod
1 111 Cash
1 63 Card
1 17 MasterCardECommerce
1 8 VisaEcommece
The vw_Payment_UserByPerformance view structure is the follow:
performance_id user_role_id userrole_name userrole_group date_transaction user_id user_name owner_user_id owner_user_name amount_adm_net amount_req_net amount_charge_charge amount_total amount_net chargeTransaction tax payment_method
And the vw_PrecioTipoZona_Paid view structure is the follow:
performance_id performance_name performance_start_date date_transaction user_role_id userrole_name userrole_group user_id user_name price_type price_zone price_zone_priority admission_count NET charge1 charge2 charge3 charge4 charge5 GROSS
Do I have to make subquery? Where is the problem here?
MySQl allows you to incorrectly use group by. You should never use the technique you used in this query.
SELECT a.performance_id,
count(distinct(a.performance_id)) as EventQuantity,
sum(a.admission_count) as TicketQuantity,
b.payment_method as PaymentMethod
FROM vw_PrecioTipoZona_Paid as a inner join vw_Payment_UserByPerformance as b
on a.performance_id = b.performance_id
WHERE
a.performance_id ='DED63133-A099-4949-AA57-13BBE9462BAF'
group by a.performance_id, b.payment_method
When you use group by the only way to guarantee correct results is to group by all the non-aggregated fields. All other databases make this part of the syntax and thus do not have this problem.
If this still does not give the correct results, then there is a problem with the specifics of what you intended vice what you wrote. We would need to see the business requirement, that table structure, the sample data in the tables and teh sample results in order to help you find the correct query.
Looking at your additional details added while I was writing this, I think you need to use a derived table.
SELECT a.performance_id,
count(a.performance_id) as EventQuantity,
a.admission_count as TicketQuantity,
b.payment_method as PaymentMethod
FROM (select performance_id, sum(admission_count) as Admissioncount vw_PrecioTipoZona_Paid
WHERE a.performance_id ='DED63133-A099-4949-AA57-13BBE9462BAF'
group by performance_id )as a
inner join vw_Payment_UserByPerformance as b
on a.performance_id = b.performance_id
group by a.performance_id, b.payment_method
You have it in your question already:
I want to get the number of tickets sold per payment method and i've
the follow query
So you're basically looking at a query like this:
SELECT payment_method, COUNT(*) FROM x GROUP BY payment_method;
You seem to be doing something differently, the results could like like so if your group on the payment_method column as well:
SELECT
a.performance_id AS performanceId,
b.payment_method AS paymentMethod,
COUNT(*) numPayments
FROM
vw_PrecioTipoZona_Paid as a
INNER JOIN
vw_Payment_UserByPerformance AS b ON (a.performance_id = b.performance_id)
WHERE
a.performance_id ='DED63133-A099-4949-AA57-13BBE9462BAF'
GROUP BY
a.performance_id,
b.payment_method
First, that doesn't make sense together:
SELECT a.performance_id,
count(distinct(a.performance_id)) as EventQuantity,
...
GROUP BY a.performance_id
You're grouping the stuff having the same performance_id, and then you're asking to show you how many DISTINCT performance_ids are in the group.
Guess what the answer always be?
Second, SQL is a declarative language, so declare what you want first.
If you can't express it in SQL adequately (which is ok) - then do it in words, and please update your question with it.
Guessing on the lack of inputs is rarely productive.
I am stuck with the following requirement and I am finding it difficult to crack the query for it.
Consider a table customer with the following fields
id signup_date first_payment_date
10 2015-03-20 null
11 2015-03-20 null
12 2015-03-20 null
13 2015-03-20 null
14 2015-05-23 null
15 2015-05-23 null
Consider another table transaction_history
id product_name
10 vod trial
10 vod trial
11 vod trial
12 vod trial
12 vod
13 vod trial
14 vod trial
15 vod trial
15 vod trial
I need to pick the idfrom customer table and look up in transaction_history table based on the signup_date and first_payment_date is null.
Now I need to check if this id is present in transaction_history and check if he has at least 1 entry with product_name = "vod trial". If he has then he is a row in the result I want.
At the end I need to calculate the total number of id's from transaction_history who has at least one row where product_name="vod_trial" and this should be on a date basis mentioned in signup_date in customer table.
I wrote a query in the following manner:
SELECT
ts.guid,
cs.signup_date,
(SELECT
COUNT(ts2.guid)
FROM
transaction_history ts2
WHERE
cs.guid = ts2.guid
AND ts2.product_name = "vod trial"
HAVING COUNT(ts2.guid) = 1) AS count_ts_guid
FROM
customer AS cs,
transaction_history AS ts
WHERE
cs.guid = ts.guid
AND cs.first_payment_date IS NULL;
But in the above query I am not able to calculate the total count signup_datewise.
Would be great if someone could help me out.
Sample result:
date new trials
2015-03-20 2
2015-05-23 1
I am not sure I fully understand. You want customers without first_payment_date that have a trial entry in the transaction table?
select *
from customer
where first_payment_date is null
and id in (select id from transaction_history where product_name = 'vod trial');
Okay, from your last comment it seems, you want customers that have no trial entry in the transaction table, too. And you want to display them with their trial transaction count. So:
select signup_date,
(
select count(*)
from transaction_history th
where th.product_name = 'vod trial'
and th.id = c.id
)
from customer c
where first_payment_date is null;
If you even want to group by date, then aggregate:
select signup_date,
sum((
select count(*)
from transaction_history th
where th.product_name = 'vod trial'
and th.id = c.id
))
from customer c
where first_payment_date is null
group by signup_date;
Next try: Join all customers and transactions, such as to only get customers present in the transactions table. Then aggregate.
select c.signup_date, count(*)
from customer c
join transaction_history th on th.id = c.id and th.product_name = 'vod trial'
where c.first_payment_date is null
group by c.signup_date;
Or do you want this:
select c.signup_date, count(case when th.product_name = 'vod trial' then 1 end)
from customer c
join transaction_history th on th.id = c.id
where c.first_payment_date is null
group by c.signup_date;
I'd better make this a separate answer. You want to find customers that have only one entry in transaction_history and that entry must be 'vod trial'. So read the transaction table, group by customer id and count. Check your criteria with HAVING. Then join the found IDs with the customer table and group by date.
select c.signup_date, count(*)
from customer c
join
(
select id
from transaction_history
group by id
having count(*) = 1
and min(product_name) = 'vod trial'
) t on t.id = c.id
group by c.signup_date;
I am trying to list out all task with the count of finished/completed task (In submissions). The problem is that I also want to show all task that no users have finished. This query does not list out count = 0 (Null). Is there any way to do this?
Wanted result:
Date | title | completed
2014-05-20 | Case 1 | 45
2014-05-24 | Case 10 | 11
2014-05-20 | Case 2 | 0
I have tried so far:
Select date, title, count(*) as completed
from users u, submissions s, task t
where u.userPK = s.user
and s.task= t.taskPK
group by taskPK
order by completed desc;
You need to use an OUTER JOIN to get your desired results. However, considering the previous answer didn't suffice, I would also guess you don't want to GROUP BY the taskPK field, but rather by the date and title fields.
Perhaps this is what you're looking for:
SELECT t.date, t.title, count(*) cnt
FROM task t
LEFT JOIN submissions s ON t.task = s.taskPK
GROUP BY t.date, t.title
ORDER BY cnt DESC
I also removed the user table as I'm not sure how it affects the results. If you need it back, just add an additional join.
I think you should be able to achive this using a LEFT JOIN:
SELECT date, title, COUNT(u.userPK) completed FROM task t
LEFT JOIN submissions s ON s.task = t.taskPK
LEFT JOIN users u ON s.user = u.userPK
GROUP BY t.taskPK
ORDER BY completed;