MySQL SELECT with LEFT JOIN意外地将空值插入每个分区的第一行

MySQL SELECT with LEFT JOIN意外地将空值插入每个分区的第一行,mysql,null,left-join,Mysql,Null,Left Join,我有两张桌子。一个是股票价格,另一个是每只股票的股数。我想把这两个表合并起来,计算每只股票的市值 下面是一个示例数据表,其中只有我创建的3只股票,用于复制这个问题 CREATE TABLE stock_prices (country_exchange_code VARCHAR(2), stock_code VARCHAR(4), date DATE, close FLOAT, PRIMARY KEY (country_exchange_code,stock_code,date)); INSER

我有两张桌子。一个是股票价格,另一个是每只股票的股数。我想把这两个表合并起来,计算每只股票的市值

下面是一个示例数据表,其中只有我创建的3只股票,用于复制这个问题

CREATE TABLE stock_prices (country_exchange_code VARCHAR(2), stock_code VARCHAR(4), date DATE, close FLOAT, PRIMARY KEY (country_exchange_code,stock_code,date));

INSERT INTO stock_prices VALUES
    ("T", "1301",   '2019-10-29',   75.2),
    ("T", "1301",   '2019-10-30',   76.6),
    ("T", "1301",   '2019-10-31',   77.6),
    ("T", "1301",   '2019-11-01',   77.2),
    ("T", "1332",   '2019-10-29',   52.5),
    ("T", "1332",   '2019-10-30',   49.7),
    ("T", "1332",   '2019-10-31',   50.8),
    ("T", "1332",   '2019-11-01',   50.4),
    ("T", "1333",   '2019-10-29',   13.9),
    ("T", "1333",   '2019-10-30',   13.8),
    ("T", "1333",   '2019-10-31',   14.3),
    ("T", "1333",   '2019-11-01',   14.4);

CREATE TABLE stock_shares (country_exchange_code VARCHAR(2), stock_code VARCHAR(4), Num_Shares INT, PRIMARY KEY (country_exchange_code,stock_code));    

INSERT INTO stock_shares VALUES
    ("T", "1301",   241587962),
    ("T", "1332",   369875187),
    ("T", "1333",   958621587);
下面的查询将连接国家代码和股票代码的两个表,然后列出股票数量和最后收盘价,这是计算出的市值的输入。我使用last_value window函数获取最后收盘价

