Replace null values with previous values in timesent - mysql

I am trying to replace null values with previous available record. i try to search answer for this query on stakeoverflow however, those answers are quite confusing hence looking for simple answer alone with explaination.
tried to get the previous value of the null value but it shows 0 records using this query :
CREATE TABLE messages (
channelid INTEGER,
messageid INTEGER,
timesent DATETIME
);
INSERT INTO messages (channelid, messageid, timesent)
VALUES (10, 2, '2022-07-04 04:04');
INSERT INTO messages (channelid, messageid, timesent)
VALUES (10, 5, '2022-07-04 08:04');
INSERT INTO messages (channelid, messageid, timesent)
VALUES (10, 3, NULL);
INSERT INTO messages (channelid, messageid, timesent)
VALUES (10, 7, '2022-07-04 08:04');
`SELECT timesent
FROM messages
WHERE timesent < messages.timesent and not null `
SQL fiddle

Supposing that the messageid column specifies the order of messages by which we can define what the previous value is, you can use a subquery as the following:
select channelid, messageid,
(
select timesent from messages t2
where t2.channelid = t1.channelid and
t2.messageid <= t1.messageid and
t2.timesent is not null
order by t2.messageid desc limit 1
) timesent
from messages t1
order by messageid
Another approch, you can use the conditional max window function to define groups that connect the null values with the last not null value as the following:
select channelid, messageid,
coalesce(timesent, max(timesent) over (partition by channelid, grp)) as timesent
from
(
select *,
max(case when timesent is not null then messageid end) over
(partition by channelid order by messageid) as grp
from messages
) t
order by messageid
And if the timesent value will not be decreased with the increasing of the messageid value, then you can use the running max window function as the following:
select channelid, messageid, timesent_not_null as timesent
from
(
select *,
max(timesent) over (partition by channelid order by messageid) timesent_not_null
from messages
) t
-- where some condition (if needed)
order by messageid
See demo

Related

MYSQL sum of max score taken users list

I have following table
CREATE TABLE Table1
(`userid` varchar(11), `score` int, `type` varchar(22));
INSERT INTO Table1
(`userid`, `score`,`type`)
VALUES
(11, 2,'leader'),
(11, 6,'leader'),
(13, 6,'leader'),
(15, 4,'leader'),
(15, 4,'leader'),
(12, 1,'leader'),
(14, 1,'leader');
I need to get userid of the maximum score take user.
if the max score is the same for two or more user need to get that userid also.
I have try following query
SELECT userid, sum(score) as totalScore
FROM Table1 WHERE type = "leader" GROUP BY userid
ORDER BY totalScore DESC;
But it gets all user data, cant get the max score take the first two users id.
But I need to get only first two row of data ..
Please help me
On MySQL 8+, I suggest using the RANK() analytic function:
WITH cte AS (
SELECT userid, SUM(score) AS totalScore,
RANK() OVER (ORDER BY SUM(score) DESC) rnk
FROM Table1
WHERE type = 'leader'
GROUP BY userid
)
SELECT userid, totalScore
FROM cte
WHERE rnk = 1;
if you need just top 2 records add limit in your query :
SELECT userid, sum(score) as totalScore
FROM Table1 WHERE type = "leader" GROUP BY userid
ORDER BY totalScore DESC LIMIT 2;

MySQL Selecting wrong column value in Group By query with alias in where clause

