Joining most recent column entry to MySQL query result - php

I have a PHP/MySQL based solution for displaying an ID and the number of entries that ID has on a MySQL database table in the past day, and year to date.
The information is simply output to a table.
We would also be able to quickly see the most recent value for a particular column associated with each ID on this same table.
The tables in use have the following descriptions:
TABLE: tags
id int auto_increment
Tag int(8)
Timestamp timestamp
Battery varchar(3) // Status for rfid tags battery condition
TABLE: tag_vehicle
id int auto_increment
Tag int(8)
VehicleId varchar(10)
And here is my MySQL query
SELECT tags.Tag,
sum(CASE WHEN year(Timestamp) = year(CURDATE()) then 1 else 0 end) as ytd,
sum(CASE WHEN date(Timestamp) = date(CURDATE()) then 1 else 0 end) as today,
tag_vehicle.VehicleId,
MAX(Timestamp) as latest
FROM tags
INNER JOIN tag_vehicle
ON tags.Tag = tag_vehicle.Tag
GROUP BY VehicleNumber;
This generates a table with these columns
| Tag | ytd | today | VehicleId | Timestamp |
|1234 | 300 | 12 | BUS1234 | 2014-09-22 |
I'm simply looking to add to my table the latest value of Battery that matches each Tag. I've been trying all morning to produce this result but I haven't had any luck.
| Tag | ytd | today | VehicleId | Timestamp | Battery |
|1234 | 300 | 12 | BUS1234 | 2014-09-22 | ok |
I'm not an expert on MySQL and its starting to feel like the query is too messy. I'm having a hard time figuring out how to get this battery value.
How should I go about obtaining the latest entry for Battery and matching it to the right row?
Is there some way to do it all in 1 (perhaps cleaner) query, or should I make 2 queries and match Battery columns to Tag columns?

You should be able to do this with the substring_index()/group_concat() trick:
SELECT t.tag,
sum(CASE WHEN year(Timestamp) = year(CURDATE()) then 1 else 0 end) as ytd,
sum(CASE WHEN date(Timestamp) = date(CURDATE()) then 1 else 0 end) as today,
tv.VehicleId,
MAX(Timestamp) as latest,
substring_index(group_concat(t.battery order by t.timestamp desc), ',', 1)
FROM tags t INNER JOIN
tag_vehicle tv
ON t.Tag = tv.Tag
GROUP BY VehicleNumber;
The tag column doesn't look right because it comes from an indeterminate row.

Related

Joining a calendar table to a sales table to get total sales for every day in a specified range

