SQL: Take only the latest entry for each "group"? - mysql

Here I created some table to test with:
CREATE TABLE IF NOT EXISTS `test` (
`index` varchar(255) NOT NULL,
`index2` varchar(255) NOT NULL,
`date` date NOT NULL,
`somenumber` int(10) NOT NULL,
PRIMARY KEY (`index`,`index2`,`date`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `test` (`index`, `index2`, `date`, `somenumber`) VALUES
('kevin', 'little', '2013-06-11', 1),
('kevin', 'little', '2013-07-03', 5),
('maria', 'smith', '2013-07-01', 3),
('martin', 'luther', '2013-07-04', 13),
('martin', 'luther', '2013-07-05', 14);
Now I want to get the latest somenumber for everyone, ordered by somenumber DESC. Here is my attempt:
SELECT * FROM `test` GROUP BY `index`, `index2` ORDER BY `somenumber` DESC
The problem is that this query does always take one somenumber for every group, but it's not always the latest.
(I know the indexnames don't make too much sense here, but I thought it would be an easier-to-read example than using random number-indexes)

Hope this will help you
SELECT *
FROM
(SELECT * FROM `test` ORDER BY `index`, `date` DESC) as temp
GROUP BY `index`, `index2`
ORDER BY `somenumber` DESC

Try this:
SELECT t1.`INDEX`, t1.`INDEX2`, t1.`SOMENUMBER` FROM TEST t1 INNER JOIN
(SELECT `INDEX`, `INDEX2`, MAX(`DATE`) DATE FROM TEST
GROUP BY `INDEX`, `INDEX2`) t2 ON t1.INDEX = t2.INDEX AND t1.INDEX2 = t2.INDEX2 AND t1.DATE = t2.DATE

Related

select last inserted row based on date

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).

Mysql 5.7 - get multiple values from sub select

In the query below I am "JOINING" another table where i.isPrimary > 0 and if all i.isPrimary are 0 I just get the first result.
The result set from the query is as expected, but I want to bring more values from each subselect.
I am getting the error: SQL Error (1241): Operand should contain 1 column(s).
How can this query be rewritten in order to get more results from each subselect?
Thanks
-- borrowed from https://stackoverflow.com/q/7745609/808921
CREATE TABLE IF NOT EXISTS `ResearchEntity` (
`id` int(6) unsigned NOT NULL,
`name` varchar(200) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `ResearchEntity` (`id`, `name`) VALUES
('1', 'one'),
('2', 'two'),
('3', 'three');
CREATE TABLE IF NOT EXISTS `ProfileImageEntity` (
`id` int(6) unsigned NOT NULL,
`isPrimary` int(1) unsigned NOT NULL,
`value` varchar(200) NOT NULL,
`researchId` int(2) unsigned NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `ProfileImageEntity` (`id`,`isPrimary`, `value`,`researchId`) VALUES
('1', 0, 'not primary',1),
('2', 0, 'not primary',1),
('3', 1, 'primary!!!',1),
('4', 0, 'primary!!!',2),
('5', 0, 'not primary',2),
('6', 0, 'not primary',2)
;
CREATE TABLE IF NOT EXISTS `UserNameEntity` (
`id` int(6) unsigned NOT NULL,
`isPrimary` int(1) unsigned NOT NULL,
`value` varchar(200) NOT NULL,
`researchId` int(2) unsigned NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `UserNameEntity` (`id`,`isPrimary`, `value`,`researchId`) VALUES
('1', 0, 'first one, should be returned',1),
('2', 0, 'not primary',1),
('3', 0, 'primary',1),
('4', 1, 'primary',3),
('5', 0, 'not primary',3),
('6', 0, 'not primary',3);
SQL FIDDLE
: http://sqlfiddle.com/#!9/028218/1
SELECT r.*,
(SELECT i.id FROM ProfileImageEntity i WHERE i.researchId = r.id ORDER BY i.isPrimary DESC, i.id ASC LIMIT 1 ) AS primaryImageId,
(SELECT i.id FROM UserNameEntity i WHERE i.researchId = r.id ORDER BY i.isPrimary DESC, i.id ASC LIMIT 1 ) AS primaryImageId
FROM ResearchEntity r
ORDER BY id DESC;
What I understood from your question and comment that you want more columns from sub Query which is not possible. So try this query:
It is easy in MySql 8 but you are using MySql 5.7 where it a little bit tricky So try this:
select
t1.*,
t2.id AS primaryImageId,
t2.value AS primaryImageValue,
t3.id AS primaryUserId,
t3.value AS primaryUserValue
from ResearchEntity t1
left join (
SELECT *,
IF(researchId=#last,#_seq:=#_seq+1,#_seq:=1) AS rn,
#last:=researchId
FROM ProfileImageEntity , (SELECT #_seq:=1, #last:=0) r
ORDER BY researchId,isPrimary DESC, id ASC
) t2 on t1.id=t2.researchId and t2.rn=1
left join (
SELECT *,
IF(researchId=#last,#_seq:=#_seq+1,#_seq:=1) AS rn,
#last:=researchId
FROM UserNameEntity , (SELECT #_seq:=1, #last:=0) r
ORDER BY researchId,isPrimary DESC, id ASC
) t3 on t1.id=t3.researchId and t3.rn=1
order by t1.id
DEMO
In MySql 8 using row_number()
with cte as (
SELECT *,
row_number() over (partition by researchId ORDER BY isPrimary DESC, id ASC) rn
FROM ProfileImageEntity
),
cte1 as (
sELECT *,
row_number() over (partition by researchId ORDER BY isPrimary DESC, id ASC) rn
FROM UserNameEntity
)
select
t1.*,
t2.id AS primaryImageId,
t2.value AS primaryImageValue,
t3.id AS primaryUserId,
t3.value AS primaryUserValue
from ResearchEntity t1 left join cte t2 on t1.id=t2.researchId and t2.rn=1
left join cte1 t3 on t1.id=t3.researchId and t3.rn=1
try left join
SELECT r.*,i.id FROM ResearchEntity r left join ProfileImageEntity i on r.id = i.researchId
ORDER BY i.isPrimary,i.id DESC;
you just need to left join 2 times
SELECT r.*,i.id,j.id FROM ResearchEntity r left join ProfileImageEntity i on r.id = i.researchId left join UserNameEntity j on r.id=j.researchId ORDER BY i.isPrimary,i.id DESC;

Average values from different table on join

CREATE TABLE `reviews` (
`id` int(11) NOT NULL,
`average` decimal(11,2) NOT NULL,
`house_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `reviews` (`id`, `average`, `house_id`) VALUES
(1, '10.00', 1),
(2, '10.00', 1);
ALTER TABLE `reviews`
ADD PRIMARY KEY (`id`);
ALTER TABLE `reviews`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
CREATE TABLE `dummy_reviews` (
`id` int(11) NOT NULL,
`average` decimal(11,2) NOT NULL,
`house_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `dummy_reviews` (`id`, `average`, `house_id`) VALUES
(0, '2.00', 1);
ALTER TABLE `dummy_reviews`
ADD PRIMARY KEY (`id`);
AND the query
SELECT
AVG(r.average) AS avg1,
AVG(dr.average) AS avg2
FROM
reviews r
LEFT JOIN
dummy_reviews dr ON r.house_id = dr.house_id
the result is
avg1 avg2
10.000000 2.000000
All good by now but (10 + 2) / 2 = 6 ... wrong result
I need (10+10+2) / 3 = 7,33 ... How can I get this result?
SQLFiddle
You have values joined and as such you wont have 3 rows, you will have 2. What you need is a union so you can have all rows from your average tables and do the calculation from it. Like this:
select avg(average) from
(select average from reviews
union all
select average from dummy_reviews
) queries
See it here: http://sqlfiddle.com/#!9/e0b75f/3
Jorge's answer is the simplest approach (and I duly upvoted it). In response to your comment, you can do the following:
select ( (coalesce(r.suma, 0) + coalesce(d.suma, 0)) /
(coalesce(r.cnt, 0) + coalesce(d.cnt, 0))
) as overall_average
from (select sum(average) as suma, count(*) as cnt
from reviews
) r cross join
(select sum(average) as suma, count(*) as cnt
from dummy_reviews
) d;
Actually, I suggest this not only because of your comment. Under some circumstances, this could be the better performing code.

{mysql} select with a cross join

im currently a little bit confused about my sql select.
i got something like:
CREATE TABLE IF NOT EXISTS `a` (
`id` int(11) NOT NULL,
`ip` int(11) unsigned NOT NULL,
`name` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
with data of:
INSERT INTO `a` (`id`, `ip`, `name`) VALUES
(1, 2147483647, 'foobar'),
(2, 2372224735, 'foobar2');
so i would like append another table to the result of
select * from a
means:
select * from a cross join (select * from b where ip <= a.ip order by ip desc limit 1)
but it isnt working, i have no idea how to fix it :/
any ideas?
Thanks advance!
select * from a cross join (select * from b where ip <= a.ip order by ip desc limit 1) aaa
I just addded aaa to be the name of the derived table which is required even if it is not referenced.

Get records for each person's each day's min datetime

CREATE TABLE IF NOT EXISTS `accesscards` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`department` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`entrydates` datetime NOT NULL, PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
INSERT INTO `accesscards` (`id`, `department`, `name`, `entrydates`) VALUES
(1, 'test', 't1', '2013-12-06 16:10:00'),
(2, 'test', 't1', '2013-12-06 15:10:00'),
(3, 'test', 't1', '2013-12-07 15:11:00'),
(4, 'test', 't1', '2013-12-07 15:24:00'),
(5, 'test', 't2', '2013-12-06 16:10:00'),
(6, 'test', 't2', '2013-12-06 16:25:00'),
(7, 'test', 't2', '2013-12-07 15:59:00'),
(8, 'test', 't2', '2013-12-07 16:59:00');
Above is my query, I want to get records for a person for each day. And that record should have min datetime for the day. I need whole record for that date time
My expected output here
I tried using
SELECT id, MIN(entrydates) FROM accesscards WHERE 1=1 AND name!='' GROUP BY DATE(entrydates) ORDER BY id
but for 't1' I got id=1 and entrydates of first row.
Please help me out. If duplicate then provide link.
SELECT a1.*
FROM accesscards a1
JOIN (SELECT name, MIN(entrydates) mindate
FROM accesscards
WHERE name != ''
GROUP BY name, date(entrydates)) a2
ON a1.name = a2.name AND a1.entrydates = a2.mindate
DEMO
If you are using mysql : GROUP_CONCAT and SUBSTRING_INDEX
SELECT
DATE(entrydates) AS grouped_date,
GROUP_CONCAT(id ORDER BY entrydates ASC SEPARATOR ',') AS id_ordered_list,
SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY entrydates ASC), ',', 1) AS min_id_for_day
FROM
accesscards
WHERE
1=1 AND name!=''
GROUP BY
DATE(entrydates)
If you need other fields besides id to be shown, add this to you select :
SUBSTRING_INDEX(GROUP_CONCAT(YOUR_FIELDNAME_HERE ORDER BY entrydates ASC), ',', 1) AS min_YOUR_FIELDNAME_for_day
Play at http://sqlfiddle.com/#!2/a2671/13
After you updated your question with new data:
http://sqlfiddle.com/#!2/a2671/20
SELECT
DATE(entrydates) AS grouped_date,
SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY entrydates ASC), ',', 1) AS min_id_for_day,
department,
name,
SUBSTRING_INDEX(GROUP_CONCAT(entrydates ORDER BY entrydates ASC), ',', 1) AS min_entrydate_for_day
FROM
accesscards
WHERE
1=1 AND name!=''
GROUP BY
name,DATE(entrydates)
ORDER BY entrydates
Try this out this will surely help you
select id,department,name,entrydates from accesscards where entrydates in (select min(entrydates) from accesscards group by to_char(entrydates,'dd-Mon-yyyy')) order by id;