counting overall results with groups in mysql - mysql

I see some questions that get at issues similar to this one, but I can't find any that quite catch it.
I'm working with this query:
SELECT COUNT(a.word) FROM concordance a, bigdic b
WHERE a.word = b.word
and a.word LIKE '%" . $name . "%'"
It works fine as it is, but I want to eliminate cases where word is duplicated in table b. If I add GROUP BY b.word, the query returns the count of the first group instead of the overall count minus the duplicates, whereas the right total would be the count of the groups. How do I get that total instead?

Select distinct words from b in a subquery and then join the result back to a, in this way only the unique words from b will be joined to a.
SELECT
COUNT(a.word)
FROM concordance a
JOIN (SELECT DISTINCT b.word
FROM bigdic b) temp
ON temp.word = a.word
WHERE a.word LIKE '%" . $name . "%'"

It can be more efficient to phrase this as an exists query:
SELECT COUNT(a.word)
FROM concordance a
WHERE a.word LIKE '%" . $name . "%'"
WHERE exists (select 1 from bigdic b.word = b.word);
Particularly if you have an index on bigdic(word).

Related

MySQL returns same value for multiple count selects

I am trying to return multiple count values within a single query. The query works but returns the same value for both of the count selectors:
$sql = 'SELECT '
. '`b`.*,'
. 'count(`ub`.`id`) `nummembers`,'
. 'count(`ca`.`id`) `numapps` '
. 'FROM '
. '`brands` `b` '
. 'LEFT JOIN `user_brands` `ub` ON `ub`.`brand_id`=`b`.`id` '
. 'LEFT JOIN `ca` ON `ca`.`brand_id`=`b`.`id` '
. 'GROUP BY `b`.`id`';
I sense I am missing a condition but not sure if the above is possible within a single query?
Use COUNT(DISTINCT col) if you want the number of unique members and apps within each brand group. The reason the counts were appearing the same in your original query is that you were counting the number of records in each group without regard to what is actually in each group. This will always give you same number regardless of which ID you choose to count.
SELECT b.*,
COUNT(DISTINCT ub.id) nummembers,
COUNT(DISTINCT ca.id) numapps,
FROM brands b
LEFT JOIN user_brands ub
ON ub.brand_id = b.id
LEFT JOIN ca
ON ca.brand_id = b.id
GROUP BY b.id

Better MySQL Syntax for my query

