MySQL Order By timestamp using IN clause - mysql

I have the below query and it works fine at returning results based on the set timestamp.
SELECT * FROM catalog WHERE part IN (SELECT part FROM query_data WHERE timestamp >= '2015-02-02') LIMIT 10
What I would like to do is get the results from the above but ORDER BY timestamp in DESC order. Something like this, but it doesn't work. The same values are returned, but not in DESC order based on the timestamp.
SELECT * FROM catalog WHERE part IN (SELECT part FROM query_data WHERE timestamp >= '2015-02-02' ORDER BY timestamp DESC) LIMIT 10
Thoughts? The timestamp column is only found in the query_data table. Not sure if this is causing the issue or not?

I believe this will work:
SELECT * FROM catalog c INNER JOIN query_data q ON c.part = q.part WHERE q.timestamp >= '2015-02-02' ORDER BY timestamp DESC;
The main problem with your approach is that you are ordering the subquery. Using a join and "order by" outside should fix it.

I would encourage you to watch this link (subselect order timestamp) The problem you have is as you thought, even if the subselect is ordered then it gets out of place on the main query, a Join would be useful for this cases.
Hope it helps you.

Related

How to select max / distinct record in MySQL using a deleted_at column

I am trying to select distinct rows under the following two rules:
If its deleted_at date is null then it is the most recent record, select it
If it is the latest deleted_at date (and there's not a record with a NULL), it is also the most recent record, select it
Consider this table:
The result I am looking for would be:
I'm using MySQL mariaDB v10.1.33 which does not have all the functions I am use to.
NULL was being ignored so I use a
coalesce(fc.deleted_at, CURRENT_TIMESTAMP())
to trick it into being the latest date. That way I can use max() function to select it. However, when I use this it is mismatching the data in the rows! i.e. this:
SELECT max(coalesce(fc.deleted_at, CURRENT_TIMESTAMP())), folder_id, code
FROM folder_code fc
WHERE fc.folder_id = 5683
returns:
I did some reading and this is a common problem where it seems to be ordering and selecting the max of each column independent of the row it is associated with and there are suggestions to use group by and order by to overcome it. However when I do this I get the same result i.e. this also returns the same as above:
SELECT max(coalesce(fc.deleted_at, CURRENT_TIMESTAMP())) as maxdeleteddate, fc.folder_id, fc.code
FROM folder_code fc
WHERE fc.folder_id = 5683
GROUP BY fc.folder_id
ORDER BY maxdeleteddate desc
How to I achieve my desired result?
Thank you
This is how I would do it:
SELECT f1.*
FROM folder f1
INNER JOIN (
SELECT folder_id,
NULLIF(MAX(IF(deleted_at IS NULL,NOW(),deleted_at)),NOW()) AS deleted_at
FROM folder
GROUP BY folder_id
) f2 ON f2.folder_id = f1.folder_id AND f2.deleted_at <=> f1.deleted_at
And here's a fiddle: https://www.db-fiddle.com/f/wzCYktpavBNnJu2uejPpe9/1
The idea is to get the groupwise-max, then join your table against itself. If you simply group the rows, you are not guaranteed to get the correct values for non-aggregated columns.
There is also a trick with deleted_at column, using NOW() if it's null, then using NULLIF() to set it back to NULL for the join.
This approach also benefits from the fact that it potentially uses indexes if they exist.
If you are using MySQL 8+, then you may use ROW_NUMBER here:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY folder_id
ORDER BY -ISNULL(deleted_at), deleted_at DESC) rn
FROM folder_code
)
SELECT folder_id, code, deleted_at
FROM cte
WHERE rn = 1;
Demo
The ORDER BY clause used in the call to ROW_NUMBER places all records having a NULL deletion date after those records have a date, for each group of folder_id records. Then, the second level of sorting places more recent deletion date records first. This means that for those folders have a NULL record, it would appear first, otherwise the most recent record would appear first.
Here is an old school solution which might also work:
SELECT f1.folder_id, f1.code, f1.deleted_at
FROM folder_code f1
INNER JOIN
(
SELECT folder_id,
CASE WHEN COUNT(*) = COUNT(deleted_at)
THEN MAX(deleted_at) END AS max_deleted_at
FROM folder_code
GROUP BY folder_id
) f2
ON f1.folder_id = f2.folder_id AND
(f1.deleted_at = f2.max_deleted_at OR
(f1.deleted_at IS NULL AND f2.max_deleted_at IS NULL));
Demo
One way to get the latest date is to make sure there is no later date. Your approach to replace NULL with a high date is good and can be used for this.
select *
from folder_code fc
where not exists
(
select *
from folder_code fc2
where fc2.folder_id = fc.folder_id
and coalesce(fc2.deleted_at, date '9999-12-31') > coalesce(fc.deleted_at, date '9999-12-31')
);
You can try below - using correlated subquery
DEMO
select * from t1 a
where coalesce(deleted_at,CURRENT_TIMESTAMP()) =
(select max(coalesce(deleted_at,CURRENT_TIMESTAMP())) from t1 a1 where a.folder_id=a1.folder_id)
OUTPUT:
older_id code deleted_at
5333 12VA1 2019-09-27
5683 12SR1-X

