MySQL: Update with nested subquery - mysql

I'm trying to make this query work. The aim is to get the minimum date between a list of minimum dates.
But I'm getting: Unknown column 'di.device_id' in 'where clause'
How can I expose the device_id field to be visible inside the second subquery?
update device_information di
set first_seen=(
select min(date) from (
select min(insert_date) date from product_state_change_event where device_id=di.device_id
union select min(insert_date) date from sensor_range_switch_change_event where device_id=di.device_id
) as dates
);
Edited
In fact, I have 19 tables to union them.

I would expect a query such as this:
update device_information di join
(select min(insert_date) as minid
from product_state_change_event psce
group by psce.device_id
) psce
on psce.device_id = di.device_id
set first_seen = least(psce.insert_date, psce.minid);
This assumes that the dates are never NULL. That is easily incorporated into the answer (by adding more logic to the least()).

If anyone want to know how I solved it:
update device_information di
join (
select identifier, min(date) date from (
select client_var_name identifier, min(insert_date) date from product_state_change_event group by client_var_name
union all select client_var_name identifier, min(insert_date) date from sensor_range_switch_change_event group by client_var_name
...
) as unique_dates group by identifier
) as dates on di.identifier = dates.identifier
set di.first_seen = dates.date;

Related

Using the results of a function multiple times for duplicates - SQL

I am trying to produce a result that shows duplicates in a table. One method I found for getting duplicates and showing them is to run the select statement again through an inner join. However, one of my columns needs to be the result of a function, and the only thing I can think to do is use an alias, however I can't use the alias twice in a SELECT statement.
I am not sure what the best way to run this code for getting the duplicates I need.
My code below
SELECT EXTRACT(YEAR_MONTH FROM date) as 'ndate', a.transponderID
FROM dispondo_prod_disposition.event a
inner JOIN (SELECT EXTRACT(YEAR_MONTH FROM date) as ???,
transponderID, COUNT(*)
FROM dispondo_prod_disposition.event
GROUP BY mdate, transponderID
HAVING count(*) > 1 ) b
ON ndate = ???
AND a.transponderID = b.transponderID
ORDER BY b.transponderID
SELECT b.ndate, transponderID
FROM dispondo_prod_disposition.event a
INNER JOIN ( SELECT EXTRACT(YEAR_MONTH FROM date) as ndate,
transponderID
FROM dispondo_prod_disposition.event
GROUP BY 1, 2
HAVING COUNT(*) > 1 ) b USING (transponderID)
WHERE b.ndate = ??? -- for example, WHERE b.ndate = 202201
ORDER BY transponderID

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

Select distinct shou

I'm stuck with this problem for days and still can't think of a solution.Maybe i'm just making it too complex.
So my query looks like this.
SELECT distinct Cast(Table1.Date as Date) AS 'Date', Concat(Round(SUM((ISNULL(Price.Morning,0) + ISNULL(Price.Day,0) + ISNULL(Price.Evening,0))*Tickets.Count),2),' €') AS 'Total'
FROM Price,Tickets,Table1
WHERE Tickets.Price_ID = Price.Price_ID AND
Tickets.Table1_ID = Table1.Table1_ID
GROUP BY Date
;
The query should display distinct dates and total SUM should be displayed that was earned in the specific date.
As you can see there still are matching dates
EDIT. I don't know if i understood the criticism correctly but i applied these changes.
SELECT distinct Cast(Tabl1.Date as Date) AS 'Date', Concat(Round(SUM((ISNULL(Price.Morning,0) + ISNULL(Price.Day,0) + ISNULL(Price.Evening,0))*Tickets.Count),2),' €') AS 'Total'
FROM Table1
JOIN Tickets ON Tickets.Table1_ID = Table1.Table1_ID
JOIN Price ON Tickets.Price_ID = Price.Price_ID
GROUP BY Date
;
And i also understand that i need to provide more information for solution.
All the ..Concat(Round(SUM((ISNULL(Price.Morning,0) + ISNULL(Price.Day,0) + ISNULL(Price.Evening,0))*Tickets.Count),2),' €') AS 'Total' .. Is necessary because in the database there are Null values, thatš why there i use ISNULL function and 0 in case it is NULL because otherwise it brakes all the calculations.You can see that table here: I edited the column names so you could see(table itself is in different language)
(The column without name is not required for this)
Use explicit join not coma separated old join method and use subquery for format of total . No need distinct
select 'Date', concat(total,' €') from
(
SELECT Cast(Table1.Date as Date) AS 'Date',
SUM(Price.Morning+Price.Day+Price.Evening) as total
FROM Price join
Tickets on Tickets.Price_ID = Price.Price_ID
join Table1 on Tickets.Table1_ID = Table1.Table1_ID
GROUP BY Date
) as t

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.

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