【问题标题】:How to find the summed difference between multiple dates?如何找到多个日期之间的总和?
【发布时间】:2021-05-19 16:06:19
【问题描述】:

下表显示了user_portfolioSTOCK_DATA_TRANSPOSED

在第一个表中,用户 1 的投资组合包含 2019 年 12 月 30 日添加的 4 只股票。

在第二个表中,我们有每只股票未来 5 天的数据。

我想获取每天每只股票的差值,以便能够将其相加并创建一个图表,显示所有名称的用户投资组合的利润变化。

我怎样才能以最聪明的方式做到这一点?

select * from user_portfolio where User_ID = 1


ID  Name    Date        Price   User_ID 
4   SO      2019-12-30  64.44   1   
5   RWE.DE  2019-12-30  32.11   1   
6   OPTT    2019-12-30  2.14    1   
7   MMM     2019-12-30  205.11  1   

Select Date, Name, Price from STOCK_DATA_TRANSPOSED

Date       Name Price   
2019-12-31 MMM 176.42
2020-01-02 MMM 180.00
2020-01-03 MMM 178.45
2020-01-06 MMM 178.62
2020-01-07 MMM 177.90
2019-12-31 SO 63.70
2020-01-02 SO 62.62
2020-01-03 SO 62.56
2020-01-06 SO 62.81
2020-01-07 SO 62.62
2020-01-02 RWE.DE 26.96
2020-01-03 RWE.DE 26.84
2020-01-06 RWE.DE 26.45
2020-01-07 RWE.DE 27.05
2020-01-08 RWE.DE 27.17
2019-12-31 OPTT 0.87
2020-01-02 OPTT 0.88
2020-01-03 OPTT 0.84
2020-01-06 OPTT 0.86
2020-01-07 OPTT 0.85

请参阅此 SQLFIDDLE 以获取测试数据:http://sqlfiddle.com/#!9/b81035 您可以忽略 STOCK_DATA_TRANSPOSED 中的时间戳和 id 列。

所需的输出可能看起来像这样 something(我知道上表中的价格与 Date_Price 不匹配):

Name    Date        Price User_ID Date_Diff Date_Price  % Diff
SO      2019-12-30  64.44   1   2019-12-31  176.4199982 173.77%
RWE.DE  2019-12-30  32.11   1   2019-12-31  180         460.57%
OPTT    2019-12-30  2.14    1   2019-12-31  178.4499969 8238.78%
MMM     2019-12-30  205.11  1   2019-12-31  178.6199951 -12.92%
SO      2019-12-30  64.44   1   2020-01-02  177.8999939 176.07%
RWE.DE  2019-12-30  32.11   1   2020-01-02  62.61999893 95.02%
OPTT    2019-12-30  2.14    1   2020-01-02  62.56000137 2823.36%
MMM     2019-12-30  205.11  1   2020-01-02  62.81000137 -69.38%
SO      2019-12-30  64.44   1   2020-01-03  62.61999893 -2.82%
RWE.DE  2019-12-30  32.11   1   2020-01-03  33.11        3.11%
OPTT    2019-12-30  2.14    1   2020-01-03  0.8799999952 -58.88%
MMM     2019-12-30  205.11  1   2020-01-03  0.8399999738 -99.59%

表示投资组合中的每个条目,将与 Date 之后的每个 Date_Diff 进行比较。

% Diff 的计算公式为:(Date_Price - Price)/Price

【问题讨论】:

  • 一个包含所有相关测试数据的 sqlfiddle 会更有用
  • 版本:5.7.33-36-log
  • 你能展示你想要的结果吗?这样会更容易回答。
  • 如果 user_portfolio 中有多行对应相同的 user_id 和 stock?
  • 您想要的结果中的股票价格与 STOCK_DATA_TRANSPOSED 中的不匹配,但我想我明白了。

标签: mysql date


【解决方案1】:

基本的计算是买入价减去当天的价格,再除以买入价,然后变成百分比。

concat((`Price` - `Price2`) / `Price2` * 100, '%') as Diff

一个简单的join 将允许我们这样做。不幸的是,表上没有声明主键和外键,所以我们只希望名称列匹配。基本的join是这样的。

select *
from `STOCK_DATA_TRANSPOSED` sdt
join user_portfolio up on up.name2 = sdt.name

