Get the 0 value if No result in column -- MySQL - mysql

I have the following query
SELECT COUNT( iContactId ) AS Users, DATE( dCreatedAt ) AS ActivityDate
FROM contacts WHERE iAppId =".$iAppId."
AND DATE(dCreatedAt) IN (CURRENT_DATE(), CURRENT_DATE()-1 )
GROUP BY ActivityDate
by this i am getting
Users |ActivityDate
1 |2014-09-19
i want 0 if there is no match rows
e.g
Users |ActivityDate
0 |CURRENT_DATE()
0 | CURRENT_DATE()-1
How can i do that.

QUERY
SELECT
COUNT(C.iContactId) AS Users,
DATE(C.dCreatedAt) AS ActivityDate
FROM
contacts C
LEFT OUTER JOIN
(
SELECT CURRENT_DATE() AS Dates FROM dual
UNION
SELECT CURRENT_DATE() - 1 AS Dates FROM dual
) D
ON
D.Dates = DATE(C.dCreatedAt)
WHERE
C.iAppId =".$iAppId."
GROUP BY
C.ActivityDate

You could use union
SELECT COUNT( iContactId ) AS Users, CURRENT_DATE() AS ActivityDate
FROM contacts WHERE iAppId =".$iAppId."
AND DATE(dCreatedAt) = CURRENT_DATE()
UNION
SELECT COUNT( iContactId ) AS Users, CURRENT_DATE() - 1 AS ActivityDate
FROM contacts WHERE iAppId =".$iAppId."
AND DATE(dCreatedAt) = CURRENT_DATE() - 1

QUERY
SELECT
ISNULL( iContactId ,0) AS Users,
DATE( dCreatedAt ) AS ActivityDate
FROM contacts
WHERE iAppId =".$iAppId."
AND DATE(dCreatedAt) IN (CURRENT_DATE(), CURRENT_DATE()-1 )
GROUP BY ActivityDate

In most answers this is used voor date substraction:
SELECT CURRENT_DATE() - 1
-> 20140919
This will result in impliciet type casting and date wil be casted to int, use this instead:
SELECT CURRENT_DATE - INTERVAL 1 DAY
-> 2014-09-19
My solution
SELECT
d.ActivityDate,
COUNT(c.iContactId) Users
FROM
(
SELECT CURRENT_DATE - INTERVAL 1 DAY ActivityDate FROM DUAL
UNION
SELECT CURRENT_DATE FROM DUAL
) d
LEFT JOIN
contacts c
ON DATE(c.dCreatedAt) = d.ActivityDate
AND c.iAppId = " . $iAppId . "
GROUP BY d.ActivityDate

Related

Calculate Ratio of two different SQL queries result that return numbers

I have Query 1
SELECT COUNT(DISTINCT user_id) total_daily_active_user_group_month FROM (SELECT user_id , MONTHNAME(time) mon , COUNT(*) cnt FROM ACTIVITIES
WHERE MONTH(time) = MONTH(NOW() - INTERVAL 1 MONTH) GROUP by user_id, MONTH(time) ) as x
Returns 18
Query 2
SELECT COUNT(DISTINCT user_id) total_daily_active_user_group_month FROM (SELECT user_id , MONTHNAME(time) mon , COUNT(*) cnt FROM ACTIVITIES
WHERE MONTH(time) = MONTH(NOW() - INTERVAL 1 MONTH) GROUP by user_id, MONTH(time) having cnt=31) as x
Return 6
I want the ratio of query 1 and two. Means
18/6 . I am using MySQL
If you use both queries as CTEs, then it becomes relatively simple:
WITH q1
AS (SELECT Count(DISTINCT user_id) total_daily_active_user_group_month
FROM (SELECT user_id,
Monthname(TIME) mon,
Count(*) cnt
FROM activities
WHERE Month(TIME) = Month(Now() - interval 1 month)
GROUP BY user_id,
Month(TIME))),
q2
AS (SELECT Count(DISTINCT user_id) total_daily_active_user_group_month
FROM (SELECT user_id,
Monthname(TIME) mon,
Count(*) cnt
FROM activities
WHERE Month(TIME) = Month(Now() - interval 1 month)
GROUP BY user_id,
Month(TIME)
HAVING cnt = 31))
SELECT q1.total_daily_active_user_group_month /
q2.total_daily_active_user_group_month
AS result
FROM dual;
You commented that you got an error pointing to the WITH keyword; switch to two subqueries, then; simplified:
select a.value / b.value as result
from (select count(distinct user_id) value
from ... your 1st query goes here
) a,
(select count(distinct user_id) value
from ... your 2nd query goes here
) b;

