MySQL: Include COUNT of SELECT Query Results as a Column (Without Grouping) - mysql

I have a simple report sending framework that basically does the following things:
It performs a SELECT query, it makes some text-formatted tables based on the results, it sends an e-mail, and it performs an UPDATE query.
This system is a generalization of an older one, in which all of the operations were hard coded. However, in pushing all of the logic of what I'd like to do into the SELECT query, I've run across a problem.
Before, I could get most of the information for my text tables by saying:
SELECT Name, Address FROM Databas.Tabl WHERE Status='URGENT';
Then, when I needed an extra number for the e-mail, also do:
SELECT COUNT(*) FROM Databas.Tabl WHERE Status='URGENT' AND TimeLogged='Noon';
Now, I no longer have the luxury of multiple SELECT queries. What I'd like to do is something like:
SELECT Tabl.Name, Tabl.Address, COUNT(Results.UID) AS Totals
FROM Databas.Tabl
LEFT JOIN Databas.Tabl Results
ON Tabl.UID = Results.UID
AND Results.TimeLogged='Noon'
WHERE Status='URGENT';
This, at least in my head, says to get a total count of all the rows that were SELECTed and also have some conditional.
In reality, though, this gives me the "1140 - Mixing of GROUP columns with no GROUP columns illegal if no GROUP BY" error. The problem is, I don't want to GROUP BY. I want this COUNT to redundantly repeat the number of results that SELECT found whose TimeLogged='Noon'. Or I want to remove the AND clause and include, as a column in the result of the SELECT statement, the number of results that that SELECT statement found.
GROUP BY is not the answer, because that causes it to get the COUNT of only the rows who have the same value in some column. And COUNT might not even be the way to go about this, although it's what comes to mind. FOUND_ROWS() won't do the trick, since it needs to be part of a secondary query, and I only get one (plus there's no LIMIT involved), and ROW_COUNT() doesn't seem to work since it's a SELECT statement.
I may be approaching it from the wrong angle entirely. But what I want to do is get COUNT-type information about the results of a SELECT query, as well as all the other information that the SELECT query returned, in one single query.
=== Here's what I've got so far ===
SELECT Tabl.Name, Tabl.Address, Results.Totals
FROM Databas.Tabl
LEFT JOIN (SELECT COUNT(*) AS Totals, 0 AS Bonus
FROM Databas.Tabl
WHERE TimeLogged='Noon'
GROUP BY NULL) Results
ON 0 = Results.Bonus
WHERE Status='URGENT';
This does use sub-SELECTs, which I was initially hoping to avoid, but now realize that hope may have been foolish. Plus it seems like the COUNTing SELECT sub-queries will be less costly than the main query since the COUNT conditionals are all on one table, but the real SELECT I'm working with has to join on multiple different tables for derived information.
The key realizations are that I can GROUP BY NULL, which will return a single result so that COUNT(*) will actually catch everything, and that I can force a correlation to this column by just faking a Bonus column with 0 on both tables.
It looks like this is the solution I will be using, but I can't actually accept it as an answer until tomorrow. Thanks for all the help.

SELECT Tabl.Name, Tabl.Address, Results.Totals
FROM Databas.Tabl
LEFT JOIN (SELECT COUNT(*) AS Totals, 0 AS Bonus
FROM Databas.Tabl
WHERE TimeLogged='Noon'
GROUP BY NULL) Results
ON 0 = Results.Bonus
WHERE Status='URGENT';
I figured this out thanks to ideas generated by multiple answers, although it's not actually the direct result of any one. Why this does what I need has been explained in the edit of the original post, but I wanted to be able to resolve the question with the proper answer in case anyone else wants to perform this silly kind of operation. Thanks to all who helped.

You could probably do a union instead. You'd have to add a column to the original query and select 0 in it, then UNION that with your second query, which returns a single column. To do that, the second query must also select empty fields to match the first.
SELECT Cnt = 0, Name, Address FROM Databas.Tabl WHERE Status='URGENT'
UNION ALL
SELECT COUNT(*) as Cnt, Name='', Address='' FROM Databas.Tabl WHERE Status='URGENT' AND TimeLogged='Noon';
It's a bit of a hack, but what you're trying to do isn't ideal...

Does this do what you need?
SELECT Tabl.Name ,
Tabl.Address ,
COUNT(Results.UID) AS GrandTotal,
COUNT(CASE WHEN Results.TimeLogged='Noon' THEN 1 END) AS NoonTotal
FROM Databas.Tabl
LEFT JOIN Databas.Tabl Results
ON Tabl.UID = Results.UID
WHERE Status ='URGENT'
GROUP BY Tabl.Name,
Tabl.Address
WITH ROLLUP;

The API you're using to access the database should be able to report to you how many rows were returned - say, if you're running perl, you could do something like this:
my $sth = $dbh->prepare("SELECT Name, Address FROM Databas.Tabl WHERE Status='URGENT'");
my $rv = $sth->execute();
my $rows = $sth->rows;

Grouping by Tabl.id i dont believe would mess up the results. Give it a try and see if thats what you want.

Related

Simplify select with count

I need to get some values from a database and count all rows.
I wrote this code:
SELECT author, alias, (select COUNT(*) from registry WHERE status='OK' AND type='H1') AS count
FROM registry
WHERE status='OK' AND type='H1'
It works, but, how can I simplify this code? Both WERE condition thereof.
If the query is returning the resultset you need, with the "total" count of rows (independent of author and alias), with the same exact value for "count" repeated on each row, we could rewrite the query like this:
SELECT t.author
, t.alias
, s.count
FROM registry t
CROSS
JOIN ( SELECT COUNT(*) AS `count`
FROM registry c
WHERE c.status='OK'
AND c.type='H1'
) s
WHERE t.status='OK'
AND t.type='H1'
I don't know if that's any simpler, but to someone reading the statement, I think it makes it more clear what resultset is being returned.
(I also tend to favor avoiding any subquery in the SELECT list, unless there is a specific reason to add one.)
The resultset from this query is a bit odd. But absent any example data, expected output or any specification other than the original query, we're just guessing. The query in my answer replicates the results from the original query, in a way that's more clear.
try this:
SELECT author, alias, count(1) AS caunt
FROM registry
WHERE status='OK' AND type='H1'
group by author, alias
The status and type are the criteria to extract the fields from the table. You cannot go more simpler than this.
Try using a group by,
SELECT author, alias, COUNT(*) AS COUNT
FROM registry
WHERE STATUS='OK' AND TYPE='H1'
GROUP BY author, alias
Depending on how your table is set up, you may actually need to do this to get the data you are looking for.
Some good reading for this: https://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html
However, if you are wanting the count to be simply ALL the records with that criteria, what you have may be simplest already.

MySQL : Count returning double the number of entries when using distinct

So I do a count like so
select distinct count(prod.id) from product as prod....
I get back 175590
I do a select like so
select distinct prod.id from product as prod.... (rest of the query is exactly the same)
and I limit it. Now if I limit the query to return anything over the half way point it returns nothing. It appears as if count is returning double the number of entries each time.
Does anyone know of anything that may be causing this?
Thanks
Tracey
The DISTINCT keyword tells MySQL to strip the duplicate rows from the result set. Because SELECT COUNT(prod.id) returns a single row (I guess this, I cannot tell for sure until I see the complete query), adding DISTINCT in front of COUNT() does not change its behaviour in any way.
What you probably want is SELECT COUNT(DISTINCT prod.id) and that's a totally different thing. It removes the duplicate values of prod.id before counting them.
Your first query is counting how many prod.id's there are.
Your second query is showing all distinct prod.id's.
This is quite different.
If you were to do the second query without the distinct key word the number would be the same.

MYSQL - displaying query result counts without losing actual result entries

Sorry if this is a really dumb question but I'm not too familiar with MYSQL syntax.
I've historically been running:
USE hg19;
SELECT DISTINCT (name2), txStart, txEnd
FROM refGene
WHERE name2 LIKE '[genename]';
which would output all entries and it was fine, except if I was looking for an entry that didn't exist, I would get a blank (which just so happened to be the same result if I disconnected from the server). This was leading to a bunch of downstream issues when I couldn't actually detect if an entry didn't exist vs my internet disconnected me.
So instead I decided to try:
USE hg19;
SELECT *, count(*) AS results
FROM (
SELECT DISTINCT (name2), txStart, txEnd
FROM refGene
WHERE name2 LIKE 'TP53'
) a;
This would now give me a 0 for results if it didn't exist (and if it didn't connect it'd remain blank). However, now for whatever reason it only displays one entry no matter what (If I query for TP53 for example, it should have two distinct entries -> however, it will give me results:2 but only display one of them). Is there a way around this? I would still like to have it displaying all distinct results.
COUNT() is a aggregate function that works on groups of rows. Without a GROUP BY clause only MySQL accepts such a statement and will return arbitrary values in the not aggregated columns - and return just one row, as you've seen.
To get your desired result, you only got to invert your logic and use a LEFT JOIN
SELECT
a.results,
b.*
FROM
(SELECT COUNT(*) results FROM refGene r1 WHERE a.name2 LIKE 'TP53') a
LEFT JOIN (
SELECT
*
FROM
refGene r2
WHERE
name2 LIKE 'TP53'
) b
ON
a.result IS NOT NULL;
If your "main" query returns no row there will be a 0 (zero) in the result column an NULL values in the columns of your "main" query.

Getting different results from group by and distinct

this is my first post here since most of the time I already found a suitable solution :)
However this time nothing seems to help properly.
Im trying to migrate information from some mysql Database I have just read-only access to.
My problem is similar to this one: Group by doesn't give me the newest group
I also need to get the latest information out of some tables but my tables have >300k entries therefore checking whether the "time-attribute-value" is the same as in the subquery (like suggested in the first answer) would be too slow (once I did "... WHERE EXISTS ..." and the server hung up).
In addition to that I can hardly find the important information (e.g. time) in a single attribute and there never is a single primary key.Until now I did it like it was suggested in the second answer by joining with subquery that contains latest "time-attribute-entry" and some primary keys but that gets me in a huge mess after using multiple joins and unions with the results.
Therefore I would prefer using the having statement like here: Select entry with maximum value of column after grouping
But when I tried it out and looked for a good candidate as the "time-attribute" I noticed that this queries give me two different results (more = 39721, less = 37870)
SELECT COUNT(MATNR) AS MORE
FROM(
SELECT DISTINCT
LAB_MTKNR AS MATNR,
LAB_STG AS FACH,
LAB_STGNR AS STUDIENGANG
FROM
FKT_LAB
) AS TEMP1
SELECT COUNT(MATNR) AS LESS
FROM(
SELECT
LAB_MTKNR AS MATNR,
LAB_STG AS FACH,
LAB_STGNR AS STUDIENGANG,
LAB_PDATUM
FROM
FKT_LAB
GROUP BY
LAB_MTKNR,
LAB_STG,
LAB_STGNR
HAVING LAB_PDATUM = MAX(LAB_PDATUM)
)AS TEMP2
Although both are applied to the same table and use "GROUP BY" / "SELECT DISTINCT" on the same entries.
Any ideas?
If nothing helps and I have to go back to my mess I will use string variables as placeholders to tidy it up but then I lose the overview of how many subqueries, joins and unions I have in one query... how many temproal tables will the server be able to cope with?
Your second query is not doing what you expect it to be doing. This is the query:
SELECT COUNT(MATNR) AS LESS
FROM (SELECT LAB_MTKNR AS MATNR, LAB_STG AS FACH, LAB_STGNR AS STUDIENGANG, LAB_PDATUM
FROM FKT_LAB
GROUP BY LAB_MTKNR, LAB_STG, LAB_STGNR
HAVING LAB_PDATUM = MAX(LAB_PDATUM)
) TEMP2;
The problem is the having clause. You are mixing an unaggregated column (LAB_PDATUM) with an aggregated value (MAX(LAB_PDATAUM)). What MySQL does is choose an arbitrary value for the column and compare it to the max.
Often, the arbitrary value will not be the maximum value, so the rows get filtered. The reference you give (although an accepted answer) is incorrect. I have put a comment there.
If you want the most recent value, here is a relatively easy way:
SELECT COUNT(MATNR) AS LESS
FROM (SELECT LAB_MTKNR AS MATNR, LAB_STG AS FACH, LAB_STGNR AS STUDIENGANG,
max(LAB_PDATUM) as maxLAB_PDATUM
FROM FKT_LAB
GROUP BY LAB_MTKNR, LAB_STG, LAB_STGNR
) TEMP2;
It does not, however, affect the outer count.

