- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我的场景一开始类似于孤岛和差距问题,我需要找到连续的工作天数。我当前的 SQL 查询回答“ProductA 是在 LocationA 从 DateA 到 DateB 生产的,总共 X 数量”。
但是,当我需要将价格纳入其中时,这还不够。价格位于单独的表中,并在事后用 C# 进行处理。价格变化本质上是一个记录列表,上面写着“LocationA 的 ProductA 现在是在 DateC 生效的每单位 Y 值”。
最终结果是,只要岛屿不与价格更改日期重叠,它就可以工作,但如果确实重叠,我会得到“接近”答案,但并不精确。
C# 代码可以有效地处理应用价格,但我需要做的是根据价格变化分割岛屿。我的目标是让 SQL 的分区考虑到其他表中的天数排名,但我在应用我想要做的事情时遇到了困难。
<小时/>当前生成我的岛屿的SQL如下
SELECT MIN(ScheduledDate) as StartDate, MAX(ScheduledDate) as
EndDate, ProductId, DestinationId, SUM(Quantity) as TotalQuantity
FROM (
SELECT ScheduledDate, DestinationId, ProductId, PartitionGroup = DATEADD(DAY ,-1 * DENSE_RANK() OVER (ORDER BY ScheduledDate), ScheduledDate), Quantity
FROM History
) tmp
GROUP BY PartitionGroup, DestinationId, ProductId;
当前从 PriceChange 表获取并对日期进行排名的 SQL 如下
DECLARE @PriceChangeDates TABLE(Rank int, SplitDate Date);
INSERT INTO @PriceChangeDates
SELECT DENSE_RANK() over (ORDER BY EffectiveDate) as Rank, EffectiveDate as SplitDate
FROM ProductPriceChange
GROUP BY EffectiveDate;
<小时/>
我的想法是以某种方式更新第一个查询内部 SELECT 语句,以某种方式利用第二个查询创建的 @PriceChangeDates 表。我认为我们可以将 DATEADD 的增量参数乘以声明表中的排名,但我很难编写它。
如果我要以某种方式用循环来做到这一点,我的思考过程将是确定 ScheduledDate 在 @PriceChangeDates 表中的排名,其中它的排名是它能找到的比它本身小的最接近日期的排名。然后采用给出的任何排名,我认为将其乘以传入的增量参数(或一些数学,例如对现有参数执行 *@PriceChangeDates.Count() ,然后添加新的排名以避免碰撞)。然而,这是“循环”逻辑而不是“集合”逻辑,在 SQL 中我需要用集合来思考。
<小时/>非常感谢任何和所有的帮助/建议。谢谢:)
<小时/>更新:
SQLFiddle 上的示例数据和示例:http://www.sqlfiddle.com/#!18/af568/1
数据在哪里:
CREATE TABLE History
(
ProductId int,
DestinationId int,
ScheduledDate date,
Quantity float
);
INSERT INTO History (ProductId, DestinationId, ScheduledDate, Quantity)
VALUES
(0, 1000, '20180401', 5),
(0, 1000, '20180402', 10),
(0, 1000, '20180403', 7),
(3, 5000, '20180507', 15),
(3, 5000, '20180508', 23),
(3, 5000, '20180509', 52),
(3, 5000, '20180510', 12),
(3, 5000, '20180511', 14);
CREATE TABLE PriceChange
(
ProductId int,
DestinationId int,
EffectiveDate date,
Price float
);
INSERT INTO PriceChange (ProductId, DestinationId, EffectiveDate, Price)
VALUES
(0, 1000, '20180201', 1),
(0, 1000, '20180402', 2),
(3, 5000, '20180101', 5),
(3, 5000, '20180510', 20);
期望的结果是有一个生成结果的 SQL 语句:
StartDate EndDate ProductId DestinationId TotalQuantity
2018-04-01 2018-04-01 0 1000 5
2018-04-02 2018-04-03 0 1000 17
2018-05-07 2018-05-09 3 5000 90
2018-05-10 2018-05-11 3 5000 26
需要澄清的是,最终结果确实需要每个拆分金额的 TotalQuantity,因此操纵结果并应用定价的程序代码知道价格变化的每一侧每种产品的数量,以准确确定值.
最佳答案
这是另一个可能比我的第一个答案表现更好的变体。我决定将其作为第二个答案,因为方法相当不同,而且答案太长。您应该将所有变体的性能与硬件上的真实数据进行比较,并且不要忘记索引。
在第一个变体中,我使用 APPLY
为 History
表中的每一行选择相关价格。对于 History
表中的每一行,引擎都会从 PriceChange
表中搜索相关行。即使在 PriceChange
表上有适当的索引,当通过单次查找完成此操作时,它仍然意味着循环连接中有 370 万次查找。
我们可以简单地将 History
和 PriceChange
表连接在一起,并在两个表上使用适当的索引,这将是一个有效的合并连接。
在这里,我还使用扩展的示例数据集来说明差距。我将这些行添加到问题的示例数据中。
INSERT INTO History (ProductId, DestinationId, ScheduledDate, Quantity)
VALUES
(0, 1000, '20180601', 5),
(0, 1000, '20180602', 10),
(0, 1000, '20180603', 7),
(3, 5000, '20180607', 15),
(3, 5000, '20180608', 23),
(3, 5000, '20180609', 52),
(3, 5000, '20180610', 12),
(3, 5000, '20180611', 14);
中间查询
我们在这里执行FULL JOIN
,而不是LEFT JOIN
,因为价格更改的日期可能不会出现在历史记录中
根本没有表。
WITH
CTE_Join
AS
(
SELECT
ISNULL(History.ProductId, PriceChange.ProductID) AS ProductID
,ISNULL(History.DestinationId, PriceChange.DestinationId) AS DestinationId
,ISNULL(History.ScheduledDate, PriceChange.EffectiveDate) AS ScheduledDate
,History.Quantity
,PriceChange.Price
FROM
History
FULL JOIN PriceChange
ON PriceChange.ProductID = History.ProductID
AND PriceChange.DestinationId = History.DestinationId
AND PriceChange.EffectiveDate = History.ScheduledDate
)
,CTE2
AS
(
SELECT
ProductID
,DestinationId
,ScheduledDate
,Quantity
,Price
,MAX(CASE WHEN Price IS NOT NULL THEN ScheduledDate END)
OVER (PARTITION BY ProductID, DestinationId ORDER BY ScheduledDate
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS grp
FROM CTE_Join
)
SELECT *
FROM CTE2
ORDER BY
ProductID
,DestinationId
,ScheduledDate
创建以下索引
CREATE UNIQUE NONCLUSTERED INDEX [IX_History] ON [dbo].[History]
(
[ProductId] ASC,
[DestinationId] ASC,
[ScheduledDate] ASC
)
INCLUDE ([Quantity])
CREATE UNIQUE NONCLUSTERED INDEX [IX_Price] ON [dbo].[PriceChange]
(
[ProductId] ASC,
[DestinationId] ASC,
[EffectiveDate] ASC
)
INCLUDE ([Price])
并且该连接将是执行计划中高效的MERGE
连接(而不是LOOP
连接)
中间结果
+-----------+---------------+---------------+----------+-------+------------+
| ProductID | DestinationId | ScheduledDate | Quantity | Price | grp |
+-----------+---------------+---------------+----------+-------+------------+
| 0 | 1000 | 2018-02-01 | NULL | 1 | 2018-02-01 |
| 0 | 1000 | 2018-04-01 | 5 | NULL | 2018-02-01 |
| 0 | 1000 | 2018-04-02 | 10 | 2 | 2018-04-02 |
| 0 | 1000 | 2018-04-03 | 7 | NULL | 2018-04-02 |
| 0 | 1000 | 2018-06-01 | 5 | NULL | 2018-04-02 |
| 0 | 1000 | 2018-06-02 | 10 | NULL | 2018-04-02 |
| 0 | 1000 | 2018-06-03 | 7 | NULL | 2018-04-02 |
| 3 | 5000 | 2018-01-01 | NULL | 5 | 2018-01-01 |
| 3 | 5000 | 2018-05-07 | 15 | NULL | 2018-01-01 |
| 3 | 5000 | 2018-05-08 | 23 | NULL | 2018-01-01 |
| 3 | 5000 | 2018-05-09 | 52 | NULL | 2018-01-01 |
| 3 | 5000 | 2018-05-10 | 12 | 20 | 2018-05-10 |
| 3 | 5000 | 2018-05-11 | 14 | NULL | 2018-05-10 |
| 3 | 5000 | 2018-06-07 | 15 | NULL | 2018-05-10 |
| 3 | 5000 | 2018-06-08 | 23 | NULL | 2018-05-10 |
| 3 | 5000 | 2018-06-09 | 52 | NULL | 2018-05-10 |
| 3 | 5000 | 2018-06-10 | 12 | NULL | 2018-05-10 |
| 3 | 5000 | 2018-06-11 | 14 | NULL | 2018-05-10 |
+-----------+---------------+---------------+----------+-------+------------+
您可以看到 Price
列有很多 NULL
值。我们需要用前面的非 NULL 值“填充”这些 NULL 值。
Itzik Ben-Gan 写了一篇很好的文章,展示了如何有效地解决这个问题 The Last non NULL Puzzle 。另请参阅Best way to replace NULL with most recent non-null value .
这是在 CTE2
中使用 MAX
窗口函数完成的,您可以看到它如何填充 grp
列。这需要 SQL Server 2012+。确定组后,我们应该删除 Quantity
为 NULL 的行,因为这些行不是来自 History
表。
现在我们可以使用 grp 列作为附加分区来执行相同的间隙和岛屿步骤。
查询的其余部分与第一个变体几乎相同。
最终查询
WITH
CTE_Join
AS
(
SELECT
ISNULL(History.ProductId, PriceChange.ProductID) AS ProductID
,ISNULL(History.DestinationId, PriceChange.DestinationId) AS DestinationId
,ISNULL(History.ScheduledDate, PriceChange.EffectiveDate) AS ScheduledDate
,History.Quantity
,PriceChange.Price
FROM
History
FULL JOIN PriceChange
ON PriceChange.ProductID = History.ProductID
AND PriceChange.DestinationId = History.DestinationId
AND PriceChange.EffectiveDate = History.ScheduledDate
)
,CTE2
AS
(
SELECT
ProductID
,DestinationId
,ScheduledDate
,Quantity
,Price
,MAX(CASE WHEN Price IS NOT NULL THEN ScheduledDate END)
OVER (PARTITION BY ProductID, DestinationId ORDER BY ScheduledDate
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS grp
FROM CTE_Join
)
,CTE_RN
AS
(
SELECT
ProductID
,DestinationId
,ScheduledDate
,grp
,Quantity
,ROW_NUMBER() OVER (PARTITION BY ProductId, DestinationId, grp ORDER BY ScheduledDate) AS rn1
,DATEDIFF(day, '20000101', ScheduledDate) AS rn2
FROM CTE2
WHERE Quantity IS NOT NULL
)
SELECT
ProductId
,DestinationId
,MIN(ScheduledDate) AS StartDate
,MAX(ScheduledDate) AS EndDate
,SUM(Quantity) AS TotalQuantity
FROM
CTE_RN
GROUP BY
ProductId
,DestinationId
,grp
,rn2-rn1
ORDER BY
ProductID
,DestinationId
,StartDate
;
最终结果
+-----------+---------------+------------+------------+---------------+
| ProductId | DestinationId | StartDate | EndDate | TotalQuantity |
+-----------+---------------+------------+------------+---------------+
| 0 | 1000 | 2018-04-01 | 2018-04-01 | 5 |
| 0 | 1000 | 2018-04-02 | 2018-04-03 | 17 |
| 0 | 1000 | 2018-06-01 | 2018-06-03 | 22 |
| 3 | 5000 | 2018-05-07 | 2018-05-09 | 90 |
| 3 | 5000 | 2018-05-10 | 2018-05-11 | 26 |
| 3 | 5000 | 2018-06-07 | 2018-06-11 | 116 |
+-----------+---------------+------------+------------+---------------+
此变体不会输出相关价格(作为第一个变体),因为我简化了“最后一个非空”查询。问题中没有要求。无论如何,如果需要的话,添加价格是相当容易的。
关于sql - 间隙和岛屿 : Splitting Islands Based On External Table,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55955942/
在 R 中,您可以使用 strsplit在分隔符( split )上分割向量的函数如下: x <- "What is this? It's an onion. What! That's| Well
我的 .split(); 方法有问题。 我称这个函数为: get_content_ajax("html/settings.html", "#ajax", 1, "Settings page have
我是Elixir的新手。我正在尝试对字符串split的基本操作,如下所示 String.split("Awesome",""); 根据elixir document,它应该根据提供的模式split字符
当我使用 =arrayformula(split(input!G2:G, ",")) 时,为什么拆分公式没有扩展到整个列? 我只得到输入的结果!G2 单元格,而不是 G 列中的其余部分。其他公式如 =
我正在尝试制作一个名为 core-splitter 的元素,该元素在 1.0 中已弃用,因为它在我们的项目中起着关键作用。 如果您不知道 core-splitter 的作用,我可以提供一个简短的描述。
我很难尝试使用多个定界符将字符串拆分为列表。我可以像下面这样将它拆分两次: myString.split(':')[1].split('.') 然而,这看起来很不优雅。在我的脑海里,我想做这样的事情:
来自使用惊喜模块的推荐引擎的代码,我在任何地方都找不到答案。 最佳答案 根据您的目标,您可以使用 cross_validation方法,它将自动为您执行拆分。示例:cross_validate(alg
我正在制作一个有丝 split 模拟器,我希望它在细胞足够大并 split 时运行有丝 split 功能。当它分割时,我希望它能够将分割从初始 x 值(前一个单元格的 x)动画化为新的 x 值(右侧的
我有一个用于三个按钮的点击处理程序,在这个处理程序中我想提取所点击按钮的 ID。我有一行这样的代码: $('#switch button').click(function(){ var cla
我需要像这样分割一个字符串 var val = "$cs+55+mod($a)"; 放入数组 arr = val.split( /[+-/*()\s*]/ ); 问题是将分隔符保留为数组元素,如 ar
我在同一个 string 上使用 split() 和 split("") .但为什么 split("") 返回的元素数量少于 split()?我想知道在什么特定的输入情况下会发生这种情况。 最佳答案
我的代码中某处有错误,但看不到我做错了什么。 我拥有的是 facebook 用户 ID 的隐藏输入,它是通过 jQuery UI 自动完成填充的: 然后,我有一个 jQuery 函数,当单击链接将其
我正在寻找一个程序来读取字符串/文件并显示其中的前三个单词。 所以我尝试了: letter= "a,b,c" print(letter.split(',')[0]) 这对获取一个单词有效,但执行 [0
我有一个存储邮件的表 Mails(谁会想到... ;))。 通过 tinyint MailStatus,我决定这是 SentMail、Draft 还是 ReceivedMail。 现在我想知道 Tab
在我的优化探索中,我发现内置的 split() 方法比等效的 re.split() 方法快大约 40%。 虚拟基准(易于复制粘贴): import re, time, random def rando
我对split有一个奇怪的问题,因为默认情况下它不会将split放入默认数组中。 以下是一些玩具代码。 #!/usr/bin/perl $A="A:B:C:D"; split (":",$A); pr
我目前正在学习 JCL,并且正在使用 SORT 程序。作为练习,我想将一些输入记录拆分为属于同一 PDS 的多个成员。这是我的 JCL 代码: //FAILJ JOB //STEP1 EX
在苦苦挣扎了半小时之后,我在使用空格分割字符串时遇到了这种差异,具体取决于您使用的语法。 简单字符串: $line = "1: 2: 3: 4: 5: " 拆分示例1 -从1开始注意带有 token
我有一个像这样的字符串: 'Agendas / Schedules meetings and speakers 4 F 1928-1209 Box 2' 我正在尝试将其
我试图了解 r-tree 的工作原理,发现有两种类型的拆分:二次拆分和线性拆分。 线性和二次实际上有什么区别?在哪种情况下,一个会比另一个更受欢迎? 最佳答案 原始 R-Tree 论文在 3.5.2
我是一名优秀的程序员,十分优秀!