Problems with MySQL COUNT() and joins - mysql

I need to get a count of which types of tickets have been ordered on each account, and only on accounts that have incurred a transaction. I'm doing this with the following query for each price:
SELECT tickets.order_id as order_id, count(tickets.id) as count
FROM tickets,transactions
WHERE price = $price
AND tickets.order_id = transactions.order_id
GROUP BY tickets.order_id
This gets the right set of results, but on orders where there are multiple transactions the number for count(tickets.id) is multiplied by that number. What query do I need to use to avoid this problem? Do I need to use a different kind of join?

use
count (distinct tickets.id)

Related

Specific where clause in Mysql query

So i have a mysql table with over 9 million records. They are call records. Each record represents 1 individual call. The columns are as follows:
CUSTOMER
RAW_SECS
TERM_TRUNK
CALL_DATE
There are others but these are the ones I will be using.
So I need to count the total number of calls for a certain week in a certain Term Trunk. I then need to sum up the number of seconds for those calls. Then I need to count the total number of calls that were below 7 seconds. I always do this in 2 queries and combine them but I was wondering if there were ways to do it in one? I'm new to mysql so i'm sure my syntax is horrific but here is what I do...
Query 1:
SELECT CUSTOMER, SUM(RAW_SECS), COUNT(*)
FROM Mytable
WHERE TERM_TRUNK IN ('Mytrunk1', 'Mytrunk2')
GROUP BY CUSTOMER;
Query 2:
SELECT CUSTOMER, COUNT(*)
FROM Mytable2
WHERE TERM_TRUNK IN ('Mytrunk1', 'Mytrunk2') AND RAW_SECS < 7
GROUP BY CUSTOMER;
Is there any way to combine these two queries into one? Or maybe just a better way of doing it? I appreciate all the help!
There are 2 ways of achieving the expected outcome in a single query:
conditional counting: use a case expression or if() function within the count() (or sum()) to count only specific records
use self join: left join the table on itself using the id field of the table and in the join condition filter the alias on the right hand side of the join on calls shorter than 7 seconds
The advantage of the 2nd approach is that you may be able to use indexes to speed it up, while the conditional counting cannot use indexes.
SELECT m1.CUSTOMER, SUM(m1.RAW_SECS), COUNT(m1.customer), count(m2.customer)
FROM Mytable m1
LEFT JOIN Mytable m2 ON m1.id=m2.id and m2.raw_secs<7
WHERE TERM_TRUNK IN ('Mytrunk1', 'Mytrunk2')
GROUP BY CUSTOMER;

How to remove a subquery from the FROM field in MySQL

I'm new to MySQL and databases and I've seen in many places that it is not considered a good programming practice to use subqueries in the FROM field of SELECT in MySQL, like this:
select userid, avg(num_pages)
from (select userid , pageid , regid , count(regid) as num_pages
from reg_pag
where ativa = 1
group by userid, pageid) as from_query
group by userid;
which calculates the average number of registers per page that the users have.
The reg_page table looks like this:
Questions:
How to change the query so that it doesn't use the subquery in the FROM field?
And is there a general way to "flatten" queries like this?
The average number of registers per page per user can also be calculated as number of registers per user divided by number of pages per user. Use count distinct to count only distinct psgeids per user:
select userid, count(regid) / count(distinct pageid) as avg_regs
from reg_pag
where ativa=1
group by userid;
There is no general way of flattening such queries. It may not even be possible to flatten some of them, otherwise there would be little point in having this feature in the first place. Do not get scared of using subqueries in the from clause, in some occasions they may be even more effective, than a flattened query. But this is vendor and even version specific.
One way is to use count(distinct):
select userid, count(*) / count(distinct pageid)
from reg_pag
where ativa = 1
group by userid;

SUM of column that has been GROUPED BY

