Joining the result of multiple select statements - mysql

I'd like to merge the results of the following three select statements horizontally. I tried using joins but no idea how to proceed since it involves COUNT and GROUP BY too.
SELECT DATE(created_at) as date,COUNT(*) as countd1 FROM b_users WHERE last_loggedin_at < DATE_ADD(created_at,INTERVAL 1 DAY) GROUP BY DATE(created_at)
SELECT DATE(created_at) as date,COUNT(*) as countd2 FROM b_users WHERE last_loggedin_at < DATE_ADD(created_at,INTERVAL 2 DAY) GROUP BY DATE(created_at)
SELECT DATE(created_at) as date,COUNT(*) as countd3 FROM b_users WHERE last_loggedin_at < DATE_ADD(created_at,INTERVAL 3 DAY) GROUP BY DATE(created_at)
The individual results would be
date countd1
2011-12-01 100
2011-12-02 120
2011-12-03 130
date countd2
2011-12-01 200
2011-12-02 220
2011-12-03 230
date countd3
2011-12-01 300
2011-12-02 320
2011-12-03 330
But I'd like to merge them so that I'll get the following result
date countd1 countd2 countd3
2011-12-01 100 200 300
2011-12-02 120 220 320
2011-12-03 130 230 330
How do I do this?
Is it possible to do something like the query below
SELECT a, COUNT(b where condition), COUNT(c where condition) FROM table GROUP BY a
.
Update
biziclop provided a great work around
SELECT DATE(created_at) AS date,
SUM(last_loggedin_at < DATE_ADD( created_at,INTERVAL 1 DAY )) AS countd1,
SUM(last_loggedin_at < DATE_ADD( created_at,INTERVAL 2 DAY )) AS countd2,
SUM(last_loggedin_at < DATE_ADD( created_at,INTERVAL 3 DAY )) AS countd3
FROM b_users GROUP BY DATE(created_at)
Solved, thank you! :)

In MySQL the results of comparisons are 1 if true, 0 if false, so you could SUM() them:
SELECT
DATE(created_at) AS date,
SUM( last_loggedin_at < DATE_ADD( created_at,INTERVAL 1 DAY )) AS countd1,
SUM( last_loggedin_at < DATE_ADD( created_at,INTERVAL 2 DAY )) AS countd2,
SUM( last_loggedin_at < DATE_ADD( created_at,INTERVAL 3 DAY )) AS countd3,
FROM b_users
GROUP BY DATE(created_at)

Related

How to retrieve data from previous 4 weeks (mySQL)

I am trying to write a query to get the last 4 weeks (Mon-Sun) of data. I want every week of data to be stored with an individual and shared table.
every week data store based on name if same name repeated on single week amt should sum and if multiple name it should be show data individual, To see an example of what I am looking for, I have included the desired input and output below.
this is my table
date
amt
name
2022-04-29
5
a
2022-04-28
10
b
2022-04-25
11
a
2022-04-23
15
b
2022-04-21
20
b
2022-04-16
20
a
2022-04-11
10
a
2022-04-10
5
b
2022-04-05
5
b
i want output like this
date
sum(amt)
name
2022-04-25 to 2020-04-29
16
a
2022-04-25 to 2020-04-29
10
b
2022-04-18 to 2022-04-24
35
b
2022-04-11 to 2022-04-17
30
a
2022-04-04 to 2022-04-10
10
b
I would appreciate any pointers or 'best-practises' which I should employ to achieve this task.
You can try to use DATE_ADD with WEEKDAY get week first day and end day.
SELECT
CASE WHEN
weekofyear(`date`) = weekofyear(NOW())
THEN 'current week'
ELSE
CONCAT(date_format(DATE_ADD(`date`, interval - WEEKDAY(`date`) day), '%Y-%m-%d'),' to ',date_format(DATE_ADD(DATE_ADD(`date`, interval -WEEKDAY(`date`) day), interval 6 day), '%Y-%m-%d'))
END 'date',
SUM(amt)
FROM T
GROUP BY
CASE WHEN
weekofyear(`date`) = weekofyear(NOW())
THEN 'current week'
ELSE
CONCAT(date_format(DATE_ADD(`date`, interval - WEEKDAY(`date`) day), '%Y-%m-%d'),' to ',date_format(DATE_ADD(DATE_ADD(`date`, interval -WEEKDAY(`date`) day), interval 6 day), '%Y-%m-%d'))
END
sqlfiddle
EDIT
I saw you edit your question, you can just add name in group by
SELECT
CONCAT(date_format(DATE_ADD(`date`, interval - WEEKDAY(`date`) day), '%Y-%m-%d'),' to ',date_format(DATE_ADD(DATE_ADD(`date`, interval -WEEKDAY(`date`) day), interval 6 day), '%Y-%m-%d')) 'date',
SUM(amt),
name
FROM T
GROUP BY
CONCAT(date_format(DATE_ADD(`date`, interval - WEEKDAY(`date`) day), '%Y-%m-%d'),' to ',date_format(DATE_ADD(DATE_ADD(`date`, interval -WEEKDAY(`date`) day), interval 6 day), '%Y-%m-%d')),
name
ORDER BY 1 desc
sqlfiddle
This is in SQL Server, and just a mess about. Hopefully it can be of some help.
with cteWeekStarts
as
(
select
n,dateadd(week,-n,DATEADD(week, DATEDIFF(week, -1, getdate()), -1)) as START_DATE
from
(values (1),(2),(3),(4)) as t(n)
), cteStartDatesAndEndDates
as
(
select *,dateadd(day,-1,lead(c.start_date) over (order by c.n desc)) as END_DATE
from cteWeekStarts as c
)
,cteSalesSumByDate
as
(
select s.SalesDate,sum(s.salesvalue) as sum_amt from
tblSales as s
group by s.SalesDate
)
select c3.n as WeekNum,c3.START_DATE,isnull(c3.END_DATE,
dateadd(day,6,c3.start_date)) as END_DATE,
(select sum(c2.sum_amt) from cteSalesSumByDate as c2 where c2.SalesDate
between c3.START_DATE and c3.END_DATE) as AMT
from cteStartDatesAndEndDates as c3
order by c3.n desc

