MYSQL - How do I show 0 is there are no results? - mysql

I have the below query which is working fine and shows any missed calls on a dashboard. However if there are no results it returns no data. How do I show '0' is there are no results? Thanks
SELECT DialledNumber, COUNT(*) As Missed
FROM CALLS
WHERE destination like '%!%' AND datetime BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 1 day)
GROUP By DialledNumber
HAVING (DialledNumber = '500') OR (DialledNumber = '580') OR (DialledNumber = '515') OR (DialledNumber = '513') OR (DialledNumber = '514')

You can use IFNULL function in MYSQL
SELECT DialledNumber, IFNULL(COUNT(DialledNumber),0) As Missed
FROM CALLS
WHERE destination like '%!%' AND datetime BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 1 day)
GROUP By DialledNumber
HAVING (DialledNumber = '500') OR (DialledNumber = '580') OR (DialledNumber = '515') OR (DialledNumber = '513') OR (DialledNumber = '514')

You want to use left join rather than in:
SELECT dn.dn, COUNT(c.diallednumber) As Missed
FROM (select '500' as dn union all select '580' union all select '515' union all
select '513' union all select '514'
) dn left join
CALLS c
ON c.diallednumber = dn.dn and
c.destination like '%!%' AND
c.datetime BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 1 day)
GROUP By dn.dn;
As a small note, in English the word "dialed" does not have two "l"s.

This could work for you (untested):
SELECT CALLS.DialedNumber, IFNULL(MISSEDCALLS.Missed, 0) FROM CALLS
LEFT JOIN (
SELECT DialedNumber, COUNT(*) As Missed FROM CALLS
WHERE destination like '%!%' AND datetime BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 1 day)
GROUP By DialledNumber
) AS MISSEDCALLS ON MISSEDCALLS.DialedNumber = CALLS.DialedNumber
WHERE CALLS.DialledNumber IN ('500', '580', '515', '513', '514')

Related

Mysql group_concat with in() function

i'm trying to use group concat with in function on mysql, it didn't give me any syntax error but i'm not sure its working.
i'm trying to do something like that:
select
case
when weekday(ps.date) in (group_concat(vd.valid_days)) then ps.date
when weekday(ps.date+1) in (group_concat(vd.valid_days)) then DATE_ADD(ps.date, INTERVAL 1 DAY)
when weekday(ps.date+2) in (group_concat(vd.valid_days)) then DATE_ADD(ps.date, INTERVAL 2 DAY)
when weekday(ps.date+3) in (group_concat(vd.valid_days)) then DATE_ADD(ps.date, INTERVAL 3 DAY)
when weekday(ps.date+4) in (group_concat(vd.valid_days)) then DATE_ADD(ps.date, INTERVAL 4 DAY)
when weekday(ps.date+5) in (group_concat(vd.valid_days)) then DATE_ADD(ps.date, INTERVAL 5 DAY)
end as valid_date
from
purchase_shopping ps
left join
purchase_region pr on pr.id = ps.idfk_region
left join
valid_weeks_days vd on vd.idfk_region = pr.id
group by ps.id
what i need basically is to return a valid date if certain day is inside a group concat from a left join. I don't know if my logic is wrong or it does not work.
Obs: The group concat function return something like that: (1,2,5), so it's supposed to work with in() function.
Sample with reworked code data:
ps.date = 2018-10-17
weekday(ps.date) = 2
group_concat(vd.valid_days) = (1,4)
then valid_date will be 2018-10-19
Perhaps you simply wants :
select (case when uc.id is not null then 'It has items' end)
from shopping_cart sc left join
user_cart uc
on uc.id = sc.idfk_cart;
You cannot use result of group_concat in same query so you have to use a subquery
select
case
when weekday(date) in valid_days then date
when weekday(date+1) in valid_days then DATE_ADD(date, INTERVAL 1 DAY)
when weekday(date+2) in valid_days then DATE_ADD(date, INTERVAL 2 DAY)
when weekday(date+3) in valid_days then DATE_ADD(date, INTERVAL 3 DAY)
when weekday(date+4) in valid_days then DATE_ADD(date, INTERVAL 4 DAY)
when weekday(date+5) in valid_days then DATE_ADD(date, INTERVAL 5 DAY)
end as valid_date
from (
select ps.date, (group_concat(vd.valid_days)) as valid_days
from
purchase_shopping ps
left join
purchase_region pr on pr.id = ps.idfk_region
left join
valid_weeks_days vd on vd.idfk_region = pr.id
group by ps.id
) as sub_query

