How to get data based on row count in MySQL? - mysql

I am working on a project where I have to fetch data from database if the row count is greater than zero then show it otherwise don't. But my query returning all rows.
This is My Query
SELECT
d.id,
d.district,
(
SELECT
COUNT(a.district_id)
FROM
ambulance AS a
WHERE
a.district_id = d.id
) AS total
FROM
district d
ORDER BY
total
DESC
That is okay, but I added a WHERE clause with my query witch is,
WHERE total > 0
But I am having a sql error Unknown column 'total' in 'where clause'
Now my question is, how can I achieve a result with WHERE total > 0, do I have to type something else in the place of total? What is the proper way to add this WHERE clause in my query.

MySQL extends the use of the HAVING clause, so it can be used in non-aggregation queries. This allows you to use an alias:
SELECT d.id, d.district,
(SELECT COUNT(*)
FROM ambulance a
WHERE a.district_id = d.id
) AS total
FROM district d
HAVING total > 0
ORDER BY total DESC;
This logic would more colloquially be written using an inner join:
select d.id, d.district, count(*) as total
from district d join
ambulance a
on a.district_id = d.id
group by d.id, d.district
order by total desc;
The join requires that there be at least one match.

Related

Conditioning a column that's being selected by subquery

I have a following query
select distinct `orders`.`id`, `order_status`,
(SELECT `setting_id` FROM `settings` WHERE `settings`.`order_id` = `orders`.`id`) AS `setting_id`,
from `orders` order by orders.id desc limit 100 OFFSET 0
which works just fine, the column "setting_id" is being fetched as it should but when I add another WHERE
and `setting_id` = 2
it cannot find it and outputs Unknown column 'setting_id' in 'where clause'. Why?
I would change your query to a JOIN vs selecting column. Slower as it has to perform the column select every time. Now, if you only want for a setting_id = 2, just add that the the JOIN portion of the condition
select distinct
o.id,
o.order_status,
s.setting_id
from
orders o
join settings s
on o.id = s.order_id
AND s.setting_id = 2
order by
orders.id desc
limit
100 OFFSET 0
(it was also failing because you had an extra comma after your select setting column).
You could also have reversed the join by starting with your setting table so you were only concerned with those with status of 2, then finding the orders. I would ensure that your setting table had an index on ( setting_id, order_id ) to optimize the query.
select distinct
s.order_id id,
o.order_status,
s.setting_id
from
settings s
JOIN orders o
on s.order_id = o.id
where
s.setting_id = 2
order by
s.order_id desc
limit
100 OFFSET 0
With proper index as suggested, the above should be lightning fast directly from the index
Another consideration for an apparent large table, is to limit the days-back you query orders to limit your 100. Go back 1 month? 2? 15 days? How large is your orders table to make it drag for 10 seconds. That may be a better choice for you.

Group by does not provide accurate grouping

Attempting to create outputs that match the following screenshot:
When I attempt the following query:
SELECT t.amount, d.DOMAIN_NAME,td.month_number
FROM transaction t
JOIN transaction_date td ON t.trans_date_key = td.trans_date_key
JOIN domain d ON t.domain_key = d.domain_key
WHERE td.month_number =7
ORDER BY amount DESC;
I get the output of:
When I implement this query:
SELECT t.amount, d.DOMAIN_NAME,td.month_number
FROM transaction t
JOIN transaction_date td ON t.trans_date_key = td.trans_date_key
JOIN domain d ON t.domain_key = d.domain_key
WHERE td.month_number =7
GROUP BY domain_name
ORDER BY amount DESC;
I get the output of:
Why is my grouping only performing accurately on a few of the domain names, but not others?
You are not using GROUP BY correctly. You would need to use an aggregate function to sum the amounts. On the other hand, all non-aggregated columns should be listed in the GROUP BY clause.
Consider:
SELECT SUM(t.amount) total_amount, d.domain_name, td.month_number
FROM transaction t
INNER JOIN transaction_date td ON t.trans_date_key = td.trans_date_key
INNER JOIN domain d ON t.domain_key = d.domain_key
WHERE td.month_number = 7
GROUP BY d.domain_name, td.month_number
ORDER BY total_amount DESC;
What happens with the way you used GROUP BY is that MySQL actually picks a random record out of those that have the same domain_name. On most other RDBMS (and in non-ancient versions of MySQL), this would have generated a syntax error.

mysql avg function not returning all records

