如何在SQL中将单行数据拆分为多行?

How to split the data in a single row into multiple rows in SQL?

我有一个table格式如下

ProjectID           LocationID
1                   [1,2,3,4]
2                   [2,3]

我可以像下面这样将 LocationID 列中的数据拆分为多行吗?

ProjectID           LocationID
1                   1
1                   2
1                   3
1                   4
2                   2
2                   3

我只需要使用 SQL 将数据加载到 Power-Bi。 可能吗?

如果 locationID 的数据类型是 varchar 那么:

 create table projects (ProjectID int, LocationID varchar(50));
 insert into projects values(1,                  '[1,2,3,4]');
 insert into projects values(2,                  '[2,3]');

查询:

select projectid, value 
 from projects 
 CROSS APPLY STRING_SPLIT(replace(replace(locationid,'[',''),']',''),',')

输出:

projectid value
1 1
1 2
1 3
1 4
2 2
2 3

dbhere

SQL Server 2014

的解决方案
 create table projects (ProjectID int, LocationID nvarchar(max));
 insert into projects values(1,                  '[1,2,3,4]');
 insert into projects values(2,                  '[2,3]');

查询:

     WITH tmp AS
(
    SELECT
        ProjectID,
        LEFT(replace(replace(locationid,'[',''),']',''), CHARINDEX(',', replace(replace(locationid,'[',''),']','') + ',') - 1) LocationID,
        STUFF(replace(replace(locationid,'[',''),']',''), 1, CHARINDEX(',', replace(replace(locationid,'[',''),']','') + ','), '') b
    FROM projects
    
    UNION all

    SELECT
        ProjectID,
        LEFT(b, CHARINDEX(',', b + ',') - 1),
        STUFF(b, 1, CHARINDEX(',', b + ','), '')
    FROM tmp
    WHERE
        b > ''
)

SELECT
    ProjectID, LocationID
FROM tmp
ORDER BY projectid
 

输出:

ProjectID LocationID
1 1
1 2
1 3
1 4
2 2
2 3

dbhere

在 SQL Server 2014 中,您可以使用递归 CTE——Kazi 也提出了这一点。我认为这是一个稍微简单的版本:

with cte as (
      select projectId, convert(varchar(max), null) as locationid,
             convert(varchar(max), substring(LocationId, 2, len(locationId) - 2)) + ',' as rest
      from t
      union all
      select projectId,
             left(rest, charindex(',', rest) - 1),
             stuff(rest, 1, charindex(',', rest), '')
      from cte
      where rest <> ''
     )
select projectid, locationid
from cte
where locationid is not null;

Here 是一个 db<>fiddle.

特别是,锚点部分只是设置数据——它不会从字符串中提取任何元素。所以,所有的逻辑都在递归部分,我觉得更容易维护。

我使用了 STRING_SPLIT(),这是一个 table 值函数,支持 SQL 服务器 2016 及更高版本。您需要将格式化的字符串提供给此函数并使用交叉应用来连接并生成所需的输出。

SELECT
    projectID
   , REPLACE(REPLACE(locationId,'[',''),']','') as [locationid]  
INTO #temp_table
FROM split_string -- Add your table name here


SELECT 
    projectID
    ,VALUE [locationid]  
FROM     #temp_table
CROSS APPLY STRING_SPLIT( [locationid] , ',');

感谢大家帮助我。此解决方案适用于我的场景。

Select 项目 ID,locationid_list 来自项目 交叉应用 OPENJSON(locationid, '$') WITH (locationid_list int '$')