How to fix SQL query with Left Join and subquery?

I have SQL query with LEFT JOIN:
SELECT COUNT(stn.stocksId) AS count_stocks
FROM MedicalFacilities AS a
LEFT JOIN stocks stn ON
(stn.stocksIdMF = ( SELECT b.MedicalFacilitiesIdUser
FROM medicalfacilities AS b
WHERE b.MedicalFacilitiesIdUser = a.MedicalFacilitiesIdUser
ORDER BY stn.stocksId DESC LIMIT 1)
AND stn.stocksEndDate >= UNIX_TIMESTAMP() AND stn.stocksStartDate <= UNIX_TIMESTAMP())
These query I want to select one row from table stocks by conditions and with field equal value a.MedicalFacilitiesIdUser.
I get always count_stocks = 0 in result. But I need to get 1
The count(...) aggregate doesn't count null, so its argument matters:
COUNT(stn.stocksId)
Since stn is your right hand table, this will not count anything if the left join misses. You could use:
COUNT(*)
which counts every row, even if all its columns are null. Or a column from the left hand table (a) that is never null:
COUNT(a.ID)
Your subquery in the on looks very strange to me:
on stn.stocksIdMF = ( SELECT b.MedicalFacilitiesIdUser
FROM medicalfacilities AS b
WHERE b.MedicalFacilitiesIdUser = a.MedicalFacilitiesIdUser
ORDER BY stn.stocksId DESC LIMIT 1)
This is comparing MedicalFacilitiesIdUser to stocksIdMF. Admittedly, you have no sample data or data layouts, but the naming of the columns suggests that these are not the same thing. Perhaps you intend:
on stn.stocksIdMF = ( SELECT b.stocksId
-----------------------------^
FROM medicalfacilities AS b
WHERE b.MedicalFacilitiesIdUser = a.MedicalFacilitiesIdUser
ORDER BY b.stocksId DESC
LIMIT 1)
Also, ordering by stn.stocksid wouldn't do anything useful, because that would be coming from outside the subquery.
Your subquery seems redundant and main query is hard to read as much of the join statements could be placed in where clause. Additionally, original query might have a performance issue.
Recall WHERE is an implicit join and JOIN is an explicit join. Query optimizers
make no distinction between the two if they use same expressions but readability and maintainability is another thing to acknowledge.
Consider the revised version (notice I added a GROUP BY):
SELECT COUNT(stn.stocksId) AS count_stocks
FROM MedicalFacilities AS a
LEFT JOIN stocks stn ON stn.stocksIdMF = a.MedicalFacilitiesIdUser
WHERE stn.stocksEndDate >= UNIX_TIMESTAMP()
AND stn.stocksStartDate <= UNIX_TIMESTAMP()
GROUP BY stn.stocksId
ORDER BY stn.stocksId DESC
LIMIT 1