Mysql - get results from complex criteria

I have database with statistics over a number of websites and I'm currently having an issue with a rather complex query that I have no idea how to do (or if it's even possible).
I have 2 tables: websites and visits. The former is a list of all websites and their properties, while the former is a list of each user's visit on a specific website.
The program I'm making is supposed to fetch websites that need to be "scanned". The interval between each scan for each site depends on the websites total number of visits for the last 30 days. Here is a table with the intended scan-interval:
The tables have the following structure:
Websites
Visits
What I want is a query that returns the websites that are either at or past their individual update deadline (can be seen from the last_scanned column).
Is this easily doable in a single query?
Here's something you can try:
SELECT main.*
FROM (
SELECT
w.web_id,
w.url,
w.last_scanned,
(SELECT COUNT(*)
FROM visits v
WHERE v.web_id = w.web_id
AND TIMESTAMPDIFF(DAY,v.added_on, NOW()) <=30
) AS visit_count,
TIMESTAMPDIFF(HOUR,w.last_scanned, NOW()) AS hrs_since_update
FROM websites w
) main
WHERE
(CASE
WHEN visit_count >= 0 AND visit_count <= 10 AND hrs_since_update >= 4320 THEN 1
WHEN visit_count >= 11 AND visit_count <= 100 AND hrs_since_update >= 2160 THEN 1
WHEN visit_count >= 101 AND visit_count <= 500 AND hrs_since_update >= 1080 THEN 1
WHEN visit_count >= 501 AND visit_count <= 1000 AND hrs_since_update >= 720 THEN 1
WHEN visit_count >= 1001 AND visit_count <= 2000 AND hrs_since_update >= 360 THEN 1
WHEN visit_count >= 2001 AND visit_count <= 5000 AND hrs_since_update >= 168 THEN 1
WHEN visit_count >= 5001 AND visit_count <= 10000 AND hrs_since_update >= 72 THEN 1
WHEN visit_count >= 10001 AND hrs_since_update >= 24 THEN 1
ELSE 0
END) = 1;
Here's the fiddle demo: http://sqlfiddle.com/#!9/1f671/1
First, I would make a subquery to get the visit counts from the visits table for each distinct web_id. Then, LEFT OUTER JOIN the websites table to this subquery. You can then query the result for each possible condition in your visits-to-update-frequency table, like so:
SELECT websites.* FROM websites
LEFT OUTER JOIN (
SELECT visits.web_id, COUNT(*) AS visits_count FROM visits GROUP BY visits.web_id
) v ON v.web_id = websites.web_id
WHERE
(v.visits_count <= 10 AND websites.last_scanned <= DATE_SUB(NOW(), INTERVAL 4320 HOUR)) OR
(v.visits_count BETWEEN 11 AND 100 AND websites.last_scanned <= DATE_SUB(NOW(), INTERVAL 2160 HOUR)) OR
(v.visits_count BETWEEN 101 AND 500 AND websites.last_scanned <= DATE_SUB(NOW(), INTERVAL 1080 HOUR)) OR
(v.visits_count BETWEEN 501 AND 1000 AND websites.last_scanned <= DATE_SUB(NOW(), INTERVAL 720 HOUR)) OR
(v.visits_count BETWEEN 1001 AND 2000 AND websites.last_scanned <= DATE_SUB(NOW(), INTERVAL 360 HOUR)) OR
(v.visits_count BETWEEN 2001 AND 5000 AND websites.last_scanned <= DATE_SUB(NOW(), INTERVAL 168 HOUR)) OR
(v.visits_count BETWEEN 5001 AND 10000 AND websites.last_scanned <= DATE_SUB(NOW(), INTERVAL 72 HOUR)) OR
(v.visits_count > 10000 AND websites.last_scanned <= DATE_SUB(NOW(), INTERVAL 24 HOUR));
Just an improvment on #morgb query, using a table for visit count ranges
SQL FIDDLE DEMO
create table visitCount (
`min` bigint(20),
`max` bigint(20),
`frequency` bigint(20)
);
SELECT main.*
FROM (
SELECT
w.web_id,
w.url,
w.last_scanned,
(SELECT COUNT(*)
FROM visits v
WHERE v.web_id = w.web_id
AND TIMESTAMPDIFF(DAY,v.added_on, NOW()) <=30
) AS visit_count,
TIMESTAMPDIFF(HOUR,w.last_scanned, NOW()) AS hrs_since_update
FROM websites w
) main inner join
visitCount v on visit_count between v.min and v.max
WHERE
main.hrs_since_update > v.frequency

