Join on datetime values - mysql

I'm looking to count the number of records based on matching a datetime values:
create table `weather` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
`dt_iso` datetime DEFAULT NULL,
`weather_main` varchar(60) DEFAULT NULL);
insert into `weather` (`dt_iso`,`weather_main`) values ("2019-01-01 23:00:00","cloud"), ("2019-01-02 00:00:00","sun"), ("2019-01-02 01:00:00","cloud");
create table `incidents` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
`Incident_Date` datetime DEFAULT NULL);
insert into `incidents` (`Incident_Date`) values ("2019-01-01 23:50:00"), ("2019-01-01 23:50:00"), ("2019-01-09 10:05:00");
In SQL Fiddle: http://sqlfiddle.com/#!9/489f57
This is the type of output I am seeking:
dt_iso, weather_main, count(incidents),
2019-01-01T23:00:00, cloud, 0
2019-01-02T00:00:00, sun, 2
2019-01-02T01:00:00, cloud, 0

It looks like you're trying to count incidents in the hour prior to the time in the weather table, which you can do with this query:
SELECT w.dt_iso, w.weather_main, COALESCE(COUNT(i.id), 0) AS incidents
FROM weather w
LEFT JOIN incidents i ON i.Incident_Date > w.dt_iso - INTERVAL 1 HOUR
AND i.Incident_Date <= w.dt_iso
GROUP BY w.id
Output:
dt_iso weather_main incidents
2019-01-01T23:00:00Z cloud 0
2019-01-02T00:00:00Z sun 2
2019-01-02T01:00:00Z cloud 0
Demo on SQLFiddle

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.

Finding records of actions in parallel times

