How get conditional count while using group by in mysql? - mysql

Mysql newbie here.
I have a table( name:'audit_webservice_aua' ) like this:
+---------+------------------------------------+-------------------+------------------------+
| auditId | device_code | response_status | request_date
+---------+------------------------------------+-------------------+------------------------+
| 10001 | 0007756-gyy66-4c6e-a59d-xxxccyyyt1 | P | 2020-03-02 00:00:08.785
| 10002 | 0007756-gyy66-4c6e-a59d-xxxccyyyt2 | F | 2020-04-06 00:00:08.785
| 10003 | 0007756-gyy66-4c6e-a59d-xxxccyyyt3 | F | 2020-04-01 00:01:08.785
| 10004 | 0007756-gyy66-4c6e-a59d-xxxccyyyt1 | P | 2020-05-02 00:02:08.785
| 10005 | 0007756-gyy66-4c6e-a59d-xxxccyyyt1 | P | 2020-05-09 00:03:08.785
| 10006 | 0007756-gyy66-4c6e-a59d-xxxccyyyt2 | P | 2020-05-09 01:00:08.785
| 10007 | 0007756-gyy66-4c6e-a59d-xxxccyyyt7 | F | 2020-06-06 02:00:08.785
+---------+------------------------------------+-------------------+------------------------+
Every time a new request is made the above table stores the requesting device_code ,response_status and request time.
I have a requirement of getting the result set which contains the each device_code, total_trans, total_successful, total_failure and date for each day between two given dates.
The query i have written is as follows:
SELECT DATE_FORMAT(aua.request_date,'%b') as month ,
YEAR(aua.request_date) as year,
DATE_FORMAT(aua.request_date,'%Y-%m-%d') as date,
(select count(aua.audit_id) )as total_trans ,
(select count(aua.audit_id) where aua.response_status 'P') as total_failure ,
(select count(aua.audit_id) where aua.response_status = 'P') as total_successful ,
aua.device_code as deviceCode
FROM audit_webservice_aua aua where DATE_FORMAT(aua.request_date,'%Y-%m-%d') between '2020-04-16' and '2020-07-17'
group by dates,deviceCode ;
In the above code im tring to get results between '2020-03-02' and '2020-06-06' but the count im getting is not correct.
Any help would be appreciated.
Thank you in advance.

I think you just want conditional aggregation:
SELECT DATE_FORMAT(aua.request_date,'%b') as month ,
YEAR(aua.request_date) as year,
DATE_FORMAT(aua.request_date, '%Y-%m-%d') as date,
COUNT(aua.audit_id) as total_trans ,
SUM(aua.response_status <> 'P') as total_failure,
SUM(aua.response_status = 'P') as total_successful,
aua.device_code as deviceCode
FROM audit_webservice_aua aua
WHERE DATE_FORMAT(aua.request_date, '%Y-%m-%d') between '2020-04-16' and '2020-07-17'
GROUP BY month, year, date, deviceCode ;
I would also advise you to change the WHERE clause to:
WHERE aua.request_date >= '2020-04-16' AND
aua.request_date >= '2020-07-18'

Related

How to get multiple records one record using query in mysql

How to be able to query from this data:
parking_place | number_of_month | from_date | end_date | monthly_unit_price
A | 3 | 2018-01 | 2018-03 | 3000000
Desire to show results:
parking_place | month | monthly_unit_price
A | 2018-01 | 3000000
A | 2018-02 | 3000000
A | 2018-03 | 3000000
please suggest me how to query?
You may join using a calendar table:
SELECT
t.parking_place,
t.month,
t.monthly_unit_price
FROM
(
SELECT '2018-01' AS month UNION ALL
SELECT '2018-02' UNION ALL
SELECT '2018-03'
) months
INNER JOIN yourTable t
ON months.month BETWEEN t.from_date AND t.end_date
ORDER BY
months.month;
Note that it would be better to store actual valid date literals to represent each month. For example, instead of storing the text '2018-01', you could store 2018-01-01 as a date literal.

MYSQL select query return list of months as string from between start/end date With Ascending order of month year

