ROUND not working when row_number() function is present - mysql

This is a simplified version of what I'm trying to do.
I am trying to rank users based on how many miles they walked overall.
This data is stored in the table called walks. Every time a user makes a walk, an entry is added.
create temporary table walks
(
id int unsigned auto_increment primary key,
user_id int unsigned not null,
miles_walked float unsigned default '0' not null,
date date not null
);
To fill in the table:
insert into walks (user_id, miles_walked, date)
values
(1, 10.1, '2022-12-20'),
(2, 60.2, '2022-12-21'),
(3, 30.3, '2022-12-22'),
(1, 0.4, '2022-12-23'),
(2, 10.5, '2022-12-24'),
(3, 10.6, '2022-12-25'),
(1, 40.7, '2022-12-26'),
(2, 80.8, '2022-12-27'),
(3, 30.9, '2022-12-28');
select * from walks;
select user_id,
SUM(miles_walked) as miles_walked_total,
ROUND(SUM(miles_walked), 1) as miles_walked_total_rounded,
row_number() over (order by SUM(miles_walked) desc) as miles_rank
from walks
group by user_id
order by user_id
As you can see, rounding is WRONG for users with id 2 and 3. What happened? Like I said, this is a simplified example. In my real case, not just rounding, but the ranking is wrong for the whole set when I use functions like ROUND and LENGTH:
ROW_NUMBER() OVER (ORDER BY (SUM(LENGTH(reports.comments)) + SUM(report_items.report_items_characters_number)) DESC) AS ranking

I can't duplicate it in 8.0.30: https://dbfiddle.uk/y04TcMlp
I suspect it's a bug that's been fixed. I recommend you upgrade.

Related

SQL Server : how to insert-into-select-from, with scalar function in select caluse

I am having trouble, while inserting data from a select statement having scalar function call. I posted sample script below, and little explanation and question in the comments.
---target table (in my case, this is not table variable, but a regular table, here for simplicity to posted this code as table variable to get the idea)
declare #tblItems table (
Id int identity(1,1)
,ItemID int
,TranNo varchar(20)
,Qty decimal(18,3)
,SomeCalculatedValue decimal(18,3)
)
--a dummay temp table, works like a source table
declare #tblTemp table (
Id int identity(1,1)
,ItemID int
,TranNo varchar(20)
,Qty decimal(18,3)
,SomeCalculatedValue decimal(18,3)
)
--put some dummy data in target table
insert into #tblItems(ItemID, TranNo, Qty, SomeCalculatedValue)
values
(1, 'GRN-001', 10, 0),
(2, 'GRN-002', 20, 0),
(3, 'GRN-003', 15, 0),
(4, 'GRN-004', 32, 0),
(5, 'GRN-005', 18, 0)
;
--insert 3 new rows in temp table, which later I want to insert in target table
insert into #tblTemp(ItemID, TranNo, Qty, SomeCalculatedValue)
values
(1, 'GRN-006', 6, 0), -- this line is working work fine,
(1, 'GRN-007', 3, 0), -- but this line is having problem, because it is not considering the last line( with TranNo='GRN-006' )
(2, 'GRN-008', 8, 0)
--here is the actual work, I need to read data from temp table to target table
--and the key requirement is the column 'SomeCalculatedValue'
--it should call a scalar function, and within that function I have to perform some calculations based on same target table
--for each ItemID passed, that scalar function will works as: it performs some sort of calculations on existing rows
--for that particular ItemID, to simplify the understanding you can think of it as Running-Total(not actually running total, but concept
--is same that each row value will based on previous row value)
insert into #tblItems(ItemID, TranNo, Qty, SomeCalculatedValue)
select
ItemID
,TranNo
,Qty
,[dbo].[GetCalculatedValue] (ItemID, Qty) as SomeCalculatedValue -- this function will perform some calcualations
from #tblTemp
select * from #tblItems
I have two tables, #tblItems and #tblTemp. I have to insert rows from #tblTemp to #tblItems, but in the select clause of #tblTemp, I used a scalar function, lets say, GetCalculatedValue(ItemID, Qty), which performs some calculations for specific ItemID from target table, and for each row it calculates a value which should be inserting in the #tblItems. It is not really Running-Total but for the sake for understanding it can think of as running total, because each row value will depend upon last previous lines.
So problem is that when #tblTemp has more than 1 row for a particular ItemID, it should consider the rows already inserted, but I think this insert-into-select statement will insert all rows at once, so it is not considering the last lines for particular ItemID which are in same select statement. You can review the code, I posted some comments also for explanation.

mysql running total as view