How to consolidate 3 SQL-queries in one

I have three separate queries and need to combine it in one to prepare data for a chart. So the common parameter for all 3 datasets is the date.
Please help me to do it.
1)
SELECT COUNT(id) Registrations, regdate
FROM wpu_users
WHERE regdate >= '2020-01-09'
GROUP BY regdate;
2)
SELECT ROUND(SUM(total)) as amount, DATE(datetime) as date
FROM wp_payments
WHERE site_id=4 AND status=1
GROUP BY date
ORDER BY date DESC
3)
SELECT ROUND(SUM(cnt)) as Matrix, DATE(datetime) as date
FROM wp_payments
WHERE site_id=4
AND status=1
AND currency='USD'
AND description LIKE '% + %'
AND datetime IS NOT NULL
GROUP BY date
The result should be a table with columns:
Registrations
amount
Matrix
date
Based on your requirement. try this
SELECT
tab1.Registrations as "Registrations",
tab2.amount as "Amount",
tab3.Matrix as "Matrix",
tab1.regdate as "Date"
FROM
( SELECT COUNT( id ) Registrations, regdate FROM wpu_users where regdate >= '2020-01-09' GROUP BY regdate ) tab1
INNER JOIN (
SELECT
ROUND( SUM( total ) ) AS amount,
DATE( datetime ) AS date
FROM
wp_payments
WHERE
site_id = 4
AND STATUS = 1
GROUP BY
date
) tab2 ON tab1.regdate = tab2.date
INNER JOIN (
SELECT
ROUND( SUM( cnt ) ) AS Matrix,
DATE( datetime ) AS date
FROM
wp_payments
WHERE
site_id = 4
AND STATUS = 1
AND currency = 'USD'
AND description LIKE '% + %'
AND datetime IS NOT NULL
GROUP BY
date
) tab3 ON tab1.regdate = tab3.date

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 get total sum and sum between 2 date in 1 query

