MySQL order by 2 date columns by whichever is greater - mysql

I came across an issue today debugging some code: given the following
MySQL data snippet:
╔════╦═════════════════════╦═════════════════════╗
║ id ║ date_created ║ date_updated ║
╠════╬═════════════════════╬═════════════════════╣
║ 1 ║ 2015-12-07 15:04:21 ║ 2016-06-06 10:59:25 ║
╠════╬═════════════════════╬═════════════════════╣
║ 2 ║ 2016-06-06 10:59:25 ║ 2016-09-09 09:44:58 ║
╠════╬═════════════════════╬═════════════════════╣
║ 3 ║ 2016-09-09 09:44:59 ║ 2017-11-30 11:36:37 ║
╠════╬═════════════════════╬═════════════════════╣
║ 4 ║ 2017-11-30 11:36:37 ║ null ║
╚════╩═════════════════════╩═════════════════════╝
I need to sort these dates accordingly:
If an entry's date_updated is null then use its date_created
If an entry's date_updated > date_created then use its date_updated (although this should always be the case)
If two entries A, B are compared, where A only has a date_created and B's date_updated is not null and these two values are equal, then A should be ordered greater then B.
So my expected outcome should be:
╔════╦═════════════════════╦═════════════════════╗
║ id ║ date_created ║ date_updated ║
╠════╬═════════════════════╬═════════════════════╣
║ 4 ║ 2017-11-30 11:36:37 ║ null ║
╠════╬═════════════════════╬═════════════════════╣
║ 3 ║ 2016-09-09 09:44:59 ║ 2017-11-30 11:36:37 ║
╠════╬═════════════════════╬═════════════════════╣
║ 2 ║ 2016-06-06 10:59:25 ║ 2016-09-09 09:44:58 ║
╠════╬═════════════════════╬═════════════════════╣
║ 1 ║ 2015-12-07 15:04:21 ║ 2016-06-06 10:59:25 ║
╚════╩═════════════════════╩═════════════════════╝
I've tried the following query:
SELECT * FROM table t
ORDER BY
CASE
WHEN t.date_updated IS NOT NULL AND t.date_updated > t.dated_created
THEN t.date_created
ELSE t.date_updated
END
DESC
However this doesn't order correctly according to point 2.
What would the correct MySQL statement here?

The logic of your case statement is incorrect. By mistake, you have interchanged the t.date_updated and t.date_created (check line number 5 and 6 in below SQL query)
Try this
SELECT * FROM table t
ORDER BY
CASE
WHEN t.date_updated IS NOT NULL AND t.date_updated > t.dated_created
THEN t.date_updated
ELSE t.date_created
END
DESC

date_updated shall have precedence over date_created. Use COALESCE for this:
SELECT *
FROM mytable
ORDER BY COALESCE(date_updated, date_created) DESC;

Related

Mysql Update column with subrequest Order By