I am still an sql greenhorn and try to convert this script, building a running total as view in mysql:
DROP TABLE IF EXISTS `table_account`;
CREATE TABLE `table_account`
(
id int(11),
account int(11),
bdate DATE,
amount DECIMAL(10,2)
);
ALTER TABLE `table_account` ADD PRIMARY KEY(id);
INSERT INTO `table_account` VALUES (1, 1, '2014-01-01', 1.0);
INSERT INTO `table_account` VALUES (2, 1, '2014-01-02', 2.1);
INSERT INTO `table_account` VALUES (4, 1, '2014-01-02', 2.2);
INSERT INTO `table_account` VALUES (5, 1, '2014-01-02', 2.3);
INSERT INTO `table_account` VALUES (3, 1, '2014-01-03', 3.0);
INSERT INTO `table_account` VALUES (7, 1, '2014-01-04', 4.0);
INSERT INTO `table_account` VALUES (6, 1, '2014-01-06', 5.0);
INSERT INTO `table_account` VALUES (8, 1, '2014-01-07', 6.0);
SET #iruntot:=0.00;
SELECT
q1.account,
q1.bdate,
q1.amount,
(#iruntot := #iruntot + q1.amount) AS runningtotal
FROM
(SELECT
account AS account,
bdate AS bdate,
amount AS amount
FROM `table_account`
ORDER BY account ASC, bdate ASC) AS q1
This is much more faster than building a sum over the whole history on each line.
The problems I cannot solve are:
Set in view
Subquery in view
I think it might be posssible to use some kind of JOIN instead of "SET #iruntot:=0.00;"
and use two views to prevent the need of a subquery.
But I do know how.
Will be happy for any hints to try.
Regards,
Abraxas
MySQL doesn't allow subqueries in the from clause for a view. Nor does it allow variables. You can do this with a correlated subquery, though:
SELECT q.account, q.b_date, q.amount,
(SELECT SUM(q2.amount)
FROM myview1 q2
WHERE q2.account < q.account OR
q2.account = q.account and q2.date <= q.date
) as running total
FROM myview1 q;
Note that this assumes that the account/date column is unique -- no repeated dates for an account. Otherwise, the results will not be exactly the same.
Also, it seems a little strange that you are doing a running total across all accounts and dates. I might expect a running total within accounts, but this is how you formulated the query in the question.

Insert record into table with position without updating all the records position field

I am using MySQL, I don't have a good way to do this.
I have a table with a position field, which I need to keep track having values from 1 to 10,000.
Let's say I insert a record in the middle at 5000th position. So position 5000 to 10,000 need to be updated to the new position; old 5000 become 5001, 5002 becomes 5003...
Is there a good way to implement this without affecting so many records, when 1 single position is added?
Adding from the position 1st is the worst.
I'd rethink the database design. If you're going to be limited to on the order of 10K records then it's not too bad, but if this is going to increase without bound then you'll want to do something else. I'm not sure what you are doing but if you want a simple ordering (assuming you're not doing a lot of traversal) then you can have a prev_id and next_id column to indicate sibling relationships. Here's the answer to your questions though:
update some_table
set some_position = some_position + 1
where some_position > 5000 and some_position < 10000
You can try the below approach :
USE tempdb;
GO
CREATE TABLE dbo.Test
(
ID int primary key clustered identity(1,1) ,
OrderNo int,
CreatedDate datetime
);
--Insert values for testing the approach
INSERT INTO dbo.Test
VALUES
(1, GETUTCDATE()),
(2, GETUTCDATE()),
(3, GETUTCDATE()),
(4, GETUTCDATE()),
(5, GETUTCDATE()),
(6, GETUTCDATE());
SELECT *
FROM dbo.Test;
INSERT INTO dbo.Test
VALUES
(3, GETUTCDATE()),
(3, GETUTCDATE());
SELECT *
FROM dbo.Test;
--To accomplish correct order using ROW_NUMBER()
SELECT ID,
OrderNo,
CreatedDate,
ROW_NUMBER() OVER(ORDER BY OrderNo, ID) AS Rno
FROM dbo.Test;
--Again ordering change
INSERT INTO dbo.Test
VALUES
(3, GETUTCDATE()),
(4, GETUTCDATE());
SELECT ID,
OrderNo,
CreatedDate,
ROW_NUMBER() OVER(ORDER BY OrderNo, ID) AS Rno
FROM dbo.Test
DROP TABLE dbo.Test;

MySql natural sorting with offset from a certain value

I have the data:
CREATE TABLE IF NOT EXISTS `sort` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`value` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `sort` (`id`, `value`) VALUES
(1, 'abc2'),
(2, 'abc20'),
(3, 'abc1'),
(4, 'abc10'),
(5, 'abc3');
I want to have all the rows starting from a specified id then to the end of that result set to be added all the rows until that specified id and all sorted by value.
So i come out with this:
SET #id=3;
SELECT * FROM sort
ORDER BY
id=#id DESC, value>=(SELECT value FROM sort WHERE id=#id) DESC, value ASC;
Everything works fine but is not a natural sort. With the query above i get the result abc1, abc10, abc2, abc20, abc3. The result I'm looking for is abc1, abc2, abc3, abc10, abc20. And of course if I change #id=4 the results should be abc10, abc20, abc1, abc2, abc3.
With the query bellow I get the natural sorting I want.
SELECT * FROM sort ORDER BY LENGTH(value), value;
So the question is: How can I combine the two ORDER BY clauses into one?
This should work:
SELECT *
FROM sort
ORDER BY
id=#id DESC,
LENGTH(value),
value>=(SELECT value FROM sort WHERE id=#id) DESC,
value ASC;`

MySQL storing view data over time, how to reference

Tables:
videoID (vidID, vidName, vidURL) etc.
viewCount (vidID, views, timestamp)
The main goal of this database is to calculate the slope between the most recent view and the previous one.
Lets say I automatically store the view data in the database once everyday.
This means all the view data for all videos is in one big table called viewCount. The views are linked to the associated video with the vidID. Would this set up be an acceptable solution? Mainly how can I retrieve those two values needed to calculate slope for each video from this current database set up?
why dont you use a summary table ("materialised view") such as the following which stores viewings by year and week. You can change the level of granularity to whatever you want - monthly, daily etc...
drop table if exists video_counts;
create table video_counts
(
year_id smallint unsigned not null,
week_id tinyint unsigned not null,
video_id int unsigned not null,
counter int unsigned not null default 0,
primary key (year_id, week_id, video_id) -- note clustered index (innodb only)
)
engine=innodb;
insert into video_counts (year_id, week_id, video_id, counter) values
(2010,1,1, 90), (2010,2,1, 80), (2010,3,1, 70),
(2010,1,2, 30), (2010,2,2, 50), (2010,3,2, 30);
update video_counts set
counter = counter + 1
where
year_id = year(now()) and week_id = week(now()) and video_id = 1;