I am trying to get the total sum of a column and the sum of the same column between 2 dates in one query. is this possible?
My table looks like this:
uid|amount|date
The two queries i am trying to make one of:
SELECT sum(amount) as `keys` FROM tbl_keys WHERE uid = 1
SELECT sum(amount) as `keys` FROM tbl_keys WHERE uid = 1 AND YEAR(`date`) = YEAR(CURRENT_DATE)
AND MONTH(`date`) = MONTH(CURRENT_DATE)
You could use a UNION query:
SELECT 'All' AS cnt, sum(amount) as `keys` FROM tbl_keys WHERE uid = 1
UNION ALL
SELECT 'Current_month' AS cnt, sum(amount) as `keys`
FROM tbl_keys
WHERE
uid = 1
AND `date`<= last_day(current_date)
`date`>= current_date - interval (day(current_date)-1) day
(I prefer to use >= and <= on the date column, as it can make use of an index if present, while functions like MONTH() or YEAR() cannot, also I assume that date is a date columnd and that it doesn't contain time informations).
If you want the result in one row, you could use an inline query:
SELECT
(SELECT sum(amount) as `keys` FROM tbl_keys WHERE uid = 1) AS total,
(SELECT sum(amount) as `keys`
FROM tbl_keys
WHERE
uid = 1
AND `date`<= last_day(current_date)
`date`>= current_date - interval (day(current_date)-1) day
) AS current_month
Something like this:
SELECT sum(amount) as `keys`,
(
SELECT sum(t.amount)
FROM tbl_keys as t
WHERE t.uid = tbl_keys.uid AND YEAR(t.`date`) = YEAR(CURRENT_DATE)
AND MONTH(t.`date`) = MONTH(CURRENT_DATE)
) as `keys2`
FROM tbl_keys
WHERE uid = 1
SELECT sum(amount) AS `keys`
FROM (
SELECT amount FROM tbl_keys
UNION ALL
SELECT amount FROM tbl_keys
WHERE uid = 1
AND YEAR(`date`) = YEAR(CURRENT_DATE)
AND MONTH(`date`) = MONTH(CURRENT_DATE)
) AS new_table;
Using a UNION clause, you will get the desired output you want.
Use CASE to count only the amount for the specified date:
SELECT SUM(amount) AS `keys`,
SUM(CASE WHEN YEAR(`date`) = YEAR(CURRENT_DATE)
AND MONTH(`date`) = MONTH(CURRENT_DATE) THEN amount ELSE 0 END) AS 'keys2'
FROM tbl_keys
WHERE uid = 1
;
My guess is that this will run more efficient than a solution using UNION SELECT.

initialise all values in a query to 0 and top to NULL if there's no result

Hi all i execute this query to get a table where there's statistics of some database information.. i'd like to intialise the fields that don't exist ( because the query is executed in different dates and sometimes there's a day where there's nothing ) so i'd like it to return 0 and NULL ( in TOP column )
SELECT
SUM(IF(`TOP` = 'one',`Nb`,0)) as first_one,
SUM(IF(`TOP` = 'two',`Nb`,0)) as second_one,
SUM(IF(`TOP` = 'three',`Nb`,0)) as thrid_one,
SUM(IF(`TOP` NOT IN ('three','two','one'),`Nb`,0)) as forth_one,
GROUP_CONCAT(IF(`TOP` NOT IN ('three','two','one'),`TOP`,'') SEPARATOR '') as `OR`
FROM (
SELECT
COUNT(*) as Nb,
'one' as `TOP`
FROM
mytable
WHERE
TYPE = 'MSS'
AND YEAR(date) = YEAR(CURDATE())
AND MONTH(date) = MONTH(CURDATE())
UNION ALL
SELECT
COUNT(*) as Nb,
'two' as `TOP`
FROM
mytable
WHERE
TYPE = 'MSS'
AND S=0
AND YEAR(date) = YEAR(CURDATE())
AND MONTH(date) = MONTH(CURDATE())
UNION ALL
SELECT
COUNT(*) as Nb,
'three' as `TOP`
FROM
mytable
WHERE
TYPE = 'MSS'
AND S<>0
AND YEAR(date) = YEAR(CURDATE())
AND MONTH(date) = MONTH(CURDATE())
UNION ALL
SELECT
`Nb`,
`TOP`
FROM(
SELECT
COUNT(*) as Nb ,
`OR` as `TOP`
FROM
mytable
WHERE
TYPE = 'MSS'
AND YEAR(date) = YEAR(CURDATE())
AND MONTH(date) = MONTH(CURDATE())
GROUP BY
`OR`
ORDER BY
Nb DESC
LIMIT 1
) as tmp
)as tmp1
Assuming that in tmp1 you have data you need but with "gaps" (days when there were no data at all) you could RIGHT JOIN tmp1 to table tmp2 using day (I assume that you have such column in tmp1 table). So tmp2 would be just list of days:
SELECT '2013-05-17' as day UNION SELECT '2013-05-18' UNION SELECT ...
I could elaborate my answer if you'd like to provide your DB schema.
You can replace each subquery with:
SELECT
IFNULL(tmp.Nb,0) as Nb,
IFNULL(tmp.`TOP`, 'value') as `TOP`
FROM (
--subquery
) as tmp
Example for the first subquery:
SELECT
IFNULL(tmp.Nb,0) as Nb,
IFNULL(tmp.`TOP`, 'one') as `TOP`
FROM (
SELECT
COUNT(*) as Nb,
'one' as `TOP`
FROM
mytable
WHERE
TYPE = 'MSS'
AND YEAR(date) = YEAR(CURDATE())
AND MONTH(date) = MONTH(CURDATE())
) as tmp
SQL is good at grouping existing entities into categories, but bad at "creating" entities itself. I would advise either a generic number table (really just the numbers from 0 to a few hundredthousand) if you have also non-date categories or as Wiktor suggested a date-Table which gets filled every now and then and has the next few years as well as the time since your program is working.
With a date table
list_dates (
id int(11) not null primary key auto_increment,
dateval date not null
)
you could start your queries from that table (with a reasonable range, of course) and count every thing else:
select list_dates.dateval as date, count(*) as cnt
from list_dates
left join actions on actions.actiontime >= (cast list_dates.date_val as datetime)
and actions.actiontime < (cast list_dates.date_val `interval 1 day as datetime)
where list_dates.dateval between '$fromDate' and '$toDate'
group by list_dates.dateval
;
or starting with a number table numbers
select $fromDate + interval numbers.number day as date, count(*) as cnt
from numbers
left join actions
on actions.actiontime >= (cast $fromDate + interval numbers.number day as datetime)
and actions.actiontime < (cast $fromDate + interval (1 + numbers.number) day as datetime)
where numbers.number >= 0 and numbers.number < $countDates
group by numbers.number
;
One Day
If you really want just that one day (today) then you can of course use a anonymous subselect- Table instead, so it becomes
select list_dates.dateval as date, count(*) as cnt
from ( select curdate() as dateval ) as list_dates
left join actions on actions.actiontime >= (cast list_dates.date_val as datetime)
and actions.actiontime < (cast list_dates.date_val `interval 1 day as datetime)
where list_dates.dateval between '$fromDate' and '$toDate'
group by list_dates.dateval
;