I have a 'sales' table called phpbb_sold which records each 'sale' as a row.
I am able to use a WHERE clause with the uitemid field to select one particular item in the sales records, as seen below:
SELECT uitemid, locktime, migrated_sold FROM phpbb_sold WHERE uitemid=342;
+---------+------------+---------------+
| uitemid | locktime | migrated_sold |
+---------+------------+---------------+
| 342 | 1632523854 | 1 |
| 342 | 1634239244 | 1 |
| 342 | 1634240072 | 1 |
| 342 | 1636367271 | 1 |
+---------+------------+---------------+
uitemid = number that identifies this as a sale of X item. locktime = UNIX timestamp that shows the datetime that the item was sold. migrated_sold = the quantity of the item sold. So this is nice, I have a table that keeps a record of each sale as it happens.
What I want to achieve though, is a record of the total number of sales of this item type, for each day in a 6 month period spanning back from the current date, and including each day regardless of whether a sale was made or not. So the desired output of my query would be:
SELECT (the query I want goes here) and returns the following rows...;
+------------+------------+
| caldate | sold_total |
+------------+------------+
| 2021-09-23 | 2 |
| 2021-09-24 | 0 |
| 2021-09-25 | 1 |
| 2021-09-26 | 0 |
| 2021-09-27 | 0 |
| 2021-09-28 | 1 |
+------------+------------+
Note that each day is included as a row in the results, even where the sales total for that day is 0. I read that to do this, I would be required to create a calendar table with one column and all the days I want as rows, so I went ahead and did that:
SELECT caldate FROM phpbb_calendar;
+------------+
| caldate |
+------------+
| 2021-09-23 |
| 2021-09-24 |
| 2021-09-25 |
| 2021-09-26 |
| 2021-09-27 |
| 2021-09-28 |
+------------+
Now all that remains is for me to make the query. I need to somehow return all the rows from the phpbb_calendar table, joining the data from sum() (?) of the total migrated_sold for those days where exists, and a 0 where no sales took place.
I anticipated some issues with the UNIX timestamp, but it's okay because I am able to get caldate and locktime fields to be the same format by using from_unixtime(locktime, '%Y-%m-%d'), so both dates will be in the YYYY-MM-DD format for comparison.
Please could someone help me with this. I've gotten so close every time but it seems that everyone else's request is only slightly different from mine, so existing questions and answers have not been able to satisfy my requirements.
End goal is to use a JS chart library (AnyChart) to show a line graph of the number of sales of the item over time. But to get there, I first need to provide it with the query necessary for it to display that data.
Thanks
Update
Using this query:
SELECT c.caldate, u.uitemid, sum(v.migrated_sold) as total_sales
from phpbb_calendar c cross join
(select distinct uitemid from phpbb_sold) u left join
phpbb_sold v
on c.caldate = from_unixtime(v.locktime, '%Y-%m-%d') WHERE u.uitemid = 39 and c.caldate <= curdate() GROUP BY c.caldate ORDER BY c.caldate;
Returns:
But as you can see, it's just tallying up the total number of sales ever made or something - its clearly incrementing in a way I don't understand.
I don't want it to do that - I want it to count the number of total sales on each day individually. The results should look like this:
So that what is returned is basically a 'histogram' of sales, if any occurred, including 'empty' days where there were no sales (so these empty days must still be returned as rows).
SELECT c.caldate, u.uitemid, COALESCE(SUM(v.migrated_sold), 0) AS total_sales
FROM phpbb_calendar c
CROSS JOIN (SELECT DISTINCT uitemid FROM phpbb_sold WHERE uitemid = 37) u
LEFT JOIN phpbb_sold v
ON v.locktime BETWEEN UNIX_TIMESTAMP(TIMESTAMP(c.caldate)) AND UNIX_TIMESTAMP(TIMESTAMP(c.caldate, '23:59:59'))
AND u.uitemid = v.uitemid
WHERE c.caldate BETWEEN CURDATE() - INTERVAL 6 MONTH AND CURDATE()
GROUP BY c.caldate, u.uitemid
ORDER BY c.caldate;
N.B. I have changed your join to use the unix_timestamp as it should be more efficient and it can use any existing index on locktime
check this out:
select id, d, sum(s) from (
select U.id, d, 0 s from (
select adddate(current_date(),-rows.r) d from (
select (#row_number := #row_number + 1) r
from information_schema.columns,
(SELECT #row_number := 0) AS x
limit 200
) rows
) dates,
(SELECT distinct uitemid id FROM `phpbb_sold`) U
where d > adddate(current_date(), interval -6 month)
union
select uitemid, date(from_unixtime(locktime)),sum(migrated_sold)
from `phpbb_sold`
group by uitemid, date(from_unixtime(locktime))
) sales_union
group by id, d
order by id, d;
see dbfiddle
no need for calendar table

MySQL PHP select rows count in date range

I have a MySQL table for product orderings named TABLE1.
Date means the date purchase has been made
The table has other columns that currently have no influence.
PRODUCT_ID | DATE | other columns
3 |2018-02-01 | other values
3 |2018-02-03 | other values
3 |2018-02-07 | other values
3 |2018-02-07 | other values
3 |2018-03-02 | other values
I know that the first time the product 3 has been ordered, is 2018-02-01
SELECT DATE FROM TABLE1 WHERE PRODUCT_ID = '3' ORDER BY DATE ASC LIMIT 1
How do I select count of product orderings per day within range of 2018-02-01 and 2019-03-16 (today) so that I could get a table like that:
DATE | ORDERS_PER_DAY
2018-02-01 | 1
2018-02-02 | 0
2018-02-03 | 1
...
2018-02-07 | 2
...
2018-03-02 | 1
...
2018-03-15 | 0
2018-03-16 | 0
Thanks for help!
You can simply use GROUP BY clause to do it.
SELECT `DATE`, COUNT(`PRODUCT_ID`) AS ORDERS_PER_DAY
FROM TABLE1
WHERE `DATE` BETWEEN '2018-02-01' AND CURDATE()
GROUP BY `DATE`
This query will result in filtering the records on your required date range and then grouping it by each day where there is data.
My syntax may not be exactly correct, but could you try something like this using the GROUP BY clause.
SELECT DATE, COUNT(*) AS ORDERS_PER_DAY
FROM TABLE1
GROUP BY DATE, PRODUCT_ID
HAVING PRODUCT_ID = '3'
you can read more about this here: https://dev.mysql.com/doc/refman/8.0/en/group-by-handling.html

MySQL Group by column, then count by another column

I have a challenging MySQL problem that is beyond my basic knowledge, I would really appreciate any help.
I currently have the following query:
select users.userid, CAST(posts.time AS DATE)
FROM users INNER JOIN posts ON users.post_id = posts.id
Sample output:
userid | CAST(posts.time AS DATE)
1............2015-01-05
2............2015-02-06
2............2015-04-07
2............2015-04-07
3............2015-04-07
1............2015-02-06
7............2015-01-05
userid can repeat itself, there could be 10 different rows with userid = 1; same goes for the date column. I would like to count how many rows each userid had for each distinct date. Based on the above data, the output should be:
-----------------------1----------2--------3---------4--------5--------6-------7
2015-01-05.............1..........0........0.........0........0........0.......1
2015-02-06.............1..........1........0.........0........0........0.......0
2015-04-07.............0..........2........1.........0........0........0.......0
I have 7 users in total. I would like to further replace the user id with a name that I define; e.g. I would define 1 in the heading/title to be displayed as Mike, 2 to be displayed as George, and so forth...
Is it possible? Thanks everyone.
If you have 7 users only, and only ever will, pivoting the data is not too difficult:
select date(posts.time),
count(case when userid = 1 then userid end) as `1`,
count(case when userid = 2 then userid end) as `2`,
count(case when userid = 3 then userid end) as `3`,
count(case when userid = 4 then userid end) as `4`,
count(case when userid = 5 then userid end) as `5`,
count(case when userid = 6 then userid end) as `6`,
count(case when userid = 7 then userid end) as `7`
users INNER JOIN posts ON users.post_id = posts.id
group by date(posts.time)
demo here
If your number of users is variable, or prone to change - it becomes annoying and you'd be better off looking to your application language to take care of it.
Here's what I have (I didn't complete it for you):
SELECT date, SUM(id_1) AS Mike, SUM(id_2) AS George FROM (SELECT CASE id WHEN 1 THEN 1 ELSE 0 END as id_1, CASE id WHEN 2 THEN 1 ELSE 0 END as id_2, date FROM test_dates) as tmp GROUP BY date;
+------------+------+--------+
| date | Mike | George |
+------------+------+--------+
| 2015-01-05 | 1 | 0 |
| 2015-02-06 | 1 | 1 |
| 2015-04-07 | 0 | 2 |
+------------+------+--------+
The trick of substituting a summation of 1s when what you want is a count is a common reporting trick that is worth remembering. Blew my mind when I first saw it.

Get Count of each record With Subquery using php mysql

My Db design is :
Tbl_id booked_by date
1 W 2014-02-01
2 P 2014-02-01
3 P 2014-02-01
4 P 2014-02-01
5 W 2014-02-04
6 W 2014-02-04
7 P 2014-02-04
I want to display counting of W , P Where given Date and Grouped by Date and with Using php mysql ..
like
I NEED OUTPUT LIKE THIS:
Tbl_id W P date
1 1 3 2014-02-01
2 2 1 2014-02-04
I need Mysql Query to get output given in above..
You need to use SUM aggregate function for summation and a temp row number for Tbl_id.
select
#rwnm:=#rwnm+1 as Tbl_id,
sum(No_people) as No_people,
sum(Breakfast) as Breakfast,
sum(Lunch) as Lunch,
date
from my_table, ( select #rwnm := 0 ) rownums
group by date
You need the SUM() function, not the COUNT().
SELECT
SUM(No_people),
SUM(Breakfast),
SUM(Lunch),
`date`
FROM yourTable
GROUP BY `date`
I did not include the Tbl_id, because every column you select must be used with an aggregate function or be included in the group by clause. Otherwise it's undetermined which row of the group is selected. Selecting it anyway only works in MySQL, but it does not adhere to the SQL standard. If you want some kind of row number instead either Ravinder's approach or create one on application level (which certainly is easier).
UPDATE:
SELECT
SUM(booked_by = 'W') AS W,
SUM(booked_by = 'P') AS P,
`date`
FROM yourTable
GROUP BY `date`
You still need the SUM() function. booked_by = 'whatever' evaluates to true (1) or false (0).
If you want just the result without slno below query will work fine
SELECT
SUM(No_People) as No_People,
SUM(Breakfast) as Breakfast,
Sum(Lunch) as Lunch,
`date` as date
FROM table_Name
GROUP BY `date`
Output:
No_people Breakfast Lunch date
84 112 160 2014-02-11
116 96 118 2014-02-04
But if you also want serial no. to be displayed then below query will work
SELECT
#slno := #slno + 1 as slno
SUM(No_People) as No_People,
SUM(Breakfast) as Breakfast,
Sum(Lunch) as Lunch,
`date` as date
FROM table_Name, (select #slno := 0 ) sln
GROUP BY `date`
Output:
slno No_people Breakfast Lunch date
1 84 112 160 2014-02-11
2 116 96 118 2014-02-04
Consider the following data set...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,booked_by CHAR(1) NOT NULL
,date DATE NOT NULL
);
INSERT INTO my_table VALUES
(1,'W','2014-02-01'),
(2,'P','2014-02-01'),
(3,'P','2014-02-01'),
(4,'P','2014-02-01'),
(5,'W','2014-02-04'),
(6,'W','2014-02-04'),
(7,'P','2014-02-04');
Here's a pivot query...
SELECT date
, COUNT(CASE WHEN booked_by = 'W' THEN 'foo' END) W
, COUNT(CASE WHEN booked_by = 'P' THEN 'foo' END) P
FROM my_table
GROUP
BY date;
+------------+---+---+
| date | W | P |
+------------+---+---+
| 2014-02-01 | 1 | 3 |
| 2014-02-04 | 2 | 1 |
+------------+---+---+
In my opinion, a better idea (more flexible, more scalable) is simply to return a GROUPED (and, therefore, ordered) result set and handle the rest at in the presentation layer (e.g. a simple PHP loop)...
SELECT date,booked_by,COUNT(*) ttl FROM my_table GROUP BY date,booked_by;
+------------+-----------+-----+
| date | booked_by | ttl |
+------------+-----------+-----+
| 2014-02-01 | P | 3 |
| 2014-02-01 | W | 1 |
| 2014-02-04 | P | 1 |
| 2014-02-04 | W | 2 |
+------------+-----------+-----+

How can make sort by votes "by date", "by year"..?

What I am trying to implement is similar to what we have on SO. I want to rank posts by upvotes in last day, last month etc. My schema makes up two tables,
post(id, post, posted_on..)
vote(post_id, vote_value, date)
I hope the schema is pretty self explanatory. The problem being, if I sort "by day" by making a inner join on posts and vote and having a where clause('votes.date >= DATE_SUB(CURDATE(), INTERVAL 1 DAY'), it does work as intended but fails to show the other posts. I mean the posts which haven't had vote in last day are completely ignored. What I want is that those posts be given low priority but do show up in the query.
While, I may think of using union operation but i was looking for another approach.
Update: Lets say, there are two posts, 1,2.
and votes table is like,
post_id vote_value date
1 1 2012-12-19
2 1 2012-12-10
If I query, as per my approach, then only the post - "1" will show up since I have put a date constraint but I want both to show up. Here is my query:
SELECT `id`, SUM(`votes`.`votes`) AS likes_t, `post`.* FROM `posts` JOIN `votes` ON (`id` = `votes`.`post_id`) WHERE `votes`.`date` >= DATE_SUB(CURDATE(), INTERVAL 2 DAY)
If you want to show all posts, but only count the recent votes, this should do it:
SELECT `id`,
SUM(IF(`votes`.`date` >= DATE_SUB(CURDATE(), INTERVAL 2 DAY, `votes`.`votes`, 0)) AS likes_t,
`post`.*
FROM `posts` JOIN `votes` ON (`id` = `votes`.`post_id`)
If I got it right:
SELECT *, IF(vote.date>=DATE_SUB(CURDATE(), INTERVAL 1 DAY), 1, 0) as rate FROM post INNER JOIN vote ON (post.id=vote.post_id) ORDER BY rate DESC;
+------+--------+---------+------+---------------------+------+
| id | post | post_id | vote | date | rate |
+------+--------+---------+------+---------------------+------+
| 1 | first | 1 | 1 | 2012-12-19 00:00:00 | 1 |
| 1 | first | 1 | 1 | 2012-12-13 00:00:00 | 0 |
| 2 | second | 2 | 1 | 2012-12-10 00:00:00 | 0 |
+------+--------+---------+------+---------------------+------+

Categories