Create month and year column to speed up timestamp query - mysql

I have a big log table in mariadb/mysql:
CREATE TABLE `logs` (
`id` CHAR(36) NOT NULL,
`user` CHAR(4) NOT, NULL,
`dateCreated` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`dateUpdated` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
I am trying to query logs based on user and date created by month and year:
select * from logs where month(dateCreated) = '9' and year(dateCreated) = '2016' and user = '1234'
Question:
Should I created two columns called month and year, and index the month, year, and user to speed up the query?

You are better off just restructuring your query's criteria to better take advantage of a possible index on the field:
WHERE dateCreated >= '2016-09-01 00:00:00'
AND dateCreated < '2016-10-01 00:00:00'
AND user = '1234'

Related

Convert MySQL query to Laravel query builder code

I am working with agricultural product management system. I have a question regarding a MySQL query. I would like to know how to create the same query using Laravel query builder:
SELECT
vegitables.name, vegitables.image, vegitables.catagory,
AVG(price_wholesale),
SUM(CASE WHEN rank = 1 THEN price_wholesale ELSE 0 END) today,
SUM(CASE WHEN rank = 2 THEN price_wholesale ELSE 0 END) yesterday
FROM (
SELECT
veg_id, price_wholesale, price_date,
RANK() OVER (PARTITION BY veg_id ORDER BY price_date DESC) as rank
FROM old_veg_prices
) p
INNER JOIN vegitables ON p.veg_id = vegitables.id
WHERE rank in (1,2)
GROUP BY veg_id
This Output result get when run query in database:
Following two table are used to get today price yesterday price and price average get from each product.
CREATE TABLE `vegitables` (
`id` bigint(20) UNSIGNED NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`image` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`catagory` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`total_area` int(11) NOT NULL COMMENT 'Total area of culativate in Sri Lanka (Ha)',
`total_producation` int(11) NOT NULL COMMENT 'Total production particular product(mt)',
`annual_crop_count` int(11) NOT NULL COMMENT 'how many time can crop pre year',
`short_dis` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ALTER TABLE `vegitables`
ADD PRIMARY KEY (`id`);
ALTER TABLE `vegitables`
MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
COMMIT;
CREATE TABLE `old_veg_prices` (
`id` bigint(20) UNSIGNED NOT NULL,
`veg_id` int(11) NOT NULL,
`price_wholesale` double(8,2) NOT NULL,
`price_retial` double(8,2) NOT NULL,
`price_location` int(11) NOT NULL,
`price_date` date NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ALTER TABLE `old_veg_prices`
ADD PRIMARY KEY (`id`);
ALTER TABLE `old_veg_prices`
MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=6;
COMMIT;
I try this site to convert to MySQL query to query builder code. But it show some error's could find it out. Any Way i want to run this code in Laravel with any method??
Your query will not return the data for yesterday and today; it will return the data for two most recent dates (e.g. if today is 2021-11-01 and most recent two dates for for carrots are 2021-10-25 and 2021-10-20 it will use those two dates). Using RANK() ... IN (1, 2) is also incorrect because it can return ranks such as 1 followed by 3 instead of 2.
To get today and yesterday prices you don't need window functions. Just use appropriate where clause and conditional aggregation:
SELECT vegitables.name
, vegitables.image
, vegitables.catagory
, AVG(old_veg_prices.price_wholesale) AS avgwholesale
, SUM(CASE WHEN old_veg_prices.price_date = CURRENT_DATE - INTERVAL 1 DAY THEN old_veg_prices.price_wholesale END) AS yesterday
, SUM(CASE WHEN old_veg_prices.price_date = CURRENT_DATE THEN old_veg_prices.price_wholesale END) AS today
FROM vegitables
INNER JOIN old_veg_prices ON vegitables.id = old_veg_prices.veg_id
WHERE old_veg_prices.price_date IN (CURRENT_DATE - INTERVAL 1 DAY, CURRENT_DATE)
GROUP BY vegitables.id -- other columns from vegitables table are functionally dependent on primary key
The Laravel equivalent would be:
DB::table('vegitables')
->Join('old_veg_prices', 'old_veg_prices.veg_id', '=', 'vegitables.id')
->whereRaw('old_veg_prices.price_date IN (CURRENT_DATE - INTERVAL 1 DAY, CURRENT_DATE)')
->select(
'vegitables.name',
'vegitables.image',
'vegitables.catagory',
DB::raw('AVG(old_veg_prices.price_wholesale) AS avgwholesale'),
DB::raw('SUM(CASE WHEN old_veg_prices.price_date = CURRENT_DATE - INTERVAL 1 DAY THEN old_veg_prices.price_wholesale END) AS yesterday'),
DB::raw('SUM(CASE WHEN old_veg_prices.price_date = CURRENT_DATE THEN old_veg_prices.price_wholesale END) AS today')
)
->groupBy(
'vegitables.id',
'vegitables.name',
'vegitables.image',
'vegitables.catagory'
)
->get();
"Query builder" features of abstraction products often leave out some possible SQL constructs. I recommend you abandon the goal of reverse engineering SQL back to Laravel and simply perform the "raw" query.
Also...
rank() OVER (PARTITION BY veg_id ORDER BY price_date DESC) as rank
requires MySQL 8.0 (MariaDB 10.2).
And suggest you avoid the alias "rank" since that is identical to the name of a function.

Mysql SUM by value in column

I have problem to get the proper result.
I have a table with registered time entries by date and user.
I also have a date table, that only consists of dates.
CREATE TABLE `jobbile_job_record` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`note` text,
`time_type` int(11) DEFAULT NULL,
`created_by` varchar(255) DEFAULT NULL,
`created` date DEFAULT NULL,
`jobbile_job_id` int(11) DEFAULT NULL,
`inserted` datetime DEFAULT CURRENT_TIMESTAMP,
`time_registered` decimal(11,2) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1145 DEFAULT CHARSET=latin1;
SET FOREIGN_KEY_CHECKS = 1;
I would like to get a result of
- date
- total time registered
- total time by user (registered)
I use the following query:
SELECT
date.date,
SUM(jobbile_job_record.time_registered) as 'total time',
SUM(jobbile_job_record.time_registered AND `jobbile_job_record`.`created_by` = '5713') as 'User 5713',
SUM(jobbile_job_record.time_registered AND `jobbile_job_record`.`created_by` = '5714') as 'User 5714'
FROM
date
LEFT JOIN jobbile_job_record
ON date.date = jobbile_job_record.created
WHERE
date.date BETWEEN '2019-11-01' AND '2019-11-30'
GROUP BY
date.date
ORDER BY
date.date ASC
Total_time works fine but the two SUM's with users filtered, is not summarized but counted.
Cant I use this method? Thanks!
I guess you need a case statement here -
SELECT
date.date,
SUM(jobbile_job_record.time_registered) as 'total time',
SUM(CASE WHEN `jobbile_job_record`.`created_by` = '5713' THEN jobbile_job_record.time_registered END) as 'User 5713',
SUM(CASE WHEN `jobbile_job_record`.`created_by` = '5714' THEN jobbile_job_record.time_registered END ) as 'User 5714'
FROM date
LEFT JOIN jobbile_job_record ON date.date = jobbile_job_record.created
WHERE date.date BETWEEN '2019-11-01' AND '2019-11-30'
GROUP BY date.date
ORDER BY date.date ASC

Mysql. Select between dates. If there isn't value in that date, returns 0

I have a user table with the addtime column as timestamp.
I want to get the number of users group by year, month, day.
The table
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
`addtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
)
Data
1,'user1','2018-01-02 10:01:01'
2,'user2','2018-01-02 10:11:01'
3,'user3','2018-01-03 10:01:01'
4,'user4','2018-01-03 10:11:01'
5,'user5','2018-01-03 10:21:01'
6,'user6','2018-01-03 10:41:01'
7,'user7','2018-01-05 10:01:01'
8,'user8','2018-01-05 10:11:01'
9,'user9','2018-01-05 10:21:01'
Query
SELECT DATE_FORMAT(addtime, '%Y-%m-%d') AS Date, COUNT(id) AS total FROM user WHERE addtime BETWEEN '2018-01-01 00:00:00' AND '2018-01-07 00:00:00' GROUP BY YEAR(addtime), MONTH(addtime), DAY(addtime);
The query only returns values if there are user in that day.
2018-01-02, 2
2018-01-03, 4
2018-01-05, 3
The result I need
2018-01-01, 0
2018-01-02, 2
2018-01-03, 4
2018-01-04, 0
2018-01-05, 3
2018-01-06, 0

Latest hour unique entries MySQL

I want to have the latest hour unique entries
This query gives me the latest unique entries (but not of the last hour):
SELECT t1.* FROM allSensors t1
JOIN (SELECT uniqueID, MAX(timestamp) timestamp FROM allSensors GROUP BY uniqueID) t2
ON t1.uniqueID = t2.uniqueID AND t1.timestamp = t2.timestamp;
This query gives me the latest hour entries (but not all of them are unique):
SELECT * FROM allSensors WHERE timeStamp > DATE_SUB(NOW(), INTERVAL 1 HOUR);
How could I get the latest hour latest entries?
How could I get this done with maximum performance??
This will be requested every 5 seconds! And the table has them all
This is the table structure:
CREATE TABLE `allSensors` (
`timeStamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`DeviceID` double DEFAULT NULL,
`UniqueID` double NOT NULL DEFAULT '0',
`DataAge` double DEFAULT NULL,
`Temp` double DEFAULT NULL,
`Light` double DEFAULT NULL,
`Humid` double DEFAULT NULL,
`LeafTemp` double DEFAULT NULL,
`SoilHumid` double DEFAULT NULL,
`SoilEC` double DEFAULT NULL,
`Batt` double DEFAULT NULL,
`SNR` double DEFAULT NULL,
`Ts` double DEFAULT NULL
)
SELECT t1.*
FROM allSensors t1
JOIN
( SELECT uniqueID, MAX(timestamp) timestamp
FROM allSensors
GROUP BY uniqueID
) t2 ON t2.uniqueID = t1.uniqueID
AND t2.timestamp = t1.timestamp;
WHERE t2.timestamp > NOW() - INTERVAL 1 HOUR
That is, get all the last ids, but then throw away the ones that are too old.
You need the composite INDEX(uniqueID, timestamp) (in that order).

MySQL compare timestamp gives error

I'm trying to write a SQL query but I keep getting an error and I have no clue on what could be wrong. I read the manual and searched a lot for it, but I can't get it working.
Query:
SELECT appid
FROM steam_app
ORDER BY last_update, appid
WHERE last_update < '2014-10-16 01:01:01'
This keeps giving an error SQL Error 1064, SQL syntax.
Now I know the error is on the WHERE line. Because if I remove it, all is well.
Solution: the problem is the order of WHERE and ORDER BY. WHERE should be above ORDER BY.
What I want in the end is a query like:
SELECT appid
FROM steam_app
ORDER BY last_update, appid
WHERE last_update < NOW() + INTERVAL 7 DAY
My database:
CREATE TABLE `steam_app` (
`appid` INT(10) UNSIGNED NOT NULL,
`name` VARCHAR(128) NULL DEFAULT NULL,
`type` VARCHAR(64) NULL DEFAULT NULL,
`header_image` VARCHAR(256) NULL DEFAULT NULL,
`last_update` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`last_change` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`appid`),
INDEX `type` (`type`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
Sample data
INSERT INTO `steam_app` (`appid`, `name`, `type`, `header_image`, `last_update`, `last_change`) VALUES (327400, 'Sid Meier\'s Colonization (Classic)', 'game', 'http://cdn.akamai.steamstatic.com/steam/apps/327400/header.jpg?t=1412883714', '2014-10-16 13:36:37', '2014-10-16 13:36:37');
INSERT INTO `steam_app` (`appid`, `name`, `type`, `header_image`, `last_update`, `last_change`) VALUES (327650, 'May’s Mysteries: The Secret of Dragonville', 'game', 'http://cdn.akamai.steamstatic.com/steam/apps/327650/header.jpg?t=1413366276', '2014-10-16 13:36:37', '2014-10-16 13:36:37');
INSERT INTO `steam_app` (`appid`, `name`, `type`, `header_image`, `last_update`, `last_change`) VALUES (327860, 'Salt', 'game', 'http://cdn.akamai.steamstatic.com/steam/apps/327860/header.jpg?t=1413399075', '2014-10-16 13:31:53', '2014-10-16 13:31:53');
You need to use the date_add function. Additionally, the order by clause comes after the where clause:
SELECT last_update, appid
FROM steam_app
WHERE last_update < DATE_ADD(NOW(), INTERVAL 7 DAY)
ORDER BY last_update, appid