Most efficient way to Select all, and to include a distinct column value - while ordering by date added

I have rows that get updated automatically. Sometimes rows are updated (via a new insert - an almost duplicate row) where some columns remain the same - and other columns have new values. I want to pull the most recent up to date row; all the values. Here's what I've got
SELECT * FROM
(SELECT * FROM
(SELECT * FROM entries
WHERE dataset_id = xxx
ORDER BY time_added DESC
) alias1 GROUP BY title
) alias2 ORDER BY timestamp
Work backwards on this list:
SELECT #1 > Reorders these to be displayed based on the timestamp initially created (not added)
SELECT #2 > Filters Select #3 to select distinct title values (most recent title)
SELECT #3 > First query actually executed. Gets the dataset orderd by timestamp added
Is there a more efficient way to do this? I get serious code bad smell from it.
Use a group by and join:
select e.*
from entries e join
(select title, max(time_added) as maxta
from entries e
where dataset_id = xxx
group by title
) emax
on emax.title = e.title and e.time_added = emax.maxta
where dataset_id = xxx
order by e.timestamp;
Your method uses a MySQL extension to group by, where you have columns in the select list that are not in the group by. This is explicitly documented to return indeterminate results. Don't use features that are documented not to work, even if they seem to under some circumstances.

Alternative to mysql WHERE IN SELECT GROUP BY when wanting max value in group by

I have the following query, which was developed from a hint found online because of a problem with a GROUP BY returning the maximum value; but it's running really slowly.
Having looked online I'm seeing that WHERE IN (SELECT.... GROUP BY) is probably the issue, but, to be honest, I'm struggling to find a way around this:
SELECT *
FROM tbl_berths a
JOIN tbl_active_trains b on a.train_uid=b.train_uid
WHERE (a.train_id, a.TimeStamp) in (
SELECT a.train_id, max(a.TimeStamp)
FROM a
GROUP BY a.train_id
)
I'm thinking I possibly need a derived table, but my experience in this area is zero and it's just not working out!
you can move that to a SUBQUERY and also select only required columns instead of All (*)
SELECT a.train_uid
FROM tbl_berths a
JOIN tbl_active_trains b on a.train_uid=b.train_uid
JOIN (SELECT a.train_id, max(a.TimeStamp) as TimeStamp
FROM a
GROUP BY a.train_id )T
on a.train_id = T.train_id
and a.TimeStamp = T.TimeStamp

MySQL ORDER BY, GROUP BY within a JOIN

I've been searching for a while and couln't find an example that worked. Hopefully you can spot the glaring mistake!
SELECT
Timestamp
, i_currency.Code AS Code
, Conversion
FROM
i_convert(
SELECT
Timestamp
, Conversion
FROM
i_convert
ORDER BY Timestamp DESC
)
JOIN i_currency
ON i_convert.CurrencyID = i_currency.CurrencyID
GROUP BY Code
I'm not sure wherer the JOIN should be, should it be in the parentheses, outside or both? I've tried all three with no luck, keep getting:
You have an error... ...near '( SELECT Timestamp , Conversion FROM i_convert ORDER '
The original query doesn't make sense. It is using GROUP BY, but attempting to select columns that are neither grouping columns nor aggregate functions of the groups. Which Timestamp and which Conversion are expected for any given result row?
I suppose the objective is to select the most recent conversion for each currency. That might look more like this:
SELECT
latest.Timestamp AS Timestamp
, i_currency.Code AS Code
, i_convert.Conversion AS Conversion
FROM i_currency
JOIN i_convert
ON i_convert.CurrencyID = i_currency.CurrencyID
JOIN (
SELECT MAX(Timestamp) as Timestamp, CurrencyId
FROM i_convert
GROUP BY CurrencyId
) latest
ON i_convert.Timestamp = latest.Timestamp
AND i_convert.CurrencyId = latest.CurrencyId