For this question
(MYSQL select query return list of months as string from between start/end date) I have found solution of query, It gives correct result , BUT I need Month list in Ascending order.
Table : Contracts
------------------------------
ID | START | END |
------------------------------
1 | 2016-05-01 | 2016-07-31 |
2 | 2016-04-01 | 2016-08-31 |
3 | 2016-01-22 | 2016-02-25 |
4 | 2016-06-15 | 2017-11-30 |
------------------------------
Here I need result as per bellow formate, one extra field which represent range/list of months between startdate and enddate of contract using SELECT query.
Result (as per give format)
----------------------------------------------------------------------------------------
ID | START | END | Description
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 | 2016-05-01 | 2016-07-31 | May-2016, Jun-2016, July-2016
2 | 2016-04-01 | 2016-07-31 | April-2016, May-2016, Jun-2016, July-2016
3 | 2016-01-22 | 2016-02-25 | January-2016, February-2016
3 | 2016-06-15 | 2017-11-30 | May-2017 ,November-2016 ,June-2016 ,August-2017 ,March-2017 ,July-2016 ,October-2016 ,November-2017 ,June-2017 ,February-2017 ,September-2016 ,September-2017 ,August-2016,April-2017 ,January-2017 ,July-2017 ,December-2016 ,October-2017
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL Query is:
Select id, DATE_FORMAT(start_Date, '%Y-%c-%d') as Start_Date,
DATE_FORMAT(end_date,'%Y-%c-%d') as END_Date,
group_concat( distinct(DATE_FORMAT(aDate, '%M %Y'))) as Descp
from (
select ss.end_date - interval (a.a ) month as aDate from
(select 0 as a union all select 1 union all select 2 union all select 3
union all select 4 union all select 5 union all select 6 union all
select 7 union all select 8 union all select 9) a, Contracts ss
) mon, Contracts sa
where aDate between sa.start_date and sa.end_date
group by id;
It give result randomly like result ie, "May-2017 ,November-2016 ,June-2016 ,August-2017 ,March-2017 ,July-2016 ,October-2016 ,November-2017 ,June-2017 ,February-2017 ,September-2016 ,September-2017 ,August-2016,April-2017 ,January-2017 ,July-2017 ,December-2016 ,October-2017"
BUT I need
"June-2016 ,July-2016 ,August-2016,September-2016,October-2016, November-2016 ,December-2016 ,January-2017 ,February-2017 ,March-2017 ,April-2017 ,May-2017 ,June-2017 ,July-2017 ,August-2017 ,September-2017 ,October-2017, November-2017
"
Please help me to find solution about above result,

Mysql join select max for all record

