Max from joined table based on value from first table - mysql

I have 2 tables.
First holds job details, second one the history of those job runs. First one also contains job period, per customer which is minimum time to wait before running next job for same customer. The time comparison needs to happen on started_on field of second table.
I need to find out the job ids to run next.
Schemas
job_details table
CREATE TABLE `job_details` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`customer_id` varchar(128) NOT NULL,
`period_in_minutes` int(11) unsigned NOT NULL,
`status` enum('ACTIVE','INACTIVE','DELETED') DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
job_run_history table
CREATE TABLE `job_run_history` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`job_id` int(10) unsigned NOT NULL,
`started_on` timestamp NULL DEFAULT NULL,
`status` enum('STREAMING','STREAMED','UPLOADING','UPLOADED','NO_RECORDS','FAILED') DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fk_job_id` (`job_id`),
CONSTRAINT `fk_job_id` FOREIGN KEY (`job_id`) REFERENCES `job_details` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Sample data for job_details table:
INSERT INTO `job_details` (`id`, `customer_id`, `period_in_minutes`, `status`)
VALUES
(1, 'cust1', 1, 'ACTIVE'),
(2, 'cust2', 1, 'ACTIVE'),
(3, 'cust3', 2, 'ACTIVE');
Sample data for job_run_history table:
INSERT INTO `job_run_history`(`job_id`, `started_on`, `status`)
VALUES
(1, '2021-07-01 14:38:00', 'UPLOADED'),
(2, '2021-07-01 14:37:55', 'UPLOADED');
Expected output (When run at 2021-07-01 14:38:56):
id
2,3
id => 1 did NOT get selected because the last job started within last 1 minute
id => 2 DID get selected because the last job started more than last 1 minute ago
id => 3 DID get selected because it has no run history
I have tried this, but this doesn't compare with max of start_time, hence, doesn't work:
select jd.id, max(jrh.started_on) from job_details jd
left join job_run_history jrh on jrh.job_id=jd.id
where
jd.status='ACTIVE'
and (jrh.status is null or jrh.status not in ('STREAMING','STREAMED','UPLOADING'))
and (jrh.`started_on` is null or jrh.`started_on` < date_sub(now(), interval jd.`period_in_minutes`*60 second))
group by jd.id;
MySql Version: 5.7.34
Any help please? Thanks in advance..

I'd prefer to use UNION ALL (it must be more fast than one complex query):
-- the subquery for the rows which have matched ones in 2nd table
SELECT t1.id
FROM job_details t1
JOIN job_run_history t2 ON t1.id = t2.job_id
WHERE t1.status = 'ACTIVE'
AND t2.status not in ('STREAMING','STREAMED','UPLOADING')
AND CURRENT_TIMESTAMP - INTERVAL t1.period_in_minutes MINUTE > t2.started_on
UNION ALL
-- the subquery for the rows which have no matched ones in 2nd table
SELECT id
FROM job_details t1
WHERE NOT EXISTS ( SELECT NULL
FROM job_run_history t2
WHERE t1.id = t2.job_id )
AND status = 'ACTIVE';
https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=8dcad95bf43ce711fdf40deda627e879

select jd.id from job_details jd
left join job_run_history jrh on jd.id= jrh.job_id
where jd.status = 'ACTIVE'
group by jd.id
having
max(jrh.started_on) < current_timestamp - interval max(jd.period_in_minutes) minute
or
max(jrh.id) is null
I'm not sure what's this filter about since you didn't explain it in your question so I didn't put it in the query: jrh.status not in ('STREAMING','STREAMED','UPLOADING'). However, I'm sure you can implement it in the query I posted.

Related

how insert registers of today from another table mysql

I'm trying to count the records in my "records" table and insert in results table but I just want to count today's records.
Below you will see some alternatives that I tried (I'm using MySQL), but I keep getting this error:
You have a syntax error in your SQL next to '' on line 2
INSERT INTO results (Data,total)
VALUES (now(), (SELECT COUNT(*) FROM records WHERE Data = now());
This SQL also causes an error:
INSERT INTO results (Data, total)
VALUES (now(), (SELECT COUNT(record.ID) AS day FROM record
WHERE date(Data) = date(date_sub(now(), interval 0 day));
and then
INSERT INTO resultS (Data,total)
VALUES (now(), (SELECT COUNT(*) FROM records
WHERE Data >= DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY));
And yet another attempt:
INSERT INTO results (Data, Total)
VALUES (now(), (SELECT COUNT(*) FROM records
WHERE DATE(Data)= CURRENT_DATE() - INTERVAL 1 DAY));
This is my sql config man:
CREATE TABLE `records`
(
`ID` char(23) NOT NULL,
`Name` varchar(255) NOT NULL,
`Total` int(255) NOT NULL,
`Data` date NOT NULL,
`QrCode` varchar(255) NOT NULL,
`City` varchar(255) NOT NULL,
`Device` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `results`
(
`id` int(11) NOT NULL,
`total` int(11) NOT NULL,
`Data` date DEFAULT NULL,
`grown` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
You have defined grown column as not null so you cannot put there NULL.
My query works :
INSERT INTO results
VALUES (1, (SELECT COUNT(1) FROM records WHERE Data= now()), now(), 1);
You should define default value for grown column. Same situation you have with column id. You should define sequence for column id:
id NOT NULL AUTO_INCREMENT;
INSERT INTO results (Data, total)
SELECT CURRENT_DATE(), COUNT(*)
FROM records
WHERE DATE(Data) = CURRENT_DATE();

Is it possible to COUNT if one value occured more than the other value in a column using SQL

I have this table called task_status which has the following structure:
CREATE TABLE `task_status` (
`task_status_id` int(11) NOT NULL,
`status_id` int(11) NOT NULL,
`task_id` int(11) NOT NULL,
`date_recorded` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ALTER TABLE `task_status`
ADD PRIMARY KEY (`task_status_id`);
ALTER TABLE `task_status`
MODIFY `task_status_id` int(11) NOT NULL AUTO_INCREMENT;
COMMIT;
INSERT INTO `task_status` (`task_status_id`, `status_id`, `task_id`, `date_recorded`) VALUES
(1, 1, 16, 'Wednesday 6th of January 2021 09:20:35 AM'),
(2, 2, 17, 'Wednesday 6th of January 2021 09:20:35 AM'),
(3, 3, 18, 'Wednesday 6th of January 2021 09:20:36 AM');
and a status_list table that has the possible statuses available
CREATE TABLE `status` (
`statuses_id` int(11) NOT NULL,
`status` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ALTER TABLE `status`
ADD PRIMARY KEY (`statuses_id`);
ALTER TABLE `status`
MODIFY `statuses_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4;
COMMIT;
INSERT INTO `status` (`statuses_id`, `status`) VALUES
(1, 'Yes'),
(2, 'Inprogress'),
(3, 'No');
Now what I want to do is check which number occurred more inside the status_id column 1 occurred more, 2 occurred more or 3 occurred more? using SQL.
Is it possible to do and if so how to?
You can try OVER and PARTITION BY clauses, you simply specify the column you want to partition your aggregated results by.
Example code
select status_id,count(*) over (partition by status_id) as Count_1 from task_status
You can count the column first then filter with max
there is a lot of different way to do this but i prefer using cte.
Here is a example :
with cte as(
select status_id,count(*) cnt from task_status
group by status_id
)
select * from cte
where cnt = (select max(cnt) from cte)
also here is db<>fiddle for better examine.
I modify some data to show the much more understandable output. But idea is same.
also I don't really think status table have any work doing here, but remind me if I misunderstand what you mean.
If you want exactly one status that occurs more often than the others, then I would recommend group by with order by and limit:
select status_id, count(*) as cnt
from task_status
group by status_id
order by cnt desc
limit 1;
This always returns one row, so if there are ties for the most common, then you only get one of the ties.