HI I have the following query:
SELECT PAS_User.user_user_id,
PAS_User.user_city,
PAS_User.user_company,
PAS_User.user_country,
PAS_User.user_account_type,
PAS_User.user_account_premium,
PAS_User.user_sign_up_date,
PAS_User.user_first_name,
PAS_User.user_last_name,
PAS_User.user_avatar_url,
PAS_User.user_cover_image_url,
PAS_User.user_bio,
PAS_User.user_sector,
PAS_User.user_job_type,
(SELECT COUNT(*) FROM `PAS_Follow` WHERE `folw_follower_user_id`=:sid) AS user_following_count,
(SELECT COUNT(*) FROM `PAS_Follow` WHERE `folw_followed_user_id`=:sid) AS user_followed_count,
(SELECT COUNT(*) FROM `PAS_Post` WHERE `post_user_id`=:sid) AS user_post_count,
(SELECT COUNT(*) FROM `PAS_Follow` WHERE `folw_follower_user_id`=:sid AND `folw_followed_user_id`=:cid) AS user_this_user_is_following,
(SELECT COUNT(*) FROM `PAS_Follow` WHERE `folw_followed_user_id`=:cid AND `folw_follower_user_id`=:sid) AS user_this_user_is_followed
FROM PAS_User
WHERE `PAS_User`.`user_user_id`=:sid
Which is designed to get counts from other tables for a profile page and the basic user details where :sid = 1 and :cid = 2.
The question is, is there any better way of achieving this in perhaps a smaller query or in a cleaner way?
The tables used are
PAS_User , PAS_Follow & PAS_Post
Thanks
Justin
You should denormalize all those counter fields and only update them when a user posts something, or presses a Follow button. Your current query is going to blow up your database server in the foreseeable future if your website gets actual active users.
I'm going to be honest. I don't really like nested select statements (select within select). However, in your case, the alternative may be uglier and prone to performance errors. The temptation is to do something like:
select . . .
from PAS_User u left outer join
(select folw_foller_user_id, count(*) as user_following_count
from PAS_Follow pf
group by folw_foller_user_id
) pf
on pf.folw_foller_user_id = u.user_user_id left outer join
. . .
Fine. But this will perform much worse than your original query, because of the outside filter on user_user_id. To fix this, you would repeat the condition in the subquery:
select . . .
from PAS_User u left outer join
(select folw_foller_user_id, count(*) as user_following_count
from PAS_Follow pf
where folw_foller_user_id = :sid
group by folw_foller_user_id
) pf
on pf.folw_foller_user_id = u.user_user_id left outer join
. . .
Or even:
select . . .
from PAS_User u cross join
(select count(*) as user_following_count
from PAS_Follow pf
where folw_foller_user_id = :sid
) pf
on pf.folw_foller_user_id = u.user_user_id left outer join
. . .
And I might even argue that repeating the condition in one subquery would be good. I cannot make that argument for five subqueries.
To make this clear, the best syntax is to user window functions:
select . . .
count(*) over (partition by folw_fllower_user_id) as user_following_count,
. . .
Alas, MySQL does not support window functions. A very reasonable replacement, in my opinion, are subqueries (which would normally be correlated). This supports your initial syntax.

MySQL excutes select after where clause

