Count with Having didn't return anything - mysql

I'm curious about how to write the correct query when I wanna use count function with having clause..
So I have a table named : seat
and here are the columns:
SEAT
ID_SEAT ID_TIME STATUS
-----------------------------------------
A1 | 1 | Available
A2 | 2 | Available
A3 | 1 | Available
A4 | 1 | Reserved
And so on.
I have 119 rows which the status is 'Available' and 1 row which the status is 'Reserved'.
And here is the query in MySQL:
SELECT status, COUNT(status) AS number FROM seat
WHERE ID_SEAT = 'A3' AND ID_TIME = '1'
HAVING COUNT(status) IN (SELECT COUNT(status) from SEAT where status = 'Available')
But the query didn't show anything..Any help and clear answer would be very helpful ..thank you :)
EDIT :
What I really want to display is something like this :
STATUS NUMBER
---------------------
Available 119
The STATUS field above is for a certain ID_SEAT...in my case the ID_SEAT = 'A3' and for ID_TIME = 1
EDITTTTT :
thank you for all the answer to me..
Now I can fix it clearly..
So I'm using "UNION" to separate the query..I'm using 2 queries to make it works..
here the code :
SELECT status from seat where ID_SEAT = 'A3' AND ID_TIME = '1' UNION
SELECT COUNT(status) from seat
where status = 'Available'
however the code above is work for mysql query.. thank you a lot :)

if you want your result like the one you showed.
SELECT status, COUNT(status) as number
FROM seat
WHERE status = 'Available'
GROUP BY status
If you wanted to see status of seat then simply select status, no need for count
SELECT status
FROM seat
WHERE ID_SEAT = 'A3' AND ID_TIME = '1'
If you must, return status of seat and a count of ALL available seats
SELECT status,(SELECT count(*) FROM seat WHERE status = 'Available') as number
FROM seat
WHERE ID_SEAT = 'A3' AND ID_TIME = '1'

Any time you use an aggregate function like count, sum, avg etc... in the select part of the query, as well as a non aggregate function--"status" in this case--you MUST have a "group by" clause.
So it would look like this:
SELECT status, COUNT(status), AS number FROM seat
HAVING COUNT(status) IN (SELECT COUNT(status) FROM SEAT WHERE status = 'Available')
GROUP BY status
This will make it so you see two rows. One for available with a count of 119, and one for reserved with a count of 1.

In your query, the expression (SELECT COUNT(status) from SEAT where status = 'Available') is going to return 119. However, you are only selecting one row, so the two values can never be equal.
I think you want:
SELECT status, COUNT(status) AS number
FROM seat
GROUP BY status;
This will return all status. If you really only want 'Available', then filter using a where:
SELECT status, COUNT(status) AS number
FROM seat
WHERE status = 'Available';
EDIT: (Based on clarification in comment)
Oh, if you only want the status information for seat number 23, you can get that with a having clause:
SELECT status, COUNT(status) AS number
FROM seat
GROUP BY status
HAVING sum(id_seat = '23A') > 0;
The having clause is counting up the number of rows, for each status, where the id_seat has a value of 23. You want the status where there is at least one row, which is what the having clause does.

Related

Get most recent records from the table if a 'ALERT' status is present otherwise most recent record with 'OK' Status - Mysql v5.6.50