I am unable to map the record as my expectation.
Doc Table
-------+-------------------
doc_id | doc_title
-------+-------------------
1 | My book
-------+-------------------
2 | My sec Book
--------------------------
Doc details Table
-----------+--------------+-----------------------
fk_doc_id | doc_version | submit_date
-----------+--------------+-----------------------
1 | 1 | 2015-10-25 14:32:01
-----------+--------------+-----------------------
1 | 2 | 2015-10-26 13:00:01
-----------+--------------+-----------------------
1 | 3 | 2015-10-27 09:00:00
--------------------------+-----------------------
2 | 1 | 2015-10-25 11:15:01
-----------+--------------+-----------------------
2 | 2 | 2015-10-26 10:00:00
--------------------------+-----------------------
Question: How do I join this two tables to get each documents with the latest version doc info? even though I get the latest version but the row info which is not correct.
So far I have tried this query
SELECT *, max(doc_version) AS latest_version
FROM d_doc
JOIN d_doc_dtl ON d_doc.doc_id = d_doc_dtl.fk_doc_id
GROUP BY d_doc.doc_id;
My expected result is
--------+--------------+----------------+--------------------
doc_id | doc_title | latest_version | submit_date
--------+--------------+----------------+--------------------
1 | My book | 3 | 2015-10-27 09:00:00
--------+--------------+----------------+--------------------
2 | My sec book | 2 | 2015-10-26 10:00:00
----------------------------------------+--------------------
but my result is
--------+--------------+----------------+--------------------
doc_id | doc_title | latest_version | submit_date
--------+--------------+----------------+--------------------
1 | My book | 3 | 2015-10-25 14:32:01
--------+--------------+----------------+--------------------
2 | My sec book | 2 | 2015-10-25 11:15:01
----------------------------------------+--------------------
NOTE: the submit_date which is no correct.
SELECT d_doc.doc_id, d_doc.doc_title, max_table.latest_version
FROM d_doc JOIN (
select fk_doc_id, max(doc_version) as latest_version from d_doc_dtl group by fk_doc_id
) as max_table ON d_doc.doc_id = max_table.fk_doc_id
This query should work as you expect. It selects latest document versions in inner subquery and than joins it with documents.
SELECT d.doc_id,
d.doc_title,
dtl.doc_version latest_version,
dtl.submit_date
FROM d_doc d
INNER JOIN (SELECT dt.*
FROM d_doc_dtl dt
INNER JOIN (SELECT fk_doc_id, MAX(doc_version) doc_version
FROM d_doc_dtl
GROUP BY fk_doc_id) dm
ON dt.fk_doc_id = dm.fk_doc_id
AND dt.doc_version = dm.doc_version) dtl
ON d.doc_id = dtl.fk_doc_id
You get wrong results because you selected only max(version), but date as it is not in group by clause can contain any value. First you need to get records containing latest version as shown above.
Easy, instead of
SELECT *, max(doc_version) AS latest_version
Use this
SELECT d_doc.*, max(doc_version) AS latest_version
What you were doing by selecting * is getting all the results after the table is joined and you only wanted the original table results.
select * from doc_table , doc_version where exists( select
max(version_id)
from
doc_version vert
where
(doc_table .DOC_ID = vert.VERSION_DOC_ID) ) group by doc_id;
You can try something like this.

Complicated overlap in Mysql query

