MYSQL GROUP BY 2 field with condition - mysql

need help
I have a table:
user | visit |....
user = 1,2,3,4
visit = 1,2,3,4
QUERY will work, but i want to agragate fields in MYSQL
SELECT COUNT(visit) as c, t.visit, t.user FROM t GROUP BY t.visit, t.user
I need return GROUP BY user fields without duplicate rows in filed user:
t.user, COUNT (t.visit where t.visit=1), COUNT( all t.visit)
user = 1 | visit1 = 10|. visit total = 100
......

To conditionally count a column, count the result of a CASE expression...
SELECT
t.user,
COUNT(CASE WHEN t.visit = 1 THEN 1 END) AS visit_1,
COUNT(*) AS visit_total
FROM
t
GROUP BY
t.user
Notes:
if a CASE expression doesn't have an ELSE clause, it defaults to ELSE NULL.
COUNT() only counts values that are NOT NULL.

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);

MySQL GROUP unless value equals 0

I would like a query that groups the results together unless the value of job_num = 0 but I have been unable to work out how to do it.
This is my query as it stands (and doesn't work, I get a SQL error)...
SELECT SQL_CALC_FOUND_ROWS * FROM calls
WHERE parent_id = '$term' GROUP BY IF (job_num != 0)
ORDER BY date_start DESC LIMIT $page_position, $item_per_page
I have tried replacing job_num != 0 with job_num IS NOT NULL and get the same result.
The rest of the query works fine until I tried to exclude the job_num != 0
This a simplified version of the table
id | call_ref | job_num
_______________________
1 | 123445 | 2389
_______________________
2 | 342537 | 0
_______________________
3 | 876483 | 2389
_______________________
4 | 644686 | 5643
_______________________
5 | 654532 | 0
I would like to group the rows where the job_num != 0 but I still want to display the rows where job_num = 0 just not grouped together. The call_ref is not unique and there are a further 31 columns in the table I need the values of.
Using the above example rows with ids 1 and 3 would be grouped and 2,4 and 5 would still return results but not grouped.
The results I would like...
1 and 3 grouped (because the job_nums are the same but != 0)
2, 4, 5 not grouped
Can you try it with UNION?
(SELECT SQL_CALC_FOUND_ROWS * FROM calls
WHERE parent_id = '$term' where job_num != 0 GROUP BY (job_num)
ORDER BY date_start DESC LIMIT $page_position, $item_per_page)
UNION
(SELECT SQL_CALC_FOUND_ROWS * FROM calls
WHERE parent_id = '$term' where job_num = 0
ORDER BY date_start DESC LIMIT $page_position, $item_per_page);
if you use this request in PHP code Try
SELECT SQL_CALC_FOUND_ROWS FROM calls
WHERE parent_id = \'$term\' GROUP BY IF job_num <> 0
ORDER BY date_start DESC LIMIT $page_position, $item_per_page;
Try this one
SELECT *
FROM
(SELECT SQL_CALC_FOUND_ROWS *
FROM calls
WHERE parent_id = '$term'
AND job_num != 0
GROUP BY job_num
UNION SELECT SQL_CALC_FOUND_ROWS *
FROM calls
WHERE parent_id = '$term'
AND job_num = 0) AS T
ORDER BY date_start DESC LIMIT $page_position,
$item_per_page
Based on this comment:
#mcNets because ideally I'd like to display the results with job_num
!= 0, I just don't want the results with that value grouped together
You do not want group by (although I don't understand what "that value" refers to). I do note this is in direct contradiction of the first sentence of the question.
Does this do what you want?
SELECT SQL_CALC_FOUND_ROWS c.*
FROM calls c
WHERE c.parent_id = '$term' AND job_num <> 0
ORDER BY date_start DESC
LIMIT $page_position, $item_per_page;
EDIT:
It occurs to me that you might just want rows with the value of 0 to appear together and the rest to appear together. If that is the case, put the condition in the ORDER BY:
SELECT SQL_CALC_FOUND_ROWS c.*
FROM calls c
WHERE c.parent_id = '$term'
ORDER BY (job_num = 0), date_start DESC
LIMIT $page_position, $item_per_page;
You can use a derived table for the main query, then LEFT JOIN it on to the table that you want to display multiple rows for. For example:
SELECT *
FROM (
SELECT a.account_id 'CRM ID', a.lead_status 'Status', a.account_name 'Client Name', DATE_FORMAT(DATE_ADD(FROM_UNIXTIME(0), INTERVAL a.date_of_birth SECOND), '%d-%m-%Y') AS 'DOB', a.postcode 'Postcode',
a.phone 'Phone #.', a.email 'Email', IFNULL(i.invoice_date, 'Never') AS 'Last Invoice'
FROM mydb.invoices i
RIGHT JOIN mydb.members m ON i.member_id = m.member_id
RIGHT JOIN mydb.accounts a ON m.crm_id = a.account_id
WHERE (i.invoice_status LIKE 'SAVE%' OR i.invoice_date IS NULL)
AND a.lead_status IN ('SIGNED UP', 'BILLING')
AND a.account_position != 'DELETED'
GROUP BY a.account_id
) x
LEFT JOIN mydb.account_owners o ON x.`CRM ID` = o.account_id
In this query, the final LEFT JOIN is the one that makes the row display more than once, despite the GROUP BY, e.g.:
Aside: ignore the fact that the columns in the above query have got 'friendly' names. This query is taken from a reporting tool that shows the columns based upon what SQL returns :)

Split column conditionally

I want to split a column based on if a condition is true or false and count the number of patients in each category for each doctor
This is my code:
SELECT p.MRP_CP_ID, COUNT(p.PTNT_ID) FROM PATIENT p
JOIN PATIENT_SCHEDULE ps ON ps.PTNT_ID = p.PTNT_ID
WHERE ps.MLTPL_PHRM_FLG = 0
GROUP BY MRP_CP_ID;
But i want to display the count of patients for where the MLTPL_PHRM_FLG is 1 in another column. Is there an easy way to do this?
current result from that displayed here
This can be done with conditional aggregation.
SELECT p.MRP_CP_ID,
COUNT(*) Total,
COUNT(CASE WHEN ps.MLTPL_PHRM_FLG = 0 then 1 END) as PHRM_FLG_0_Counts,
COUNT(CASE WHEN ps.MLTPL_PHRM_FLG = 1 then 1 END) as PHRM_FLG_1_Counts
FROM PATIENT p
JOIN PATIENT_SCHEDULE ps ON ps.PTNT_ID = p.PTNT_ID
GROUP BY MRP_CP_ID;

mysql count multiple types with a single query

I have a table "users" where each user has a column "status" containing the value "1" or "2" or "3";
Now with a single mysqli query, I just want to count how many users with status = 1, how many users with status = 2, and how many users with status = 3
NB: The query must be count ONLY and no data has to selected or output except the number of each status.
I found other questions here covering nearly the same topic but they are not exactly the same.
Thanks.
How about this:
SELECT
count(case when status = 1 then 1 else null end) AS STATUS_1,
count(case when status = 2 then 1 else null end) AS STATUS_2,
count(case when status = 3 then 1 else null end) AS STATUS_3
FROM users;
count will only count non-null entries and case-when will work on other databases as well.
This can be done by selectivity counting: just use a CASE expression inside count that only passed a non-null value for the desired rows:
SELECT count(CASE WHEN status = 1 THEN 1 END) status_1
, count(CASE WHEN status = 2 THEN 1 END) status_2
, count(CASE WHEN status = 3 THEN 1 END) status_3
FROM users
This is the same as the nicer, but not nativley supported syntax using FILTER:
SELECT count(*) FILER(WHERE status = 1) status_1
, count(*) FILER(WHERE status = 2) status_2
, count(*) FILER(WHERE status = 3) status_3
FROM users
Read more about this on my website modern SQL: http://modern-sql.com/feature/filter
Use something like this:
SELECT
SUM(IF(value=1,1,0)) as one,
SUM(IF(value=2,1,0)) as two,
SUM(IF(value=3,1,0)) as trhee
FROM users
The IF gives only a '1' back when your values is what you want it to be.

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