Getting data from the last 12 month using mysql

I have the following data structure in my table:
id member_from member_till
1 2014/03/01 2014/05/18
2 2014/01/09 2014/08/13
...
How can i get a count of active members for the last 12 month, grouped by month?
Ex:
...
2014/12/01,5
2015/01/01,12
As a future development is it possible to make the count the average of the first and last day of each month?
First of all you need the last twelve months. Then outer-join the members and count those where the month is in the membership range.
select
date_format(all_months.someday, '%Y %m') as mymonth,
count(membership.member_from) as members
from
(
select current_date as someday
union all
select date_add(current_date, interval -1 month)
union all
select date_add(current_date, interval -2 month)
union all
select date_add(current_date, interval -3 month)
union all
select date_add(current_date, interval -4 month)
union all
select date_add(current_date, interval -5 month)
union all
select date_add(current_date, interval -6 month)
union all
select date_add(current_date, interval -7 month)
union all
select date_add(current_date, interval -8 month)
union all
select date_add(current_date, interval -9 month)
union all
select date_add(current_date, interval -10 month)
union all
select date_add(current_date, interval -11 month)
) all_months
left join membership
on date_format(all_months.someday, '%Y %m')
between
date_format(membership.member_from, '%Y %m')
and
date_format(membership.member_till, '%Y %m')
group by date_format(all_months.someday, '%Y %m');
SQL fiddle: http://www.sqlfiddle.com/#!2/6dc5a/10.
As to your future requirement: You can join the membership table twice, once for the members on the first of a month, once for the last of the month (loosing those who participated only some days in the middle of a month). Then add both counts and divide by two.
You can try with SQL Query:
SELECT `member_from`, count(id) FROM tbl_test WHERE `member_from` BETWEEN <From date> AND <To date> GROUP BY `member_from`;
Complete with future requirement:
SELECT
d.ymonth,
COUNT(m.member_from) total,
COALESCE(SUM(d.fday BETWEEN m.member_from AND m.member_till), 0) total_fom,
COALESCE(SUM(d.lday BETWEEN m.member_from AND m.member_till), 0) total_lom
FROM
(
SELECT
CAST(#first_day := #first_day + INTERVAL 1 MONTH AS DATE) fday,
LAST_DAY(#first_day) lday,
DATE_FORMAT(#first_day, '%Y-%m') ymonth
FROM
information_schema.collations
CROSS JOIN
(SELECT #first_day := LAST_DAY(CURRENT_DATE) - INTERVAL 13 MONTH + INTERVAL 1 DAY) x
LIMIT 12
) d
LEFT JOIN
membership m
ON d.lday >= m.member_from
AND d.fday <= m.member_till
GROUP BY d.ymonth
The subquery generates a virtual lookup table with 3 columns:
+ ---------- + ---------- + ------- +
| fday | lday | ymonth |
+ ---------- + ---------- + ------- +
| 2014-02-01 | 2014-02-28 | 2014-02 |
| \/ | \/ | \/ |
| 2015-01-01 | 2015-01-31 | 2015-01 |
+ ---------- + ---------- + ------- +
Then the membership table can be joined on the overlaps member_from-member_till and the beginning and ending of each month.

my sql query for last 7 days data?if data not there then even fetch date and shows with 0 or null

i have made one demo Enity in sql Fidldle.
my table have following collumn
displayId | displaytime | campaignId
now what i want is to select the last 7 days entry for the particular campaignId (i mean where campaignId="")
there should be multiple entry for the same date with same campaign id..so i want to show sum of total campaignId with their date
so if there is 7 records for date 2013-3-8 and other date then it shows me record like
2013-03-02 | 1
2013-03-03 | 1
2013-03-04 | 0
2013-03-05 | 0
2013-03-06 | 0
2013-03-07 | 2
2013-03-08 | 7
in following query its just shows the date with their count which have more then 0 count...
e.g.
what i have tried is as follows..but it just give me one record which have entry in database...
2013-03-08 | 7
SELECT date(a.displayTime) AS `DisplayTime`,
ifnull(l.TCount,0) AS TCount
FROM a_ad_display AS a
INNER JOIN
(SELECT count(campaignId) AS TCount,
displayTime
FROM a_ad_display
WHERE CONVERT_TZ(displaytime,'+00:00','-11:00') >= DATE_SUB(CONVERT_TZ(CURDATE(),'+00:00','-11:00') ,INTERVAL 1 DAY)
AND CONVERT_TZ(displaytime,'+00:00','-11:00') <= CONVERT_TZ(CURDATE(),'+00:00','-11:00')
AND campaignId = 20747
GROUP BY DATE(displayTime)) AS l ON date(a.displayTime) = date(l.displayTime)
GROUP BY DATE(a.displayTime)
ORDER BY a.displaytime DESC LIMIT 7
i have implemented time zone in my query so if u can help me with the simple query then its ok..dont include Convert_Tz line.
this is http://sqlfiddle.com/#!2/96600c/1 link of my dummy entitiy
If you use a dates table, your query can be as simple as:
select dates.fulldate, count(a_ad_display.id)
from dates
left outer join a_ad_display ON dates.fulldate = a_ad_display.displaytime
[AND <other conditions here>]
where dates.fulldate BETWEEN date_add(curdate(), interval -6 day) AND curdate()
group by dates.fulldate
http://sqlfiddle.com/#!2/51e17/3
Without using a dates table, here's one way. It's ugly and probably terrible for performance:
select displaytime, sum(c)
from
(
select displaytime, count(a_ad_display.id) AS c
from a_ad_display
where displaytime BETWEEN date_add(curdate(), interval -6 day) AND curdate()
group by displaytime
union all
select curdate(), 0
union all
select date_add(curdate(), interval -1 day), 0
union all
select date_add(curdate(), interval -2 day), 0
union all
select date_add(curdate(), interval -3 day), 0
union all
select date_add(curdate(), interval -4 day), 0
union all
select date_add(curdate(), interval -5 day), 0
union all
select date_add(curdate(), interval -6 day), 0
) x
group by displaytime
order by displaytime
http://sqlfiddle.com/#!2/96600c/16
Simple!You can done by PHP. First create date array like
date = date('Y-m-d'); $date1 = date('Y-m-d', strtotime('-1 days')); $date2 = date('Y-m-d', strtotime('-2 days'));$date3 = date('Y-m-d', strtotime('-3 days'));$date4 = date('Y-m-d', strtotime('-4 days'));$date5 = date('Y-m-d', strtotime('-5 days'));$date6 = date('Y-m-d', strtotime('-6 days'));
$dt_arr = array($date6,$date5,$date4,$date3,$date2,$date1,$date);
for last 7 days from today then use foreach and run query with where date field inside foreach like
foreach ($dt_arr as $value)
{
$qry = mysqli_fetch_array(mysqli_query($link,"select * from table where dt_field = $value"));
if($qry['value'] == '')
{
$valfield = 0;
$dtfiled = $value;
}

How to get unique values for several different time ranges WHERE exec_datetime >= NOW() - INTERVAL n DAY?

I can select the total amount of unique ip addresses between a single time range
SELECT COUNT(DISTINCT ip_address) as ip_addr, exec_datetime
FROM requests
WHERE exec_datetime >= NOW() - INTERVAL 1 DAY;
ip_addr exec_datetime
45 12/10/2012 5:21
How do I return a result set for the following clauses in a single query...
WHERE exec_datetime >= NOW() - INTERVAL 1 DAY;
WHERE exec_datetime >= NOW() - INTERVAL 2 DAY;
...
WHERE exec_datetime >= NOW() - INTERVAL 14 DAY;
...so that the result set would look like this?
ip_addr exec_datetime
45 11/26/2012 5:21
85 11/27/2012 5:21
130 11/28/2012 5:21
170 11/29/2012 5:21
... ...
I would use something like this:
SELECT
COUNT(DISTINCT requests.ip_address) ip_addr,
days.day
FROM
(SELECT now()-INTERVAL 1 DAY as day UNION
SELECT now()-INTERVAL 2 DAY UNION
...
SELECT now()-INTERVAL 14 DAY) days
INNER JOIN requests
on requests.exec_datetime >= days.day
GROUP BY
days.day