然后将其缩小到一位用户并根据需要订购。

select *
from `STOCK_DATA_TRANSPOSED` sdt
join user_portfolio up on up.name2 = sdt.name
where up.user_id = 1
order by `sdt`.`Date` asc, sdt.Name desc

现在添加所需的列,使用一些更好的名称,然后进行计算。

select
  sdt.Name,
  up.Date2 as 'Purchase Date',
  up.Price2 as 'Purchase Price',
  up.User_ID,
  sdt.`Date` as 'Closing Date',
  sdt.Price as 'Closing Price',
  concat((sdt.Price - up.Price2) / up.Price2 * 100, '%') as Diff
from `STOCK_DATA_TRANSPOSED` sdt
join user_portfolio up on up.name2 = sdt.name
where up.user_id = 1
order by `sdt`.`Date` asc, sdt.Name desc;

Try it.

请注意,此查询存在根本缺陷。它假设每只股票都有一个单一的购买价格。人们在给定时间购买一定数量的股票。有人可以在周一以 3.50 美元的价格购买 5 股,在周二以 3.60 美元的价格购买 10 股,在周三以 4 美元的价格购买 20 股。 user_portfolio 缺少金额字段来反映这一点,如果有多次购买,则查询没有意义。


此架构可以显着改进。

  • Change the charset to utf8mb4 to support full UTF-8.
  • 让你的 ID 类型一致,不是有的有符号有的没有符号。
  • 对主键使用 bigint,20 亿个很快就出来了。
  • 使用一致的表名样式。
  • 使用关于表中内容的信息表名称。
  • 避免使用日期等关键字列名称。
    • purchased_at 和 closing_date 是描述性时间戳约定。
  • 不要使用像 Name2 这样的列来避免查询中的冲突。
    • 在查询中使用表别名和完全限定名称,例如 up.price。
  • 声明所有主键和外键。
  • 按 ID 而非名称关联表。名字变了。
  • 使用独特的约束来保证不可能的情况不会发生。
  • 使用检查约束来防止不可能的值。
    • MySQL 5.7 忽略了它们,但最终你会升级到 MySQL 8,它们对读者很有用。
  • 如果没有必要,不要对列设置限制,例如名称。
    • varchar(255) 除非有根本理由限制它。
  • 使用numeric 表示精确数字,而不是varchar。
  • 使用views 保持与旧查询的向后兼容性。
create table users (
    id bigint primary key auto_increment,
    email varchar(255) not null
) default charset=utf8mb4;

-- Store each entity in its own table.
-- This avoids repetition and allows referential integrity.
create table stocks (
    id bigint primary key auto_increment,
    symbol varchar(255) not null,
    name varchar(255) not null
) default charset=utf8mb4;

create table daily_stock_prices (
    -- Declaring a foreign key ensures the stock exists and lets us
    -- clean up related data when it is deleted.
    stock_id bigint not null,
      foreign key(stock_id) references stocks(id) on delete cascade,

    -- numeric provides accurate numbers
    price numeric(18,13) not null check(price > 0),
    closing_date date not null,
    retreived_at datetime not null default now(),
    
    -- prevent duplicate entries
    unique(stock_id, closing_date)
) default charset=utf8mb4;

create table user_portfolio (
    user_id bigint not null,
      foreign key(user_id) references users(id) on delete cascade,
    stock_id bigint not null,
      foreign key(stock_id) references stocks(id) on delete cascade,
    
    paid numeric(18,13) not null check(price > 0),
    amount integer not null check(amount > 0),
    
    purchased_at datetime not null default now()
) default charset=utf8mb4;

【讨论】:

  • 多么好的答案!非常感谢。我对数据管理非常陌生,我一定会将您的改进点带给我并加以应用。
  • @doomdaam 祝你好运。并尽快升级到 MySQL 8。有非常有价值的改进,例如 window functionsCommon Table Expressions
  • 我想念 CTE 的。我使用 PHPMyAdmin,默认版本是 5.7.33。不知道如何升级。
猜你喜欢
  • 1970-01-01
  • 2011-02-13
  • 2016-11-14
  • 1970-01-01
  • 1970-01-01
  • 2020-10-11
  • 1970-01-01
  • 2023-03-10
  • 1970-01-01
相关资源
最近更新 更多