MySQL to generate daily profile views reports with filled gaps - mysql

I'm about to plot daily profile views in chart.js. I have two table one for track every day profile visit named "stats_profile_views" and second one carries all date of an year named "calendar"
I would like to show last 7 days profile views when logged in each vendor.
Tried following query,
SELECT datefield AS DATE ,`id`, `vendor_id`, `view_date`,IFNULL(view_count,0) AS view_count
FROM (`stats_profile_views` pv)
RIGHT JOIN bp_calendar ON (DATE(view_date) = bp_calendar.datefield)
WHERE `vendor_id` = '110'
AND bp_calendar.datefield BETWEEN DATE_SUB(NOW(), INTERVAL 7 DAY) AND NOW()
GROUP BY DATE
ORDER BY `view_date` asc
I like to get past 7 days records filled with view_count zero for non-available records in "stats_profile_views" table. But when apply WHERE vendor_id = '110' it fails to return such record and gives only 3 records related with vendor_id = '110'. But it work great without vendor_id filter.
How can I get last 7 days records filled with view_count as zero when filter using vendor_id. Anybody guide me to a solution please.

SELECT
c.datefield AS DATE
, pv.id
, pv.vendor_id
, pv.view_date
, IFNULL(pv.view_count, 0) AS view_count
FROM bp_calendar c
LEFT JOIN stats_profile_views pv ON (DATE(pv.view_date) = c.datefield)
AND pv.vendor_id = '110'
WHERE c.datefield BETWEEN DATE_SUB(NOW(), INTERVAL 7 DAY) AND NOW()
ORDER BY
pv.view_date ASC
When using outer joins the where clause MUST be careful not to override the unmatched rows, otherwise you get the effect of an inner join instead. That's what happened in your original query; because EVERY row is required to have a vendor_id = '110' but an unmatched date cannot have any vendor_id.
Additionally it generally helps to write the query FROM (the table with the all the rows) LEFT JOIN (table with gaps in it). Then include the extra conditions of the left joined table as extra join conditions instead of the where clause.
If you want to group the results then you need to indicate what it is you are attempting to do. Perhaps summing the view_counts? However if you are intending to do that then you would not include columns like id in the result.
Also, please, include the table alias in EVERY reference to column.

Related

SQL Join with data associated to dates

Currently I have a simple SQL request to get aall group departure date and the associated group size (teamLength) between 2 dates but it doesn't work properly.
SELECT `groups`.`departure`, COUNT(`group_users`.`group_id`) as 'teamLength'
FROM `groups`
INNER JOIN `group_users`
ON `groups`.`id` = `group_users`.`group_id`
WHERE departure BETWEEN '2017-03-01' AND '2017-03-31'
In fact, if I have more than 1 group between the 2 dates, only 1 date will be recovered in association with the total number of teamLength.
For exemple, if I have 2 groups in the same interval with, for group 1, 2 people and for group 2, 1 people, the result will be:
Here are 2 screenshots of the current state of my groups and group_users tables:
Is it even possible to do what I want in only 1 SQL request ? Thanks
In addition to what jarlh commented (JOIN with ON). Don't ever group data without an explicit GROUP BY. I don't know why MYSQL still allows this...
Change your query to something like this and you should get the result you are looking for. Currently, the other departure dates get lost in the aggregation.
SELECT
groups.departure,
COUNT(1) as team_length
FROM
groups
INNER JOIN group_users
ON groups.id = group_users.group_id
WHERE
groups.departure BETWEEN '2017-03-01' AND '2017-03-31'
GROUP BY
groups.departure
I think that you have a syntax issue in your query. You are missing the ON statement so your database could be trying to get a cartesian product since there is no join clause.
SELECT `groups`.`departure`, COUNT(`group_users`.`id`) as 'teamLength'
FROM `groups`
INNER JOIN `group_users` ON `groups`.`id` = `group_users`.`group_id`
WHERE departure BETWEEN '2017-03-01' AND '2017-03-31'
GROUP BY `groups`.`departure`
You also are missing the GROUP BYclause which is not mandatory in all RDBS but it is a good practice to set it.

MySQL GROUP BY w/ ORDER BY not having desired result

I have a query that attempts to retrieve IDs of people, but only if those people have more than one address. I'm also checking that the last time I called them was at least 30 days ago. Finally, I'm trying to order the results, because I want to pull up results with the oldest last_called datetime:
SELECT
p.id,
COUNT(*) AS cnt
FROM
people p
LEFT JOIN addresses a
ON p.id = a.id
WHERE p.last_called <= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY p.id
HAVING COUNT(*) > 1
ORDER BY p.last_called ASC
LIMIT 25
Right now, the results are not excluding people with only one address. I haven't even got to the point where I know if the sort order is correct, but right now I'd just like to know why it is that my query isn't pulling up results where there is at least 2 addresses for the person.
If you don't want to include people with no address then I would recommend using INNER JOIN instead of LEFT JOIN and DISTINCT to get distinct address ids
(just in case if you have duplicate mappings), e.g.:
SELECT
p.id,
COUNT(DISTINCT(a.id)) AS cnt
FROM
people p
JOIN addresses a
ON p.id= a.peopleid
WHERE p.last_called <= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY p.id
HAVING COUNT(DISTINCT(a.id)) > 1
As far as Ordering is concerned, MySQL evaluates GROUP BY before ordering the results and hence, you need to wrap the query inside another query to get the ordered results.
Update
Instead of joining on aid, you need to join on peopleId of an address record to get the people record.