I am using following query
select
*,
dealer.id As dealerID,
services.id as serviceID
from services
LEFT JOIN dealer
on services.dealer=dealer.id
LEFT JOIN reviews
ON reviews.dealer_id=dealer.id
where services.brand_id = '9' and
services.model_id='107' and
services.petrol > 0
ORDER BY services.total asc ,
AVG(reviews.rating) desc
I have 6 records and it should display 6 records instead its displaying only 1. When i remove AVG(reviews.rating) desc. It display all records.
mysql tables are
services
dealer
brand_id
model_id
petrol
id
total
dealer
id
name
reviews
id
dealer_id
rating
I am not sure where i am doing mistake. If some can help.
avg() is an aggregation function. That is, it takes data from multiple rows and summarizes it.
Without a group by, the query is an aggregation query over all the data. Such a query always returns exactly one row.
Most databases would return an error when you use select *, use an aggregation function, and have no group by. MySQL has a (mis)feature where this syntax is allowed (although on the newest versions, the default settings disallow this).
I'm not sure what you are trying to do, but avg() doesn't make sense in this context. Perhaps this does what you want:
ORDER BY services.total asc, reviews.rating desc
As already mentioned AVG() is aggregate ftn, so I have changed the desc of your order by to include to select the average values.
For future reference:
Providing snippets of raw data also helps. Creating an sql fiddle helps even more
select
*,
dealer.id As dealerID,
services.id as serviceID
from services
LEFT JOIN dealer
on services.dealer=dealer.id
LEFT JOIN reviews
ON reviews.dealer_id=dealer.id
where services.brand_id = '9' and
services.model_id='107' and
services.petrol > 0
ORDER BY services.total asc ,
(SELECT AVG(r2.rating) FROM reviews r2 RIGHT JOIN ON r2.dealer_id=dealer.id) desc
You might try:
SELECT
*,
AVG(c.rating) AS `avg__rating`,
b.id AS dealerID,
a.id AS serviceID
FROM services a
LEFT JOIN dealer b
on a.dealer = b.id
LEFT JOIN reviews c
ON c.dealer_id = b.id
WHERE a.brand_id = '9' and
a.model_id='107' and
a.petrol > 0
GROUP BY a.dealer, a.brand_id, a.model_id, a.petrol, a.id, a.total
ORDER BY a.total asc,
AVG(c.rating) desc
This adds a GROUP BY on the columns in your services table so you will get one row per services/dealer.

Is it possible to find COUNT function and then to show only certain values that are higher than 10

for example:
SELECT doctor.name
, doctor.surname
, COUNT(checkup.doctor)
FROM doctor
, checkup
WHERE doctor.id = checkup.doctor
GROUP
BY doctor.name
ORDER
BY checkup.doctor
this gives me list of all doctor that had checkup with patients, but i want to show only doctor with number of checkup more than 10 what to add to my sql
You want to limit your result rows according to an aggregation result. You'd do that in the HAVING clause.
As to your query: What is name? A unique name for every doctor? Otherwise better group by the ID - only then is this query valid according to the SQL standard. Then you are using a join syntax that we stopped using in the 1990s. Please use proper ANSI joins instead.
I prefer aggregating before joining, but that's just personal preference:
select d.name, d.surname, c.checkups
from doctor
join
(
select doctor as doctor_id, count(*) as checkups
from checkup
group by doctor
having count(*) > 10
) c on c.doctor_id = d.id
order by d.id;
You could just as well use
select d.name, d.surname, count(*) as checkups
from doctor d
join checkup c on c.doctor = d.id
group by d.id
having count(*) > 10
order by d.id;
TRY THIS: You can use HAVING clause to filter out the aggregation result and try to avoid outdated joins. Surname doesn't meaning there in GROUP BY so you use MAX to pick in select and remove from GROUP BY or leave as it is:
SELECT doctor.name
, doctor.surname
, COUNT(checkup.doctor)
FROM doctor
INNER JOIN checkup ON doctor.id = checkup.doctor
GROUP BY doctor.name, doctor.surname
HAVING COUNT(checkup.doctor) > 10
Do the fact you are using aggregation function as count If you want only the result with more then 10 COUNT(checkup.doctor)
you could filter the result using having
SELECT doctor.name,doctor.surname,COUNT(checkup.doctor)
FROM doctor
INNER JOIN checkup doctor.id=checkup.doctor
GROUP BY doctor.name
Having COUNT(checkup.doctor) > 10
ORDER BY COUNT(checkup.doctor)
the where clause work on the selecting row so don't know the result of an aggregated result .. for this SQL use the having clause that filter the result of a select
I think this will work for you:
SELECT
doctor.name, doctor.surname, checkup_stat.checkup_count
FROM
(SELECT doctor, COUNT(1) AS checkup_count FROM checkup GROUP BY doctor ORDER BY doctor) AS checkup_stat
RIGHT JOIN
doctor
ON
doctor.id = checkup_stat.doctor
WHERE
checkup_stat.checkup_count > 10;
Firstly, use a subquery to get the checkup count for every doctor.
Secondly, select the records from the subquery result where checkup count > 10.
Then, just join them together.

Sql query to join two table for the final output

Below are the two tables, customer and department. I am struggling very hard to get the output.
I want to write the query which shows only the department name which have maximum number of employees.
Answer should be like this ...
Please help me to write the query.
I would suggest using a sub-query, and then selecting from that query. With the outer select, you order by the number of employees and then limit it to 1. This will give you the top department, but also has the flexibility to be modified to give you a list of the x-number of top departments.
SELECT Dep_Name FROM (
SELECT
d.Dep_Name, COUNT(*) AS `count`
FROM
Departments d
JOIN Employees e ON e.Dep_id = d.Dep_id
GROUP BY
d.Dep_id
) AS q
ORDER BY `count` DESC
LIMIT 1
UPDATE
Per a comment by #Dems, you can actually handle this without a sub-query:
SELECT
d.Dep_Name
FROM
Departments d
JOIN Employees e ON e.Dep_id = d.Dep_id
GROUP BY
d.Dep_id
ORDER BY
COUNT(*) DESC
LIMIT 1
SELECT *
FROM
(
SELECT
d.dep_id,
d.dep_name,
count(c.cus_id) cusCount
FROM
cus c,
dep d
WHERE
c.dep_id = d.dep_id
GROUP BY
d.dep_id,d.dep_name
ORDER BY
cusCount desc)
WHERE
ROWNUM = 1;
I created cus & dep tables in Oracle 10g database and tested my code successfully.
What database are you using, and could you post you code.
There error message shows that your "Order by" clause is wrong.