SQL Invalid group function - mysql

I want to select the columns with the max number of canceled trip. I know how to count the number of trips using the SQL statement, however, when I try to use the max function it gives the invalid group function error.
my SQL statement is as below:enter image description here
select t.ServiceNumber, t.RouteNumber, remark,
max(count(if(Cancelled =1 ,1,null))) as"Total Cancelled Trip"
from trip t inner join
route r
on r.ServiceNumber = t.ServiceNumber and
r.RouteNumber = t.RouteNumber
group by t.ServiceNumber, t.RouteNumber,remark;

If you want just one row with the maximum, you can do:
select t.ServiceNumber, t.RouteNumber, remark,
count(*) as "Total Cancelled Trip"
from trip t inner join
route r
on r.ServiceNumber = t.ServiceNumber and
r.RouteNumber = t.RouteNumber
where Cancelled = 1
group by t.ServiceNumber, t.RouteNumber, remark
order by count(*) desc
limit 1;
Getting all such rows in the event of ties is tricky in MySQL. Perhaps the easiest way uses variables:
select tr.*
from (select t.ServiceNumber, t.RouteNumber, remark,
count(*) as "Total Cancelled Trip",
(#maxtct = greatest(#maxtct, count(*))
from trip t inner join
route r
on r.ServiceNumber = t.ServiceNumber and
r.RouteNumber = t.RouteNumber cross join
(select #maxtct := 0) params
where Cancelled = 1
group by t.ServiceNumber, t.RouteNumber, remark
) tr
where `Total Cancelled Trip` = #maxtct;

Related

Calculating Checkout Conversion using MySQL

I am trying to calculate the checkout conversion by using the following query but I keep getting error:
SELECT
(SELECT seller.name, count(checkout.id) AS 'Completed_checkout'
FROM checkout
JOIN seller
ON seller.id = checkout.seller_id
WHERE checkout.completed = 1
GROUP BY seller.name)
/
(SELECT seller.name, count(checkout.id) AS 'Total_Checkouts'
FROM checkout
JOIN seller
ON seller.id = checkout.seller_id
GROUP BY seller.name) * 100
AS 'Checkout_Converstion';
The end goal after being able to calculate it is:
filter by seller with more than 15 checkouts total
order it by DESC
limit it by then 5 first records
Any ideas on how to make this work?
How about simplifying this?
SELECT s.name, AVG(co.completed) as completed_ratio
FROM checkout co JOIN
seller s
ON s.id = co.seller_id
GROUP BY s.name;
If you want the counts:
SELECT s.name, AVG(co.completed = 1) as completed_ratio,
SUM(c.completed = 1) as num_completed,
COUNT(*) as total
FROM checkout co JOIN
seller s
ON s.id = co.seller_id
GROUP BY s.name;
These make use of the fact that MySQL treats boolean expressions as numbers in a numeric context -- with "1" for true and "0" for false.
You can do it with 1 query:
SELECT s.name,
100 * AVG(c.completed) AS Checkout_Conversion
FROM checkout c JOIN seller s
ON s.id = c.seller_id
GROUP BY s.id, s.name
HAVING SUM(c.completed) > 15
ORDER BY Checkout_Conversion DESC
LIMIT 5

how to mysql group by date multiple left join

I have the following schema:
http://sqlfiddle.com/#!9/bd3a4/1
I would like to
group by date() and add where user_id = ?..
per day and count the results per day.
required result Day|TotalRequests|TotalOrders
Since you could have an order on Day 1, and a request on Day 8, you may have entries on one side but not the other. To qualifify your needs, I would do a UNION of all orders and requests individually by date. Then roll those values up. The inner Pre-Aggregate result query is where the WHERE clause per user would be applied. The pre-aggregate query also has a recSource column to indicate where the record originated from as 'O' from orders and 'R' from requests, so the roll-up knows which column to store the total count respectively.
select
preAgg.recDate,
SUM( case when preAgg.recSource = 'O' then preAgg.recCount else 0 end ) as OrderCount,
SUM( case when preAgg.recSource = 'R' then preAgg.recCount else 0 end ) as RequestCount
from
( select
date(o.created_at) recDate,
'O' as recSource,
count(*) as recCount
from
orders o
where
o.user_id = 3
group by
date(o.created_at)
UNION ALL
select
date(r.created_at) recDate,
'R' as recSource,
count(*) as recCount
from
requests r
where
r.user_id = 3
group by
date(r.created_at) ) preAgg
group by
preAgg.recDate
order by
preAgg.recDate
For query optimization, I would ensure your order and request table both have have an index on ( user_id, created_at ).
SQL Fiddle result
You can use the following query:
SELECT
DATE(o.created_at) AS Day
,COUNT(r.id) AS TotalRequests
,COUNT(o.id) AS TotalOrders
FROM orders o
LEFT JOIN
requests r ON
r.id = o.request_id
WHERE o.user_id = 3
GROUP BY DATE(r.created_at), DATE(o.created_at),o.user_id

Fetching a big number of records using JOIN

I have this query that I use in order to get records from several tables, using JOIN.
SELECT c.id AS contestant_id,
c.created_date,
c.name AS contestant_name,
counter.total AS score,
c.email
FROM submission AS s,
(SELECT ans.id AS ans_id, sub.contestant_id, count(sub.id) AS total
FROM submission AS sub
JOIN (SELECT id, is_true FROM answer) AS ans
WHERE sub.answer_id = ans.id
AND ans.is_true = 1
GROUP BY sub.contestant_id) AS counter
JOIN (SELECT id, name, email, type, created_date
FROM contestant
WHERE contest_type = 1
AND submission_status = 1) AS c
WHERE counter.contestant_id = c.id
GROUP BY c.id
ORDER BY c.created_date DESC
The problem is that each record in table contestant will have 30 record in the submission table. So, when I retrieve 1000 contestant or more, the server hangs.
Please try the following restructured query:
SELECT
c.id AS contestant_id,
c.created_date,
c.name AS contestant_name,
counter.total AS score,
c.email
FROM
(
SELECT
sub.contestant_id, count(sub.id) AS total
FROM
submission AS sub
JOIN answer AS ans
ON sub.answer_id = ans.id AND ans.is_true = 1
GROUP BY
sub.contestant_id
)
AS counter
JOIN contestant c
ON c.contest_type = 1 AND c.submission_status = 1 AND c.id = counter.contestant_id
WHERE
counter.contestant_id = c.id
GROUP BY
c.id
ORDER BY
c.created_date DESC
It is possible to change the query result row limit, or remove the limit entirely.
Go to "Edit -> Preferences... -> SQL Editor (tab)"
Locate the "Query Results" section and untick the "Limit Rows" checkbox
Click "OK"
Re-run your query
Exporting query results in MySQL Workbench beyond 1000 records

MySQL GROUP BY grouping by lowest field value

I'm trying to fetch the lowest price per day per hotel, I get multiple results.
I first try to fetch the lowest amount with the MIN() function, then inner join.
When i later try to group by outside the subquery, it just groups by the lowest id.
The SQL itself:
SELECT mt.id, mt.amount, mt.fk_hotel, mt.start_date
FROM price mt
INNER JOIN
(
SELECT price.id, MIN(price.amount) minAmount
FROM price
WHERE 1=1 AND price.start_date >= '2014-10-08' AND price.start_date <= '2014-10-10' AND price.active = 1 AND price.max_people = 2
GROUP BY id
) t
ON mt.id = t.id AND mt.amount = t.minAmount
ORDER BY mt.fk_hotel, mt.amount;
And the results looks like this:
http://jsfiddle.net/63mg3b2j/
I want to group by the start date and fk_hotel so that it groups by the lowest amount value, can anybody help me? Am I being clear?
Edit: I also need a field fk_room from the corresponding row, so i can inner join
Try this:
SELECT MIN(mt.amount) AS min_amount, mt.fk_hotel, mt.start_date
FROM price mt
WHERE
mt.active = 1 AND
mt.max_people = 2 AND
mt.start_date >= '2014-10-08' AND mt.start_date <= '2014-10-10'
GROUP BY mt.fk_hotel, mt.start_date
ORDER BY mt.fk_hotel, min_amount;
Well first of all get a table with minimum value in top row using ORDER BY and then GROUP BY for your required result
SELECT mt.id, mt.amount, mt.fk_hotel, mt.start_date
FROM
(SELECT id, amount, fk_hotel, start_date
FROM price
WHERE start_date >= '2014-10-08' AND start_date <= '2014-10-10'
AND active = 1 AND max_people = 2
ORDER BY amount DESC) AS mt
GROUP BY mt.id
Well I had to still go with a subquery, cause i needed some additional foreign key fields from the corresponding row to inner join some other stuff. It isn't a great solution, cause it fetches too much stuff, the rest is filtered out programmatically.
The most annoying thing here, when I try to use MIN() or MAX() function and get the appropriate fields to that row, it fetches the first results from the DB, which are incorrect and so i have to use a subquery to inner join to get the other fields, I can use grouping, but I had too many fields to group. Maybe I'm missing something. The amount of data doesn't grow in time, so I guess it works for me. So this is the final SQL i came up with, for future reference..
SELECT mt.*, roomtype.name roomname, hotel.name hotelname
FROM booking.price mt
INNER JOIN roomtype ON roomtype.id = mt.fk_roomtype
INNER JOIN hotel ON hotel.id = mt.fk_hotel
INNER JOIN(
SELECT price.id, MIN(price.amount) minAmount
FROM booking.price WHERE 1=1 AND price.start_date >= '2014-10-22' AND price.start_date <= '2014-10-31' AND price.max_people = 2 AND price.active = 1
GROUP BY id
) t
ON mt.id = t.id AND mt.amount = t.minAmount
ORDER BY mt.start_date, mt.amount

Better way to write MySQL sub-query

I have two tables in my MySQL database: allele and locus. I want to know for a given locus how many alleles there are and of those how many have the status Tentative. I currently have the following query with subquery:
SELECT COUNT(*) as alleleCount,
(SELECT COUNT(*)
FROM allele
INNER JOIN locus ON allele.LocusID = locus.PrimKey
WHERE Status = 'Tentative'
AND locus.ID = 762
) as newAlleleCount
FROM allele
INNER JOIN locus ON allele.LocusID = locus.PrimKey
WHERE locus.ID = 762
but I feel there must be a better way to write this query.
You can use SUM() using sum with condition will result in a boolean 1 or 0 so it will give you the count for your conditions
SELECT locus.ID,COUNT(*) `all_alleles_per_locus`,
SUM(Status = 'Tentative') `tentative_alleles_762`
FROM allele
INNER JOIN locus ON allele.LocusID = locus.PrimKey
GROUP BY locus.ID
One way would be to group the locus by its statuses and fetch each status's respective count; using the WITH ROLLUP modifier will add a NULL status at the end representing the total:
SELECT status, COUNT(*)
FROM allele JOIN locus ON locus.PrimKey = allele.LocusID
WHERE locus.ID = 762
GROUP BY status WITH ROLLUP
If you absolutely do not want a list of all statuses, you can instead GROUP BY status = 'Tentative' (optionally WITH ROLLUP if desired)—but it will not be sargable.