COUNT not returning expected values - mysql

Given the following relationship between my tables:
I need to show the name of films which only have two starring actors like 'Seconday'.
SELECT act.cod_film,
(SELECT COUNT(act.cod_film) FROM film, act WHERE act.starring like
'Seconday' and act.cod_film=film.cod_film) as namecounter
FROM act;
This always returns the films with the 'Seconday' actor, but the count is always same number. I need the number of 'Seconday' actors in each film.
Any help would be appreciated

This will locate films with 2 'Secondary' actors
SELECT act.cod_film
FROM act
GROUP BY act.cod_film
HAVING SUM(CASE WHEN act.starring like = 'Seconday' THEN 1 ELSE O END) = 2
which you can use in a subquery like this (or in many other possible ways)
SELECT *
FROM FILMS
WHERE cod_film IN (
SELECT act.cod_film
FROM act
GROUP BY act.cod_film
HAVING SUM(CASE WHEN act.starring like = 'Seconday' THEN 1 ELSE O END) = 2
)
or: Which you can use this way in a join
SELECT *
FROM FILMS
INNER JOIN (
SELECT act.cod_film, COUNT(*) NUM_OF
FROM act
GROUP BY act.cod_film
HAVING SUM(CASE WHEN act.starring like = 'Seconday' THEN 1 ELSE O END) = 2
) A ON FILMS.cod_film = A.cod_film
Notes:
like requires the use of a wildcard to be effective, don't substitute equals with like they are not the same things
you can adjust the = to >= as in sum(case...) >= 2 if you wanted a minimum of 2 secondary actors

The act table is mentioned 2 times, try remove the act from the select count()
Preposition 1
SELECT act.cod_film,
(SELECT COUNT(act.cod_film) FROM film WHERE act.starring like
'Seconday' and act.cod_film=film.cod_film) as namecounter
FROM act;
Preposition 2
SELECT act.cod_film, count(*)
FROM act JOIN film ON (act.act.cod_film = film.cod_film)
WHERE act.starring like 'Seconday'
GROUP BY act.cod_film

Related

Counting two tables with several conditions

I have two tables,
fenotipos
id DTHHRDX sex
GTEX-1117F 0 2
GTEX-ZE9C 2 1
K-562 1 2
atributos
SAMPID SMTS
K-562-SM-26GMQ Blood vessel
K-562-SM-2AXTU Blood_dry
GTEX-1117F-0003-SM-58Q7G Brain
GTEX-ZE9C-0006-SM-4WKG2 Brain
GTEX-ZE9C-0008-SM-4E3K6 Urine
GTEX-ZE9C-0011-R11a-SM-4WKGG Urine
I need to know how many women (sex=2) have DTHHRDX = 1 and have blood on SMTS.
For instance, the answer in this case would be 2
You could do:
select count(*) as cnt
from fenotipos f
where
sex = 2
and exists (
select 1
from atributos a
where a.sampid like concat(f.id, '%') and a.smts like 'Blood%'
)
This properly handles potential multiple matches in atributos
Alternatively, you could join:
select count(distinct f.id) as cnt
from fenotipos f
inner join atributos a on a.sampid like concat(f.id, '%')
where f.sex = 2 and a.smts like 'Blood%'
If there are no duplicates, then count(*) is more efficient than count(distinct f.id) in the second query.

How to do a subquery in group_concat

