Understanding MySQL return - mysql

The MySQL query is:
SELECT orders.scientific_name AS 'Order',
families.scientific_name AS 'Family',
COUNT(*) AS 'Number of Birds'
FROM birds, bird_families AS families, bird_orders AS orders
WHERE birds.family_id = families.family_id
AND families.order_id = orders.order_id
AND orders.scientific_name = 'Pelecaniformes';
The Output is:
+----------------+-------------+-----------------+
| Order | Family | Number of Birds |
+----------------+-------------+-----------------+
| Pelecaniformes | Pelecanidae | 224 |
+----------------+-------------+-----------------++
But I have 5 Families in the DB. Why did it return only one?

You are using COUNT(*) which turns this into an aggregation query. Without a GROUP BY, this returns exactly one row.
I would recommend getting started by:
Removing the COUNT(*).
Replacing the commas with explicit JOIN syntax.
Use table aliases.
Don't use single quotes for column aliases.
Then work toward the query you really want to write. So, to get started:
SELECT o.scientific_name AS `Order`,
bf.scientific_name AS Family
FROM birds b JOIN
bird_families bf
ON b.family_id = bf.family_id JOIN
bird_orders bo
ON bf.order_id = o.order_id
WHERE o.scientific_name = 'Pelecaniformes';
At this point, you can probably add the COUNT(*) and GROUP BY o.scientific_name, bf.scientific_name.

If you have 5 Families in the orders.scientific_name = 'Pelecaniformes'
then you should use group by
SELECT orders.scientific_name AS 'Order',
families.scientific_name AS 'Family',
COUNT(*) AS 'Number of Birds'
FROM birds, bird_families AS families, bird_orders AS orders
WHERE birds.family_id = families.family_id
AND families.order_id = orders.order_id
AND orders.scientific_name = 'Pelecaniformes'
Group by orders.scientific_name , families.scientific;

Related

SQL Distinct based on different colum

I have problem to distinct values on column based on other column. The case study is:
Table: List
well | wbore | op|
------------------
wella|wbore_a|op_a|
wella|wbore_a|op_b|
wella|wbore_a|op_b|
wella|wbore_b|op_c|
wella|wbore_b|op_c|
wellb|wbore_g|op_t|
wellb|wbore_g|op_t|
wellb|wbore_h|op_k|
So, I want the output to be appear in different field/column like:
well | total_wbore | total_op
----------------------------
wella | 2 | 3
---------------------------
wellb | 2 | 2
the real study case come from different table but to simplify it I just assume this case happened in 1 table.
The sql query that I tried:
SELECT well.well_name, wellbore.wellbore_name, operation.operation_name, COUNT(*)
FROM well
INNER JOIN wellbore ON wellbore.well_uid = well.well_uid
INNER JOIN operation ON wellbore.well_uid = operation.well_uid
GROUP BY well.well_name,wellbore.wellbore_name
HAVING COUNT(*) > 1
But this query is to calculate the duplicate row which not meet the requirement. Anyone can help?
you need to use count distinct
SELECT
count(distinct wellbore.wellbore_name) as total_wbore
count(distinct operation.operation_name) as total_op
FROM well
INNER JOIN wellbore ON wellbore.well_uid = well.well_uid
INNER JOIN operation ON wellbore.well_uid = operation.well_uid
Final query:
SELECT
well.well_name,
COUNT(DISTINCT wellbore.wellbore_name) AS total_wbore,
COUNT(DISTINCT operation.operation_name) AS total_op
FROM well
INNER JOIN wellbore ON wellbore.well_uid = well.well_uid
INNER JOIN operation ON wellbore.well_uid = operation.well_uid
GROUP BY well.well_name

MySQL condition statement across multiple rows