How to change format of the MySQL result?

I have a complex mysql query language, including several sub queries and my final result is as below. There is something that I am dealing with it and I can't solve it and this is a way result is being presented. I am wondering to know how can i change the structure of the result in a way that the result is being presented only in one row and I don't want to see NULL fields. I mean something like below
This is mysql query
select count(*) as userRetentionSameDay, null as 'userRetentionDiffDay' from (SELECT date(`timestamp`), `user_xmpp_login`
FROM table1
WHERE DATE(`timestamp` ) = CURDATE() - INTERVAL 1 DAY) as res1
right join (select date(ts), user
from table2
WHERE DATE(ts ) = CURDATE() - INTERVAL 1 DAY
and product_id REGEXP ("^(europe+$" )) as lej1
on lej1.user = res1.`user_xmpp_login`
where res1.`user_xmpp_login` IS not NULL
union all
select null as 'userRetentionSameDay', count(*) as userRetentionDiffDay from (SELECT date(`timestamp`), `user_xmpp_login`
FROM table1
WHERE DATE(`timestamp` ) = CURDATE() - INTERVAL 1 DAY) as res1
right join (select date(ts), user
from table2
WHERE DATE(ts ) = CURDATE() - INTERVAL 1 DAY
and product_id REGEXP ("^(europe+$" )) as lej2
on lej2.user = res1.`user_xmpp_login`
where res1.`user_xmpp_login` IS NULL;
What are the recommended solutions to doing that?
try this.
SELECT A.userRetentionSameDay,B.userRetentionDiffDay FROM (
SELECT COUNT() AS userRetentionSameDay FROM
(
SELECT DATE(timestamp), user_xmpp_login
FROM table1
WHERE DATE(timestamp ) = CURDATE() - INTERVAL 1 DAY) AS res1
RIGHT JOIN (SELECT DATE(ts), USER
FROM table2
WHERE DATE(ts ) = CURDATE() - INTERVAL 1 DAY
AND product_id REGEXP ("^(europe+$" )) AS lej1
ON lej1.user = res1.user_xmpp_login
WHERE res1.user_xmpp_login IS NOT NULL
) A,
(
SELECT COUNT() AS userRetentionDiffDay FROM (
SELECT DATE(timestamp), user_xmpp_login
FROM table1
WHERE DATE(timestamp ) = CURDATE() - INTERVAL 1 DAY
) AS res1
RIGHT JOIN (SELECT DATE(ts), USER
FROM table2
WHERE DATE(ts ) = CURDATE() - INTERVAL 1 DAY
AND product_id REGEXP ("^(europe+$" )
) AS lej2
ON lej2.user = res1.user_xmpp_login
WHERE res1.user_xmpp_login IS NULL
) B;

MySQL using IF or CASE statement across joined tables

