select last inserted row based on date - mysql

Main Problem Is:- select last inserted row based on date
i want to be able to select distinct ref row with the last created_At date.
this is my table and data
DROP TABLE IF EXISTS `transactions_logs`;
CREATE TABLE IF NOT EXISTS `transactions_logs` (
`trans_log_Id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`etat_de_commande` varchar(100) NOT NULL,
`ref` varchar(10) NOT NULL,
`commentaire` text NOT NULL,
`staffId` bigint(20) UNSIGNED NOT NULL,
`Created_At` datetime NOT NULL,
PRIMARY KEY (`trans_log_Id`)
) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=latin1;
INSERT INTO `transactions_logs` (`trans_log_Id`, `etat_de_commande`, `ref`, `commentaire`, `staffId`, `Created_At`) VALUES
(1, 'waiting confirmation', '429735061', '', 1, '2020-11-09 12:11:43'),
(2, 'waiting confirmation', '472143970', '', 1, '2020-11-09 13:45:57'),
(3, 'confirmed', '429735061', '', 1, '2020-11-09 13:46:12'),
(4, 'ready', '429735061', '', 1, '2020-11-09 13:46:18'),
(5, 'picked', '429735061', '', 1, '2020-11-09 14:46:25');
COMMIT;
I want to be able to get this result
(2,'waiting confirmation','472143970',1,'2020-11-09 13:45:57'),
(5,'picked','429735061',1,'2020-11-09 14:46:25')

One option uses window functions, available in MySQL 8.0:
select *
from (
select t.*,
rank() over(partition by ref order by created_at desc) rn
from transactions_logs t
) t
where rn = 1
You can also use a correalted subquery for filtering - this works in all MySQL versions:
select t.*
from transactions_logs t
where t.created_at = (
select max(t1.created_at)
from transactions_logs t1
where t1.ref = t.ref
)
The latter would take advantage of an index on (ref, created_at).

Related

Only show latest message from each conversation

I have a messaging system which has the tables "message" which just contains the "subject" then "message_user" which contains the message body, who sent it, who its for and whether its deleted / unread.
#Message Table
CREATE TABLE `message` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`subject` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
INSERT INTO `message` (`id`, `subject`)
VALUES
(1, 'Test'),
(2, 'Test Again');
#Message User Table
CREATE TABLE `message_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`message_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`interlocutor` int(11) DEFAULT NULL,
`body` text,
`folder` enum('inbox','sent') NOT NULL,
`starmark` tinyint(1) NOT NULL DEFAULT '0',
`unread` tinyint(1) NOT NULL DEFAULT '1',
`deleted` enum('none','trash','deleted') NOT NULL DEFAULT 'none',
`date` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;
INSERT INTO `message_user` (`id`, `message_id`, `user_id`, `interlocutor`, `body`, `folder`, `starmark`, `unread`, `deleted`, `date`)
VALUES
(1, 1, 1, 2, 'Hi, how are you?', 'sent', 0, 1, 'none', '2018-10-23 09:36:02'),
(2, 1, 2, 1, 'Hi, how are you?', 'inbox', 0, 1, 'none', '2018-10-23 09:36:02'),
(3, 1, 2, 1, 'I am good thanks, you?', 'sent', 0, 1, 'none', '2018-10-23 09:46:02'),
(4, 1, 1, 2, 'I am good thanks, you?', 'inbox', 0, 1, 'none', '2018-10-23 09:46:02'),
(5, 2, 1, 3, 'Hi!', 'sent', 0, 1, 'none', '2018-10-23 09:50:22'),
(6, 2, 3, 1, 'Hi!', 'inbox', 0, 1, 'none', '2018-10-23 09:50:22');
I wrote the following query:
SELECT
*
FROM message m
JOIN message_user mu ON m.id = mu.message_id
WHERE mu.deleted = 'none'
AND mu.user_id = 1 #user_id of person checking messages
ORDER BY mu.id DESC;
But this is currently returning 3 rows even though there is only two conversations. I tried to GROUP BY but it still showed 3 rows.
I would expect the first two rows in the above example not the last one.
I want the query to return a list of the conversations with the latest message which has been sent which I (user_id) am involved in.
Since your MySQL version is 8.0+, we can utilize Window functions, such as Row_number(); otherwise the solution would have been much verbose, using Session variables.
For a partition (group) of m.id, we will determine the row number values. Row number values will be ordered in descending order of date.
Now, we simply need to use this result-set as a Derived Table, and just consider those rows where row number value is 1.
Date is a keyword in MySQL. You should avoid naming column/table using it. Still if you have to do so, you will need to use backticks around it.
Try the following (DB Fiddle DEMO):
SELECT dt.*
FROM (
SELECT m.id,
m.subject,
mu.id AS message_user_id,
mu.message_id,
mu.user_id,
mu.interlocutor,
mu.body,
mu.folder,
mu.starmark,
mu.unread,
mu.deleted,
mu.`date`,
Row_number()
OVER (PARTITION BY m.id
ORDER BY mu.`date` DESC) AS row_no
FROM message m
JOIN message_user mu
ON m.id = mu.message_id
WHERE mu.deleted = 'none'
AND mu.user_id = 1 ) AS dt
WHERE dt.row_no = 1
ORDER BY dt.id DESC
Try this :
select
m.id as id_message, m.subject as subject_message,
mu.id as id_message_user, mu.interlocutor, mu.body, mu.folder, mu.starmark, mu.deleted, mu.date
from message as m
inner join message_user as mu on mu.message_id = m.id and mu.deleted = 'none' and mu.user_id = 1
group by id_message
order by id_message_user desc
I removed
mu.user_id : it's in the inner join condition so always 'none'
mu.unread :same, always 1
mu.message_id : duplicate of id_message
http://sqlfiddle.com/#!9/91a5e4/15

MySQL limit result based on value in joined table

I have two tables, the first one contains a limit column. The number in this column must be used to limit the number of records received from the second table.
Is it possible to do this in just one query?
Below my tables and DEMO:
# Create table a
CREATE TABLE `a` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`limit` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
# Create table b
CREATE TABLE `b` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
`master` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
# Fill table a
INSERT INTO `a` (`id`, `limit`)
VALUES
(1, 3);
# Fill table b
INSERT INTO `b` (`id`, `name`, `master`)
VALUES
(1, 'record 1', 'groupA'),
(2, 'record 2', 'groupB'),
(3, 'record 3', 'groupA'),
(4, 'record 4', 'groupB'),
(5, 'record 5', 'groupC'),
(6, 'record 6', 'groupC'),
(7, 'record 7', 'groupC'),
(8, 'record 8', 'groupA'),
(9, 'record 9', 'groupD'),
(10, 'record 10', 'groupD');
Query I tested:
SELECT b.*
FROM b
JOIN a ON a.id = 1
GROUP BY b.master
LIMIT 3
This selects only 3 records.
But now I want the limit to be read from table a. I tried to limit like this, but that fails:
SELECT b.*
FROM b
JOIN a ON a.id = 1
GROUP BY b.master
LIMIT a.limit
EDIT:
I've updated the question including the group by statement
You cannot use user-defined MySQL variables or table fields in the LIMIT clause. What you can do is use a variable to enumerate records of table b. Then use this variable to apply the limit:
SELECT t.id, t.name
FROM (
SELECT id, name, #rn := #rn + 1 AS rn
FROM b
CROSS JOIN (SELECT #rn := 0) AS v
ORDER BY id) AS t
INNER JOIN a ON a.id = 1 AND t.rn <= a.`limit`;
Demo here
Edit:
Here's a version that handles groups. It limits the records of b to those groups having the biggest population:
SELECT b.id, b.name, b.master
FROM b
INNER JOIN (
SELECT master, #rn := #rn + 1 AS rn
FROM b
CROSS JOIN (SELECT #rn := 0) AS v
GROUP BY master
ORDER BY COUNT(*) DESC) AS t ON b.master = t.master
INNER JOIN a ON a.id = 1 AND t.rn <= a.`limit`;
Demo here

How can I get the median time of the times returned from this query in MySQL? [duplicate]

This question already has answers here:
Simple way to calculate median with MySQL
(48 answers)
Closed 6 years ago.
I have the following query..
SELECT
TIME(date_time)
FROM
login_attempts
WHERE
DATE_FORMAT(DATE(date_time), '%d-%m-%Y') = DATE_FORMAT('2016-08-09', '%d-%m-%Y')
AND login_successful = 1
Which returns the following times from the database..
TIME(date_time)
---------------
09:08:14
09:08:36
10:08:12
10:08:29
10:08:39
11:08:52
11:08:54
How can I get the median time of the times returned from this query?
EDIT -
Here is the information to build a table in SQLFiddle.
CREATE TABLE `login_attempts` (
`id` int(11) UNSIGNED NOT NULL,
`username` varchar(100) NOT NULL,
`ip_address` varbinary(128) NOT NULL,
`date_time` datetime DEFAULT NULL,
`login_successful` tinyint(1) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `login_attempts` (`id`, `username`, `ip_address`, `date_time`, `login_successful`) VALUES
(1, 'test#test.com', '127.0.0.1', now(), 1),
(2, 'test', '127.0.0.1', now(), 0),
(3, 'test#test.com', '127.0.0.1', now(), 1),
(4, 'test', '127.0.0.1', now(), 0),
(5, ' test#test.com', '127.0.0.1', now(), 1);
ALTER TABLE `login_attempts`
ADD PRIMARY KEY (`id`);
ALTER TABLE `login_attempts`
MODIFY `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5;
You can get the median time value in the following way:
SELECT
TIME_FORMAT(SEC_TO_TIME(AVG(TIME_TO_SEC(timeTable.only_time))),'%H:%i:%s') AS medianTime
FROM
(
SELECT
TIME(date_time) only_time,
#rn := #rn + 1 AS row_number
FROM
login_attempts
CROSS JOIN (SELECT #rn := 0) var
WHERE
DATE_FORMAT(DATE(date_time), '%d-%m-%Y') = DATE_FORMAT('2016-08-09', '%d-%m-%Y') AND
login_successful = 1
ORDER BY only_time
) AS timeTable
CROSS JOIN
(
SELECT
COUNT(*) total_rows
FROM
login_attempts
WHERE
DATE_FORMAT(DATE(date_time), '%d-%m-%Y') = DATE_FORMAT('2016-08-09', '%d-%m-%Y') AND
login_successful = 1
) AS t
WHERE timeTable.row_number IN ( floor((total_rows+1)/2), floor((total_rows+2)/2))
See Demo

specify conditions from outer query on a materialized subquery

i have got the below query which references couple of views 'goldedRunQueries' and 'currentGoldMarkings'. My issue seems to be from the view that is referred in the subquery - currentGoldMarkings. While execution, MySQL first materializes this subquery and then implements the where clauses of 'queryCode' and 'runId', which therefore results in execution time of more than hour as the view refers tables that has got millions of rows of data. My question is how do I enforce those two where conditions on the subquery before it materializes.
SELECT goldedRunQueries.queryCode, goldedRunQueries.runId
FROM goldedRunQueries
LEFT OUTER JOIN
( SELECT measuredRunId, queryCode, COUNT(resultId) as c
FROM currentGoldMarkings
GROUP BY measuredRunId, queryCode
) AS accuracy ON accuracy.measuredRunId = goldedRunQueries.runId
AND accuracy.queryCode = goldedRunQueries.queryCode
WHERE goldedRunQueries.queryCode IN ('CH001', 'CH002', 'CH003')
and goldedRunQueries.runid = 5000
ORDER BY goldedRunQueries.runId DESC, goldedRunQueries.queryCode;
Here are the two views. Both of these also get used in a standalone mode and so integrating any clauses into them is not possible.
CREATE VIEW currentGoldMarkings
AS
SELECT result.resultId, result.runId AS measuredRunId, result.documentId,
result.queryCode, result.queryValue AS measuredValue,
gold.queryValue AS goldValue,
CASE result.queryValue WHEN gold.queryValue THEN 1 ELSE 0 END AS correct
FROM results AS result
INNER JOIN gold ON gold.documentId = result.documentId
AND gold.queryCode = result.queryCode
WHERE gold.isCurrent = 1
CREATE VIEW goldedRunQueries
AS
SELECT runId, queryCode
FROM runQueries
WHERE EXISTS
( SELECT 1 AS Expr1
FROM runs
WHERE (runId = runQueries.runId)
AND (isManual = 0)
)
AND EXISTS
( SELECT 1 AS Expr1
FROM results
WHERE (runId = runQueries.runId)
AND (queryCode = runQueries.queryCode)
AND EXISTS
( SELECT 1 AS Expr1
FROM gold
WHERE (documentId = results.documentId)
AND (queryCode = results.queryCode)
)
)
Note: The above query reflects only a part of my actual query. There are 3 other left outer joins which are similar in nature to the above subquery which makes the problem far more worse.
EDIT: As suggested, here is the structure and some sample data for the tables
CREATE TABLE `results`(
`resultId` int auto_increment NOT NULL,
`runId` int NOT NULL,
`documentId` int NOT NULL,
`queryCode` char(5) NOT NULL,
`queryValue` char(1) NOT NULL,
`comment` varchar(255) NULL,
CONSTRAINT `PK_results` PRIMARY KEY
(
`resultId`
)
);
insert into results values (100, 242300, 'AC001', 'I', NULL)
insert into results values (100, 242300, 'AC001', 'S', NULL)
insert into results values (150, 242301, 'AC005', 'I', 'abc')
insert into results values (100, 242300, 'AC001', 'I', NULL)
insert into results values (109, 242301, 'PQ001', 'S', 'zzz')
insert into results values (400, 242400, 'DD006', 'I', NULL)
CREATE TABLE `gold`(
`goldId` int auto_increment NOT NULL,
`runDate` datetime NOT NULL,
`documentId` int NOT NULL,
`queryCode` char(5) NOT NULL,
`queryValue` char(1) NOT NULL,
`comment` varchar(255) NULL,
`isCurrent` tinyint(1) NOT NULL DEFAULT 0,
CONSTRAINT `PK_gold` PRIMARY KEY
(
`goldId`
)
);
insert into gold values ('2015-02-20 00:00:00', 138904, 'CH001', 'N', NULL, 1)
insert into gold values ('2015-05-20 00:00:00', 138904, 'CH001', 'N', 'aaa', 1)
insert into gold values ('2016-02-20 00:00:00', 138905, 'CH002', 'N', NULL, 0)
insert into gold values ('2015-12-12 00:00:00', 138804, 'CH001', 'N', 'zzzz', 1)
CREATE TABLE `runQueries`(
`runId` int NOT NULL,
`queryCode` char(5) NOT NULL,
CONSTRAINT `PK_runQueries` PRIMARY KEY
(
`runId`,
`queryCode`
)
);
insert into runQueries values (100, 'AC001')
insert into runQueries values (109, 'PQ001')
insert into runQueries values (400, 'DD006')
CREATE TABLE `runs`(
`runId` int auto_increment NOT NULL,
`runName` varchar(63) NOT NULL,
`isManual` tinyint(1) NOT NULL,
`runDate` datetime NOT NULL,
`comment` varchar(1023) NULL,
`folderName` varchar(63) NULL,
`documentSetId` int NOT NULL,
`pipelineVersion` varchar(50) NULL,
`isArchived` tinyint(1) NOT NULL DEFAULT 0,
`pipeline` varchar(50) NULL,
CONSTRAINT `PK_runs` PRIMARY KEY
(
`runId`
)
);
insert into runs values ('test1', 0, '2015-08-04 06:30:46.000000', 'zzzz', '2015-08-04_103046', 2, '2015-08-03', 0, NULL)
insert into runs values ('test2', 1, '2015-12-04 12:30:46.000000', 'zzzz', '2015-08-04_103046', 2, '2015-08-03', 0, NULL)
insert into runs values ('test3', 1, '2015-06-24 10:56:46.000000', 'zzzz', '2015-08-04_103046', 2, '2015-08-03', 0, NULL)
insert into runs values ('test4', 1, '2016-05-04 11:30:46.000000', 'zzzz', '2015-08-04_103046', 2, '2015-08-03', 0, NULL)
First, let's try to improve the performance via indexes:
results: INDEX(runId, queryCode) -- in either order
gold: INDEX(documentId, query_code, isCurrent) -- in that order
After that, update the CREATE TABLEs in the question and add the output of:
EXPLAIN EXTENDED SELECT ...;
SHOW WARNINGS;
What version are you running? You effectively have FROM ( SELECT ... ) JOIN ( SELECT ... ). Before 5.6, neither subquery had an index; with 5.6, an index is generated on the fly.
It is a shame that the query is built that way, since you know which one to use: and goldedRunQueries.runid = 5000.
Bottom Line: add the indexes; upgrade to 5.6 or 5.7; if that is not enough, then rethink the use of VIEWs.

Mysql Correlated Query Error

I am trying to pair records according to four conditions, one of which requires a one-level correlated subquery to match a +/- date range condition.
Why is the old.stdt unknown in the subquery's where clause? Thank you.
Here are both data and query:
DROP TABLE IF EXISTS `sbids`;
CREATE TABLE `sbids` (
`bid` int(11) DEFAULT NULL,
`sid` int(11) DEFAULT NULL,
`ctle` varchar(20) DEFAULT NULL,
`stype` char(1) CHARACTER SET utf8 DEFAULT '',
`sbid` int(8) DEFAULT NULL,
`stdt` date DEFAULT NULL,
`sedt` date DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `sbids` VALUES
( 11228, 8367, 'RRR', 'A', 11495, '2012-02-01', '2014-01-01'),
( 11228, 8367, 'RRR', 'A', 8182, '2014-03-01', '2015-02-01'),
( 11228, 8367, 'CCC', 'B', 29256, '2014-09-01', '2015-08-31'),
( 11228, 18030, 'RRR', 'A', 23319, '2013-01-01', '2013-11-01'),
( 11228, 18030, 'CCC', 'A', 25304, '2014-01-01', '2014-11-01'),
( 11228, 18030, 'CCC', 'A', 25304, '2015-01-01', '2015-11-01');
drop table if exists prd;
create table prd as
select
old.bid,
old.sid,
old.sbid as old_sbid,
old.ctle as old_ctle,
old.stype as old_stype,
old.stdt as old_stdt,
old.exdt as old_exdt,
can.sbid as can_sbid,
can.ctle as can_ctle,
can.stype as can_stype,
can.stdt as can_stdt,
can.exdt as can_exdt
from
sbids old
inner join ( select
innerT.bid,
innerT.sid,
innerT.ctle,
min(innerT.stdt) as stdt
from
sbids innerT
where
abs(datediff( old.stdt , innerT.exdt )) <= 65
group by
innerT.bid,
innerT.sid,
innerT.ctle ) can
on old.bid = can.bid
and old.sid = can.sid
and old.ctle = can.ctle