Mysql how to join tables more than two

I have problem with my query,
I have tables below:
CREATE TABLE IF NOT EXISTS `klik_zona` (
`kode_zona` int(10) unsigned NOT NULL,
`klik` int(10) unsigned NOT NULL,
PRIMARY KEY (`kode_zona`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `klik_zona` (`kode_zona`, `klik`) VALUES
(1, 45);
CREATE TABLE IF NOT EXISTS `tampil_zona` (
`kode_zona` int(10) unsigned NOT NULL,
`tanggal` date NOT NULL,
`tampil` int(10) unsigned NOT NULL,
PRIMARY KEY (`kode_zona`,`tanggal`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `tampil_zona` (`kode_zona`, `tanggal`, `tampil`) VALUES
(1, '2014-03-16', 100),
(1, '2014-03-17', 23);
CREATE TABLE IF NOT EXISTS `zona_iklan` (
`kode_zona` int(10) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`kode_zona`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;
INSERT INTO `zona_iklan` (`kode_zona`) VALUES
(1),
(2),
(3);
I have query:
SELECT z.kode_zona, SUM( tz.tampil ) , SUM( kz.klik )
FROM zona_iklan z
LEFT JOIN tampil_zona tz ON tz.kode_zona = z.kode_zona
LEFT JOIN klik_zona kz ON kz.kode_zona = z.kode_zona
GROUP BY z.kode_zona
but it give result:
kode_zona SUM(tz.tampil) SUM(kz.klik)
1 123 90
2 NULL NULL
3 NULL NULL
I want get result:
kode_zona SUM(tz.tampil) SUM(kz.klik)
1 123 45
2 NULL NULL
3 NULL NULL
please help me.. how to make query so that I get result that I hope it..
thanks,
In your example you join two records from tampil_zona on to one record from zona_iklan, which essentially causes that one record to duplicate. Then you are joining one record in klik_zona on to both of those duplicated records, causing the doubling of results that you want to avoid.
Instead, you need to aggregate the records before you join them, to ensure that you are always joining the records 1-to-1.
SELECT
z.kode_zona, tz.tampil, kz.klik
FROM
zona_iklan AS z
LEFT JOIN
(SELECT kode_zona, SUM(tampil) AS tampil FROM tampil_zona GROUP BY kode_zona) AS tz
ON tz.kode_zona = z.kode_zona
LEFT JOIN
(SELECT kode_zona, SUM(klik) AS klik FROM klik_zona GROUP BY kode_zona) AS kz
ON kz.kode_zona = z.kode_zona
Try removing the GROUP BY and look at the result. You will see that there are two records with kode_zona = 1. This because there are two records in tampil_zona matching that id. You could divide by count(*) but that seems futile. You probably want to think about how to modify the join.

How to use INSERT ... SELECT with a particular column auto-incrementing, starting at 1?

I am using INSERT ... SELECT to insert a data from specific columns from specific rows from a view into a table. Here's the target table:
CREATE TABLE IF NOT EXISTS `queue` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`customerId` int(11) NOT NULL,
`productId` int(11) NOT NULL,
`priority` int(11) NOT NULL,
PRIMARY KEY (`ID`),
KEY `customerId` (`customerId`),
KEY `productId` (`productId`),
KEY `priority` (`priority`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
The INSERT ... SELECT SQL I have works, but I would like to improve it if possible, as follows: I would like the inserted rows to start with 1 in the priority column, and each subsequent row to increment the priority value by 1. So, if three rows were inserted, the first would be priority 1, the second 2, and the third 3.
A exception to the "start at 1" rule: if there are existing rows in the target table for the specified customer, I would like the inserted rows to start with MAX(priority)+1 for that customer.
I thought I could use a subquery, but here's the problem: sometimes the subquery returns NULL (when there are no records in the queue table for the specified customer), which breaks the insert, as the priority column does not allow nulls.
I tried to CAST the column to an integer, but that still gave me NULL back when there are no records with that customer ID in the table.
I've hardcoded the customer ID in this example, but naturally in my application that would be an input parameter.
INSERT INTO `queue`
(
`customerId`,
`productId`,
`priority`,
`status`,
`orderId`)
SELECT
123, -- This is the customer ID
`PRODUCT_NO`,
(SELECT (MAX(`priority`)+1) FROM `queue` WHERE `customerId` = 123),
'queued',
null
FROM
`queue_eligible_products_view`
Is there a way to do this in one SQL statement, or a small number of SQL statements, i.e., less than SQL statement per row?
I do not think I can set the priority column to auto_increment, as this column is not necessarily unique, and the auto_increment attribute is used to generate a unique identity for new rows.
As Barmar mentions in the comments : use IFNULL to handle your sub query returning null. Hence:
INSERT INTO `queue`
(
`customerId`,
`productId`,
`priority`,
`status`,
`orderId`)
SELECT
123, -- This is the customer ID
`PRODUCT_NO`,
IFNULL((SELECT (MAX(`priority`)+1) FROM `queue` WHERE `customerId` = 123),1),
'queued',
null
FROM
`queue_eligible_products_view`
Here's how to do the incrementing:
INSERT INTO queue (customerId, productId, priority, status, orderId)
SELECT 123, product_no, #priority := #priority + 1, 'queued', null
FROM queue_eligible_products_view
JOIN (SELECT #priority := IFNULL(MAX(priority), 0)
FROM queue
WHERE customerId = 123) var

MySQL Select Records from 2 not-related Tables Ordering by Timestamp

I'd like to collect data from 2 different mysql tables ordering the result by a timestamp but without merging the columns of the 2 tables in a single row.
T_ONE(one_id,one_someinfo,one_ts)
T_TWO(two_id,two_otherinfo,two_ts)
Notice that the field two_otherinfo is not the same as one_someinfo, the only columns in common are id and timestamp.
The result should be a mix of the two tables ordered by the timestamp but each row, depending on the timestamp, should contain only the respective columns of the table.
For example, if the newest record comes from T_TWO that row should have the T_ONE one_someinfo column empty.
I just need to order the latest news from T_ONE and the latest messages posted on T_TWO so the tables are not related. I'd like to avoid using 2 queries and then merging and ordering the results by timestamp with PHP. Does anyone know a solution to this? Thanks in advance
This is the structure of the table
CREATE TABLE `posts` (
`id` int(10) unsigned NOT NULL auto_increment,
`fromid` int(10) NOT NULL,
`toteam` int(10) NOT NULL,
`banned` tinyint(1) NOT NULL default '0',
`replyid` int(15) default NULL,
`cont` mediumtext NOT NULL,
`timestamp` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
CREATE TABLE `stars` (
`id` int(10) unsigned NOT NULL auto_increment,
`daynum` int(10) NOT NULL,
`userid` int(10) NOT NULL,
`vote` tinyint(2) NOT NULL default '3',
`timestamp` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
INSERT INTO `posts` (`fromid`, `toteam`, `banned`, `replyid`, `cont`, `timestamp`) VALUES(5, 12, 0, 0, 'mess posted#1', 1222222220);
INSERT INTO `posts` (`fromid`, `toteam`, `banned`, `replyid`, `cont`, `timestamp`) VALUES(5, 12, 0, 0, 'mess posted#2', 1222222221);
INSERT INTO `posts` (`fromid`, `toteam`, `banned`, `replyid`, `cont`, `timestamp`) VALUES(5, 12, 0, 0, 'mess posted#3', 1222222223);
INSERT INTO `stars` (`daynum`, `userid`, `vote`, `timestamp`) VALUES(3, 160, 4, 1222222222);
INSERT INTO `stars` (`daynum`, `userid`, `vote`, `timestamp`) VALUES(4, 180, 3, 1222222224);
The result ordering by timestamp DESC should be the second record of table stars with timestamp 1222222224 then the third record of table posts with timestamp 1222222223 and following... Since the tables have got different fields from each other, the first row of the result should contain the columns of the table stars while the columns of table posts should be empty.
The columns of a UNION must be the same name and datatype on every row. In fact, declare column aliases in the first UNION subquery, because it ignores any attempt to rename the column in subsequent subqueries.
If you need the columns from the two subqueries to be different, put in NULL as placeholders. Here's an example, fetching the common columns id and timestamp, and then fetching one custom column from each of the subqueries.
(SELECT p.id, p.timestamp AS ts, p.fromid, NULL AS daynum FROM posts)
UNION
(SELECT s.id, s.timestamp, NULL, s.daynum, FROM stars)
ORDER BY ts DESC
Also put the subqueries in parentheses, so the last ORDER BY applies to the whole result of the UNION, not just to the last subquery.
SELECT one_id AS id, one_someinfo AS someinfo, one_ts AS ts
UNION
SELECT two_id AS id, two_someinfo AS someinfo, two_ts AS ts
ORDER BY ts
SELECT one_id AS id
, one_someinfo AS one_someinfo
, NULL AS two_someinfo
, one_ts AS ts
FROM t_ONE
UNION ALL
SELECT two_id
, NULL
, two_someinfo
, two_ts
FROM t_TWO
ORDER BY ts