I have a below data in my activity table. I want to show those records at the top whose followup_date is today onwards in ascending order, after that those records whose followup_date is past date in ascending order and after that those records whose followup_date is null.
DROP TABLE IF EXISTS `activity`;
CREATE TABLE IF NOT EXISTS `activity` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type_id` int(11) NOT NULL,
`followup_date` date DEFAULT NULL,
`created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
--
-- Dumping data for table `activity`
--
INSERT INTO `activity` (`id`, `type_id`, `followup_date`, `created`) VALUES
(1, 1, '2022-03-22', '2022-03-24 18:51:23'),
(2, 1, '2022-03-23', '2022-03-24 18:51:23'),
(3, 1, '2022-03-24', '2022-03-24 18:51:58'),
(4, 1, '2022-03-25', '2022-03-24 18:51:58'),
(5, 1, '2022-03-26', '2022-03-24 18:52:21'),
(6, 1, '2022-03-13', '2022-03-24 18:52:21'),
(7, 1, NULL, '2022-03-24 18:54:15'),
(8, 1, NULL, '2022-03-24 18:54:15');
I tried using below query but could not understand how would i use ORDER BY CASE statement to get the result mentioned below.
SELECT * FROM `activity` ORDER BY CASE WHEN followup_date IS NULL THEN 2 WHEN followup_date >= '2022-03-24' THEN 1 END ASC
Current Output:
Expected Output
What changes i will need to make in above query to get Expected Output
I moved the expression into the select-list so we could see it in the result, but you may keep it in the ORDER BY clause:
SELECT CASE WHEN followup_date IS NULL THEN 2
WHEN followup_date < '2022-03-24' THEN 1
ELSE 0 END AS sort_bucket,
id, followup_date
FROM `activity`
ORDER BY sort_bucket ASC, followup_date ASC
Output:
+-------------+----+---------------+
| sort_bucket | id | followup_date |
+-------------+----+---------------+
| 0 | 3 | 2022-03-24 |
| 0 | 4 | 2022-03-25 |
| 0 | 5 | 2022-03-26 |
| 1 | 6 | 2022-03-13 |
| 1 | 1 | 2022-03-22 |
| 1 | 2 | 2022-03-23 |
| 2 | 7 | NULL |
| 2 | 8 | NULL |
+-------------+----+---------------+
Related
I'm really not sure how to exactly phrase what I'm looking to do therefore I'm having trouble searching. I have a table of pages that each have an id, title, order and parent_id if the parent_id is NULL that's considered a top level page. I'm able to almost sort this correctly with ORDER BY by parent_id and order with the following query:
select `id`, `title`, `order`, `parent_id`
from pages
order by `order` AND COALESCE(`parent_id`, `order`), `parent_id` is not null, `order`
The query spits out the following:
id
title
order
parent_id
107fa138
video
0
NULL
8eeda86c
mn
2
NULL
cac640ad
xxe title
3
NULL
1ce4d070
sdfsdfsdf
4
NULL
b45dc24d
another
1
8eeda86c
d3490141
hello
9
8eeda86c
This is almost what I want. Ideally, I'd have the rows with parent_ids directly under the row with that id so ideally the sort order would look like this:
id
title
order
parent_id
107fa138
video
0
NULL
8eeda86c
mn
2
NULL
b45dc24d
another
1
8eeda86c
d3490141
hello
9
8eeda86c
cac640ad
xxe title
3
NULL
1ce4d070
sdfsdfsdf
4
NULL
I don't even know how I would go about this. If someone can point me in the right direction that would be very awesome.
I think your problem is here:
order by `order` AND COALESCE(`parent_id`, `order`), ...
This probably isn't doing what you think it's doing. The AND is a boolean operator, so it will be as if you had written this expression:
order by (`order` > 0) AND (COALESCE(`parent_id`, `order`) > 0), ...
That is, if both order and the other term are nonzero, then 1, else 0.
I think the following would get closer to what you describe:
order by COALESCE(`parent_id`, `id`),
`parent_id` is not null,
`order`
Demo:
create table pages ( id varchar(10) primary key, title text, `order` int, parent_id varchar(10) );
insert into pages (id, title, `order`, parent_id) values
('107fa138', 'video', 0, NULL),
('8eeda86c', 'mn', 2, NULL),
('cac640ad', 'xxe title', 3, NULL),
('1ce4d070', 'sdfsdfsdf', 4, NULL),
('b45dc24d', 'another', 1, '8eeda86c'),
('d3490141', 'hello', 9, '8eeda86c');
select `id`, `title`,
COALESCE(parent_id, id) as cpi,
parent_id is not null as pinn,
`order`,
`parent_id`
from pages
order by COALESCE(`parent_id`, `id`), `parent_id` is not null, `order`
+----------+-----------+----------+------+-------+-----------+
| id | title | cpi | pinn | order | parent_id |
+----------+-----------+----------+------+-------+-----------+
| 107fa138 | video | 107fa138 | 0 | 0 | NULL |
| 1ce4d070 | sdfsdfsdf | 1ce4d070 | 0 | 4 | NULL |
| 8eeda86c | mn | 8eeda86c | 0 | 2 | NULL |
| b45dc24d | another | 8eeda86c | 1 | 1 | 8eeda86c |
| d3490141 | hello | 8eeda86c | 1 | 9 | 8eeda86c |
| cac640ad | xxe title | cac640ad | 0 | 3 | NULL |
+----------+-----------+----------+------+-------+-----------+
By adding in the columns that show the sorting expressions we can see how the sort occurs.
First it sorts by cpi alphabetically. This prefers the parent_id if it is set, but defaults to id.
For ties of cpi, then it sorts by pinn. So 0 comes before 1.
For ties of pinn (i.e. when multiple rows have a value 1), then it sorts by order.
Is this not what you wanted?
I have the below table, that stores the rank of person participating in respective events.
event_running and event_jumping are the events and the ranks stored.
CREATE TABLE `ranks` (
`id` int(11) NOT NULL,
`personid` int(11) NOT NULL,
`event_running` int(11) DEFAULT NULL,
`event_longjump` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Sample data
INSERT INTO `ranks` (`id`, `personid`, `event_running`, `event_longjump`) VALUES
(1, 1, 4, 8),
(2, 2, 10, 6),
(3, 3, 5, 0),
(4, 5, 20, 1),
(5, 4, 9, 3),
(6, 6, 1, 2);
SQL Fiddle Link
I want to build a leaderboard as below
| Standing | PersonID | RunningRank | JumpingRank |
| 1 | 6 | 1 | 2 |
| 2 | 4 | 9 | 3 |
| 3 | 1 | 4 | 8 |
| 4 | 3 | 5 | 0 |
| 5 | 2 | 10 | 6 |
This has to be sorted in ascending order - irrespective of the events lowest come first and also ranks above 20 are ignored.
And inputs on how can this be done?
you can use something similar to below
select PersonID,
RunningRank,
JumpingRank,
(RunningRank + JumpingRank) as Total
from ranks
order by Total asc
limit 20;
Here's your query.
set #row_number = 0;
select (#row_number:=#row_number + 1) as standing, personid, event_running, event_longjump from ranks
where event_running < 20 and event_longjump < 20
order by
case when if(event_longjump=0,event_running ,event_longjump) < event_running
then event_longjump else event_running end
see dbfiddle
Your sorting criteria is a bit vague. I am assuming that you want to sort on the basis of cumulative of the ranks across all events and its jumping score.
Also, please explain the position of person Id 3 in your queation.
You can do,
select PersonID,
RunningRank,
JumpingRank,
(JumpingRank + RunningRank) as cumulativeRank
from ranks
ORDER BY cumulativeRank, JumpingRank aesc
limit 20;
This will get you all the positions baring person id 3
Basically what I wanted is that I can select all the race records with record holder and best time. I looked up about similar queries and managed to find 3 queries that were faster than the rest.
The problem is it completely ignores the race the userid 2 owns the record of.
These are my tables, indexes, and some sample data:
CREATE TABLE `races` (
`raceid` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL,
PRIMARY KEY (`raceid`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `users` (
`userid` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL,
PRIMARY KEY (`userid`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `race_times` (
`raceid` smallint(5) unsigned NOT NULL,
`userid` mediumint(8) unsigned NOT NULL,
`time` mediumint(8) unsigned NOT NULL,
PRIMARY KEY (`raceid`,`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `races` (`raceid`, `name`) VALUES
(1, 'Doherty'),
(3, 'Easter Basin Naval S'),
(5, 'Flint County'),
(6, 'Fort Carson'),
(4, 'Glen Park'),
(2, 'Palomino Creek'),
(7, 'Tierra Robada');
INSERT INTO `users` (`userid`, `name`) VALUES
(1, 'Player 1'),
(2, 'Player 2');
INSERT INTO `race_times` (`raceid`, `userid`, `time`) VALUES
(1, 1, 51637),
(1, 2, 50000),
(2, 1, 148039),
(3, 1, 120516),
(3, 2, 124773),
(4, 1, 101109),
(6, 1, 89092),
(6, 2, 89557),
(7, 1, 77933),
(7, 2, 78038);
So if I run these 2 queries:
SELECT rt1.raceid, r.name, rt1.userid, p.name, rt1.time
FROM race_times rt1
LEFT JOIN users p ON (rt1.userid = p.userid)
JOIN races r ON (r.raceid = rt1.raceid)
WHERE rt1.time = (SELECT MIN(rt2.time) FROM race_times rt2 WHERE rt1.raceid = rt2.raceid)
GROUP BY r.name;
or..
SELECT rt1.*, r.name, p.name
FROM race_times rt1
LEFT JOIN users p ON p.userid = rt1.userid
JOIN races r ON r.raceid = rt1.raceid
WHERE EXISTS (SELECT NULL FROM race_times rt2 WHERE rt2.raceid = rt1.raceid
GROUP BY rt2.raceid HAVING MIN(rt2.time) >= rt1.time);
I receive correct results as shown below:
raceid | name | userid | name | time |
-------+----------------------+--------+----------+--------|
1 | Doherty | 2 | Player 2 | 50000 |
3 | Easter Basin Naval S | 1 | Player 1 | 120516 |
6 | Fort Carson | 1 | Player 1 | 89092 |
4 | Glen Park | 1 | Player 1 | 101109 |
2 | Palomino Creek | 1 | Player 1 | 148039 |
7 | Tierra Robada | 1 | Player 1 | 77933 |
and here is the faulty query:
SELECT rt.raceid, r.name, rt.userid, p.name, rt.time
FROM race_times rt
LEFT JOIN users p ON p.userid = rt.userid
JOIN races r ON r.raceid = rt.raceid
GROUP BY r.name
HAVING rt.time = MIN(rt.time);
and the result is this:
raceid | name | userid | name | time |
-------+----------------------+--------+----------+--------|
3 | Easter Basin Naval S | 1 | Player 1 | 120516 |
6 | Fort Carson | 1 | Player 1 | 89092 |
4 | Glen Park | 1 | Player 1 | 101109 |
2 | Palomino Creek | 1 | Player 1 | 148039 |
7 | Tierra Robada | 1 | Player 1 | 77933 |
As you can see, race "Doherty" (raceid: 1) is owned by "Player 2" (userid: 2) and it is not shown along with the rest of race records (which are all owned by userid 1). What is the problem?
Regards,
Having is a post filter. The query gets all the results, and then further filters them based on having. The GROUP BY compacting the rows based on the group, which gives you the first entry in each set. Since player 1 is the first entry for race 1, that's the result that is being processed by the HAVING. It is then filtered out because its time does not equal the MIN(time) for the group result.
This is why the other ones you posted are using a sub-query. My personal preference is for the first example, as to me it's slightly easier to read. Performance wise they should be the same.
While it's not a bad idea to try and avoid sub queries in the where clause, this is mostly valid when you can accomplish the same result with a JOIN. Other times it's not possible to get the result with a JOIN and a sub query is required.
Hello i am a beginner in PHP and MYSQL. I have made two tables. The Primary table 'customers' is:
id | name | place | email
1 bob berlin bob#bob.com
2 kary dresden kary#kary.com
3 sam zurich sam#sam.com
My child table 'orders' where 'menu_id' is a foreign key is:
id | menu_name | menu_id | date_of_order
1 sandwich 2 2016-05-03
2 fruits 3 2016-05-02
3 pizza 1 2016-05-04
4 salad 1 2016-05-06
Now with a simple LEFT JOIN i can see which orders are placed by which customer in which date.
But problem is when in case of adding a new user in my 'customers' table i cannot insert its 'id' into the 'menu_id' column as the foreign key.
After this suppose i do
INSERT INTO customers (name, place, email)
VALUES ('joe', 'melbourne', 'joe#xyz.com')
and in the orders table i do
INSERT INTO orders(menu_name, menu_id, date_of_order)
VALUES('pizza', 'SELECT id FROM customers WHERE email = joe#xyz.com', '2016-05-09')
After this the 'orders' table looks like
id | menu_name | menu_id | date_of_order
1 sandwich 2 2016-05-03
2 fruits 3 2016-05-02
3 pizza 1 2016-05-04
4 salad 1 2016-05-06
5 pizza 0 2016-05-09
The menu_id in 'orders' table should be showing '4' instead of 0. I have also tried LAST_INSERT_ID() but still the value it gets is 0. I dont know why this happens. Please help
You problem is that you are doing this wrong
INSERT INTO orders(menu_name, menu_id, date_of_order) VALUES('pizza', 'SELECT id FROM customers WHERE email = joe#xyz.com', '2016-05-09')
should be
INSERT INTO `orders`(`menu_name`, `menu_id`, `date_of_order`) VALUES('pizza', (SELECT `id` FROM `customers` WHERE `email` = 'joe#xyz.com' limit 1), '2016-05-09');
So you basically trying to insert 'SELECT id FROM customers WHERE email = joe#xyz.com' as string which is converting to int (if you have it as int)
Create statments:
CREATE TABLE `customers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(245) DEFAULT NULL,
`place` varchar(245) DEFAULT NULL,
`email` varchar(245) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
CREATE TABLE `orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`menu_name` varchar(245) DEFAULT NULL,
`menu_id` int(11) DEFAULT NULL,
`date_of_order` date DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK orders menu_id customer id_idx` (`menu_id`),
CONSTRAINT `FK orders menu_id customer id` FOREIGN KEY (`menu_id`)
REFERENCES `customers` (`id`)
ON DELETE SET NULL ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
And you insertions:
INSERT INTO `customers` (`name`, `place`, `email`) VALUES ('bob', 'berlin', 'bob#bob.com');
INSERT INTO `customers` (`name`, `place`, `email`) VALUES ('kary', 'dresden', 'kary#kary.com');
INSERT INTO `customers` (`name`, `place`, `email`) VALUES ('sam', 'zurich', 'sam#sam.com');
INSERT INTO `orders`(`menu_name`, `menu_id`, `date_of_order`) VALUES('sandwich', (SELECT id FROM customers WHERE email = 'kary#kary.com' limit 1), '2016-05-09');
INSERT INTO `orders`(`menu_name`, `menu_id`, `date_of_order`) VALUES('fruits', (SELECT id FROM customers WHERE email = 'sam#sam.com' limit 1), '2016-05-09');
INSERT INTO `orders`(`menu_name`, `menu_id`, `date_of_order`) VALUES('pizza', (SELECT id FROM customers WHERE email = 'bob#bob.com' limit 1), '2016-05-09');
INSERT INTO `orders`(`menu_name`, `menu_id`, `date_of_order`) VALUES('salad', (SELECT id FROM customers WHERE email = 'bob#bob.com' limit 1), '2016-05-09');
INSERT INTO `customers` (`name`, `place`, `email`) VALUES ('joe', 'melbourne', 'joe#xyz.com');
INSERT INTO `orders`(`menu_name`, `menu_id`, `date_of_order`) VALUES('pizza', (SELECT id FROM customers WHERE email = 'joe#xyz.com' limit 1), '2016-05-09');
and result
mysql> select * from customers;
+----+------+-----------+---------------+
| id | name | place | email |
+----+------+-----------+---------------+
| 1 | bob | berlin | bob#bob.com |
| 2 | kary | dresden | kary#kary.com |
| 3 | sam | zurich | sam#sam.com |
| 4 | joe | melbourne | joe#xyz.com |
+----+------+-----------+---------------+
4 rows in set (0.02 sec)
mysql> select * from orders;
+----+-----------+---------+---------------+
| id | menu_name | menu_id | date_of_order |
+----+-----------+---------+---------------+
| 1 | sandwich | 2 | 2016-05-09 |
| 2 | fruits | 3 | 2016-05-09 |
| 3 | pizza | 1 | 2016-05-09 |
| 4 | salad | 1 | 2016-05-09 |
| 5 | pizza | 4 | 2016-05-09 |
+----+-----------+---------+---------------+
5 rows in set (0.02 sec)
I'm not sure if this is possible in one mysql query so I might just combine the results via php.
I have 2 tables: 'users' and 'billing'
I'm trying to group summed activity for every date that is available in these two tables. 'users' is not historical data but 'billing' contains a record for each transaction.
In this example I am showing a user's status which I'd like to sum for created date and deposit amounts that I would also like to sum by created date. I realize there is a bit of a disconnect between the data but I'd like to some all of it together and display it as seen below. This will show me an overview of all of the users by when they were created and what the current statuses are next to total transactions.
I've tried UNION as well as LEFT JOIN but I can't seem to get either to work.
Union example is pretty close but doesn't combine the dates into one row.
(
SELECT
created,
SUM(status) as totalActive,
NULL as totalDeposit
FROM users
GROUP BY created
)
UNION
(
SELECT
created,
NULL as totalActive,
SUM(transactionAmount) as totalDeposit
FROM billing
GROUP BY created
)
I've also tried using a date lookup table and joining on the dates but the SUM values are being added multiple times.
note: I don't care about the userIds at all but have it in here for the example.
users table
(where status of '1' denotes "active")
(one record for each user)
created | userId | status
2010-03-01 | 10 | 0
2010-03-01 | 11 | 1
2010-03-01 | 12 | 1
2010-03-10 | 13 | 0
2010-03-12 | 14 | 1
2010-03-12 | 15 | 1
2010-03-13 | 16 | 0
2010-03-15 | 17 | 1
billing table
(record created for every instance of a billing "transaction"
created | userId | transactionAmount
2010-03-01 | 10 | 50
2010-03-01 | 18 | 50
2010-03-01 | 19 | 100
2010-03-10 | 89 | 55
2010-03-15 | 16 | 50
2010-03-15 | 12 | 90
2010-03-22 | 99 | 150
desired result:
created | sumStatusActive | sumStatusInactive | sumTransactions
2010-03-01 | 2 | 1 | 200
2010-03-10 | 0 | 1 | 55
2010-03-12 | 2 | 0 | 0
2010-03-13 | 0 | 0 | 0
2010-03-15 | 1 | 0 | 140
2010-03-22 | 0 | 0 | 150
Table dump:
CREATE TABLE IF NOT EXISTS `users` (
`created` date NOT NULL,
`userId` int(11) NOT NULL,
`status` smallint(6) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO `users` (`created`, `userId`, `status`) VALUES
('2010-03-01', 10, 0),
('2010-03-01', 11, 1),
('2010-03-01', 12, 1),
('2010-03-10', 13, 0),
('2010-03-12', 14, 1),
('2010-03-12', 15, 1),
('2010-03-13', 16, 0),
('2010-03-15', 17, 1);
CREATE TABLE IF NOT EXISTS `billing` (
`created` date NOT NULL,
`userId` int(11) NOT NULL,
`transactionAmount` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO `billing` (`created`, `userId`, `transactionAmount`) VALUES
('2010-03-01', 10, 50),
('2010-03-01', 18, 50),
('2010-03-01', 19, 100),
('2010-03-10', 89, 55),
('2010-03-15', 16, 50),
('2010-03-15', 12, 90),
('2010-03-22', 99, 150);
Try this:
Select created, sum(status) as totalActive, sum(transactionAmount) as totalDeposit
From
( (
SELECT
created,
status,
0 as transactionAmount
FROM users
)
UNION
(
SELECT
created,
0 as status,
transactionAmount
FROM billing
) ) as x group by created
Ah. Thanks to p.g.I.hall I was able to modify the query and get my desired result:
Select
createdDate,
SUM(statusSum),
SUM(transactionAmountSum)
From
( (
SELECT
created as createdDate,
sum(status) as statusSum,
'0' as transactionAmountSum
FROM users
GROUP BY createdDate
)
UNION
(
SELECT
created as createdDate,
'0' as statusSum,
sum(transactionAmount) as transactionAmountSum
FROM billing
GROUP BY createdDate
) )
as x
group by createdDate
A word of warning - your users table does not have a unique key. I'm going to take a wild guess here and say that you should probably create a primary key with the userId column.
A table without primary keys means you have no protection against bad, duplicate data slipping into your tables! Aaaaaah!