sql - varchar字段的分组字符

标签 sql sql-server sql-server-2008-r2 sql-server-express

我正在创建来自多个供应商的导入数据工具。不幸的是,数据不是我生成的,所以我必须使用它。我遇到过以下情况。

我有一个如下表:

ID    |SartDate    |Availability
========================================
H1    |20130728    |YYYYYYNNNNQQQQQ
H2    |20130728    |NNNNYYYYYYY
A3    |20130728    |NNQQQQNNNNNNNNYYYYYY
A2    |20130728    |NNNNNYYYYYYNNNNNN

解释一下这个数据的含义是: Availability 列中的每个字母都是特定日期的可用性标志,从 StartDate 列中注明的日期开始。

  • 是:可用
  • N:不可用
  • 问:根据要求

例如,ID H1 20130728 - 20130802 可用,则 20130803 - 20130806 不可用,而 20130807 - 20130811 可根据要求提供。

我需要做的就是将此表转换为以下设置:

ID    |Available   |SartDate    |EndDate     
========================================
H1    |Y           |20130728    |20130802    
H1    |N           |20130803    |20130806    
H1    |Q           |20130806    |20130811    
H2    |N           |20130728    |20130731
H2    |Y           |20130801    |20130807
A3    |N           |20130728    |20130729
A3    |Q           |20130730    |20130802
A3    |N           |20130803    |20130810
A3    |Y           |20130811    |20130816
A2    |Y           |20130728    |20130801
A2    |Y           |20130802    |20130807
A2    |Y           |20130808    |20130813

初始表大约有 40,000 行。 Availability 列可能有几天(我见过最多 800 天)。

我尝试过的是将可用性转换为行,然后将连续的天分组在一起,然后获取每组的最小和最大日期。为此,我使用了三四个 CTE

这对于一些 ID 来说效果很好,但是当我尝试将它应用到整个表时,它需要很长时间(我在傻瓜时间 sleep 后停止了初始测试运行,它还没有完成,是的,我的意思是我是运行时 sleep !!!)

我估计,如果我将每个字符转为一行,那么我最终会得到大约 1450 万行。

所以,我想问,有没有更有效的方法来做到这一点? (我知道有,但我需要你告诉我)

提前致谢。

最佳答案

这可以在 SQL Server 中使用递归 CTE 来完成。这是一个例子:

with t as (
    select 'H1' as id, cast('20130728' as date) as StartDate,
           'YYYYYYNNNNQQQQQ' as Availability union all
    select 'H2' as id, cast('20130728' as date) as StartDate,
           'NNNNYYYYYYY' as Availability union all
    select 'H3' as id, cast('20130728' as date) as StartDate,
           'NQ' as Availability 
   ),
   cte as (
     select id, left(Availability, 1) as Available,
            StartDate as thedate,
            substring(Availability, 2, 1000) as RestAvailability,
            1 as i,
            1 as periodcnt
     from t
     union all
     select t.id, left(RestAvailability, 1),
            dateadd(dd, 1, thedate),
            substring(RestAvailability, 2, 1000) as RestAvailability,
            1 + cte.i,
            (case when substring(t.Availability, i, 1) = substring(t.Availability, i+1, 1)
                  then periodcnt
                  else periodcnt + 1
             end)
     from t join
          cte
          on t.id = cte.id
     where len(RestAvailability) > 0

   )
select id, min(thedate), max(thedate), Available
from cte
group by id, periodcnt, Available;

其工作方式是首先分散日期。这将是 CTE 的“典型”用途。在此过程中,它还会跟踪 Available 是否较之前的值发生更改(在变量 periodcnt 中)。它为此使用字符串操作。

有了这些信息,最终结果就是这个 CTE 的聚合。

关于sql - varchar字段的分组字符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17943169/

相关文章:

sql - UNION 与 CROSS APPLY 性能

mysql:从不同列填充列中的数据

c# - 通过 C# 启动 SQL Server

sql - 获取层次结构表中子项的根父项

sql - 如果行有关系,如何插入 BIT 值

sql - db2 sql 脚本文件

iphone - iOS 核心数据使用现有的 SQLite?

sql-server - 将约束移至连接是否比连接和 where 子句更有效?

使用非管理员帐户时,SQL Linked Server 返回错误 "no login-mapping exists"

sql-server - 如何使它成为示例 SQL Atomic?