SELECT Stock_Code, Date, Num_Shares, 
        last_value(Close) OVER (PARTITION BY Stock_Code ORDER BY Date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS Last_Close,
        Num_Shares * last_value(Close) OVER (PARTITION BY Stock_Code ORDER BY Date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS Mkt_Cap
    FROM stock_prices LEFT JOIN stock_shares USING (Country_Exchange_Code, Stock_Code)
    WHERE Country_Exchange_Code = 'T' AND Date >= '2019-10-29' 
    ORDER BY Stock_Code, Date;
这将按预期工作,并产生以下结果:

结果1:

接下来,我想使用DISTINCT语句为每只股票得出一行。但是,我首先需要去掉除Stock_Code和Mkt_Cap之外的所有列。这就是问题所在。从select语句中删除最后一列时:

SELECT Stock_Code, Date, Num_Shares, 

        Num_Shares * last_value(Close) OVER (PARTITION BY Stock_Code ORDER BY Date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS Mkt_Cap
    FROM stock_prices LEFT JOIN stock_shares USING (Country_Exchange_Code, Stock_Code)
    WHERE Country_Exchange_Code = 'T' AND Date >= '2019-10-29' 
    ORDER BY Stock_Code, Date;
我会在每个股票代码分区的第一行出现这些意外的空值

结果2:

为什么会这样?我的表中没有空值,正如我们从第一个结果中看到的,计算Mkt_Cap所需的所有数据都在那里

额外资料: 当我从SELECT语句中删除日期和/或Num_共享时,没有问题。只有删除最后一个_值函数才会导致问题

CREATE TABLE stock_prices (country_exchange_code VARCHAR(2), stock_code VARCHAR(4), date DATE, close FLOAT, PRIMARY KEY (country_exchange_code,stock_code,date));

INSERT INTO stock_prices VALUES
    ("T", "1301",   '2019-10-29',   75.2),
    ("T", "1301",   '2019-10-30',   76.6),
    ("T", "1301",   '2019-10-31',   77.6),
    ("T", "1301",   '2019-11-01',   77.2),
    ("T", "1332",   '2019-10-29',   52.5),
    ("T", "1332",   '2019-10-30',   49.7),
    ("T", "1332",   '2019-10-31',   50.8),
    ("T", "1332",   '2019-11-01',   50.4),
    ("T", "1333",   '2019-10-29',   13.9),
    ("T", "1333",   '2019-10-30',   13.8),
    ("T", "1333",   '2019-10-31',   14.3),
    ("T", "1333",   '2019-11-01',   14.4);

CREATE TABLE stock_shares (country_exchange_code VARCHAR(2), stock_code VARCHAR(4), Num_Shares INT, PRIMARY KEY (country_exchange_code,stock_code));    

INSERT INTO stock_shares VALUES
    ("T", "1301",   241587962),
    ("T", "1332",   369875187),
    ("T", "1333",   958621587);

有趣的是,删除WHERE子句后,问题就消失了。我无法理解这是如何影响结果的,因为在我的小样本中,WHERE子句甚至不起任何作用。我所有的数据都有国家/地区交换代码='T'和日期>='2019-10-29'。但是在我有数百万行的实际数据集中,WHERE子句是非常必要的。因此,删除WHERE子句不是一个解决方案。

首先,我建议在表中使用别名。 代码中可能很重要的示例是:
where Country\u Exchange\u code='T'
,因为两个表中都有一个Country\u Exchange\u code列。当你完成这部分的时候,我建议你把日期列放在后面的引号里。然后看一下本文档,尤其是本部分:

在以下函数描述中,over_子句表示 第12.21.2节“窗口功能概念”中所述的OVER条款 和语法”。某些窗口函数允许null_处理子句 指定计算结果时如何处理空值。这 子句是可选的。它是SQL标准的一部分,但是MySQL 实现只允许尊重null(这也是默认值)。 这意味着在计算结果时会考虑空值。 将分析IGNORE NULLS,但会产生错误

我也在这里找到了这样的解释: 这是有帮助的(这是为Vertica,但…)

检查你对最后一次收盘的计算是否符合你的要求(你的查询是因为它对最后一次收盘给出了相同的结果),这是我玩的地方,也许它会帮助其他人或你。。。以下是我的建议:

select sp.Stock_Code
       , sp.`Date`
       , ss.Num_Shares
       , last_value(sp.Close) OVER (PARTITION BY sp.Stock_Code ORDER BY sp.`Date`) AS Last_Close
from stock_prices sp
LEFT JOIN stock_shares ss USING (Country_Exchange_Code, Stock_Code)
    WHERE ss.Country_Exchange_Code = 'T'
    AND sp.`Date` >= '2019-10-29' 
    ORDER BY ss.Stock_Code, sp.`Date`;

首先,我建议在你的桌子上使用别名。 代码中可能很重要的示例是:
where Country\u Exchange\u code='T'
,因为两个表中都有一个Country\u Exchange\u code列。当你完成这部分的时候,我建议你把日期列放在后面的引号里。然后看一下本文档,尤其是本部分:

在以下函数描述中,over_子句表示 第12.21.2节“窗口功能概念”中所述的OVER条款 和语法”。某些窗口函数允许null_处理子句 指定计算结果时如何处理空值。这 子句是可选的。它是SQL标准的一部分,但是MySQL 实现只允许尊重null(这也是默认值)。 这意味着在计算结果时会考虑空值。 将分析IGNORE NULLS,但会产生错误

我也在这里找到了这样的解释: 这是有帮助的(这是为Vertica,但…)

检查你对最后一次收盘的计算是否符合你的要求(你的查询是因为它对最后一次收盘给出了相同的结果),这是我玩的地方,也许它会帮助其他人或你。。。以下是我的建议:

select sp.Stock_Code
       , sp.`Date`
       , ss.Num_Shares
       , last_value(sp.Close) OVER (PARTITION BY sp.Stock_Code ORDER BY sp.`Date`) AS Last_Close
from stock_prices sp
LEFT JOIN stock_shares ss USING (Country_Exchange_Code, Stock_Code)
    WHERE ss.Country_Exchange_Code = 'T'
    AND sp.`Date` >= '2019-10-29' 
    ORDER BY ss.Stock_Code, sp.`Date`;

我不明白,你还能做什么,我认为这仍然是一个bug。 为了规避它:

SELECT
  Stock_Code, `Date`, Num_Shares, (Num_Shares * Mkt_Cap) Mkt_Cap
FROM
(SELECT Stock_Code, Date, Num_Shares, Close,

         (last_value(Close) OVER (PARTITION BY Stock_Code
         ORDER BY `Date` 
         ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)) AS Mkt_Cap
    FROM stock_prices3 LEFT JOIN stock_shares3 USING (Country_Exchange_Code, Stock_Code)
    WHERE Country_Exchange_Code = 'T' AND Date >= '2019-10-29' 
    ) t1
 ORDER BY Stock_Code, `Date`;
就像他在底部最后一个选择中看到的一样
我不明白,你还能做什么,我认为这仍然是一个bug。 为了规避它:

SELECT
  Stock_Code, `Date`, Num_Shares, (Num_Shares * Mkt_Cap) Mkt_Cap
FROM
(SELECT Stock_Code, Date, Num_Shares, Close,

         (last_value(Close) OVER (PARTITION BY Stock_Code
         ORDER BY `Date` 
         ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)) AS Mkt_Cap
    FROM stock_prices3 LEFT JOIN stock_shares3 USING (Country_Exchange_Code, Stock_Code)
    WHERE Country_Exchange_Code = 'T' AND Date >= '2019-10-29' 
    ) t1
 ORDER BY Stock_Code, `Date`;
就像他在底部最后一个选择中看到的一样

当您仅使用一个股票代码测试样本时,它会提供正确的数据,当您省略被乘数时,您也不会得到NUL值L,因此这显然是一个错误。当你用Mkt_Cap进行乘法、加法或数学运算时,这种行为也会表现出来。我相信这不是一个bug,而是一种常规的预测行为。如图所示,它是确定性的,但在我看来,所有select都应该显示完整的答案,并且没有空值。因此,当re不是空值时,它仍然是一个bug addid或multilpaling值应该产生一个值seen@VBokšićVertica的例子与此无关。出现问题的原因是用户没有指定OVER子句的完整范围。但我在无界前向和无界后向之间使用了行,所以这在这里不是问题。@VBokšIć是的,Mkt_上限的计算是我想要的(每行的结果相同),因为我想要的Mkt_上限仅根据最后一个价格计算。如果我能去掉这些空值,我将用DISTINCT子句折叠所有相同的行。当你只使用一个stock_代码测试样本时,它会提供正确的数据,当你去掉被乘数时,你也不会得到NUL值,所以这显然是一个Bug。当你用Mkt_Cap乘法、加法或做一些数学运算时,这种行为也会表现出来