I am using the following query:
SELECT mgap_growth
FROM mgap_orders
WHERE account_manager_id = '159795'
GROUP BY mgap_ska_report_category
mgap_growth is a column with identical amounts that differ only per mgap_ska_report_category, which is the reason for the grouping. Now hat I have normalized the individual amounts per category, how can I use SUM to tally their total?
Here is a screenshot of the data:
I only need the SUM of the growth amounts per category, not of all of the mgap_growth records, but Im unsure as to how to SUM after the grouping.
Thanks!
EDIT FOR ADDITIONAL QUERY:
Let me throw another issue into the mix: we know I need to SUM only once per category, but what if I needed to GROUP BY CUSTOMER? I just found out that there are multiple customers in the data, each is duplicated per growth record, but differ by category. I really need to use two groupings, one for category to single out and SUM the growth amount and then another the single out the customer.
Here is an image describing the data:
If I understand you correctly, you need to sum the results from the subquery.
SELECT SUM(mgap_growth) AS total_mgap_growth
FROM (SELECT mgap_growth
from mgap_orders
WHERE account_manager_id = '159795'
GROUP BY mgap_ska_report_category) AS x
This should should show the total growth per category for that particular account manager:
SELECT sum(mgap_growth) AS Growth, mgap_ska_report_category as Category
FROM mgap_orders
WHERE account_manager_id = '159795'
GROUP BY mgap_ska_report_category
Rather than thinking of doing the SUM after the grouping, you can do the two together in the one statement. You were 99% of the way there with what you had already.
To answer your additional question in the comment, you can add another column to group by. The order that you list them in the group by section is the important part. The overall grouping comes first. So assuming 'Customer' is the customer column name you would do this:
SELECT mgap_ska_report_category as Category, Customer, sum(mgap_growth) AS Growth
FROM mgap_orders
WHERE account_manager_id = '159795'
GROUP BY mgap_ska_report_category, customer
WITH ROLLUP
Note that changing the SELECT columns in the top line was just for aesthetics, you can put them in any order and will get the same data, but this will be the easiest to read.
This shows the growth per customer by category for that particular account manager.
Edited again to add WITH ROLLUP. This will give you the totals per category as well. Try it with and without the WITH ROLLUP to see the how it changes things.

Mysql how to Count rows and group by value?

I am trying to get this simple query to work. I like to know how many times a certain vouchercode has been used and what the total discount value is per used voucher code.
The database table has fields discount_value, discount_data. The discount_data holds the vouchercode and discount_value the sum of discount per purchase id.
SELECT discount_data,
COUNT(*)
FROM wp_wpsc_purchase_logs
GROUP BY
discount_data
seems to work to get amount of voucher code used.
But how do i get the total discount_value per used voucher code?
regards
Is the SUM() function what you are looking for?
SELECT discount_data, COUNT(*), SUM(discount_value)
FROM wp_wpsc_purchase_logs
GROUP BY discount_data
Sum() function may be
SELECT discount_data, SUM(discount_value), count(*)
FROM wp_wpsc_purchase_logs
GROUP BY discount_data

SQL with condition on calculated value

I have a table with products, their amount and their price. I need to select all entries where the average price per article is between a range.
My query so far:
SELECT productid,AVG(SUM(price)/SUM(amount)) AS avg
FROM stock WHERE avg>=$from AND avg<=$to GROUP BY productid
If do this, it tells me avg doesn't exist.
Also I obviously need to group by because the sum and average need to be per wine
You need to put it in the HAVING clause. You cannot filter by the result of aggregates using WHERE.
MySQL does allow you to reference column aliases in HAVING. You might need
SELECT
productid,
AVG(price/amount) AS avg ,
/*SUM(price)/SUM(amount) AS avg (<--- Or perhaps this?)*/
FROM stock
GROUP BY productid
HAVING avg>=$from AND avg<=$to
But I'm not sure exactly what you are trying to do with AVG(SUM(price)/SUM(amount)) can you show some example data?