This query gives an error unknown column company in where clause. I found that where clause runs first and select runs next. That could be the error here. But i dont know how to correct this in order to get company in result set.
SELECT trnsdriverid,
name,
(SELECT transcompany.name
FROM transcompany,
transcompdriver
WHERE transcompany.trnscompid = transcompdriver.trnscompid) AS 'company',
address,
dob,
license,
telephone
FROM transcompdriver
WHERE ? LIKE 'All'
OR name LIKE '%"+keyword+"%'
OR company LIKE '%"+keyword+"%'
OR trnsdriverid LIKE '%"+keyword+"%'
You can't reference column aliases in where statements. You should rewrite this query to use a JOIN and then do your filtering on the actual TransCompany.name column, for example:
select
d.trnsDriverID
,d.name
,c.name as [Company]
,d.address
,d.dob
,d.license
,d.telephone
from
TransCompDriver d
join
TransCompany c
on
c.trnscompid = d.trnscompid
where
? = 'All'
or
d.name like '%" + keyword + "%'
or
c.name like '%" + keyword + "%'
or
d.trnsDriverID like '%" + keyword + "%'
Also, don't use LIKE where a simple equality operator would do. I changed the query above to use = 'All'.
You should really be doing this using a join rather than a subselect. I would recommend this:
SELECT
d.trnsDriverID,
d.name,
c.name AS `company`,
d.address,
d.dob,
d.license,
d.telephone
FROM
TransCompDriver AS d
INNER JOIN TransCompany AS c
ON d.trnsCompID = c.trnsCompID
WHERE
? like 'All'
OR d.name LIKE '%"+keyword+"%'
OR `company` LIKE '%"+keyword+"%'
OR d.trnsDriverID LIKE '%"+keyword+"%'
The sub-query that pulls the column "company" does not have matching number of rows, try the join statement instead
select trnsDriverID, name, t1.name AS company, address, dob, license, telephone
from TransCompDriver JOIN (select trnsDriverID, name,
(select TransCompany.name from TransCompany LEFT JOIN TransCompDriver
ON TransCompany.trnsCompID=TransCompDriver.trnsCompID) AS t1
where ? like 'All' or name like '%"+keyword+"%' or company like '%"+keyword+"%'
or trnsDriverID like '%"+keyword+"%'

How can I add second join which calculates an AVG to this query?

I have a query which joins two tables and counts the total in a second table by song ID.
How can I modify this query to include an average of the column 'ratings' in a third table ($sTable3) again, with the same song id.
$sQuery = "
SELECT SQL_CALC_FOUND_ROWS ".str_replace(" , ", " ", implode(", ", $aColumns))."
FROM $sTable b
LEFT JOIN (
SELECT COUNT(*) AS projects_count, a.songs_id
FROM $sTable2 a
GROUP BY a.songs_id
) bb ON bb.songs_id = b.songsID
$sWhere
$sOrder
$sLimit
";
This is all put into a JSON array and I would like to return this 'average' under a new column 'ratings'.
To summarize (as i'm aware I may not be articulating this well):-
I have three tables $sTable, $sTable2, $sTable3. All three share a songID column. My current query joins the first two and returns all results exactly as I want. However I need to also retrieve data related to the songID from the 'rating' column of $sTable3.
Had to guess a lot of your problem, as you don't reveal much about the setup, just some (inexplicably cryptically-named) variables.
SELECT songs.title, AVG(ratings.rating), COUNT(something.songs_id)
FROM songs
LEFT JOIN something ON (songs.songs_id=something.songsID)
LEFT JOIN ratings ON (songs.songs_id=ratings.songsID)
GROUP BY songs.title

mysql select where count = 0

In my db, I have a "sales" table and a "sales_item". Sometimes, something goes wrong and the sale is recorded but not the sales item's.
So I'm trying to get the salesID from my table "sales" that haven't got any rows in the sales_item table.
Here's the mysql query I thought would work, but it doesn't:
SELECT s.*
FROM sales s NATURAL JOIN sales_item si
WHERE s.date like '" . ((isset($_GET['date'])) ? $_GET['date'] : date("Y-m-d")) . "%'
AND s.sales_id like '" . ((isset($_GET['shop'])) ? $_GET['shop'] : substr($_COOKIE['shop'], 0, 3)) ."%'
HAVING count(si.sales_item_id) = 0;
Any thoughts?
Where does the table alias v does come from?
Showing the table definition would be a really good idea ;)
It has to be a left join:
SELECT *
FROM table1
LEFT JOIN table2 ON(table1.id = table2.table1_id)
WHERE table2.table1_id IS NULL
Table1 is your sales-Table and table2 is you sales_item
Having always used with Group By
GROUP BY si.sales_item_id
HAVING count(si.sales_item_id) = 0;
You don't need to join the tables, you can use something like:
[...] WHERE sales.id NOT IN (SELECT sales_id FROM sales_item)
This filters only the sales that do not have any corresponding sales_item entries.
For completeness ....
SELECT S.*
FROM SALES S
WHERE NOT EXISTS (
SELECT 1
FROM SALES_ITEM SI
WHERE SI.SALES_ITEM_ID = S.ID)
MySQL can have issues with IN clauses.
Assuming that each item in sales_item has an associated sales_id against it, you are probably looking for all sales that have no items.
How about using a subquery? Get all the sales_ids from the sales table where the id does not exist in the items table...
SELECT * from sales where sales_id not in (SELECT DISTINCT sales_id from sales_item)
(note : Exact syntax may be wrong, but the idea should be sound, if I understood the question correctly)
The join is restricting the rows to be displayed.
My advice is to forget about the join and use instead something like this:
select *
from sales
where salesId not in (select salesId from sales_item)
Basically, returns sales that doesn't have any associated sales_item.
Good luck
You should probably group rows by sales item id.
SELECT s.id, count(*) as no_of_items
FROM sales s NATURAL JOIN sales_item si
WHERE s.date like '" . ((isset($_GET['date'])) ? $_GET['date'] : date("Y-m-d")) . "%'
AND v.sales_id like '" . ((isset($_GET['shop'])) ? $_GET['shop'] : substr($_COOKIE['shop'], 0, 3)) ."%'
GROUP BY si.salesitem_id
HAVING no_of_items = 0;