Combine found results and not found results - mysql

I have a table that has the information about a log, to know how many hits there were on the pages of the website.
This is the query that shows me the above:
select pageview_page, DATE(pageview_date) as pageview_date, count(*) as view_count
from pageviews
group by pageviews.pageview_page, DAY(pageviews.pageview_date)
order by pageviews.pageview_date desc
Resulting in the following:
Page Day view_count
index 2016-01-12 50
index 2016-01-11 10
index 2016-01-10 20
contact 2016-01-12 5
contact 2016-01-11 5
PD: using desc on date because the chart must start on the latest date.
Notice: in the above table, contact is not present on day 2016-01-10, meaning no one used that page on that day.
I want to get the query to show 0 if there is nothing on that date, how can I achieve that? The result must be like the following
Page Day view_count
index 2016-01-12 50
index 2016-01-11 10
index 2016-01-10 20
contact 2016-01-12 5
contact 2016-01-11 5
contact 2016-01-10 0 <-------- (I want this to appear, as it is missing in the table above, in the first table)
Lets take the next 3 dates as an example: 2016-01-10, 2016-01-11, 2016-01-12
The point is to view the statistics by day, I use the next to get the dates above:
select DATE(pageview_date) as pageview_date from pageviews GROUP by DAY(pageview_date)
I have tried a combination with IN and NOT IN with the query above but I cant get it working.

I am not sure about DAY function in MySQL but by looking your first query I think you can do something like this -
select T2.pageview_page AS pageview_page,
T1.pageview_date AS pageview_date,
(select count(*)
from pageviews t3
where t3.pageview = t2.pageview
and DAY(t3.pageview_date) = t1.pageview_date) as view_count
from (select distinct DAY(pageview_date) pageview_date FROM pageviews) t1,
(SELECT DISTINCT pageview_page FROM pageviews) t2
group by pageview_page, pageview_date
order by pageview_date desc

Create a table of days (or integers) and do a LEFT JOIN from it to your query:
SELECT ...
FROM AllDays a
LEFT JOIN ( your query ) b ON b.date = a.date
WHERE a.date BETWEEN ...;
Even better would be to use a MariaDB "sequence table".

Related

User Churn - Final outer statement in a cte