I have a table biblek2 items with those 4 columns :
id (autoincrement)
catid(int)
introtext(varchar)
ordering(int)
Table biblek2_items
╔════╦═══════╦═══════════╦══════════╗
║ ID ║ catid ║ introtext ║ ordering ║
╠════╬═══════╬═══════════╬══════════╣
║ 1 ║ 3024 ║ orange ║ 122 ║
║ 2 ║ 2024 ║ zebra ║ 45 ║
║ 3 ║ 3010 ║ juice ║ 55 ║
║ 4 ║ 3002 ║ build ║ 17 ║
║ 5 ║ 2003 ║ car ║ 87 ║
║ 6 ║ 1610 ║ other ║ 1521 ║
║ 7 ║ 1620 ║ other ║ 200 ║
╚════╩═══════╩═══════════╩══════════╝
I expect that
Table biblek2_items
╔════╦═══════╦═══════════╦══════════╗
║ ID ║ catid ║ introtext ║ ordering ║
╠════╬═══════╬═══════════╬══════════╣
║ 5 ║ 2003 ║ car ║ 1 ║
║ 4 ║ 3002 ║ build ║ 2 ║
║ 3 ║ 3010 ║ juice ║ 3 ║
║ 1 ║ 3024 ║ orange ║ 4 ║
║ 2 ║ 2024 ║ zebra ║ 5 ║
╚════╩═══════╩═══════════╩══════════╝
I want to
select * from biblek2_items where catid between 2001 and 3024
ORDER BY introtext ASC
empty the ordering column
reorder the ordering column by increment from 1 to n according to the result of the order column
I tried this with no success
DECLARE #variable int
SET #variable = 0
UPDATE `biblek2_items`
SET #variable = ordering = #variable + 1
WHERE ordering IN (SELECT ordering
FROM `biblek2_items`
WHERE catid BETWEEN 2001 AND 3024
ORDER BY `introtext` DESC)
I read in the forum that MySQL can't allow subrequests with ORDER BY, so could you help me
As explained in the comments :
The ORDER BY in your sub query makes no sense anyway, because, you don't LIMIT. So all rows will be returned and it doesn't matter how they are ordered because all of them are taken into account with the IN in your main query.
But there are other issues with your query.
Do this instead :
SET #row_number = 0 ;
UPDATE biblek2_items,
(select id, catid,introtext,ordering, (#row_number:=#row_number + 1) AS newordering
from biblek2_items
where catid between 2001 and 3024
ORDER BY introtext ASC
) as temp
SET biblek2_items.ordering = temp.newordering
WHERE biblek2_items.ID = temp.ID
Additionally, if you have a large table, and a lot of users actively writing on it, to avoid inconsistencies or locking issues, I would suggest a slightly different method, using a temporary table to store the computed new ordering.
CREATE TABLE biblek2_items_TEMP (ID INT, ordering INT);
SET #row_number = 0 ;
INSERT INTO biblek2_items_TEMP
select id, (#row_number:=#row_number + 1) AS newordering
from biblek2_items
where catid between 2001 and 3024
ORDER BY introtext ASC
;
UPDATE biblek2_items, biblek2_items_TEMP
SET biblek2_items.ordering = biblek2_items_TEMP.ordering
WHERE biblek2_items.ID = biblek2_items_TEMP.ID;
DROP TABLE biblek2_items_TEMP;
Tested successfully on MySQL 5.7 and MariaDB 10

Get oldest timestamp from each user_id

Database of "sales"
Each "sold_by" is the person who essentially has made that particular sale. My aim is to get the most recent "sale_date" grouped by "sold_by" to return one record for each unique "sold_by" record (which is stored as an integer in my database, but this is an example)
╔═════════╦═════════╦══════════════════╗
║ sale_id ║ sold_by ║ sale_date ║
╠═════════╬═════════╬══════════════════╣
║ 0 ║ PETER ║ 01/01/2017 00:00 ║
║ 1 ║ JOHN ║ 01/01/2017 00:00 ║
║ 2 ║ PETER ║ 30/03/2017 00:00 ║
║ 3 ║ JOHN ║ 03/02/2017 00:00 ║
║ 4 ║ SIMON ║ 04/02/2017 00:00 ║
║ 5 ║ JOHN ║ 05/01/2017 00:00 ║
║ 6 ║ SIMON ║ 26/01/2017 00:00 ║
║ 7 ║ PETER ║ 07/01/2017 00:00 ║
║ 8 ║ SIMON ║ 28/01/2017 00:00 ║
║ 9 ║ JOHN ║ 09/01/2017 00:00 ║
║ 0 ║ PETER ║ 20/01/2017 00:00 ║
╚═════════╩═════════╩══════════════════╝
Database example for account_manager_sellers (please note, the ID matches sold_by in the table above.
╔════╦══════════════╗
║ id ║ company_name ║
╠════╬══════════════╣
║ 0 ║ PETER ║
║ 1 ║ JOHN ║
║ 2 ║ SIMON ║
╚════╩══════════════╝
The example below works but is not working as desired, it is not getting MIN or MAX date but seemingly returning a random date from the middle of the database.
SELECT `sold_by`, `sale_date`
FROM `sales`
NATURAL JOIN (
SELECT `sold_by`, MAX(`sale_date`) AS entry_date
FROM `sales`
GROUP BY `sold_by`
) AS tmin
JOIN `account_manager_sellers` USING (`id`)
WHERE `sale_date` < '2017-03-31 00:00:00';
So ultimately, I need to get ONE record for each unique sold_by but it needs to be the most recent date. So from the first table above it would return:
+---+-------+------------------+
| 2 | PETER | 30/03/2017 00:00 |
+---+-------+------------------+
| 3 | JOHN | 03/02/2017 00:00 |
+---+-------+------------------+
| 4 | SIMON | 04/02/2017 00:00 |
+---+-------+------------------+
P.S I also tried removing the MAX(sale_date) from the sub query, and replacing that with an ORDER BY sale_date LIMIT 1 (but obviously it only returned one sole result)
The basic query you want might look something like this:
SELECT p1.*
FROM plans p1
INNER JOIN
(
SELECT seller_id, MAX(plan_written) AS entry_date
FROM plans
GROUP BY seller_id
) p2
ON p1.seller_id = p2.seller_id AND
p1.plan_written = p2.entry_date;
You also have another join to the account_manager_sellers table, but since I can't see your table definitions, don't know your columns, etc., I won't attempt an answer for that.
You might want to move away from using natural joins, because they can obfuscate the join conditions in your query.

Filter orders that have a mismatch with order state with the last order state in order history

I'm using Prestashop and I need to verify data integrity comparing the current state of each order with the last order history state registered.
The orders table:
╔══════════╦═══════════════╦
║ id_order ║ current_state ║
╠══════════╬═══════════════╬
║ 1 ║ 3 ║
║ 2 ║ 1 ║
║ 3 ║ 2 ║
║ 4 ║ 1 ║
╚══════════╩═══════════════╩
The order_history table:
╔══════════════════╦══════════╦════════════════╦═════════════════════╦
║ id_order_history ║ id_order ║ id_order_state ║ date_add ║
╠══════════════════╬══════════╬════════════════╬═════════════════════╬
║ 1 ║ 1 ║ 1 ║ 2016-08-01 11:00:00 ║
║ 2 ║ 2 ║ 1 ║ 2016-08-02 12:00:00 ║
║ 3 ║ 1 ║ 3 ║ 2016-08-03 13:00:00 ║
║ 4 ║ 3 ║ 1 ║ 2016-08-04 14:00:00 ║
║ 5 ║ 3 ║ 2 ║ 2016-08-05 15:00:00 ║
║ 6 ║ 2 ║ 3 ║ 2016-08-06 16:00:00 ║
║ 7 ║ 4 ║ 1 ║ 2016-08-07 17:00:00 ║
╚══════════════════╩══════════╩════════════════╩═════════════════════╩
(The voluntary missed table is to give a name for order states : 1 = "Paid", 2 = "Confirmed", 3 = "Shipped"…)
Normally, current_state order must be equal to the last state history entry, but sometimes not (in my sample, for order #2, current_state = 1 but the id_order_state of the last history update is 2), and thats what I want to reveal.
I do this to get the each last order state update:
SELECT o.id_order, o.current_state, h.id_order_state, max(h.date_add)
FROM orders o, order_history h
WHERE o.id_order = h.id_order
GROUP BY o.id_order, o.current_state, h.id_order_state
ORDER BY o.id_order ASC
But it's not enough because firstly I want only the states of the last update of each order:
╔══════════╦═══════════════╦════════════════╦═════════════════════╗
║ id_order ║ current_state ║ id_order_state ║ max(h.date_add) ║
╠══════════╬═══════════════╬════════════════╬═════════════════════╣
║ 1 ║ 3 ║ 3 ║ 2016-08-03 13:00:00 ║
║ 2 ║ 1 ║ 3 ║ 2016-08-06 16:00:00 ║
║ 3 ║ 2 ║ 2 ║ 2016-08-05 15:00:00 ║
║ 4 ║ 1 ║ 1 ║ 2016-08-07 17:00:00 ║
╚══════════╩═══════════════╩════════════════╩═════════════════════╝
And secondly add a filter with WHERE current_state <> id_order_state to show only corrupted data (like order #2):
╔══════════╦═══════════════╦════════════════╦═════════════════════╗
║ id_order ║ current_state ║ id_order_state ║ max(h.date_add) ║
╠══════════╬═══════════════╬════════════════╬═════════════════════╣
║ 2 ║ 1 ║ 3 ║ 2016-08-06 16:00:00 ║
╚══════════╩═══════════════╩════════════════╩═════════════════════╝
Does a full SQL request can do this ?
E.g.:
SELECT o.id_order
, o.current_state
, x.id_order_state
, x.date_add
FROM order_history x
JOIN
( SELECT id_order,MAX(date_add) date_add FROM order_history GROUP BY id_order ) y
ON y.id_order = x.id_order
AND y.date_add = x.date_add
JOIN orders o
ON o.id_order = x.id_order
WHERE x.id_order_state <> o.current_state;
Select the order history into a temporary table first by using the max(h.date_add) clause you already have.
You can then join to this using your query WHERE o.id_order = h.id_order
By putting it into a temporary table (or subquery) first, you have eliminated the problem of the multiple rows. You can then do the comparison on the order states which will tell you which ones are out of sync.
Think of it as pre-filtering the data you don't need from the history table first.
Klemart3D this is not a basic SQL query :) Sorry, I didn't see that the title was edited by another user :), however I guess (and I hope XD) that is the query that does what you need:
SELECT
o.`id_order`, o.`current_state`, h.`id_order_state`, h.`date_add`
FROM
`ps_order_history` h INNER JOIN (SELECT `id_order`, MAX(`date_add`) AS maxdateadd FROM `ps_order_history` GROUP BY id_order) laststatus ON (h.`id_order` = laststatus.`id_order` AND h.`date_add` = laststatus.`maxdateadd`)
INNER JOIN `ps_orders` o ON (o.`id_order` = h.`id_order` AND o.`current_state` <> h.`id_order_state`)
ORDER BY h.`id_order` ASC
I have added the standard prefix ps to the tables name.
I found this workaround, but some other queries posted here are better than mine:
SELECT o.id_order, o.current_state, h.id_order_state, h.date
FROM ps_orders o
LEFT JOIN (
SELECT id_order, id_order_state, max(date_add) as date
FROM ps_order_history
GROUP BY id_order
ORDER BY id_order DESC
) h ON o.id_order = h.id_order
WHERE o.current_state <> h.id_order_state
GROUP BY o.id_order
ORDER BY o.id_order DESC
I have added the standard prefix ps to the tables name.
NB: This query don't works inside Prestashop's Request SQL (the subquery generates an "Undefined checkedFrom error" with PS v1.6.0.14).

MySQL Query Grouped Conditional Count

Here is the mySQL table data:
╔════╦══════╦══════════╦══════════════╗
║ ID ║ USER ║ DATE ║ NUMDOWNLOADS ║
╠════╬══════╬══════════╬══════════════╣
║ 1 ║ John ║ xx-xx-xx ║ 1 ║
║ 2 ║ Mary ║ xx-xx-xx ║ 3 ║
║ 3 ║ John ║ xx-xx-xx ║ 5 ║
║ 4 ║ Mary ║ xx-xx-xx ║ 2 ║
║ 5 ║ Mary ║ xx-xx-xx ║ 6 ║
║ 6 ║ John ║ xx-xx-xx ║ 7 ║
║ 7 ║ John ║ xx-xx-xx ║ 1 ║
║ 8 ║ Mary ║ xx-xx-xx ║ 8 ║
║ 9 ║ Mary ║ xx-xx-xx ║ 9 ║
╚════╩══════╩══════════╩══════════════╝
What I want to accomplish is to group the data by USER, and display the total NUMDOWNLOADS per USER where NUMDOWNLOADS is > X. For example, if X=5:
John: 1 (since 1 NUMDOWNLOADS > 5, and others count collectively as 1)
Mary: 3 (since 3 NUMDOWNLOADS > 5, and others count collectively as 1)
So, (1) output per user, and (2) output total, which in this case would be 4. Clear as mud :) Ideas on statement to use?
Your query is here. Try it
SELECT USER, COUNT(NUMDOWNLOADS)
FROM table_name
WHERE NUMDOWNLOADS > 5
GROUP BY USER
SELECT USER, COUNT(NUMDOWNLOADS)
FROM downloads
WHERE NUMDOWNLOADS > 5
GROUP BY USER
Follow the link below for a running demo:
SQLFiddle
I think you just want to count records where NUMDOWNLOADS > 5:
select USER, count(*)
from myTable
where NUMDOWNLOADS > 5
group by USER
The WHERE filter is performed before any grouping is done, so first this query filters out any rows that do not match NUMDOWNLOADS > 5, then it groups by USER and counts.
Alternatively if there is something about your actual query that requires you to use a conditional sum, you can do so as well:
select USER, sum(case when NUMDOWNLOADS > 5 then 1 else 0 end)
from myTable
group by USER

MySQL data sort

I have a table that has four columns: id, item_number, feature, value.
The table looks like this and has about 5 million entries.
╔════╦═════════════╦═════════╦═══════╗
║ id ║ item_number ║ feature ║ value ║
╠════╬═════════════╬═════════╬═══════╣
║ 1 ║ 234 ║ 5 ║ 15 ║
║ 2 ║ 234 ║ 3 ║ 256 ║
║ 3 ║ 453 ║ 5 ║ 14 ║
║ 4 ║ 453 ║ 4 ║ 12 ║
║ 5 ║ 453 ║ 7 ║ 332 ║
║ 6 ║ 17 ║ 5 ║ 88 ║
║ 7 ║ 17 ║ 9 ║ 13.86 ║
╚════╩═════════════╩═════════╩═══════╝
How can I sort the table so that I can get the item_numbers in descending order based on the feature value?
I am also selecting other feature numbers with their values but I only want to sort by feature number 5.
You need to do order by first with feature and then with item_numbers
select * from `table` order by `feature`, `item_numbers` desc
Using order by with desc and where clauses:
select `item_numbers`
from `tbl`
where `feature` = 5
order by `value` desc
In your query, add
order by item_number desc
If you are trying to query based on a specific feature, so only receive one set of data for a feature at at time, add
where feature = 'feature'
where "feature" is the feature value you want to search for. If you are looking to provide all features but sort them, you can add
order by feature, item_number desc
and you will be give all features in ascending order and together (grouped) then the items_number(s) in descending order
EDIT::
Sounds like from your latest comment, this may be your solution:
SELECT item_number FROM table WHERE feature = '5' ORDER BY value DESC