Max() + group by not working as expected in mysql - mysql

CREATE TABLE IF NOT EXISTS `order_order_status` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`order_status_id` int(11) NOT NULL,
`order_id` int(11) NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `order_order_status_order_status_id_index` (`order_status_id`),
KEY `order_order_status_order_id_index` (`order_id`),
KEY `order_order_status_created_at_index` (`created_at`),
KEY `order_order_status_updated_at_index` (`updated_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=5 ;
--
-- Dumping data for table `order_order_status`
--
INSERT INTO `order_order_status` (`id`, `order_status_id`, `order_id`, `created_at`, `updated_at`) VALUES
(1, 2, 1, '2016-10-01 01:57:37', '2016-10-01 01:57:37'),
(2, 2, 2, '2016-10-01 01:57:54', '2016-10-01 01:57:54'),
(3, 2, 3, '2016-10-02 02:12:49', '2016-10-02 02:12:49'),
(4, 6, 3, '2016-10-02 02:14:19', '2016-10-02 02:14:19');
What i want to select is:
1, 2, 1, '2016-10-01 01:57:37', '2016-10-01 01:57:37'
2, 2, 2, '2016-10-01 01:57:54', '2016-10-01 01:57:54'
4, 6, 3, '2016-10-02 02:14:19', '2016-10-02 02:14:19'
that is the newest entry of order_order_status grouped by order_id
now the problem:
running
select *, max(created_at) from `order_order_status` group by `order_order_status`.`order_id`
returns me:
or in prosa
it returns me NOT the newest entry, instead it returns the older one for order_id 3

MySQL is working exactly as expected. The problem is your expectations.
select * with a group by doesn't make sense. You want to get the maximum, do something like this:
select oos.*
from order_order_status
where oos.created_at = (select max(oos2.created_at)
from order_order_status oos2
where oos2.order_id = oos.order_id
);
Aggregation (group by) produces one row per group. An aggregation function such as max() gets the maximum value of a column -- nothing more. It just operates on a column.
When you use select *, you have a bunch of columns that are not in the group by and not the arguments to aggregation columns. MySQL allows this syntax (unfortunately -- few other databases do). The values for the unaggregated columns are arbitrary values from indeterminate rows in the group.

using order by order_id desc may solve your problem
select *, max(created_at) from `order_order_status` group by `order_order_status`.`order_id` order by `order_order_status`.`order_id` desc

Related

mysql. slipt date and time and set min as checkin and maximum as checkout

i hv an table in MySQL in which my users attendance store, i want to split it and min time as checkin and max time as checkout (eg, id,date,checkin,checkout)
my table is as ...
You have to use MIN(), MAX(), DATE(), TIME() and GROUP BY functions
Lets create a sample db
CREATE TABLE `my_table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`check_in` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `my_table` (`id`, `user_id`, `check_in`) VALUES
(1, 3, '2021-07-04 10:18:19'),
(2, 3, '2021-07-04 11:18:19'),
(3, 3, '2021-07-04 13:18:19'),
(4, 3, '2021-07-05 08:18:19'),
(5, 3, '2021-07-05 20:18:19');
And Query is...
SELECT
user_id,
DATE(`check_in`) as date,
TIME(MIN(`check_in`)) AS CheckIn,
TIME(MAX(`check_in`)) AS Checkout
FROM
`my_table`
GROUP BY
user_id, DATE(`check_in`)
And the result will be
user_id
date
CheckIn
Checkout
3
2021-07-04
10:18:19
13:18:19
3
2021-07-05
08:18:19
20:18:19
db<>fiddle here

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.

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

Simple SQL query but wrong results returned

I have a simple click tracking system that consists of three tables "tracking" (which holds unique views), "views" (which holds raw views) and "products" (which holds products).
Here's how it works: each time a user clicks on a tracking link, if the hash present in the link does not exist in the database, it will be saved in the "tracking" table as an unique view and also in the "views" table as a raw view. If the hash present in the link does exist in the database, then it will be saved only in the "views" table. So basically the number of "raw views" can not be smaller than the number of "unique views" because each "unique view" also counts as a "raw view".
I wrote a query to create reports based on products, but the number of "raw views" returned is not correct.
I've also created a fiddle which I hope it will give a better overview of my problem.
Here's the table structure:
CREATE TABLE `products` (
`id` int(10) UNSIGNED NOT NULL,
`name` varchar(128) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `products` (`id`, `name`) VALUES
(1, 'Test product');
CREATE TABLE `tracking` (
`id` int(10) UNSIGNED NOT NULL,
`product_id` int(11) NOT NULL,
`hash` varchar(32) NOT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `tracking` (`id`, `product_id`, `hash`, `created`) VALUES
(1, 1, '7ddf32e17a6ac5ce04a8ecbf782ca509', '2020-02-09 18:50:19'),
(2, 1, '00bb28eaf259ba0c932d67f649d90783', '2020-02-09 18:55:34');
CREATE TABLE `views` (
`id` int(10) UNSIGNED NOT NULL,
`hash` varchar(32) NOT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `views` (`id`, `hash`, `created`) VALUES
(1, '7ddf32e17a6ac5ce04a8ecbf782ca509', '2020-02-09 18:46:30'),
(2, '7ddf32e17a6ac5ce04a8ecbf782ca509', '2020-02-09 18:46:30'),
(3, '7ddf32e17a6ac5ce04a8ecbf782ca509', '2020-02-09 18:46:35'),
(4, '7ddf32e17a6ac5ce04a8ecbf782ca509', '2020-02-09 18:46:42'),
(5, '00bb28eaf259ba0c932d67f649d90783', '2020-02-09 18:56:31'),
(6, '00bb28eaf259ba0c932d67f649d90783', '2020-02-09 18:57:01');
And here's the query I wrote so far:
SELECT products.name AS `param`,
SUM(IF(tracking.product_id<>24, 1, 0)) AS `uniques`,
IF(SUM(IF(tracking.product_id<>24, 1, 0))=0, 0,
(SELECT COUNT(`hash`)
FROM `views` WHERE tracking.hash = views.hash)) AS `views`
FROM tracking
LEFT JOIN products ON products.id = tracking.product_id
WHERE tracking.created BETWEEN '2019-01-01 00:00:00' AND '2020-02-10 00:00:00'
GROUP BY products.name
As you can see I have 2 unique views and 6 raw views (4 for one hash and 2 for the other hash).
My expectation would be for the query result to be 2 uniques and 6 raw views for this given product, but instead I'm getting 2 uniques and 4 raw views. Like it's counting the views only for the first hash.
The next query can solve your situation:
SELECT
products.name,
COUNT(DISTINCT `tracking`.`hash`) AS `uniques`, -- count unique hashes
COUNT(*) AS `views` -- count total
FROM `tracking`
JOIN `views` ON `views`.hash = tracking.hash
LEFT JOIN products ON products.id = tracking.product_id
WHERE tracking.created BETWEEN '2019-01-01 00:00:00' AND '2020-02-10 00:00:00'
GROUP BY products.name;
;

MySQL - GROUP results BY a single column alongside ORDER BY and LIMIT

I want to select a list of tasks from my database. The tasks have a category_id. I want to get a singlur task per category_id. So if I, for example, had 10 tasks that are linked to like 6 categories that would result in 6 results. The 6 results I want are determined by their id, the lowest id among the GROUP BY is the correct record for that GROUP. Also the maximum result set can be no larger than 20 ('LIMIT').
SELECT * FROM `task` WHERE `datetime`<NOW() `task_status_id`=1 GROUP BY `category_id` ORDER BY `id` ASC LIMIT 20
What is wrong with the above query, I got no clue, I'm also at a loss getting any google results for this.
ADDED LATER
http://sqlfiddle.com/#!9/fa39cf
CREATE TABLE `category` (
`id` int(10) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `category` (`id`) VALUES
(1),
(2),
(3);
CREATE TABLE `task` (
`id` int(10) UNSIGNED NOT NULL,
`category_id` int(10) UNSIGNED NOT NULL,
`task_status_id` int(10) UNSIGNED DEFAULT '1',
`datetime` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `task` (`id`, `category_id`, `task_status_id`, `datetime`) VALUES
(3, 2, 1, '2018-07-24 11:20:26'),
(4, 2, 1, '2018-07-24 11:20:26'),
(5, 3, 1, '2018-07-24 11:21:35'),
(6, 3, 1, '2018-07-24 11:21:35');
You can try first finding the smallest id for each category and then joining it with the task table to get the remaining details.
SELECT t.* FROM task t
JOIN (SELECT category_id, min(id) id from task group by category_id) tc
ON (t.id = tc.id)
LIMIT 20