I have a table below as
timestamp | user_id | activity
2021-02-01 03:21:11 mike12 read
2021-02-02 03:45:22 bob55 like
2021-02-03 04:21:33 sarah22 post
2021-02-01 04:11:33 cindy11 sign-in
I want to calculate # users churned in last 7 days as =
number of all users - active users (where active are those who like, read, comment, or post
with active_users as
(
select count(distinct user_id)
from table
where activity IN ('comment','post','read','like')
and date_diff(timestamp, current_date()) <= 7
)
, inactive_users as
(select count(distinct user_id)
from table
where activity IN ('sign-in')
and date_diff(timestamp, current_date()) <= 7)
What would be the correct way to subtract the two above? I am unsure of how to join the two ctes in the final query, thanks for helping!

Getting daily counts for events that don't happen every day

I have a customer table in which a new row is inserted when a customer signup occurs.
Problem
I want to know the total number of signup per day for a given date range.
For example, find the total number of signup each day from 2015-07-01 to 2015-07-10
customer table
sample data [relevant columns shown]
customerid username created
1 mrbean 2015-06-01
2 tom 2015-07-01
3 jerry 2015-07-01
4 bond 2015-07-02
5 superman 2015-07-10
6 tintin 2015-08-01
7 batman 2015-08-01
8 joker 2015-08-01
Required Output
created signup
2015-07-01 2
2015-07-02 1
2015-07-03 0
2015-07-04 0
2015-07-05 0
2015-07-06 0
2015-07-07 0
2015-07-08 0
2015-07-09 0
2015-07-10 1
Query used
SELECT
DATE(created) AS created, COUNT(1) AS signup
FROM
customer
WHERE
DATE(created) BETWEEN '2015-07-01' AND '2015-07-10'
GROUP BY DATE(created)
ORDER BY DATE(created)
I am getting the following output:
created signup
2015-07-01 2
2015-07-02 1
2015-07-10 1
What modification should I make in the query to get the required output?
You're looking for a way to get all the days listed, even those days that aren't represented in your customer table. This is a notorious pain in the neck in SQL. That's because in its pure form SQL lacks the concept of a contiguous sequence of anything ... cardinal numbers, days, whatever.
So, you need to introduce a table containing a source of contiguous cardinal numbers, or dates, or something, and then LEFT JOIN your existing data to that table.
There are a few ways of doing that. One is to create yourself a calendar table with a row for every day in the present decade or century or whatever, then join to it. (That table won't be very big compared to the capability of a modern database.
Let's say you have that table, and it has a column named date. Then you'd do this.
SELECT calendar.date AS created,
ISNULL(a.customer_count, 0) AS customer_count
FROM calendar
LEFT JOIN (
SELECT COUNT(*) AS customer_count,
DATE(created) AS created
FROM customer
GROUP BY DATE(created)
) a ON calendar.date = a.created
WHERE calendar.date BETWEEN start AND finish
ORDER BY calendar.date
Notice a couple of things. First, the LEFT JOIN from the calendar table to your data set. If you use an ordinary JOIN the missing data in your data set will suppress the rows from the calendar.
Second, the ISNULL in the toplevel SELECT to turn the missing, null, values from your dataset into zero values.
Now, you ask, where can I get that calendar table? I respectfully suggest you look that up, and ask another question if you can't figure it out.
I wrote a little essay on this, which you can find here.http://www.plumislandmedia.net/mysql/filling-missing-data-sequences-cardinal-integers/
Look here
Create teble with calendar and join it in your query.
DECLARE #MinDate DATE = '2015-07-01',
#MaxDate DATE = '2015-07-10';
Create Table tblTempDates
(created date, signup int)
insert into tblTempDates
SELECT TOP (DATEDIFF(DAY, #MinDate, #MaxDate) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #MinDate), 0 As Signup
FROM sys.all_objects a
CROSS JOIN sys.all_objects b;
Create Table tblTempQueryDates
(created date, signup int)
INSERT INTO tblTempQueryDates
SELECT
created AS created, COUNT(scandate) AS signup
FROM
customer
WHERE
created BETWEEN #MinDate AND #MaxDate
GROUP BY created
UPDATE tblTempDates
SET tblTempDates.signup = tblTempQueryDates.signup
FROM tblTempDates INNER JOIN
tblTempQueryDates ON tblTempDates.created = tblTempQueryDates.created
select * from tblTempDates
order by created
Drop Table tblTempDates
Drop Table tblTempQueryDates
Not pretty, but it gives you what you want.

Top-10 mysql query

I'm in need of a better way of retrieving top 10 distinct UID from some tables I have.
The setup:
Table user_view_tracker
Contains pairs of {user id (uid), timestamp (ts)}
Is growing every day (today it's 41k entries)
My goal:
To produce a top 10 of most viewed user id's in the table user_view_tracker
My current code is working, but killing the database slowly:
select
distinct uvt.uid as UID,
(select count(*) from user_view_tracker temp where temp.uid=uvt.uid and temp.ts>date_sub(now(),interval 1 month)) as CLICK
from user_view_tracker uvt
order by CLICK
limit 10
It's quite obvious that a different data structure would help. But I can't do that as of now.
First of all, delete that subquery, this should be enough ;)
select
uvt.uid as UID
,count(*) as CLICK
from
user_view_tracker uvt
where
uvt.ts > date_sub(now(),interval 1 month)
group by
uvt.uid
order by CLICK DESC
limit 10
Try:
select uid, count(*) as num_stamps
from user_view_tracker
where ts > date_sub(now(), interval 1 month)
group by uid
order by 2 desc limit 10
I kept your criteria as far as getting the count for just the past month. You can remove that line if you want to count all.
The removal of DISTINCT should improve performance. It is not necessary if you aggregate in your outer query and group by uid, as that will aggregate the data to one row per uid with the count.
You should use Aggregate functions in MySQL
SELECT UID, COUNT(ts) as Number_Of_Views FROM user_view_tracker
GROUP BY UID
ORDER BY Number_Of_Views DESC
LIMIT 10
A simple demo which selects the top 10 UID viewed
http://sqlfiddle.com/#!2/907c10/3

How do I query a mysql table to get the current value of a column and the max for the day

I have a table that looks something like this. It updates every 5 minutes for each game_id
game_id players date
12 420 2013-06-19 12:30:00
13 345 2013-06-19 12:30:00
14 600 2013-06-19 12:30:00
12 375 2013-06-19 12:25:00
13 475 2013-06-19 12:25:00
14 575 2013-06-19 12:25:00
12 500 2013-06-19 12:20:00
...
I need a query for each game id, get the current players (latest timestamp) and the max for the day. so the results would look like this
game_id max current
12 500 420
13 475 345
14 600 600
I tried something like this but, had no luck and can't figure it out :(
select game_id, max(players) as max, players as current from players where date >= '2013-06-19' order by date desc group by game_id;
Thanks for your help!
http://www.sqlfiddle.com/#!2/e5157/5
select game_id, max(players) as maximum,
players as current
from tab where date >= '2013-06-19'
group by game_id
To get the last value, you need a trick of one sort or another. Instead of using a join, this version uses the substring_index()/group_concat() trick:
select game_id, max(players) as MaxPlayers,
substring_index(group_concat(players order by date desc), ',', 1) + 0 as Latest
from players
group by game_id;
The nice thing about this approach is that it is guaranteed to work and does not require any additional joins.
In particular, it does not use the MySQL extension that allows columns to be included in the select clause without their being in the group by clause. The results are indeterminate when there are multiple values, as expressly stated in the documentation:
You can use this feature to get better performance by avoiding
unnecessary column sorting and grouping. However, this is useful
primarily when all values in each nonaggregated column not named in
the GROUP BY are the same for each group. The server is free to choose
any value from each group, so unless they are the same, the values
chosen are indeterminate.
select game_id, max(players) as max, players as current from players where date >= '2013-06-19' group by game_id order by date desc ;
select
t.game_id
, maxes.max_players as max
, t.players as current_players as current
from
(
select
t.game_id
, max(t.players) as max_players
, max(t.date) as max_date
from
t
where
t.game_id = :game_id
and t.date >= :todays_date
group by
t.game_id
) maxes
inner join t on t.game_id = maxes.game_id and t.date = maxes.max_date
where
t.date >= :todays_date
I don't have a mysql db configured to test this right now but I think it will give you the desired results.

Multiple rows come back of same ID, just need one

I am trying to list several products on a page. My query returns multiples of the same product and I am trying to figure out how to limit it to one only with my query.
The primary key on the first table that we will call table_one is ID.
The second table has a column of ProductID that references the primary key on table_one.
My query brings me back multiples of my ProductID that is equal to 6 below. I just want one result to be brought back, BUT I still want my all of my data in DateReserved on table_two to be queried. Pretty sure I need to add one more thing to my query, but I have not had much luck.
The results I want back are as follows.
ID Productname Quantity Image Date Reserved SumQuantity
6 productOne 6 'image.jpg' 03-31-2013 3
7 productTwo 1 'product.jpg' 04-04-2013 1
Here is my first table. table_one
ID Productname Quantity Image
6 productOne 6 'image.jpg'
7 productTwo 1 'product.jpg'
Here is my second table. table_two
ID ProductID DateReserved QuantityReserved
1 6 03-31-2013 3
2 6 04-07-2013 2
3 7 04-04-2013 1
Here is my query that I am trying to use.
SELECT *
FROM `table_one`
LEFT JOIN `table_two`
ON `table_one`.`ID` = `table_two`.`ProductID`
WHERE `table_one`.`Quantity` > 0
OR `table_two`.`DateReserved` + INTERVAL 5 DAY <= '2013-03-27'
ORDER BY ProductName
Sorry for posting another answer, but as it seems my first try on it was not so good ;)
To only get one result row per reservation you need to sum them up somehow.
First I suggest you explicitly select the columns you want back in your result and don't use "*".
I suggest you try something like this:
SELECT
`table_one`.`ID`, `table_one`.`Productname`, `table_one`.`Image`, `table_one`.`Quantity`,
`table_two`.`ProductID`, SUM(`table_two`.`QuantityReserved`)
FROM
`table_one`
LEFT JOIN
`table_two` ON `table_one`.`ID` = `table_two`.`ProductID`
WHERE
`table_one`.`Quantity` > 0
OR `table_two`.`DateReserved` + INTERVAL 5 DAY <= '2013-03-27'
GROUP BY `table_two`.`ProductID`
ORDER BY ProductName
As you see I used "SUM" to get a combined quantity, this is called aggregation and the "GROUP BY" helps you getting rid of multiple occurences of the same ProductID.
One problem that you have now is that you will have to get the reservation date from a seperate query (well at least I am now unsure how you would get it into the same query)
Since you are using MySQL
LIMIT <NUMBER>
should exactly do what you want, you just insert it after your ORDER BY clause, but probably you should also add one more ordering to that, so you can be sure that you will always get the one entity that you wanted and not just some "random" entity ;)
So without further ordering your query would look like this:
SELECT
*
FROM `table_one`
LEFT JOIN `table_two` ON `table_one`.`ID` = `table_two`.`ProductID`
WHERE
`table_one`.`Quantity` > 0
OR `table_two`.`DateReserved` + INTERVAL 5 DAY <= '2013-03-27'
ORDER BY ProductName
LIMIT 1
here some more description about that
SELECT a.member_id,a.member_name,a.gender,a.amount,b.trip_id,b.location
FROM tbl_member a
LEFT JOIN (SELECT trip_id, MAX(amount) as amount FROM tbl_member GROUP BY trip_id ) b ON a.trip_id= b.trip_id
LEFT JOIN tbl_trip b ON a.trip_id=c.trip_id
ORDER BY member_name