Sql 如何为视图将字符串拆分为列?
我在一个表中有一列(Sql 如何为视图将字符串拆分为列?,sql,sql-server,sql-server-2008,tsql,Sql,Sql Server,Sql Server 2008,Tsql,我在一个表中有一列(full\u location\u id),其中包含一个以“-”分隔的字符串,我需要在一个视图中将其拆分为4列(Test\u SplitColumn)。并非full\u location\u id中的每条记录都包含相同长度的id。有些可能具有A1-BF-35-B1等ID,而另一些可能仅具有AR-B3。我不确定这样做的最佳方式。我能够检索到第一列,但到目前为止没有检索到其他列 CREATE VIEW [dbo].[Test_SplitColumn] AS select p.na
full\u location\u id
),其中包含一个以“-”分隔的字符串,我需要在一个视图中将其拆分为4列(Test\u SplitColumn
)。并非full\u location\u id
中的每条记录都包含相同长度的id。有些可能具有A1-BF-35-B1等ID,而另一些可能仅具有AR-B3。我不确定这样做的最佳方式。我能够检索到第一列,但到目前为止没有检索到其他列
CREATE VIEW [dbo].[Test_SplitColumn]
AS
select p.name, location.aisle_id, location.full_location_id, SUBSTRING(location.full_location_id, 0,charindex('-', location.full_location_id )) as Aisle,
SUBSTRING(location.full_location_id, charindex('-', location.full_location_id ) + 1, charindex('-', location.full_location_id, LEN(SUBSTRING(location.full_location_id, 0,charindex('-', location.full_location_id ))) )) as ShelvingUnit
from location
inner join product p on p.id = location.product_id
GO
任何帮助或指导都将不胜感激。这是您的模型的失败。与其将位置存储为分隔字符串,不如创建一个1-n表来存储位置。事实上,对你的问题的正确“答案”可能是“重新设计数据库的这一部分!” 但是,要想做你想做的事情,你可以做如下事情:
USE tempdb
GO
/* udfSplit (A Fast String Splitter) **************************************************************
*
* Uses a number table to *very* quickly split the text (@text). Splits on the delimiter (@d)
* Returns Table of ( [RowID], [SplitText] ). Inlineable for CROSS APPLY etc.
*
* Charlie
*
*************************************************************************************************/
CREATE FUNCTION [dbo].[udfSplit] (@text NVARCHAR(4000), @d NVARCHAR(50))
RETURNS TABLE AS RETURN (
WITH numbers(n) AS (
SELECT ROW_NUMBER() OVER (ORDER BY a.[n])
FROM
( VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9) ) AS a ([n])
CROSS JOIN ( VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9) ) AS b ([n])
CROSS JOIN ( VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9) ) AS c ([n])
CROSS JOIN ( VALUES (0), (1), (2), (3), (4)) AS d ([n])
)
SELECT
[RowID] = ROW_NUMBER() OVER ( ORDER BY [n] ASC )
, [SplitText] = SUBSTRING(
@d + @text + @d
, [n] + LEN(@d)
, CHARINDEX(@d, @d + @text + @d, [n] + LEN(@d)) - [n] - LEN(@d)
)
FROM numbers AS n
WHERE [n] <= LEN(@d + @text + @d) - LEN(@d)
AND SUBSTRING(@d + @text + @d, [n], LEN(@d)) = @d
)
GO
IF OBJECT_ID('tempdb..#sample') IS NOT NULL DROP TABLE #sample
GO
CREATE TABLE #sample (
name VARCHAR(255)
, locations VARCHAR(MAX)
)
INSERT #sample (name, locations)
VALUES ('a', 'ab-cd')
, ('b', 'ab-cd-ef')
, ('c', 'gh')
, ('d', NULL)
; WITH SPLIT AS (
SELECT [name], l.*
FROM #sample AS s
OUTER APPLY dbo.[udfSplit](s.locations,'-') AS l
)
SELECT
s.name
, MAX(CASE WHEN s.rowId = 1 THEN s.SplitText ELSE '' END) AS a
, MAX(CASE WHEN s.rowId = 2 THEN s.SplitText ELSE '' END) AS b
, MAX(CASE WHEN s.rowId = 3 THEN s.SplitText ELSE '' END) AS c
, MAX(CASE WHEN s.rowId = 4 THEN s.SplitText ELSE '' END) AS d
FROM
SPLIT AS s
GROUP BY
s.name
MAX(CASE…)表达式是SQLServer2000Land中的一种旋转技巧。我从未掌握过枢轴操纵器的窍门
SQL Fiddle:
这里有一个简单快捷的方法:
DECLARE @T TABLE(full_location_id varchar(100));
INSERT INTO @T
VALUES ('A1-BF-35-B1'),
('AR-B3');
WITH CTE AS
(
SELECT full_location_id,
LEN(full_location_id)-LEN(REPLACE(full_location_id,'-','')) N
FROM @T
)
SELECT full_location_id,
PARSENAME(REPLACE(full_location_id,'-','.'),N+1),
PARSENAME(REPLACE(full_location_id,'-','.'),N),
PARSENAME(REPLACE(full_location_id,'-','.'),N-1),
PARSENAME(REPLACE(full_location_id,'-','.'),N-2)
FROM CTE
结果:
╔══════════════════╦══════╦══════╦══════╦══════╗
║ full_location_id ║ Col1 ║ Col2 ║ Col3 ║ Col4 ║
╠══════════════════╬══════╬══════╬══════╬══════╣
║ A1-BF-35-B1 ║ A1 ║ BF ║ 35 ║ B1 ║
║ AR-B3 ║ AR ║ B3 ║ NULL ║ NULL ║
╚══════════════════╩══════╩══════╩══════╩══════╝
还有一个演示。这里有一个更通用的方法。这假设您知道将拥有的最大列数
CREATE TABLE #tmp (
full_location_id varchar(255) );
INSERT INTO dbo.#tmp VALUES ( 'A1-BF-35-B1' );
INSERT INTO dbo.#tmp VALUES ( 'AR-B3' );
INSERT INTO dbo.#tmp VALUES ( 'A1-BF-35' );
INSERT INTO dbo.#tmp VALUES ( 'A1' );
with tmp( full_location_id, c, position, single ) as (
select #tmp.full_location_id
, STUFF( #tmp.full_location_id, 1, CHARINDEX('-', #tmp.full_location_id + ' -'), '') AS c
, 1 AS position
, convert(nvarchar(max),left(#tmp.full_location_id, CHARINDEX('-', #tmp.full_location_id + ' -') -1)) AS single
from #tmp
union all
select full_location_id
, STUFF(c, 1, CHARINDEX('-', c + ' -'), '')
, position + 1
, convert(nvarchar(max),left(c, CHARINDEX('-', c + ' -') -1))
from tmp
where c > ''
)
SELECT pvt.full_location_id
, [1]
, [2]
, [3]
, [4]
FROM
( SELECT full_location_id
, single
, position
FROM tmp ) AS src
PIVOT
(
MAX( single )
FOR position IN ( [1], [2], [3], [4] )
) AS pvt;
很好——也想过滥用PARSENAME,但那总是让我紧张!但是PARSENAME不能处理4个以上的值。@TabAlleman是的,但要求是:
我需要在一个视图中分成4列
没错,我试图找到一个重复项来标记另一个问题,但4列限制排除了这个问题。我用了这个问题来代替,如果换个方式解决这个问题会更好。在您的表中有4列和一个视图/计算列,以显示作为分隔字符串组合的4列。我能够使用此处找到的函数以及创建视图的SQL代码中的一些大小写逻辑来解决我的问题。
CREATE TABLE #tmp (
full_location_id varchar(255) );
INSERT INTO dbo.#tmp VALUES ( 'A1-BF-35-B1' );
INSERT INTO dbo.#tmp VALUES ( 'AR-B3' );
INSERT INTO dbo.#tmp VALUES ( 'A1-BF-35' );
INSERT INTO dbo.#tmp VALUES ( 'A1' );
with tmp( full_location_id, c, position, single ) as (
select #tmp.full_location_id
, STUFF( #tmp.full_location_id, 1, CHARINDEX('-', #tmp.full_location_id + ' -'), '') AS c
, 1 AS position
, convert(nvarchar(max),left(#tmp.full_location_id, CHARINDEX('-', #tmp.full_location_id + ' -') -1)) AS single
from #tmp
union all
select full_location_id
, STUFF(c, 1, CHARINDEX('-', c + ' -'), '')
, position + 1
, convert(nvarchar(max),left(c, CHARINDEX('-', c + ' -') -1))
from tmp
where c > ''
)
SELECT pvt.full_location_id
, [1]
, [2]
, [3]
, [4]
FROM
( SELECT full_location_id
, single
, position
FROM tmp ) AS src
PIVOT
(
MAX( single )
FOR position IN ( [1], [2], [3], [4] )
) AS pvt;