I have a Profile table like this
|--------|-----------|
| People | Favorite |
|--------|-----------|
| A | Movie |
| B | Movie |
| B | Jogging |
|--------|-----------|
Q: How to retrieve the people whose favorite is movie but not jogging?
In this table, the result is only People A.
Although I came out with this
select People from Profile
where
People
in
(select People from Profile
where favorite='Movie')
and
People
not in
(select People from Profile
where favorite='Jogging')
But it seem like can be better, any suggestion or answer (without using join or union clause)?
https://www.db-fiddle.com/f/rboiDpxxbABCpjtduEz7uY/1
SELECT People
FROM `profile`
GROUP BY people
HAVING SUM('Movie' = favorite) > 0
AND SUM('Jogging' = favorite) = 0
There's lots of ways. While you can use a UNION, its rather messy and innefficient. MySQL doesn't have a MINUS clause which would give a fairly easy to understand query.
You could aggregate the data:
SELECT people
, MAX(IF(favorite='jogging', 1, 0)) as jogging
, MAX(IF(favorite='movie', 1, 0)) as movie
FROM profile
GROUP BY people
HAVING movie=1 AND jogging=0
Or use an outer join:
SELECT m.people
FROM profile m
LEFT JOIN
( SELECT j.people
FROM joggers j
WHERE j.favorite='jogging' ) joggers
ON m.people=joggers.people
WHERE joggers.people IS NULL
AND m.favorite='movies'
Using a NOT IN/NOT EXISTS gives clearer syntax but again would be very innefficient.
There are several query patterns that will return a result that satisfies the specification.
We can use NOT EXISTS with a correlated subquery:
SELECT p.people
FROM profile p
WHERE p.favorite = 'Movie'
AND NOT EXISTS ( SELECT 1
FROM profile q
WHERE q.favorite = 'Jogging'
AND q.people = p.people /* related to row in out query */
)
ORDER
BY p.people
An equivalent result can also be done with an anti-join pattern:
SELECT p.people
FROM profile p
LEFT
JOIN profile q
ON q.people = p.people
AND q.favorite = 'Jogging'
WHERE q.people IS NULL
AND p.favorite = 'Movie'
ORDER BY p.people
Another option is conditional aggregation. Without a guarantee about uniqueness, and some MySQL shorthand:
SELECT p.people
FROM profile p
GROUP
BY p.people
HAVING 1 = MAX(p.favorite='Movie')
AND 0 = MAX(p.favorite='Jogging')
A more portable more ANSI standard compliant syntax for the conditional aggregation:
SELECT p.people
FROM profile p
GROUP
BY p.people
HAVING 1 = MAX(CASE p.favorite WHEN 'Movie' THEN 1 ELSE 0 END)
AND 0 = MAX(CASE p.favorite WHEN Jogging' THEN 1 ELSE 0 END)
This is a common problem when you want to have multiple conditions with the same column. I have answered this here and there are other methods like intersect and subqueries.
SELECT people, GROUP_CONCAT(favorite) as fav
FROM profile
GROUP BY people
HAVING fav REGEXP 'Movie'
AND NOT fav REGEXP 'Jogging';
With group by people and checking the minimum and maximum values of favorite to be 'Movie':
select people from tablename
where favorite in ('Movie', 'Jogging')
group by people
having min(favorite) = 'Movie' and max(favorite) = 'Movie'

GROUP_CONCAT within CONCAT

Database hierarchical structure is as follow
Student Name List of fee Assigned to Each
Student
List of Scholarship Assigned to Each Fee
As structure, expected output is
Student Name-Fee->Scholarship1, Scholarship2
Karan-1.Annual Fee->Economic Scholarship,Incapable Scholarship,2.Monthly Fee
But what I am getting
Student Name-Fee->Scholarship1, Student Name-Fee->Scholarship2
Karan-1.Annual Fee->Economic Scholarship,1.Annual Fee->Incapable Scholarship,2.Monthly Fee
What is wrong in here ? Though I am nesting CONCAT, but not getting expected output
CONCAT(student.en_ttl,'-',GROUP_CONCAT(DISTINCT fee.id,'.',fee.en_ttl,
COALESCE(CONCAT('->',sch.en_ttl),''))) AS fee
SQL Fiddle
You basically need to two levels of GROUP BY. So, we will need to use a Derived Table here. First subquery will aggregate at the level of fee; and then the second level would aggregate those fee details at the level of student.
Also, in newer (and ANSI SQL compliant) versions of MySQL, you need to ensure that any non-aggregated column in the SELECT clause should be in the GROUP BY clause as well.
Query
SELECT
CONCAT(stud_ttl,'-',GROUP_CONCAT(CONCAT(fee_det, COALESCE(CONCAT('->',fee_sch), '')))) AS fee
FROM
(
SELECT student.ttl AS stud_ttl,
CONCAT(fee.id,'.',fee.ttl) AS fee_det,
Group_concat(DISTINCT sch.ttl) AS fee_sch
FROM inv_id
JOIN student
ON student.id = inv_id.std
JOIN inv_lst
ON inv_lst.ftm = inv_id.ftm
JOIN fee
ON fee.id = inv_lst.fee
JOIN sec_fee
ON sec_fee.fee = fee.id
AND sec_fee.cls = student.cls
AND sec_fee.sec = student.sec
LEFT JOIN std_sch
ON std_sch.std = student.id
LEFT JOIN sec_sch
ON sec_sch.sch = std_sch.sch
AND sec_sch.fee = fee.id
LEFT JOIN sch
ON sch.id = sec_sch.sch
GROUP BY student.ttl, fee_det, fee.ttl
) dt
GROUP BY stud_ttl;
Result
| fee |
| -------------------------------------------------------------------- |
| Karan-1.Annual->Economic Scholarship,Incapable Scholarship,2.Monthly |
View on DB Fiddle

MYSQL Multiple Group Concat

Hi I have a table called Engineers and a table called Post_Codes
When I use the following sql I get a list of engineers and the postcodes associated with them by using the Group Concat statement but I cannot figure out how to also include in another Group Concat (if indeed I need one) to also list in another field called Secondary_Post_Codes_Assigned those post codes linked to the same engineer via the Secondary_Engineer_id field.
SELECT
Engineer.Engineer,GROUP_CONCAT(Post_Code SEPARATOR ', ') as Post_Codes_Assigned,
Engineer.Region,
Engineer.active,
Engineer.Engineer_id
FROM Engineer INNER JOIN Post_Code ON Engineer.Engineer_id = Post_Code.Engineer_id
GROUP BY Engineer_id
What I need is output similar to this.
Engineer_id | Post_Codes_Assigned | Secondary_Post_Codes_Assigned
----------
1 | AW, AW3 | B12 |
2 | B12 | AW, CV12 |
I hope this is clear as I am pretty new to mysql.
Regards
Alan
You are already joining the primary post codes and list them, now do the same with the secondary ones.
SELECT
e.Engineer,
GROUP_CONCAT(DISTINCT pc1.Post_Code) AS Primary_Post_Codes_Assigned,
GROUP_CONCAT(DISTINCT pc2.Post_Code) AS Secondary_Post_Codes_Assigned,
e.Region,
e.active,
e.Engineer_id
FROM Engineer e
JOIN Post_Code pc1 ON e.Engineer_id = pc1.Engineer_id
JOIN Post_Code pc2 ON e.Engineer_id = pc2.Secondary_Engineer_id
GROUP BY e.Engineer_id;
As you see, you need DISTINCT because when selecting all primary and all secondary postcodes, you are getting rows for all combinations of them in the intermediate result. So you must get rid of duplicates. For this reason ist is better to aggregate before joining. (Which I generally consider a good idea, so you may want to make this a habit when working with aggregates.)
SELECT
e.Engineer,
pc1.Post_Codes AS Primary_Post_Codes_Assigned,
pc2.Post_Codes AS Secondary_Post_Codes_Assigned,
e.Region,
e.active,
e.Engineer_id
FROM Engineer e
JOIN
(
SELECT Engineer_id, GROUP_CONCAT(Post_Code) AS Post_Codes
FROM Post_Code
GROUP BY Engineer_id
) pc1 ON e.Engineer_id = pc1.Engineer_id
JOIN
(
SELECT Secondary_Engineer_id, GROUP_CONCAT(Post_Code) AS Post_Codes
FROM Post_Code
GROUP BY Secondary_Engineer_id
) pc2 ON e.Engineer_id = pc2.Secondary_Engineer_id;
A third option would be subqueries in the SELECT clause. I usually prefer them to be in the FROM clause as shown, because then it is easy to add more columns to the subqueries, which is not possible in the SELECT clause.
SELECT
e.Engineer,
(
SELECT GROUP_CONCAT(pc1.Post_Code)
FROM Post_Code pc1
WHERE pc1.Engineer_id = e.Engineer_id
) AS Primary_Post_Codes_Assigned,
(
SELECT GROUP_CONCAT(pc2.Post_Code)
FROM Post_Code pc2
WHERE pc2.Secondary_Engineer_id = e.Engineer_id
) AS Secondary_Post_Codes_Assigned,
e.Region,
e.active,
e.Engineer_id
FROM Engineer e;

Select distinct ,count and groupBy in Sql

Hi I am joining the tables to return data but I want that data to be grouped by specific location and also count/sum the values that are related to that location
at the moment I can only retrieve the data but it does not sum/count the values of that location Here is my query
select brand_locations.locationName,campaign_stats.clicks,
campaign_stats.impressions,
campaign_stats.day,brand_locations.city,
click_actions.walkTo,click_actions.showMap,
click_actions.call,click_actions.coupon,driveTo,campaigns_details.state
from campaign_stats
inner join campaigns_details on campaign_stats.campaignId = campaigns_details.campaignId
inner join click_actions on click_actions.campaignId=campaign_stats.campaignId
inner join brand_locations on brand_locations.brandId = campaigns_details.brandId
where brand_locations.city = 'cape town'
The output is
bellville 58
water front 12
bellville 4
bellville 1
century city 2
century city 4
My goal is to have
bellville 63
water front 12
century city 6
Thanks
MySQL SUM() function retrieves the sum value of an expression which has undergone a grouping operation by GROUP BY clause. Have you tried this option?
select brand_locations.locationName,SUM(campaign_stats.clicks),
campaign_stats.impressions,
campaign_stats.day,brand_locations.city,
click_actions.walkTo,click_actions.showMap,
click_actions.call,click_actions.coupon,driveTo,campaigns_details.state
from campaign_stats
inner join campaigns_details on campaign_stats.campaignId = campaigns_details.campaignId
inner join click_actions on click_actions.campaignId=campaign_stats.campaignId
inner join brand_locations on brand_locations.brandId = campaigns_details.brandId
where brand_locations.city = 'cape town' group by brand_locations.locationName
Try this query. I have added a group by location name to the query and also used function sum to calcualate the total number of clicks.
select brand_locations.locationName,SUM(campaign_stats.clicks) as campaign_stats_cnt,
sum(campaign_stats.impressions) as impressions_cnt,
campaign_stats.day,brand_locations.city,
click_actions.walkTo,click_actions.showMap,
click_actions.call,click_actions.coupon,driveTo,campaigns_details.state
from campaign_stats
inner join campaigns_details on campaign_stats.campaignId = campaigns_details.campaignId
inner join click_actions on click_actions.campaignId=campaign_stats.campaignId
inner join brand_locations on brand_locations.brandId = campaigns_details.brandId
where brand_locations.city = 'cape town' group by brand_locations.locationName
You can use sum() to get the results you want from the result you are getting
SELECT title, SUM(count)
FROM (resultQuery)
GROUP BY title
NOTE: Replace title with the name of your first column, count with the name of your second column and resultQuery with they query you have above
And yes, this is the hacky sub-query way of doing it....