I know this question was already asked in a similar way, but I could not found any with an alias in the where clause.
I have a table structure like this:
CREATE TABLE Orders
( ID int NOT NULL Primary Key
, OrderNr VARCHAR(6) NOT NULL
, Date DATE NOT NULL
, Time CHAR(6) NOT NULL
, GeoCode CHAR(6) NULL) ;
My insert looks like this:
INSERT INTO orders (ID, OrderNr, Date, Time, GeoCode) VALUES (1, '123456', '2022-02-
15', '111110', '4022')
, (2, '123457', '2022-02-15', '121210', '4022')
, (3, '123455', '2021-04-15', '171515', '4020')
, (4, '123455', '2021-04-16', '150302', '4022')
, (5, '123466', '2022-03-03', '191810', '4020')
, (6, '123466', '2022-03-04', '121410', '4022')
Now I´m trying to get the latest Date and Time values for all OrderNr like this:
SELECT ID, OrderNr, MAX(cast(concat(Date, ' ', cast(Time as Time)) as datetime)) as
DateAndTime, GeoCode
FROM Orders o1
GROUP BY OrderNr
The Results shows the right latest date and time but the GeoCode is wrong. E.g for the
OrderNr 123455 it is 4020 but should be 4022.
I know that similar question were already asked but I cant use the alias in the where clause. Can somebody explain to me what I´m doing wrong?
Thank you very much in advance.
If your mysql version support ROW_NUMBER window function you can try this
SELECT *
FROM (
SELECT ID,
OrderNr,
cast(concat(Date, ' ', cast(Time as Time)) as datetime) DateAndTime,
GeoCode,
ROW_NUMBER() OVER(PARTITION BY OrderNr ORDER BY cast(concat(Date, ' ', cast(Time as Time)) as datetime) DESC) rn
FROM Orders o1
) t1
WHERE rn = 1
or use subquery with EXISTS
SELECT *
FROM Orders o1
WHERE EXISTS (
SELECT 1
FROM Orders oo
WHERE oo.OrderNr = o1.OrderNr
HAVING MAX(oo.Date) = o1.Date
)
sqlfiddle

Find the latest rows by filtering the status

I have a table called person_list. The data is,
Insert into person_list(person_allocation_id, person_id, created_datetime, boss_user_name, allocation_status_id) values
(111008, 1190016, '2021-01-05 11:09:25', 'Rajesh', '2'),
(111007, 1190015, '2020-12-12 09:23:31', 'Sushmita', '2'),
(111006, 1190014, '2020-12-11 10:48:26', '', '3'),
(111005, 1190014, '2020-12-10 13:46:15', 'Rangarao', '2'),
(111004, 1190014, '2020-12-10 13:36:10', '', '3');
Here person_allocation_id is the primary key.
person_id may be duplicated some times.
All of these rows are sorted by person_allocation_id (in descending order)
Now, I would like to filter the rows which are having allocation_status_id = '2' and boss_user_name should be non-empty for the person_id.
The difficulty here is that I have to exclude the row if the person_id is having allocation_status_id = '3' as their latest status (according to date).
I am unable to understand how could I compare the dates in one row with another in the previous row.
So finally I should get only 2 rows in my final result set (person_allocation_id are 111008 and 111007).
Somehow I achieved this in Oracle.
select person_id, person_allocation_id, create_datetime, boss_user_name, allocation_status_id
from (
select person_id, person_allocation_id, create_datetime, boss_user_name, allocation_status_id,
rank() over (partition by person_id order by create_datetime desc) rnk
from person_list
where allocation_status_id = '2')
where rnk = 1;
But, I need this for MySql DB. Anyone, please help?
Thanks.
SELECT t1.*
FROM person_list t1
JOIN ( SELECT MAX(t2.person_allocation_id) person_allocation_id, t2.person_id
FROM person_list t2
GROUP BY t2.person_id ) t3 USING (person_allocation_id, person_id)
WHERE t1.allocation_status_id = '2'
fiddle
Add more conditions to WHERE clause if needed (for example, AND boss_user_name != '').
You can use a correlated subquery to get the latest allocation_status_id value per person_id:
select person_allocation_id
, person_id
, created_datetime
, boss_user_name
, allocation_status_id
from (
select person_allocation_id
, person_id
, created_datetime
, boss_user_name
, allocation_status_id
, (select pl2.allocation_status_id
from person_list pl2
where pl2.person_id = pl.person_id
order by pl2.created_datetime desc
limit 1) latest_allocation_status_id
from person_list pl) t
where
allocation_status_id = '2' and latest_allocation_status_id <> '3'
and boss_user_name <> ''
The outer query is able to check the latest status and return the expected result set. The query works for MySQL 5.7
Demo here
As a side note, for MySQL 8.0 you can replace the correlated subquery with a window function:
last_value(allocation_status_id) over (partition by person_id
order by created_datetime desc)
Demo for window function

MySQL INSERT multiple rows if certain values don't exist

I have the following query which works correctly:
INSERT INTO Events (user_ID, event_type, event_creation_datetime, unit_ID)
SELECT 10, 'user_other_unit_moved', now(), 8383
FROM Events
WHERE NOT EXISTS (SELECT event_ID FROM Events WHERE event_type = 'user_other_unit_moved' AND unit_ID = 8383)
LIMIT 1;
What the query does is check to see if a row exists in my Events table that matches the event type and unit ID I wish to INSERT. If it finds an existing record, it does not proceed with the INSERT. However, if it does not find a record then it proceeds with the INSERT.
This is the structure of my Events table:
CREATE TABLE `Events` (
`event_ID` int(11) NOT NULL,
`user_ID` int(11) NOT NULL,
`event_type` varchar(35) NOT NULL,
`event_creation_datetime` datetime NOT NULL,
`unit_ID` int(10) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `Events`
ADD PRIMARY KEY (`event_ID`),
ADD KEY `unit_ID` (`unit_ID`);
ALTER TABLE `Events`
MODIFY `event_ID` int(11) NOT NULL AUTO_INCREMENT;
COMMIT;
The problem I have is trying to get the above query to work correctly when trying to INSERT multiple rows. I know how to INSERT multiple rows using comma delimited VALUES, but no matter what I try I get syntax errors. Here is the query I have been playing with:
INSERT INTO Events (user_ID, event_type, event_creation_datetime, unit_ID)
VALUES (
(SELECT 10, 'user_other_unit_moved', now(), 8383
FROM Events
WHERE NOT EXISTS (SELECT event_ID FROM Events WHERE event_type = 'user_other_unit_moved' AND unit_ID = 8383)
LIMIT 1)),
(SELECT 10, 'user_other_unit_moved', now(), 8380
FROM Events
WHERE NOT EXISTS (SELECT event_ID FROM Events WHERE event_type = 'user_other_unit_moved' AND unit_ID = 8380)
LIMIT 1))
);
However, no matter what I try (inserting, removing parentheses etc.) I get either the generic "You have an error in your SQL syntax;" or "Operand should contain only 1 column".
I have also tried this alternative based on other StackOverflow posts:
INSERT IGNORE INTO Events (event_ID, user_ID, event_type, event_creation_datetime, unit_ID)
VALUES
(SELECT (SELECT event_ID FROM Events WHERE event_type = 'user_other_unit_moved' AND unit_ID = 8383), 10, 'user_other_unit_moved', now(), 8383),
(SELECT (SELECT event_ID FROM Events WHERE event_type = 'user_other_unit_moved' AND unit_ID = 8383), 10, 'user_other_unit_moved', now(), 8383);
But this fails with "Can't specify target table for update in FROM clause" even if I try to return results using temporary tables.
Is it just an error with my syntax, or am I trying to do something not possible with the way my query is laid out? And if it's just an error, how would I write the query so that it works as I've intended? Note that I do not want to use multi-queries - I want the query to work as one statement.
Thanks,
Arj
Don't use VALUES, just INSERT ... SELECT and not FROM events.
Then UNION ALL.
This code works for MySql 5.6:
INSERT INTO Events (user_ID, event_type, event_creation_datetime, unit_ID)
SELECT *
FROM (
SELECT 10 user_ID, 'user_other_unit_moved' event_type,
now() event_creation_datetime, 8383 unit_ID
UNION ALL
SELECT 10, 'user_other_unit_moved', now(), 8380
) t
WHERE NOT EXISTS (
SELECT 1 FROM Events e
WHERE e.event_type = t.event_type AND e.unit_ID = t.unit_ID
);
See the demo.
This code works for MySql 5.7+:
INSERT INTO Events (user_ID, event_type, event_creation_datetime, unit_ID)
SELECT * FROM (
SELECT 10, 'user_other_unit_moved', now(), 8383
WHERE NOT EXISTS (SELECT 1 FROM Events WHERE event_type = 'user_other_unit_moved' AND unit_ID = 8383)
UNION ALL
SELECT 10, 'user_other_unit_moved', now(), 8380
WHERE NOT EXISTS (SELECT 1 FROM Events WHERE event_type = 'user_other_unit_moved' AND unit_ID = 8380)
) t
See the demo
And this for MySql 8.0+:
INSERT INTO Events (user_ID, event_type, event_creation_datetime, unit_ID)
SELECT 10, 'user_other_unit_moved', now(), 8383
WHERE NOT EXISTS (SELECT 1 FROM Events WHERE event_type = 'user_other_unit_moved' AND unit_ID = 8383)
UNION ALL
SELECT 10, 'user_other_unit_moved', now(), 8380
WHERE NOT EXISTS (SELECT 1 FROM Events WHERE event_type = 'user_other_unit_moved' AND unit_ID = 8380);
See the demo.
Although you can write this with just union all:
INSERT INTO Events (user_ID, event_type, event_creation_datetime, unit_ID)
SELECT x.user_id, x.event_type, now(), x.unit_id
FROM (SELECT 10 as user_id, 8383 as unit_id, 'user_other_unit_moved' as event_type
) x
WHERE NOT EXISTS (SELECT 1 FROM Events e2 WHERE e2.event_type = x.event_type AND e2.unit_ID = x.unit_ID)
UNION ALL
SELECT x.user_id, x.event_type, now(), x.unit_id
FROM (SELECT 10 as user_id, 8380 as unit_id, 'user_other_unit_moved' as event_type
) x
WHERE NOT EXISTS (SELECT 1 FROM Events e2 WHERE e2.event_type = x.event_type AND e2.unit_ID = x.unit_ID);
I suspect there is a better way. If a unit_id can have only one row for each event type, then you should specify that using a unique constraint or index:
create unique constraint unq_events_unit_id_event_type on events(unit_id, event_type);
It is better to have the database ensure integrity. In particularly, your version is subject to race conditions. And to duplicates being inserted within the same statement.
Then you can use on duplicate key to prevent duplicate rows:
INSERT INTO Events (user_ID, event_type, event_creation_datetime, unit_ID)
VALUES (10, 'user_other_unit_moved', now(), 8383),
(10, 'user_other_unit_moved', now(), 8380)
ON DUPLICATE KEY UPDATE unit_ID = VALUES(unit_ID);
The update actually does nothing (because unit_ID already has that value). But it does prevent an error and a duplicate row from being inserted.