Here is my problem, I have a MYSQL table with the following columns and data examples :
id | user | starting date | ending date | activity code
1 | Andy | 2010-04-01 | 2010-05-01 | 3
2 | Andy | 1988-11-01 | 1991-03-01 | 3
3 | Andy | 2005-06-01 | 2008-08-01 | 3
4 | Andy | 2005-08-01 | 2008-11-01 | 3
5 | Andy | 2005-06-01 | 2010-05-01 | 4
6 | Ben | 2010-03-01 | 2011-06-01 | 3
7 | Ben | 2010-03-01 | 2010-05-01 | 4
8 | Ben | 2005-04-01 | 2011-05-01 | 3
As you can see in this table users can have same activity code and similar dates or periods. And For a same user, periods can overlap others or not. It is also possible to have several overlap periods in the table.
What I want is a MYSQL QUERY to get the following result :
new id | user | starting date | ending date | activity code
1 | Andy | 2010-04-01 | 2010-05-01 | 3 => ok, no overlap period
2 | Andy | 1988-11-01 | 1991-03-01 | 3 => ok, no overlap period
3 | Andy | 2005-06-01 | 2008-11-01 | 3 => same user, same activity but ending date coming from row 4 as extended period
4 | Andy | 2005-06-01 | 2010-05-01 | 4 => ok other activity code
5 | Ben | 2005-04-01 | 2011-06-01 | 3 => ok other user, but as overlap period rows 6 and 8 for the same user and activity, I take the widest range
6 | Ben | 2010-03-01 | 2010-05-01 | 4 => ok other activity for second user
In other words, for a same user and activity code, if there is no overlap, I need the starting and ending dates as they are. If there is an overlap for a same user and activity code, I need the lower starting date and the higher ending date coming from the different related rows. I need this for all the users and activity code of the table and in SQL for MYSQL.
I hope it is clear enough and someone can help me because I try different codes from solutions supplied on this site and others without success.
I have somewhat convoluted (strictly MySQL-specific) solution:
SET #user = NULL;
SET #activity = NULL;
SET #interval_id = 0;
SELECT
MIN(inn.`starting date`) AS start,
MAX(inn.`ending date`) AS end,
inn.user,
inn.`activity code`
FROM
(SELECT
IF(user <> #user OR `activity code` <> #activity,
#interval_id := #interval_id + 1, NULL),
IF(user <> #user OR `activity code` <> #activity,
#interval_end := STR_TO_DATE('',''), NULL),
#user := user,
#activity := `activity code`,
#interval_id := IF(`starting date` > #interval_end,
#interval_id + 1,
#interval_id) AS interval_id,
#interval_end := IF(`starting date` < #interval_end,
GREATEST(#interval_end, `ending date`),
`ending date`) AS interval_end,
t.*
FROM Table1 t
ORDER BY t.user, t.`activity code`, t.`starting date`, t.`ending date`) inn
GROUP BY inn.user, inn.`activity code`, inn.interval_id;
The underlying idea was shamelessly borrowed from the 1st answer to this question.
You can use this SQL Fiddle to review the results and try different source data.
Here is a solution - (see http://sqlfiddle.com/#!2/fda3d/15)
SELECT DISTINCT summarized.`user`
, summarized.activity_code
, summarized.true_begin
, summarized.true_end
FROM (
SELECT t1.id,t1.`user`,t1.activity_code
, MIN(LEAST(t1.`starting`, COALESCE(overlap.`starting` ,t1.`starting`))) as true_begin
, MAX(GREATEST(t1.`ending`, COALESCE(overlap.`ending` ,t1.`ending`))) as true_end
FROM t1
LEFT JOIN t1 AS overlap
ON t1.`user` = overlap.`user`
AND t1.activity_code = overlap.activity_code
AND overlap.`ending` >= t1.`starting`
AND overlap.`starting` <= t1.`ending`
AND overlap.id <> t1.id
GROUP BY t1.id, t1.`user`, t1.activity_code) AS summarized;
I am not sure how it will perform with a large data set with many overlaps. You will definitely need an index on the user and activity_code fields - probably the starting and ending date fields also as part of that index.

Get the count per date of first logged in

I have a table containing the logging of a web app which tracks when people log in. An example of my table is:
| user_id | date_time |
+---------+------------------+
| 0033 | 2012-11-22 10:33 | <- first login of 0033 on 2012-11-22
| 0034 | 2012-11-22 10:38 | <- first login of 0034 on 2012-11-22
| 0052 | 2012-11-22 10:43 | <- first login of 0052 on 2012-11-22
| 0052 | 2012-11-23 09:23 |
| 0066 | 2012-11-23 15:58 | <- first login of 0066 on 2012-11-23
| 0033 | 2012-11-23 16:14 |
The thing I want is a table with the amount of people that logged in for the first time on each date, i.e.:
| count | date |
+-------+------------+
| 3 | 2012-11-22 | <- there were 3 users that logged in for the first time on 2012-11-22
| 1 | 2012-11-23 |
I know I can get the date only, by doing
SELECT DATE(`date_time`) AS `date`
FROM `logging`
GROUP BY `date`
ORDER BY `date` ASC
I would like to get the second table in one query, I know it's possible, I just don't know how. Thanks in advance
You can use an uncorrelated subquery to get the first login date for every user and then group those dates together to get the number of first logins per day.
SELECT dd, COUNT(*)
FROM (SELECT MIN(DATE(`date_time`)) AS dd
FROM `logging`
GROUP BY `user_id`) a
GROUP BY dd
ORDER BY dd;
Demo
Count the number of logins per day, for user_ids that have do not have a previous login record:
select DATE(`date_time`) as `date`,
count(user_id)
from `logging` l1
where user_id not in (
select user_id from `logging` l2 where l1.user_id = l2.user_id and l2.date_time < l1.date_time)
group by DATE(`date_time`)
I think you need this:
SELECT count(1) ,
DATE(`date_time`)
from my_table
group by DATE(`date_time`)
If you need users which had been logged in day wise
Select
user_id, `date_time` from my_table group by DATE(`date_time`), user_id