MySQL Group By and Order by not playing nicely together

I have read a few post on this, but not seeming to be able to fix my problem.
I am calling two database queries to populate two array's that run along side by side of each other, but they aren't matching, as the order that they come out is different. I believe i have something to do with the Group By, and this may require a sub query, but again a little lost...
Query 1:
SELECT count(bids_bid.total_bid), bidtime_bid, users_usr.company_usr, users_usr.id_usr
FROM bids_bid
INNER JOIN users_usr
ON bids_bid.user_bid = users_usr.id_usr
WHERE auction_bid = 36
GROUP BY user_bid
ORDER BY bidtime_bid ASC
Query 2:
SELECT auction_bid, user_bid, bidtime_bid, bids_bid.total_bid
FROM bids_bid
WHERE auction_bid = 36
ORDER BY bidtime_bid ASC
Even though the 'Order by' is the same the results aren't matching. The users are coming out in a different sequence.
I hope this makes sense, and thanks in advance.
* Update *
I just wanted to add a bit of clarity on what the output I want is. I need to only show 1 result by one user (user_bid) the second query show all users rows. I only need the first one to show the first row entered for each user. So if I could order before the the group and by min date, that would be ace...
It's to be expected. You're fetching fields that are NOT involved in the grouping, and are not part of an aggregate function. MySQL allows such things, but generally the results of the ungrouped/unaggregated functions can be wonky.
Because MySQL is free to chose WHICH of the potentially multiple 'free' rows to choose for the actual result row, you will get different results. Generally it picks the first-encountered 'free choice' result, but that's not defined/guaranteed.
You use grouping when you want unique results in result set according to some
group id (column name). usually grouping is used with aggregate functions such as
(min, max,count,sum..).
Ordering or inner query is nothing to do with result set, i suggest read some introductory
tutorials about grouping and think/treat Sql as a set based language and most of the set theory is applied on sql you'll be fine.
So I was complicating issues that I didn't need to. The solution I found was before.
SELECT users_usr.company_usr,
users_usr.id_usr,
bids_bid.bidtime_bid, min(bidtime_bid) as minbid FROM bids_bid INNER JOIN users_usr ON bids_bid.user_bid = users_usr.id_usr
WHERE auction_bid = 36
GROUP BY id_usr
ORDER BY minbid ASC
Thanks everyone for making me look (try) harder...