I have the following data model:
`title`
- id
- name
`version`
- id
- name
- title_id
`version_price`
- id
- version_id
- store
- price
And here is an example of the data:
`title`
- id=1, name=titanic
- id=2, name=avatar
`version`
- id=1, name="titanic (dubbed in spanish)", title_id=1
- id=2, name="avatar directors cut", title_id=2
- id=3, name="avatar theatrical", title_id=2
`version_price`
- id=1, version_id=1, store=itunes, price=$4.99
- id=1, version_id=1, store=google, price=$4.99
- id=1, version_id=2, store=itunes, price=$5.99
- id=1, version_id=3, store=itunes, price=$5.99
I want to construct a query that will give me all titles that have a version_price on iTunes but not on Google. How would I do this? Here is what I have so far:
select
title.id, title.name, group_concat(distinct store order by store)
from
version inner join title on version.title_id=title.id inner join version_price on version_price.version_id=version.id
group by
title_id
This gives me a group_concat which shows me what I have:
id name group_concat(distinct store order by store)
1 Titanic Google,iTunes
2 Avatar iTunes
But how would I construct a query to include whether the item is on Google (using a case statement or whatever's needed)
id name group_concat(distinct store order by store) on_google
1 Titanic Google,iTunes true
2 Avatar iTunes false
It would basically be doing a group_concat LIKE '%google%' instead of a normal where clause.
Here's a link for a SQL fiddle of the current query I have: http://sqlfiddle.com/#!9/e52b53/1/0
Use conditional aggregation to determine if the title is in a specified store.
select title.id, title.name, group_concat(distinct version_price.store order by store),
if(count(case when store = 'google' then 1 end) >= 1,'true','false') as on_google
from version
inner join title on version.title_id=title.id
inner join version_price on version_price.version_id=version.id
group by title.id, title.name
count(case when store = 'google' then 1 end) >= 1 counts all the rows for a given title after assigning 1 to the rows which have google in them. (Or else they would be assigned null and the count ignores nulls.) Thereafter, the if checks for the countand classifies a title if it has atleast one google store on it.
http://sqlfiddle.com/#!9/b8706/2
you can just:
SELECT
title.id,
title.name,
group_concat(distinct version_price.store),
MAX(IF(version_price.store='google',1,0)) on_google
FROM version
INNER JOIN title
ON version.title_id=title.id
INNER JOIN version_price
ON version_price.version_id=version.id
GROUP BY title_id;
and add HAVING to the query if need records to be filtered:
SELECT
title.id,
title.name,
group_concat(distinct version_price.store),
MAX(IF(version_price.store='google',1,0)) on_google
FROM version
INNER JOIN title
ON version.title_id=title.id
INNER JOIN version_price
ON version_price.version_id=version.id
GROUP BY title_id
HAVING on_google;
This will give you the number of version prices not on google, and the number on google. (COUNT does not count null values.)
SELECT t.id, t.name
, COUNT(DISTINCT vpNotG.id) > 0 AS onOtherThanGoogle
, COUNT(DISTINCT vpG.id) > 0 AS onGoogle
FROM title AS t
INNER JOIN version AS v ON t.id=v.title_id
LEFT JOIN version_price AS vpNotG
ON v.id=vpNotG.version_id
AND vpNotG.store <> 'Google'
LEFT JOIN version_price AS vpG
ON v.id=vpG.version_id
AND vpG.store = 'Google'
GROUP BY t.id
or for another solution similar to vkp's:
SELECT t.id, t.name
, COUNT(DISTINCT CASE WHEN store = 'Google' THEN vp.id ELSE NULL END) AS googlePriceCount
, COUNT(DISTINCT CASE WHEN store = 'iTunes' THEN vp.id ELSE NULL END) AS iTunesPriceCount
, COUNT(DISTINCT CASE WHEN store <> 'Google' THEN vp.id ELSE NULL END) AS nonGooglePriceCount
FROM title AS t
INNER JOIN version AS v ON t.id = v.title_id
INNER JOIN version_price AS vp ON v.id = vp.version_id
GROUP BY t.id
Note: The ELSE NULL can be omitted, because if no ELSE is provided it is implied; but I included for clarity.
I would do it like below
SELECT
*
FROM
title t
INNER JOIN
version v ON
v.title_id = t.id
CROSS APPLY (
SELECT
*
FROM
version_price vp
WHERE
vp.store <> 'google'
) c ON c.version_id == v.id
Syntax may be just a little off as I can't test it right now, but I believe this is the spirit of what you would want. Cross apply is also a very efficient join which is always helpful!
This might be the most inefficient of the above answers, but the following subquery would work, using a %like% condition:
select *, case when stores like '%google%' then 1 else 0 end on_google
from (select title.id, title.name, group_concat(distinct store order by store) stores
from version inner join title on version.title_id=title.id inner join version_price
on version_price.version_id=version.id group by title_id) x
id name stores on_google
1 Titanic Google,iTunes 1
2 Avatar iTunes 0

mysql COUNT function on two or more columns as per true column value

I am trying to count the number of times the two columns have their assigned value appears. The sql statement I have so far outputs wrong results. Take a look:
SELECT
UPPER(school.district) AS District_name,
COUNT(DISTINCT task1.wp_status) AS Status_New,
COUNT(task1.wp_type) AS Type_Bolehole
FROM school
RIGHT OUTER JOIN task1 ON school.s_name=task1.name
WHERE task1.wp_status="New" OR task1.wp_type="Bolehole"
GROUP BY district AND task1.wp_status="New" AND task1.wp_type="Bolehole"
ORDER BY district;
The two tables am using are as below
School
s_name district
matero lusaka
kema lusaka
naka kabwe
task1
name wp_status wp_type
matero New Bolehole
kema New Bolehole
naka New ProtectedWell
expected output
D_Name Status_New Type_Bolehole
KABWE 1 2
LUSAKA 2 0
I would recommend writing the query like this:
SELECT UPPER(s.district) AS District_name,
SUM(t1.wp_status = 'New') AS Status_New,
SUM(t1.wp_type = 'Bolehole') AS Type_Bolehole
FROM school s JOIN
task1 t1
ON s.s_name = t1.name
WHERE t1.wp_status = 'New' or t1.wp_type = 'Bolehole'
GROUP BY UPPER(s.district)
ORDER BY District_Name;
This makes the following changes:
Table aliases are introduced to make the query easier to read and write.
The join is changed to an inner join. You are selecting on one table and aggregating by the other, so it seems that the query expects a match in both tables.
The group by is based on the actual expression used in the select.
Conditional aggregation is used to get the two columns of counts.
Note: This assumes that the sample results in the question are not correct.
you dont need a right join.. all you need is one conditional sum of the rows
SELECT
UPPER(s.district) AS District_name,
SUM(case when t.wp_status = 'New' then 1 else 0 end) AS Status_New,,
SUM(case when t.wp_type = 'Bolehole' then 1 else 0 end) AS Type_Bolehole
FROM school s
LEFT JOIN task1 t ON s.s_name=t.name
WHERE t.wp_status = 'New' or t.wp_type = 'Bolehole'
GROUP BY District_name
ORDER BY District_name;
DEMO
Try this
SELECT UPPER(school.district) AS District_name,
sum( task1.wp_status ="New") AS Status_New,
sum(task1.wp_type="Bolehole") AS Type_Bolehole FROM school
RIGHT OUTER JOIN task1 ON school.s_name=task1.name
WHERE task1.wp_status="New" OR task1.wp_type="Bolehole"
GROUP BY district
ORDER BY district;
Also in your expected result set bolehole count for firt row should be 0 because task naka don't have type of bolehole
Fiddle Demo

MySQL GROUP BY on a calculated value gives an error - using other values produces unexpected results

This query returns all 23 of these music.id's:
39,64,1327,1608,1644,1657,1666,1676,1681,1686,1691,1711,1726,1730,1811,1851,2346,2440,2967,2968,2996,2998,3110
But... I want to group on a calculated value, then group by category (since 'name' is unique to each record and category isn't)
SELECT
music.id,
music.name AS name,
music.filename,
music.url_name,
music.file_path_high,
music.filesize,
music.categories AS category,
music.duration,
music.folder AS folder,
SUM(active0.weight) + SUM(active1.weight) AS total_weight
FROM (music)
INNER JOIN linked_tags AS active0
ON active0.track_id = music.id AND active0.tag_id = 1
INNER JOIN linked_tags AS active1
ON active1.track_id = music.id AND active1.tag_id = 11
GROUP BY name
ORDER BY total_weight DESC
But... when I try to GROUP BY total_weight I get an error (it says I "can't group on total_weight"). Can I not GROUP BY a calculated value? Also, if I try to GROUP BY any other value, the results that are returned are unique for that GROUP BY parameter, so depending on the parameter, I may get 0 results. GROUP BY 'music.categories' for instance only returns 7 items, 7 items all with different categories. Each item's 'name' value is unique, so it really can't group anything anyway, so I figured that by grouping by 'categories' would at least group them by their like integers of a category they belong to, but that's not what I'm seeing.
My table structure is like so:
//music table
ID name categories
1 Hopeful 02 1
2 Organic 01b 3
3 Organic 01c 3
4 Instrumental 01 8
// linked_tags table
track_id tag_id weight
1 1 3
2 2 4
2 3 5
2 1 2
You can't group by an aggregate function. It has to calculate the sum using the group and it gets the group using the sum, it's a catch 22 situation.
You may have meant to group on active0.weight + active1.weight
I think you have misunderstood what group by does in SQL - it ensures that only one row is returned for each combination of groups.
In order to retrieve multiple rows in a specific order, you need to use order by - like so:
SELECT
music.id,
music.name AS name,
music.filename,
music.url_name,
music.file_path_high,
music.filesize,
music.categories AS category,
music.duration,
music.folder AS folder,
SUM(active.weight) AS total_weight
FROM (music)
INNER JOIN linked_tags AS active
ON active.track_id = music.id AND active.tag_id in (1, 11)
GROUP BY music.id
HAVING COUNT(DISTINCT active.tag_id)=2
ORDER BY category, total_weight DESC
I think this is what you want:
SELECT
music.id,
music.name AS name,
music.filename,
music.url_name,
music.file_path_high,
music.filesize,
music.categories AS category,
music.duration,
music.folder AS folder,
total_weights.weight as total_weight
FROM music
JOIN (SELECT
music.categories as category,
SUM(active0.weight + active1.weight) as weight
FROM (music)
INNER JOIN linked_tags AS active0
ON active0.track_id = music.id AND active0.tag_id = 1
INNER JOIN linked_tags AS active1
ON active1.track_id = music.id AND active1.tag_id = 11
GROUP BY music.categories
) total_weights
ON music.categories = total_weights.category
INNER JOIN linked_tags AS active0
ON active0.track_id = music.id AND active0.tag_id = 1
INNER JOIN linked_tags AS active1
ON active1.track_id = music.id AND active1.tag_id = 11
ORDER BY total_weight DESC

count multiple in one statement

i have a table with data title and date of the data inserted.
and right now i want to do count to make the statistic out of it.
can i do multiple count in one sql statement?
like from, the column date, i want to count how many on this month, and how many in this year, until month selected. is it possible? if yes, how?
this is what i have come up, for now.
SELECT a.trigger_type_code
, c.trigger_name
, COUNT(*) AS number
FROM issue_trigger a
INNER JOIN cs_issue b
ON b.issue_id = a.issue_id
INNER JOIN ref_trigger_type c
ON c.trigger_type_code = a.trigger_type_code
WHERE MONTH(b.created_date) = '05'
AND YEAR(b.created_date) = '2011'
GROUP BY trigger_type_code,trigger_name
by this is only for one count.help.:(
You could use a case:
select sum(case when MONTH(b.created_date) = '05'
AND YEAR(b.created_date) = '2011' then 1 end) as Count_2011_05
, sum(case when YEAR(b.created_date) = '2011'
then 1 end) as Count_2011
from ... etc ...
I think you could go like this:
SELECT
a.trigger_type_code,
c.trigger_name,
COUNT(MONTH(b.created_date) < 5 OR NULL) AS before_the_month,
COUNT(MONTH(b.created_date) = 5 OR NULL) AS in_the_month
FROM issue_trigger a
INNER JOIN cs_issue b
ON b.issue_id = a.issue_id
INNER JOIN ref_trigger_type c
ON c.trigger_type_code = a.trigger_type_code
WHERE YEAR(b.created_date) = 2011
GROUP BY a.trigger_type_code, c.trigger_name