In Mysql I have the following table - property_alert_status having columns :
id (primary), propertyId, status, updatedAt
All record - select * from property_alert_status
id
propertyId
status
updatedAt
1
1
ALERT
1658304031
2
2
OK
1658300273
3
3
ALERT
1658312336
4
3
ALERT
1658313979
5
3
OK
1658312336
6
2
OK
1658312336
From the above table, I want to fetch the most recent record for the property based on status. If Status is 'ALERT' then most recent 'ALERT' record otherwise Most recent 'OK' record.
Ex - For propertyId '3' there are three records but most recent alert status is of id 4 so the output for the above propertyId 3 should be:
id
propertyId
status
updatedAt
4
3
ALERT
1658313979
Expected Output should be:
id
propertyId
status
updatedAt
1
1
ALERT
1658304031
4
3
ALERT
1658313979
6
2
OK
1658312336
I have made one query but the output is not as expected:
Select mainStatus.* from (
SELECT *
FROM property_alert_status
ORDER BY
(CASE
WHEN status = "ALERT" THEN 0
ELSE 1
END) ASC, updatedAt DESC
) mainStatus group by propertyId;
Innerquery is giving the right result but when selecting only a single record by grouping propertyId, giving the wrong result.
Inner query giving result:
id
propertyId
status
updatedAt
4
3
ALERT
1658313979
3
3
ALERT
1658312336
1
1
ALERT
1658304031
5
3
OK
1658312336
6
2
OK
1658312336
2
2
OK
1658300273
The final query gives result:
id
propertyId
status
updatedAt
1
1
ALERT
1658304031
2
2
OK
1658300273
3
3
ALERT
1658312236
Note: Using Mysql v5.6.50.
Tables in SQL are unordered data set. A query result is a table. So the ORDER BY clause in your subquery doesn't have to sort the rows. Don't rely on it. Some DBMS even raise an error when you have an ORDER BY at the end of a subquery.
Moreover, select * from ... group by ... is invalid. If you group by a column, you can select that column plus aggregates, i.e. sums, maximums, averages and so on. You cannot select other original column values (except for the case they are functionally dependent on your group, such as a person's name when you group by the person's ID). MySQL should raise an error, and if it doesn't, this probably means that you are working in a cheat mode that MySQL invented in their early days. Make sure to always SET sql_mode = 'ONLY_FULL_GROUP_BY'; when working with MySQL in order to have the DBMS help you with invalid aggregation queries.
As to the task: You can rank your rows with ROW_NUMBER.
SELECT *
FROM
(
SELECT
s.*,
ROW_NUMBER() OVER (PARTITION BY propertyid ORDER BY status, updatedat DESC) AS rn
FROM vk_property_temperature_alert_status s
WHERE temperature_status IN ('ALERT', 'OK')
) ranked
WHERE rn = 1;
For old MySQL versions I see two approaches. Either select only those rows for which not exists a better row or select those rows that are the best for their group. The second approach seems easier. It's basically writing a subquery that determines the top row for the property ID, so you can check whether the row you are looking at is a top row.
SELECT *
FROM vk_property_temperature_alert_status s
WHERE id =
(
SELECT s2.id
FROM vk_property_temperature_alert_status s2
WHERE s2.temperature_status IN ('ALERT', 'OK')
AND s2.propertyid = s.propertyid
ORDER BY s2.status, s2.updatedat DESC
LIMIT 1
);
I don't know if this will work, wrote just for fun as the question was very interesting:
SELECT
MAX(maxId),
propertyId,
`status`,
MAX(dates) updatedAt
FROM
(
SELECT
firstResult.*,
(CASE WHEN #running_propertyId=0 THEN #running_propertyId:=propertyId ELSE #running_propertyId:=#running_propertyId END) runningPro,
(CASE WHEN #running_status='' THEN #running_status:=`status` ELSE #running_status:=#running_status END) runningStat,
(CASE WHEN #running_variable >0 AND #running_propertyId =propertyId THEN #running_variable:=#running_variable+1 ELSE #running_variable:=1 END )var,
(CASE WHEN #running_variable =1 THEN #running_date:=updatedAt ELSE (CASE WHEN `status`='ALERT' THEN #running_date:=updatedAt ELSE
( CASE WHEN #running_status=`status` THEN #running_date:=updatedAt ELSE #running_date:=#running_date END) END) END )dates,
(CASE WHEN #running_variable =1 THEN #running_id:=id ELSE (CASE WHEN `status`='ALERT' THEN #running_id:=id ELSE
( CASE WHEN #running_status=`status` THEN #running_id:=id ELSE #running_id:=#running_id END) END) END )maxId,
#running_propertyId:=propertyId,
#running_status:=`status`
FROM (SELECT
a.*,
#running_propertyId:=0,
#running_status:='',
#running_variable:=0,
#running_date:=0,
#running_id:=0
FROM
property_alert_status a
ORDER BY
`propertyId`
,`updatedAt`) firstResult
) final
GROUP BY propertyId
By combining my query with some changes into the #Thorsten Kettner's query -
Following Query giving expected result:
SELECT
*
FROM
property_alert_status s
WHERE
id = (SELECT
s2.id
FROM
property_alert_status s2
WHERE
s2.propertyId = s.propertyId
ORDER BY (CASE
WHEN s2.status = 'ALERT' THEN 0
ELSE 1
END) ASC, s2.updatedAt DESC
LIMIT 1);

Sort by the sum of mutual likes/dislikes of all users, separated by like or dislike

I want to sort page likes/dislikes by all users that I share the same page likes/dislikes with and order it by the most likes/dislikes combined.
I tried joining the same table itself but I don't know how to separate the COUNT that I get likes and dislikes at the same time without changing the WHERE-condition.
Assuming my uid (user id) is 544 I have this query:
SELECT l1.id, l1.uid, l1.item_id, l1.status, l2.id, l2.uid, l2.item_id, l2.status,
COUNT(*) AS common_likes,
COUNT(*) AS common_dislikes // should check for status = 0
FROM pages_likes AS l1
JOIN pages_likes AS l2 ON l1.item_id = l2.item_id
WHERE l1.uid != 544 AND l2.uid = 544
GROUP BY l1.uid
ORDER BY common_likes DESC, l1.uid DESC
LIMIT 10
In fact this query is delivering wrong results in general since I can check single user-pages for common likes/dislikes and the count is different that the result I'm getting from this query of common_likes. For example I have www.domain.com/user/123/ and see all common likes/dislikes where I'm using uid IN (123,544) to check common likes/dislikes of specific user id's. But now I need an overview of all users (www.domain.com/users/), so it should be a different query.
So the query I posted is the one I came up with and needs to be improved.
Table structure:
page_likes: id, uid, item_id, status, date
uid means user id.
status = 1 means like.
status = 0 means dislike.
Query output:
Try using SUM with case statement instead of count like below
SELECT l1.id, l1.uid, l1.item_id, l1.status,
SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) AS common_likes,
SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) AS common_dislikes
FROM pages_likes AS l1
WHERE l1.uid = 544
GROUP BY l1.uid
ORDER BY common_likes DESC, l1.uid DESC
LIMIT 10;
Also there is no need for self-join in your scenario.
Hope this would help you out.

Count DISTINCT on a single column over multiple conditions

I have a table, and I want to get the DISTINCT count of usernames over a certain period of time. Currently I'm running this query
SELECT DISTINCT username FROM user_activity WHERE company_id = 9 AND timestamp BETWEEN '2015-09-00' AND '2015-10-01' AND action = "Login Success";
It works great, however, I have multiple Companies that I want to select the count for. How do I expand the previous query to show me the distinct counts for multiple companies?
select count(distinct username),
sum(case when company_id = 1 then 1 else 0 end) A,
sum(case when company_id = 9 then 1 else 0 end) B
from `user_activity` Where timestamp BETWEEN '2015-09-00' AND '2015-10-01' AND action = "Login Success"
I've done something like this, however, I'm not getting the correct numbers. Ideally I would like to list each count as a different value for ease of reading, like the previous query illustrates. I don't need the count(distinct username) column to appear in my result, just the conditionals.
Thanks in advance.
If you don't mind two rows instead of two columns:
SELECT company_id, COUNT(DISTINCT username)
FROM user_activity
WHERE company_id IN (1,9)
AND timestamp >= '2015-09-01'
AND timestamp < '2015-09-01' + INTERVAL 1 MONTH
AND action = "Login Success"
GROUP BY company_id

Mysql query with joins

I am not able to make a query to get some crossed values. My knowledge of mysql is not so good to make this kind of things.
I have two tables, signedup and signedupLocal. Both with same fields: name, surname1, surname2, NIF, year,city, status and dateSigned.
I want to get all values from both tables together when the status is 0 in both ordered by surname1 and surname2 (A to Z). ALso I want to have a new field called for example "app" in which it says yes or no depending on which table the data came. If signedpupLocal, yes else no.
After that, I want to do the same thing when status is 1 ordered by dateSigned (older first).
I haven't tried to go so far to test a complete query because I have problems with just part of it.
I already tried something like:
SELECT signedupLocal.name,
signedupLocal.surname1,
signedupLocal.surname2,
signedupLocal.NIF,
signedupLocal.year,
signedupLocal.city,
signedup.name,
signedup.surname1,
signedup.surname2,
signedup.NIF,
signedup.year,
signedup.city
FROM signedup, signedupLocal
WHERE signedup.id_Event = 78
AND signedupLocal.id_Event = 78
AND signedupLocal.status = 0
AND signedup.status = 0
But it fails.
SELECT name, surname1, surname2, NIF, year, city, status, 'no' as app,
case when status = 1 then datesigned else '2099-12-31' end as datesigned_sort,
case when status = 0 then surname1 else '' end as surname1_sort,
case when status = 0 then surname2 else '' end as surname2_sort
FROM signedup
WHERE id_Event = 78 AND status in(0,1)
UNION ALL
SELECT name, surname1, surname2, NIF, year, city, status, 'yes' as app,
case when status = 1 then datesigned else '2099-12-31' end as datesigned_sort,
case when status = 0 then surname1 else '' end as surname1_sort,
case when status = 0 then surname2 else '' end as surname2_sort
FROM signedupLocal
WHERE id_Event = 78 AND status in(0,1)
ORDER BY 7 asc, 9 desc, 10 asc, 11 asc
Note that when using UNION, you need to use column numbers, not column names in the ORDER BY. And I used UNION ALL as it is more efficient than UNION, as the latter remove duplicates - which causes effort for the database engine. So if you know there are none, you can avoid this.
For the sorting, I use a constant for the cases where the record should not be sorted by a sort column.
Use a UNION of 2 queries to concatenate the results.
SELECT * FROM (
SELECT *, "signedup" AS tablename FROM signedup WHERE …
UNION
SELECT *, "signedupLocal" AS tablename FROM signedupLocal WHERE …
) U
ORDER BY U.dateSigned DESC

MySQL Query to find row duplicates based on condition with limit

I have two tables:
Members:
id username
Trips:
id member_id flag_status created
("YES" or "NO")
I can do a query like this:
SELECT
Trip.id, Trip.member_id, Trip.flag_status
FROM
trips Trip
WHERE
Trip.member_id = 1711
ORDER BY
Trip.created DESC
LIMIT
3
Which CAN give results like this:
id member_id flag_status
8 1711 YES
9 1711 YES
10 1711 YES
My goal is to know if the member's last three trips all had a flag_status = "YES", if any of the three != "YES", then I don't want it to count.
I also want to be able to remove the WHERE Trip.member_id = 1711 clause, and have it run for all my members, and give me the total number of members whose last 3 trips all have flag_status = "YES"
Any ideas?
Thanks!
http://sqlfiddle.com/#!2/28b2d
In that sqlfiddle, when the correct query i'm seeking runs, I should see results such as:
COUNT(Member.id)
2
The two members that should qualify are members 1 and 3. Member 5 fails because one of his trips has flag_status = "NO"
You could use GROUP_CONCAT function, to obtain a list of all of the status ordered by id in ascending order:
SELECT
member_id,
GROUP_CONCAT(flag_status ORDER BY id DESC) as status
FROM
trips
GROUP BY
member_id
HAVING
SUBSTRING_INDEX(status, ',', 3) NOT LIKE '%NO%'
and then using SUBSTRING_INDEX you can extract only the last three status flags, and exclude those that contains a NO. Please see fiddle here. I'm assuming that all of your rows are ordered by ID, but if you have a created date you should better use:
GROUP_CONCAT(flag_status ORDER BY created DESC) as status
as Raymond suggested. Then, you could also return just the count of the rows returned using something like:
SELECT COUNT(*)
FROM (
...the query above...
) as q
Although I like the simplicity of fthiella's solution, I just can't think of a solution that depends so much on data representation. In order not to depend on it you can do something like this:
SELECT COUNT(*) FROM (
SELECT member_id FROM (
SELECT
flag_status,
#flag_index := IF(member_id = #member, #flag_index + 1, 1) flag_index,
#member := member_id member_id
FROM trips, (SELECT #member := 0, #flag_index := 1) init
ORDER BY member_id, id DESC
) x
WHERE flag_index <= 3
GROUP BY member_id
HAVING SUM(flag_status = 'NO') = 0
) x
Fiddle here. Note I've slightly modified the fiddle to remove one of the users.
The process basically ranks the trips for each of the members based on their id desc and then only keeps the last 3 of them. Then it makes sure that none of the fetched trips has a NO in the flag_status. FInally all the matching meembers are counted.