Syntax error near (

I am trying to query something and I am not so good with Mysql, so I was wondering if someone can tell me what is the issue here. Here is my table:
create table #transfers (
sender varchar not null,
recipient varchar not null,
date date not null,
amount integer not null
);
INSERT INTO #transfers(sender,recipient,date,amount) VALUES ('Smith','Williams','2000-01-01',200);
INSERT INTO #transfers(sender,recipient,date,amount) VALUES ('Smith','Taylor','2002-09-27',1024);
INSERT INTO #transfers(sender,recipient,date,amount) VALUES ('Smith','Johnson','2005-06-26',512);
INSERT INTO #transfers(sender,recipient,date,amount) VALUES ('Williams','Johnson','2010-12-17',100);
INSERT INTO #transfers(sender,recipient,date,amount) VALUES ('Williams','Johnson','2004-03-22',10);
INSERT INTO #transfers(sender,recipient,date,amount) VALUES ('Brown','Johnson','2013-03-20',500);
INSERT INTO #transfers(sender,recipient,date,amount) VALUES ('Johnson','Williams','2007-06-02',400);
INSERT INTO #transfers(sender,recipient,date,amount) VALUES ('Johnson','Williams','2005-06-26',400);
INSERT INTO #transfers(sender,recipient,date,amount) VALUES ('Johnson','Williams','2005-06-26',200);
This is the query:
WITH cte AS
(
SELECT *, rn = ROW_NUMBER() OVER (PARTITION BY recipient ORDER BY amount DESC)
FROM #transfers
)
SELECT recipient
FROM cte
WHERE rn <= 3
GROUP BY recipient
HAVING SUM(amount) >= 1024
ORDER BY recipient
However I get this error:
near "(": syntax error
CTEs and ROW_NUMBER are not available in MySQL.
Try with Subquery:
SELECT recipient
FROM
(
SELECT t.*, #rownum := #rownum + 1 AS rank
FROM #transfers t, (SELECT #rownum := 0) r
)X
WHERE rank <= 3
GROUP BY recipient
HAVING SUM(amount) >= 1024
ORDER BY recipient
Fiddle Demo
use this create table query
create table transfers (
id INT NOT NULL AUTO_INCREMENT,
sender VARCHAR(100) NOT NULL,
recipient VARCHAR(100) NOT NULL,
date DATE,
amount VARCHAR(100) NOT NULL,
PRIMARY KEY ( id )
);