HI all here is a MySQL problem that uses results from a 2 table join, conditionally assess them and outputs 2 values.
Here is the database structure.
The 1st table gtpro contains
a user ID (column name id)
a samples/year number ie 2, 4 or 12 times/year (column name labSamples__yr)
The 2nd table labresults contains
that same user ID (column name idgtpro)
and a date column for the sample dates (when the samples were provided) column name date
so this query returns an overview of all id's and when were the last samples submitted for that id.
SELECT a.id, a.labSamples__yr, max(b.date) as ndate from gtpro as a
join labresults as b on a.id = b.idgtpro group by a.id
the conditions I want to evaluate looks like this.
a.labSamples__yr = 2 and ndate >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH)
a.labSamples__yr = 4 and ndate >= DATE_SUB(CURDATE(), INTERVAL 3 MONTH)
a.labSamples__yr = 12 and ndate >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH)
So if number of samples /year is 2 and the last samle date was more than 6 months ago I want to know the id and latest date of samples for that id.
I tried using CASE and IF statements but can't quite get it right. This was my latest attempt.
select id, ndate,
case when (labSamples__yr = 2 and ndate <= DATE_SUB(CURDATE(), INTERVAL 6 MONTH))is true
then
(SELECT id from gtpro as a join labresults as b on a.id = b.idgtpro where
labSamples__yr = 2 and max(b.date) <= DATE_SUB(CURDATE(), INTERVAL 6 MONTH)) end as id
from (SELECT a.id, a.labSamples__yr, max(b.date) as ndate from gtpro as a
join labresults as b on a.id = b.idgtpro group by a.id) d
this tells me invalid use of group function.
Desperate for a bit of help
EDIT I messed up some of the names in the code above which i have now fixed.
If I understand your question correctly, you should be able to put the conditions in the where clause:
SELECT a.id, a.labSamples__yr, max(b.date) as ndate
from gtpro a join
labresults b
on a.id = b.idgtpro
where (a.labSamples__yr = 2 and b.date >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH)) or
(a.labSamples__yr = 4 and b.date >= DATE_SUB(CURDATE(), INTERVAL 3 MONTH)) or
(a.labSamples__yr = 12 and b.date >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH))
group by a.id;
That fixes your syntax problem. But, if you want the id with the maximum date, try doing this:
select a.labSamples__yr, max(b.date) as ndate,
substring_index(group_concat(a.id order by b.date desc)) as maxid
from gtpro a join
labresults b
on a.id = b.idgtpro
where (a.labSamples__yr = 2 and b.date >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH)) or
(a.labSamples__yr = 4 and b.date >= DATE_SUB(CURDATE(), INTERVAL 3 MONTH)) or
(a.labSamples__yr = 12 and b.date >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH))
group by a.labSamples__yr;
Putting a.id in the group by is not going to give you the maximum id of anything.
Is this meant to be valid MySQL? I wasn't aware of "is true" being valid in a CASE statement. In fairness though I'm more familiar with Oracle and SQL Server but nevertheless... does any part of this statement work?
EDIT
Ok, here is what I have edited the code to be:
select id, ndate,
case when (labSamples__yr = 2 and ndate <= DATE_SUB(CURDATE(), INTERVAL 6 MONTH))
then
(SELECT id from bifipro as a join labresults as b on a.id = b.idBifipro where
labSamples__yr = 2 and max(b.date) <= DATE_SUB(CURDATE(), INTERVAL 6 MONTH) where a.id=d.id) end as id
from (SELECT a.id, a.labSamples__yr, max(b.date) as ndate from bifipro as a
join labresults as b on a.id = b.idBifipro group by a.id) d
In your correlated subquery I have added a predicate of "where a.id =
d.id"
I have removed the text "is true" from your case statement (this may
be incorrect but I didnt' think it should be there.
The answer partly inspired by Tomas (sql clarification and syntax clarification) I got rid of the CASE all together. It seems nice and clean to me but I would like to hear any other suggestions
select id, labSamples__yr, ndate from
(SELECT a.id, a.labSamples__yr, max(b.date) as ndate from gtpro as a
join labresults as b on a.id = b.idgtpro group by a.id)d
where (ndate <= DATE_SUB(CURDATE(), INTERVAL 6 MONTH) and labSamples__yr = 2)
or (ndate <= DATE_SUB(CURDATE(), INTERVAL 3 MONTH) and labSamples__yr = 4)
or (ndate <= DATE_SUB(CURDATE(), INTERVAL 1 MONTH) and labSamples__yr = 12)
Thanks for looking but it would still be nice to see a solution using a CASE statement for future reference???

Convert NOT IN query to better performance

I'm using MySQL 5.0, and I need to fine tune this query. Can anyone please tell me what tuning I can do in this?
SELECT DISTINCT(alert_master_id) FROM alert_appln_header
WHERE created_date < DATE_SUB(CURDATE(), INTERVAL (SELECT parameters FROM schedule_config WHERE schedule_name = "Purging_Config") DAY)
AND alert_master_id NOT IN (
SELECT DISTINCT(alert_master_id) FROM alert_details
WHERE end_date IS NULL AND created_date < DATE_SUB(CURDATE(), INTERVAL (SELECT parameters FROM schedule_config WHERE schedule_name = "Purging_Config") DAY)
UNION
SELECT DISTINCT(alert_master_id) FROM alert_sara_header
WHERE sara_master_id IN
(SELECT alert_sara_master_id FROM alert_sara_lines
WHERE end_date IS NULL) AND created_date < DATE_SUB(CURDATE(), INTERVAL (SELECT parameters FROM schedule_config WHERE schedule_name = "Purging_Config") DAY)
) LIMIT 5000;
The first thing that I'd do is rewrite the subqueries as joins:
SELECT h.alert_master_id
FROM alert_appln_header h
JOIN schedule_config c
ON c.schedule_name = 'Purging_Config'
LEFT JOIN alert_details d
ON d.alert_master_id = h.alert_master_id
AND d.end_date IS NULL
AND d.created_date < CURRENT_DATE - INTERVAL c.parameters DAY
LEFT JOIN (
alert_sara_header s
JOIN alert_sara_lines l
ON l.alert_sara_master_id = s.sara_master_id
)
ON s.alert_master_id = h.alert_master_id
AND s.end_date IS NULL
AND s.created_date < CURRENT_DATE - INTERVAL c.parameters DAY
WHERE h.created_date < CURRENT_DATE - INTERVAL c.parameters DAY
AND d.alert_master_id IS NULL
AND s.alert_master_id IS NULL
GROUP BY h.alert_master_id
LIMIT 5000
If it's still slow after that, re-examine your indexing strategy. I'd suggest indexes over:
alert_appln_header(alert_master_id,created_date)
schedule_config(schedule_name)
alert_details(alert_master_id,end_date,created_date)
alert_sara_header(sara_master_id,alert_master_id,end_date,created_date)
alert_sara_lines(alert_sara_master_id)
OK, this may be just a shot in the dark, but I think you don't need as many DISTINCT here.
SELECT DISTINCT(alert_master_id) FROM alert_appln_header
WHERE created_date < DATE_SUB(CURDATE(), INTERVAL (SELECT parameters FROM schedule_config WHERE schedule_name = "Purging_Config") DAY)
AND alert_master_id NOT IN (
-- removed distinct here --
SELECT alert_master_id FROM alert_details
WHERE end_date IS NULL AND created_date < DATE_SUB(CURDATE(), INTERVAL (SELECT parameters FROM schedule_config WHERE schedule_name = "Purging_Config") DAY)
UNION
-- removed distinct here --
SELECT alert_master_id FROM alert_sara_header
WHERE sara_master_id IN
(SELECT alert_sara_master_id FROM alert_sara_lines
WHERE end_date IS NULL)
AND created_date < DATE_SUB(CURDATE(), INTERVAL (SELECT parameters FROM schedule_config WHERE schedule_name = "Purging_Config") DAY)
) LIMIT 5000;
Since using the DISTINCT is very costly, try to avoid it. In the first WHERE clause you are checking for ids that are NOT within some result, so it shouldn't matter if in that result some ids appear more than once.

mysql join same table different result set

I would like to combine different results from the same table as one big result.
SELECT host_name,stats_avgcpu,stats_avgmem,stats_avgswap,stats_avgiowait
FROM sar_stats,sar_hosts,sar_appgroups,sar_environments
WHERE stats_host = host_id
AND host_environment = env_id
AND env_name = 'Staging 2'
AND host_appgroup = group_id
AND group_name = 'Pervasive'
AND DATE(stats_report_time) = DATE_SUB(curdate(), INTERVAL 1 DAY)
SELECT AVG(stats_avgcpu),AVG(stats_avgmem),AVG(stats_avgswap),AVG(stats_avgiowait)
FROM sar_stats
WHERE stats_id = "stat_id of the first query" and DATE(stats_report_time)
BETWEEN DATE_SUB(curdate(), INTERVAL 8 DAY) and DATE_SUB(curdate(), INTERVAL 1 DAY)
SELECT AVG(stats_avgcpu),AVG(stats_avgmem),AVG(stats_avgswap),AVG(stats_avgiowait)
FROM sar_stats
WHERE stats_id = "stat_id of the first query" and DATE(stats_report_time)
BETWEEN DATE_SUB(curdate(), INTERVAL 31 DAY) and DATE_SUB(curdate(), INTERVAL 1 DAY)
Desired output would be something like ...
host_name|stats_avgcpu|stats_avgmem|stats_avgswap|stats_avgiowait|7daycpuavg|7daymemavg|7dayswapavg|7dayiowaitavg|30daycpuavg|30daymemavg|....etc
SQL Fiddle
http://sqlfiddle.com/#!8/4930b/3
It seems like this is what you want. I updated the first query to use proper ANSI JOIN syntax and then for the additional two queries they were joined via a LEFT JOIN on the stats_host field:
SELECT s.stats_host,
h.host_name,
s.stats_avgcpu,
s.stats_avgmem,
s.stats_avgswap,
s.stats_avgiowait,
s7.7dayavgcpu,
s7.7dayavgmem,
s7.7dayavgswap,
s7.7dayavgiowait,
s30.30dayavgcpu,
s30.30dayavgmem,
s30.30dayavgswap,
s30.30dayavgiowait
FROM sar_stats s
INNER JOIN sar_hosts h
on s.stats_host = h.host_id
INNER JOIN sar_appgroups a
on h.host_appgroup = a.group_id
and a.group_name = 'Pervasive'
INNER JOIN sar_environments e
on h.host_environment = e.env_id
and e.env_name = 'Staging 2'
LEFT JOIN
(
SELECT s.stats_host,
AVG(s.stats_avgcpu) AS '7dayavgcpu',
AVG(s.stats_avgmem) AS '7dayavgmem',
AVG(s.stats_avgswap) AS '7dayavgswap',
AVG(s.stats_avgiowait) AS '7dayavgiowait'
FROM sar_stats s
WHERE DATE(stats_report_time) BETWEEN DATE_SUB(curdate(), INTERVAL 8 DAY) AND DATE_SUB(curdate(), INTERVAL 1 DAY)
GROUP BY s.stats_host
) s7
on s.stats_host = s7.stats_host
LEFT JOIN
(
SELECT s.stats_host,
AVG(s.stats_avgcpu) AS '30dayavgcpu',
AVG(s.stats_avgmem) AS '30dayavgmem',
AVG(s.stats_avgswap) AS '30dayavgswap',
AVG(s.stats_avgiowait) AS '30dayavgiowait'
FROM sar_stats s
WHERE DATE(s.stats_report_time) BETWEEN DATE_SUB(curdate(), INTERVAL 31 DAY) AND DATE_SUB(curdate(), INTERVAL 1 DAY)
GROUP BY s.stats_host
) s30
on s.stats_host = s30.stats_host
WHERE DATE(s.stats_report_time) = DATE_SUB(curdate(), INTERVAL 1 DAY);
see SQL Fiddle with Demo