I hate to ask a question here, as I know it's been done (slightly), however my problem is slightly different because of the field formats. I've tried a bunch of things on joins and hour selections and others, and I'm stuck. Would love a quick hand.
I've got a MySQL table like so:
CREATE TABLE `VAT` (
`VATid` int(11) NOT NULL AUTO_INCREMENT,
`VATDate` date DEFAULT NULL,
`VATTime` varchar(10) DEFAULT NULL,
`VATPledgeAmount` float DEFAULT NULL,
PRIMARY KEY (`VATid`),
UNIQUE KEY `VATid_UNIQUE` (`VATid`)
) ENGINE=MyISAM AUTO_INCREMENT=6531 DEFAULT CHARSET=latin1;
The problem that is that the VATDate fields have dates in the following format YYYY-MM-DD and the VATTime fields are a HHMM with leading zeros, as shown in the following table with some example data.
Sample Table Data:
VATid | VATDate | VATTime | VATPledgeAmount |
--------------------------------------------------
1 | 2016-06-30 | 0730 | 100 |
2 | 2016-06-30 | 0733 | 20 |
3 | 2016-06-30 | 0840 | 70 |
4 | 2016-06-30 | 0943 | 100 |
5 | 2016-06-30 | 0730 | 50 |
6 | 2016-07-01 | 2113 | 50 |
7 | 2016-07-01 | 2302 | 300 |
8 | 2016-07-02 | 0416 | 10 |
9 | 2016-07-02 | 0417 | 10 |
10 | 2016-07-02 | 0418 | 10 |
What I want to do is have the MySQL server do the calculations for me, so that when the data comes out the dates are still shown, but the total values from the PledgeAmount for each hour are summed up and grouped.
Counts | g_VATDate | g_VATTime | Total_VATPledgeAmount |
--------------------------------------------------
2 | 2016-06-30 | 0700 | 120 |
1 | 2016-06-30 | 0800 | 70 |
1 | 2016-06-30 | 0900 | 100 |
1 | 2016-07-01 | 2100 | 50 |
1 | 2016-07-01 | 2300 | 300 |
3 | 2016-07-02 | 0400 | 30 |
I hope this makes sense. I'm planning on using this for a JS graph, but as I don't need or want the individual pledges and their amount values, I just want the amounts and totals for each hour.
You can use concat(left(VATTime, 2), '00') to get the hour:
select count(VATid) counts, VATDate g_VATDate,
concat(left(VATTime, 2), '00') g_VATTime,
sum(VATPledgeAmount) Total_VATPledgeAmount
from VAT
group by g_VATDate, g_VATTime;
Related
I'm developing a dashboard with graphs.
What's the problem?
Let's say, that I have a table with the folowing sctructure:
+-------+------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------+------+-----+---------+-------+
| total | int | NO | | NULL | |
| new | int | NO | | NULL | |
| date | date | YES | | NULL | |
+-------+------+------+-----+---------+-------+
where total stands for Total Members and new for New Members (date is a date of course - in format: yyyy-mm-dd).
Example of columns:
+-------+-------+------------+
| total | new | date |
+-------+-------+------------+
| 3450 | 21 | 2021-11-06 |
| 3650 | 200 | 2021-11-07 |
| 3694 | 34 | 2021-11-08 |
| 3520 | 26 | 2021-11-09 |
| 3399 | -321 | 2021-11-10 |
| 3430 | 31 | 2021-11-11 |
| 3450 | 20 | 2021-11-12 |
| 3410 | -40 | 2021-11-13 |
| 3923 | 513 | 2021-11-14 |
| 4019 | 96 | 2021-11-15 |
| 4119 | 100 | 2021-11-16 |
| 4000 | -119 | 2021-11-17 |
| 3000 | -1000 | 2021-11-18 |
| 3452 | 452 | 2021-11-19 |
| 3800 | 348 | 2021-11-20 |
| 3902 | 102 | 2021-11-21 |
| 4050 | 148 | 2021-11-22 |
+-------+-------+------------+
And there are a few options, where the dashboard user can select between 2 dates and type of graphs (daily, weekly, monthly).
Image, that describes the Setting options.
The Point
I need to take these 2 dates and somehow get all data from the database between the given term. Well, but that's not all. The Daily, Weekly and Monthly option means, that graphs will be showing average newcoming and total members per every week (so if I will grab 7 days from the database, I need to create an average - and do this between all these days / weeks / months in a term), if it's weekly, etc. So the final graph will be showing something like:
250 new 20 new 31 new
1000 total 1020 total 1051 total
Nov 7 Nov 14 Nov 21
etc...
More informations:
Ubuntu: 21.04
MySQL: 8.0.27
PHP: 7.4.23
Apache: 2.4.46
Does anyone have any ideas?
I don't get where your numbers come from
But your query would go like this.
For the month you need to group by MONTHof course
CREATE TABLE members (
`total` INTEGER,
`new` INTEGER,
`date` date
);
INSERT INTO members
(`total`, `new`, `date`)
VALUES
('3450', '21', '2021-11-06'),
('3650', '200', '2021-11-07'),
('3694', '34', '2021-11-08'),
('3520', '26', '2021-11-09'),
('3399', '-321', '2021-11-10'),
('3430', '31', '2021-11-11'),
('3450', '20', '2021-11-12'),
('3410', '-40', '2021-11-13'),
('3923', '513', '2021-11-14'),
('4019', '96', '2021-11-15'),
('4119', '100', '2021-11-16'),
('4000', '-119', '2021-11-17'),
('3000', '-1000', '2021-11-18'),
('3452', '452', '2021-11-19'),
('3800', '348', '2021-11-20'),
('3902', '102', '2021-11-21'),
('4050', '148', '2021-11-22');
SELECT `new`,sumtotal, `date` FROM members m
INNER JOIN (SELECT SUM(`new`) sumtotal, MIN(`date`) mindate FROM members GROUP BY WEEK(`date`)) t1
ON m.`date`= t1.mindate
WHERE m.`date` BETWEEN '2021-11-07' AND '2021-11-22'
new | sumtotal | date
--: | -------: | :---------
200 | -50 | 2021-11-07
513 | 390 | 2021-11-14
102 | 250 | 2021-11-21
db<>fiddle here
Let's suppose I have a MySQL table 'orders' with the following data:
| id | order_no | item_id | amount | datetime |
| 1 | 123 | 901 | 1 | 2020-08-05 00:00:01 |
| 2 | 324 | 902 | 2 | 2020-08-06 00:00:01 |
| 3 | 324 | 905 | 1 | 2020-08-06 00:00:01 |
| 4 | 511 | 902 | 1 | 2020-08-07 00:00:01 |
| 5 | 400 | 904 | 3 | 2020-08-08 00:00:01 |
| 6 | 195 | 903 | 1 | 2020-08-09 00:00:01 |
| 7 | 195 | 905 | 2 | 2020-08-09 00:00:01 |
| 8 | 250 | 908 | 1 | 2020-08-10 00:00:01 |
| 9 | 222 | 901 | 3 | 2020-08-11 00:00:01 |
| 10 | 315 | 903 | 1 | 2020-08-12 00:00:01 |
| 11 | 315 | 905 | 2 | 2020-08-12 00:00:01 |
| 12 | 198 | 903 | 1 | 2020-08-13 00:00:01 |
| 13 | 651 | 902 | 2 | 2020-08-14 00:00:01 |
| 14 | 651 | 907 | 2 | 2020-08-14 00:00:01 |
| 15 | 405 | 902 | 1 | 2020-08-15 00:00:01 |
| 16 | 112 | 905 | 1 | 2020-08-16 00:00:01 |
and in my website I want to display the orders according to user's settings like: orders per page/ page number. The data need to be ordered by 'datetime' in ascending order, so if the page number is 2 with orders per page = 5, I would need data of id-s 8-14 (as rows of id 1-7 make the first 5 orders and 8-14: the second one). Note that some orders (in bold) have 2 rows (and can have more) with the same order_no but different item_id.
The simple LIMIT and OFFSET clauses are of no use here unless I combine them with some subqueries but so far I have not found the solution.
I have come to a solution that I think will work best for me: one table 'orders' with all the order 'header' data + first item of the order (each piece of item data in a separate column, like item_id, amount etc.) then, another column: 'items' of type JSON to store 2nd and further items if there are 2 or more; this way I'll be able to use LIMIT and OFFSET and will need only one query especially for inserting a new order, which had worried me the most because with 2 tables I would have to use a transaction.
A select query, in most cases will be simple, only with 2 or more items per order I will need to handle the items from the JSON column, it will not harm performance at all with, as I mentioned in a comment, most of orders containing only one item.
Thank you #Shadow for your comments, they have really helped me to find the solution as I think I'd been going in a wrong direction.
I have this table in MySQL:
id | Action | DateTime | Time(ms)
-----+------------------------------+----------------------------+-------------
1 | Player1_rightKeyPress_Start | 2014-10-01 18:51:17.795000 | 7322
2 | Player1_shootKeyPress_Start | 2014-10-01 18:51:17.971000 | 7498
3 | Player1_rightKeyPress_Start | 2014-10-01 18:51:18.035000 | 7562
4 | Player2_leftKeyPress_Start | 2014-10-01 18:51:18.126000 | 7653
5 | Player1_shootKeyPress_Start | 2014-10-01 18:51:18.214000 | 7741
6 | Player1_rightKeyPress_Start | 2014-10-01 18:51:18.305000 | 7832
7 | Player2_leftKeyPress_Start | 2014-10-01 18:51:18.369000 | 7896
8 | Player1_shootKeyPress_Start | 2014-10-01 18:51:18.374000 | 7901
9 | Player1_rightKeyPress_Start | 2014-10-01 18:51:18.467000 | 7994
10 | Player1_shootKeyPress_Start | 2014-10-01 18:51:18.580000 | 8107
11 | Player1_rightKeyPress_Start | 2014-10-01 18:51:18.676000 | 8203
12 | Player1_shootKeyPress_Start | 2014-10-01 18:51:18.812000 | 8339
13 | Player1_rightKeyPress_Start | 2014-10-01 18:51:18.899000 | 8426
14 | Player1_shootKeyPress_Start | 2014-10-01 18:51:19.050000 | 8577
15 | Player1_rightKeyPress_Start | 2014-10-01 18:51:19.123000 | 8650
This is just a part of the table in the database. What I want to do is to count the number of times Player1_rightKeyPress_Start, Player1_shootKeyPress_Start and Player2_leftKeyPress_Start occurred in every 10,000second. The time runs up to 600,000 miliseconds. I did not bother to include all of it because it is really large.
You probably could do something along these lines: convert to seconds, divide by 10 to get the 10 second intervals, and then apply the group by:
SELECT Action, FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(DateTime)/10)), COUNT(*)
FROM myTable
GROUP BY Action, FLOOR(UNIX_TIMESTAMP(DateTime)/10)
Check the mysql docs for the time functions.
I'm not sure if I understand your question right, but the following sql should count the occurrence of Player1_rightKeyPress_Start in the entire table (swap [TABLENAME]):
SELECT COUNT(*) FROM [TABLENAME] WHERE Action = 'Player1_rightKeyPress_Start';
Paste the result of desc [TABLENAME] and SELECT * FROM [TABLENAME] LIMIT 10; would help. And add your desired result as #Sean said should also help.
BACKGROUND
I am working on a project where I need to capture the 30 day average of values for some id# then use this average to determine if some new value is anomalous. For the purposes of this question, we can assume I only need a 10-day average since the solutions are probably similar. I currently have two tables: history which holds the actual values that I have recorded for specific id# numbers by day but can have some missing days and calendar a date table that has all of the days that I need in my 30 day average.
create table history (
day date not null,
id bigint not null,
category int not null,
value int not null default '0',
primary key (day, id, category),
key category (category)
);
create table calendar (
day date not null primary key
);
I would like to take the existing data that I have in the history table and fill in the missing data by either copying forward a previous value or copying back a forward value. E.g given this data in the history table:
+------------+-----------+----------+-------+
| day | id | category | value |
+------------+-----------+----------+-------+
| 2015-02-19 | 159253663 | 364 | 212 |
| 2015-02-20 | 159253663 | 364 | 211 |
| 2015-02-22 | 159253663 | 364 | 199 |
| 2015-02-23 | 159253663 | 364 | 192 |
| 2015-02-24 | 159253663 | 364 | 213 |
+------------+-----------+--------+---------+
Note: there is no entry for 2015-02-21
I would like to fill in enough data so that I can compute the 10-day average i.e. copy the oldest value (2015-02-19) back to the beginning of my 10-day range then fill in the missing 2015-02-21 value with the previous day's value. The result would be this (stars mark the newly added rows):
+------------+-----------+----------+-------+
| day | id | category | value |
+------------+-----------+----------+-------+
| 2015-02-14 | 159253663 | 364 | 212 | *
| 2015-02-15 | 159253663 | 364 | 212 | *
| 2015-02-16 | 159253663 | 364 | 212 | *
| 2015-02-17 | 159253663 | 364 | 212 | *
| 2015-02-18 | 159253663 | 364 | 212 | *
| 2015-02-19 | 159253663 | 364 | 212 |
| 2015-02-20 | 159253663 | 364 | 211 |
| 2015-02-21 | 159253663 | 364 | 211 | *
| 2015-02-22 | 159253663 | 364 | 199 |
| 2015-02-23 | 159253663 | 364 | 192 |
| 2015-02-24 | 159253663 | 364 | 213 |
+------------+-----------+--------+---------+
ATTEMPT
My initial thought was to left join to a calendar table that has the date ranges I need, when I do that I get something like this:
select c.day, h.id, h.value
from calendar c
left join history h using (day)
where c.day between curdate() - interval 10 day and curdate();
+------------+-----------+----------+-----------+
| day | id | category | value |
+------------+-----------+----------+-----------+
| 2015-02-14 | NULL | NULL | NULL |
| 2015-02-15 | NULL | NULL | NULL |
| 2015-02-16 | NULL | NULL | NULL |
| 2015-02-17 | NULL | NULL | NULL |
| 2015-02-18 | NULL | NULL | NULL |
| 2015-02-19 | 159253663 | 364 | 212 |
| 2015-02-19 | 159253690 | 364 | 222 |
| 2015-02-20 | 159253663 | 364 | 211 |
| 2015-02-20 | 159253690 | 364 | 221 |
| 2015-02-21 | NULL | NULL | NULL |
| 2015-02-22 | 159253663 | 364 | 199 |
| 2015-02-22 | 159253690 | 364 | 209 |
| 2015-02-23 | 159253663 | 364 | 192 |
| 2015-02-23 | 159253690 | 364 | 202 |
| 2015-02-24 | 159253663 | 364 | 213 |
| 2015-02-24 | 159253690 | 364 | 213 |
+------------+-----------+----------+-----------+
I am not sure where to proceed from this point, because I need an entry for each day for each distinct id#. This join only returns a single day if they are missing. I am looking for a better approach. I would like to push as much of the work as possible on the MySQL server, but can do some things programmaticaly. Any/all ideas or suggestions are welcome.
Here is a SQLFiddle that has the DDL definitions I am testing with: http://sqlfiddle.com/#!2/cc206/2
The following uses an # variable and in-statement assignments to roll backward the value (and id):
SET #lastval = 0, #lastid = 0;
SELECT c.day, #lastid := COALESCE(h.id,#lastid) id, #lastval := COALESCE(h.value,#lastval) VALUE, h.id id1,h.value v1
FROM (SELECT DISTINCT c.day,h.id FROM history h, calendar c) c
LEFT JOIN history h ON h.day = c.day AND h.id = c.id
WHERE c.day BETWEEN CURDATE() - INTERVAL 10 DAY AND CURDATE()
ORDER BY COALESCE(h.id,#lastid),c.day DESC
The sub-query seems to be necessary, never been too sure why (some do, some don`t).
If it looks like the results are in the wrong order you might have to add :
SET optimizer_switch='block_nested_loop=off';
before the statement as the block nested loop optimisation can mess with the Order mysql uses when collecting the rows.
I have a table named nca_totals.
Table: nca_totals
+----------+-----------+------------+--------------+
| total_id | nca_total | nca_date | account_type |
+----------+-----------+------------+--------------+
| 13 | 10450 | 2015-01-21 | DBP-TRUST |
| 14 | 5000 | 2015-02-05 | DBP-TRUST |
| 15 | 7000 | 2015-04-02 | DBP-TRUST |
| 16 | 4000 | 2015-05-02 | DBP-TRUST |
+----------+-----------+------------+--------------+
Now I want to display all the data by quarter base on its date. Let's say I want to display all the records who belong to 1st Quarter like this:
+----------+-----------+------------+--------------+
| total_id | nca_total | nca_date | account_type |
+----------+-----------+------------+--------------+
| 13 | 10450 | 2015-01-21 | DBP-TRUST |
| 14 | 5000 | 2015-02-05 | DBP-TRUST |
+----------+-----------+------------+--------------+
This date belongs to the 1st quarter of the year (Jan, Feb, March). I only have this query to select the date and return its quarter number as:
SELECT QUARTER('2015-01-11'); /* returns 1 */
How can I combine that query to display all the records by quarter ? Can anyone help ? Thanks.
select *
from nca_totals
where QUARTER(nca_date) = 1
SELECT
CEIL(MONTH(`nca_date`) / 3) AS `quarter`
FROM `nca_totals`;