WHERE clause behaving in the wrong way

i want to create a weekly report using a php script. in the mysql query of this, i want to take out tables that functioned during the last week by using its timestamp in the meantime get the other values(especially the counts of unique users and overall users) since the creation date of those tables. but at the moment its returning incorrect data in terms of counts where the counts are given only within the last week not since the creation of project.
this happens inside a loop so for the example query im using table table_1
QUERY
SELECT DISTINCT b.ID, name, accountname, c.accountID, status,
total_impr, min(a.timestamp), max(a.timestamp),COUNT(DISTINCT userid)
AS unique_users,COUNT(userid) AS overall_users
FROM table_1 a INNER JOIN logs b on a.ID = b.ID INNER JOIN
accounts c on b.accountID = c.accountID
WHERE a.timestamp > DATE_ADD(NOW(), INTERVAL -1 WEEK

MySQL getting SUM of balance grouped by user's job location

Im trying to get the SUM of all user balances in a specific month, and grouped by the user's region, which depends on the Point of Sell they work at.
balance
id_balance
date
id_user
value ($$$)
user
id_user
id_pos
name (not relevant)
pos (Point of Sell)
id_pos
id_region
name (not relevant)
location_region
id_region
name (Florida, Texas, etc)
Basically, I would need it to present this data (filtered by month):
location_region.name | SUM(balance.value)
---------------------|-------------------
Florida | 45730
Texas | 43995
I've tried a few approaches with no luck. This was my closest attempt.
SELECT location_region.name, SUM(balance.value) AS money
FROM balance
LEFT JOIN user ON user.id_user
LEFT JOIN pos ON pos.id_pos = user.id_pos
LEFT JOIN location_region ON location_region.id_region = pos.id_region
WHERE balance.date BETWEEN '2014-02-01' AND DATE_ADD('2014-02-01', INTERVAL 1 MONTH)
GROUP BY location_region.id_region
ORDER BY money DESC
Any ideas? Thanks!
Your current query has a logical error, JOIN condition between balance and user tables is incomplete (missing balance.id_user). So instead of balance LEFT JOIN user ON user.id_user you should have balance LEFT JOIN user ON user.id_user=balance.id_user. This is causing the JOINed table to have more rows (number of rows in balance times number of rows in user table). So the final SUM is bringing a way too higher value.
I tried the following query on your sample data (I changed some values) and it seems to be working fine:
SELECT location_region.name, SUM(balance.value) AS money
FROM balance
LEFT JOIN user USING(id_user)
LEFT JOIN pos USING(id_pos)
LEFT JOIN location_region USING(id_region)
WHERE balance.date BETWEEN '2014-02-01' AND DATE_ADD('2014-02-01', INTERVAL 1 MONTH)
GROUP BY location_region.id_region
ORDER BY money DESC
Working demo: http://sqlfiddle.com/#!2/dda28/3
On having a detailed look at your table structure and the query that you gave, what I feel is this mismatch could happen because of duplicate number of rows that might be cropping up due to the JOIN. What I suggest in this case is to find the DISTINCT rows and summing it up so that you get an exact result. Now since SUM DISTINCT is not available in MySQL, you could try this different approach to accomplish what you want:
SELECT location_region.name,
SUM(balance.value)*COUNT(DISTINCT id_user)/COUNT(id_user) AS money
FROM balance
LEFT JOIN user ON user.id_user = balance.id_user
LEFT JOIN pos ON pos.id_pos = user.id_pos
LEFT JOIN location_region ON location_region.id_region = pos.id_region
WHERE balance.date BETWEEN '2014-02-01' AND DATE_ADD('2014-02-01', INTERVAL 1 MONTH)
GROUP BY location_region.id_region
ORDER BY money DESC
In my comment, was wondering why u did not JOIN user table with ON clause as user.id_user = balance.id_user. I have added that however in my query. Hope this helps.

If my resultset consists of grouped and summed records, how can I then sum/avg these?

I can't help but think there's a simple way to do this - but I want to make sure I do it right and don't miss any simplicity that the community is bound to offer! I have two tables: order and line, to model orders place and items in those orders respectively, in a 1-to-many relationship like you'd expect. Each record in the line relation has a foreign key to the order.
I want to create a simple report to work out the average weight of all orders for the last 6 months. The weight is stored in the line table and represents the weight of a single item, which needs working out by using the quantity (qty). My plan was to sum the rows together first, then work out the average on them, but although the sum seems to work fine, I'm not sure how to apply the average here. Likewise, I'm not sure how to get the sum of all these sums so I can manually make the calculation.
Example SQL
SELECT
SUM(a.weight * a.qty)
FROM
line AS a
LEFT JOIN
order AS b ON a.order_id = b.id
WHERE
b.datetime > DATE_SUB(NOW(), INTERVAL 6 MONTH) AND b.status = 'P'
GROUP BY a.order_id
ORDER BY b.datetime ASC;
Example Results
20.0000
66.7400
20.7300
7.5000
7.1100
How should I take these results to work out the average, or at least the total?
The simplest way to get there from your existing query (using a derived table):
SELECT AVG(weight) AS Average, SUM(weight) AS Total
FROM
(
SELECT
SUM(a.weight * a.qty) AS weight
FROM
`line` AS a
LEFT JOIN
`order` AS b ON a.order_id = b.id
WHERE
b.datetime > DATE_SUB(NOW(), INTERVAL 6 MONTH) AND b.status = 'P'
GROUP BY a.order_id
) a