sql - 如何使用字符串的顺序创建跨行的累积字符串连接?
问题描述
我希望在我的数据库中添加一个列,该列对每个 ID 的另一列的值执行逐行累积连接,并按不同的层次结构对结果字符串进行排序。数据集非常大,我设计的较小测试数据的结果无法在更大范围内使用,因此我需要帮助重新设计它。
到目前为止,我已经使用递归 CTE 的组合来编写查询来执行累积连接(下面的步骤 1 输出),然后是一个稍微笨重的函数(下面的步骤 2 输出)来根据单独的层次结构对字符串进行排序,这也删除了'1' 值。这些适用于我的数据的一小部分(n = 60),但是当我尝试运行一个更大的子集(n = 500,000)时,CTE 表会永远运行(在 2 小时内没有完成就停止)。真实的数据集将是数亿行的数量级,因此解决方案不适用于该规模。
ID Start_Date End_Date Seg step1 step2
1 01/04/1946 31/12/1990 1 1 1
1 01/01/1991 08/01/2007 4 4 4
1 09/01/2007 04/02/2007 1 1 1
1 05/02/2007 18/10/2017 4 14 4
1 01/04/2013 18/10/2017 8 148 48
1 11/11/2014 18/10/2017 7 1487 487
2 01/05/1931 31/12/1997 1 1 1
2 01/01/1998 20/01/2014 4 4 4
2 31/01/2011 20/01/2014 6 146 46
2 21/02/2013 20/01/2014 5 1465 456
2 01/04/2013 20/01/2014 8 14658 4586
2 29/04/2013 20/01/2014 7 146587 45876
还有其他复杂的逻辑元素,例如仅在开始日期早于上一行结束日期时才开始累积,因此通过添加where
或case when
语句允许灵活性的解决方案是关键。
我使用的递归 CTE 和排序函数的示例(不适用于所示的简化表,但表示我使用的结构)如下所示。
递归 CTE(输出步骤 1 列)
with t (ID, Segment,start_date, start_comb,updated_end_date ,rn) as (
select ID, Segment, start_date, case when Segment_end_date <> resolved_date OR Segment_end_date is null then 1 else 0 end as start_comb
,updated_end_date
,row_number() over (partition by ID order by start_date) as rn
from #test_IDs
)
,r (ID, orig_seg, Segment, rn, start_comb, start_date, updated_end_date) as (
select ID, cast(Segment as varchar(max)), cast(Segment as varchar(max)),rn, start_comb, start_date, updated_end_date
from t
where start_comb=0
union all
select r.ID, cast(t.segment as varchar(max)) as orig_seg
, Segment = cast( (concat(r.Segment,t.Segment)) as varchar(max))
, t.rn, t.start_comb, t.start_date, t.updated_end_date
from r
join t on t.ID = r.ID and t.rn = r.rn + 1 and t.start_comb <> 0
)
排序函数(输出步骤 2 列)
if object_id ('reformat') is not null
drop function reformat
create function dbo.reformat
(
@unordered_Segs varchar(max)
)
returns varchar(255)
as
begin
declare @healthy int, @first int, @second int, @third int, @fourth int, @fifth int, @outtext int
if Charindex('4',@unordered_segs) > 0
set @first = 4
else set @first = ''
if Charindex('5',@unordered_segs) > 0
set @second = 5
else set @second = ''
if Charindex('8',@unordered_segs) > 0
set @third = 8
else set @third = ''
if Charindex('7',@unordered_segs) > 0
set @fourth = 7
else set @fourth = ''
if Charindex('6',@unordered_segs) > 0
set @fifth = 6
else set @fifth = ''
if Charindex('1',@unordered_segs) > 0 and len(@unordered_segs) = 1
set @outtext = 1
else
set @outtext = Replace((concat(@first,@second,@third,@fourth,@fifth)),'0','')
return @outtext
end
谢谢!
解决方案
推荐阅读
- python - 用于 Python 脚本的 Crontab 每 5 分钟
- typescript - 打字稿/量角器。循环和 ElementFinderArray 并解决承诺
- sql - SQL Server:单个更新多个属性
- anaconda - 找不到 cplex 可执行文件
- javascript - 对于(Android/iOS)WebView 混合应用程序,我应该使用哪个 OAuth 流程以及在哪里存储令牌?
- bash - 我如何在 bash 中读取两个文件来安排和填充缺失的字段
- python-3.x - 当列不存在时如何在数据中添加列?
- ruby-on-rails - 生产中的 Active_Storage(加载资源失败:500 错误)
- c++ - TeamViewer 如何以编程方式在 Windows 上模拟 Ctrl-Alt-Del?
- javascript - React中的RBAC,如何实现,构想?