I have a call data records table (CDRs) with about 7 million rows,
Each row holds a call record:
unique identifier, caller number, receiving number, answer_datetime and duration_in_seconds
I am looking for an efficient way of finding calls handled by the same receiving number in parallel times.
Any query I tried took too long
The table structure:
CREATE TABLE `cdrs` (
`global_identifier` varchar(32) DEFAULT NULL,
`caller_num` int(14) DEFAULT NULL,
`receiving_num` int(14) DEFAULT NULL,
`call_answer` datetime DEFAULT NULL,
`call_duration` int(7) DEFAULT NULL,
KEY `caller_num` (`caller_num`),
KEY `receiving_num` (`receiving_num`),
KEY `call_answer` (`call_answer`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
;
The query I have already tried:
SELECT
DATE_FORMAT(call_answer,'%Y%m') AS Ym,
b_num,
COUNT(*) AS cnt,
SUM(call_duration) / 60 AS c_dur
FROM
(
SELECT
ycdr.*
FROM
cdrs ycdr
INNER JOIN cdrs ycdr2 ON
ycdr2.receiving_num = ycdr.receiving_num
AND ycdr2.caller_num != ycdr.caller_num
WHERE
ycdr2.call_answer BETWEEN
ycdr.call_answer AND ycdr.call_answer
AND ycdr.call_answer >= '2015-01-01'
AND ycdr.call_answer < '2015-01-05'
GROUP BY
ycdr.global_identifier
) a
;
The EXPLAIN result:

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

View with join between field that can be null

I have 2 tables with more than 50,000 records each, each one of these.
CREATE TABLE IF NOT EXISTS `call_attempt` (
`csvleads_id_fk` int(11) NOT NULL,
`users_id_fk` int(11) NOT NULL,
`status` int(11) NOT NULL,
`call_id` varchar(50) default NULL,
KEY `csvlead` (`csvleads_id_fk`),
KEY `user_id` (`users_id_fk`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `recorded` (
`record_id` int(11) NOT NULL auto_increment,
`agent_id_fk` int(11) NOT NULL,
`lead_id_fk` int(11) NOT NULL,
`duration` int(11) NOT NULL,
`recording_url` text collate utf8_unicode_ci NOT NULL,
`recorded_at` timestamp NULL default NULL,
`call_id` varchar(50) character set utf8 default NULL,
PRIMARY KEY (`record_id`),
UNIQUE KEY `filterme` (`recording_url`(100))
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=72158 ;
I added a call_id field to make the link. This means that new records must have these fields filled, at least in table call_attempt.
call_attempt recorded
------------------- ----------------
users_id_fk call_id call_id dutarion
------------------- ----------------
1 NULL NULL 10
2 NULL NULL 8
3 NULL NULL 5
4 NULL ca12 19
50000 ca12 ca14 9
50001 ca13
50002 ca14
I need to throw me a view that all records in the table call_attempt, and if you find records in table recorded the duration is brought.
I tried LEFT JOIN but takes more than 50 seconds.
SELECT
`cal`.`csvleads_id_fk` AS `Id`,
`rc`.`duration` AS `duration`
FROM `call_attempt` AS cal
LEFT JOIN recorded AS rc ON (cal.call_id = rc.call_id AND (rc.call_id IS NOT NULL))
84.625 sec / 0.000 sec
Next, I tried doing a LEFT JOIN to a SELECT table recorded with only the records that call_id are NOT NULL but don't let me create the view.
CREATE
ALGORITHM = UNDEFINED
DEFINER = `benscott`#`%`
SQL SECURITY DEFINER
VIEW `callHistory4` AS
SELECT
`cal`.`csvleads_id_fk` AS `Id`,
`rc`.`duration` AS `duration`
FROM `call_attempt` AS cal
LEFT JOIN (SELECT call_id , duration FROM `recorded` WHERE call_id IS NOT NULL) AS rc ON (rc.call_id = cal.call_id)
Error Code: 1349. View's SELECT contains a subquery in the FROM clause
Then I create another view with just the results NOT NULL from call_id.
CREATE
ALGORITHM = UNDEFINED
DEFINER = `benscott`#`%`
SQL SECURITY DEFINER
VIEW `view_recorded` AS
SELECT call_id , duration FROM `recorded` WHERE call_id IS NOT NULL
And in the new view I join with view_recorded but also takes more than 40 seconds.
SELECT
`cal`.`csvleads_id_fk` AS `Id`,
`rc`.`duration` AS `duration`
FROM `call_attempt` AS cal
LEFT JOIN view_recorded AS rc ON (rc.call_id = cal.call_id)
92.094 sec / 0.000 sec
So, what options Do I have? Thank you!!
For this query:
SELECT `cal`.`csvleads_id_fk` AS `Id`, `rc`.`duration` AS `duration`
FROM `call_attempt_log` cal LEFT JOIN
recorded_calls rc
ON cal.call_id = rc.call_id AND rc.call_id IS NOT NULL;
The best index is recorded_calls(call_id, duration).

Counting records in normalized table

Older questions seen
Counting one table of records for matching records of another table
MySQL Count matching records from multiple tables
Count records from two tables grouped by one field
Table(s) Schema
Table entries having data from 2005-01-25
CREATE TABLE `entries` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`ctg` VARCHAR(15) NOT NULL,
`msg` VARCHAR(200) NOT NULL,
`nick` VARCHAR(30) NOT NULL,
`date` DATETIME NOT NULL,
PRIMARY KEY (`id`),
INDEX `msg` (`msg`),
INDEX `date` (`date`)
)
COLLATE='utf8_general_ci'
ENGINE=MyISAM;
Child table magnets with regular data from 2011-11-08(There might be a few entries from before that)
CREATE TABLE `magnets` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`eid` INT(10) UNSIGNED NOT NULL,
`tth` CHAR(39) NOT NULL,
`size` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0',
`nick` VARCHAR(30) NOT NULL DEFAULT 'hjpotter92',
`date` DATETIME NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `eid_tth` (`eid`, `tth`),
INDEX `entriedID` (`eid`),
INDEX `tth_size` (`tth`, `size`)
)
COLLATE='utf8_general_ci'
ENGINE=MyISAM;
Question
I want to get the count of total number of entries by any particular nick(or user) entered in either of the table.
One of the entry in entries is populated at the same time as magnets and the subsequent entries of magnets can be from the same nick or different.
My Code
Try 1
SELECT `e`.id, COUNT(1), `e`.nick, `m`.nick
FROM `entries` `e`
INNER JOIN `magnets` `m`
ON `m`.`eid` = `e`.id
GROUP BY `e`.nick
Try 2
SELECT `e`.id, COUNT(1), `e`.nick
FROM `entries` `e`
GROUP BY `e`.nick
UNION ALL
SELECT `m`.eid, COUNT(1), `m`.nick
FROM `magnets` `m`
GROUP BY `m`.nick
The second try is generating some relevant outputs, but it contains double entries for all the nick which appear in both tables.
Also, I don't want to count twice, those entries/magnets which were inserted in the first query. Which is what the second UNION statement is doing. It takes in all the values from both tables.
SQL Fiddle link
Here is the link to a SQL Fiddle along with randomly populated entries.
I really hope someone can guide me through this. If it's any help, I will be using PHP for final display of data. So, my last resort would be to nest loops in PHP for the counting(which I am currently doing).
Desired output
The output that should be generated on the fiddle should be:
************************************************
** Nick ||| Count **
************************************************
** Nick1 ||| 10 **
** Nick2 ||| 9 **
** Nick3 ||| 6 **
** Nick4 ||| 10 **
************************************************
There might be a more efficient way but this works if I understand correctly:
SELECT SUM(cnt), nick FROM
(SELECT count(*) cnt, e.nick FROM entries e
LEFT JOIN magnets m ON (e.id=m.eid AND e.nick=m.nick)
WHERE eid IS NULL GROUP BY e.nick
UNION ALL
SELECT count(*) cnt, nick FROM magnets m GROUP BY nick) u
GROUP BY nick