存在する場合と存在しない場合がある区切り文字に基づいて分割したいデータがあります。
サンプルデータ:
John/Smith
Jane/Doe
Steve
Bob/Johnson
次のコードを使用して、このデータを姓と名に分割しています。
SELECT SUBSTRING(myColumn, 1, CHARINDEX('/', myColumn)-1) AS FirstName,
SUBSTRING(myColumn, CHARINDEX('/', myColumn) + 1, 1000) AS LastName
FROM MyTable
私が望む結果:
FirstName---LastName
John--------Smith
Jane--------Doe
Steve-------NULL
Bob---------Johnson
このコードは、すべての行に予想される区切り文字がある限り正常に機能しますが、行にない場合はエラーになります。
"Invalid length parameter passed to the LEFT or SUBSTRING function."
これを書き直して適切に動作させるにはどうすればよいですか?
これが役に立つかもしれません。
SELECT SUBSTRING(myColumn, 1, CASE CHARINDEX('/', myColumn)
WHEN 0
THEN LEN(myColumn)
ELSE CHARINDEX('/', myColumn) - 1
END) AS FirstName
,SUBSTRING(myColumn, CASE CHARINDEX('/', myColumn)
WHEN 0
THEN LEN(myColumn) + 1
ELSE CHARINDEX('/', myColumn) + 1
END, 1000) AS LastName
FROM MyTable
SQL Server 2016+の回答を探している人向け。組み込みのSTRING_SPLIT関数を使用します
例えば:
DECLARE @tags NVARCHAR(400) = 'clothing,road,,touring,bike'
SELECT value
FROM STRING_SPLIT(@tags, ',')
WHERE RTRIM(value) <> '';
ケースの選択 CHARINDEX( '/'、myColumn、0)= 0 THEN myColumn ELSE LEFT(myColumn、CHARINDEX( '/'、myColumn、 0)-1) END AS FirstName 、CASE CHARINDEX( '/'、myColumn、0)= 0 THEN '' ELSE RIGHT(myColumn、CHARINDEX( '/'、REVERSE(myColumn)、0)-1) END AS LastName FROM MyTable
文字列を含む行を区切り文字でフィルタリングして、次のようなものだけを操作してください:
SELECT SUBSTRING(myColumn, 1, CHARINDEX('/', myColumn)-1) AS FirstName,
SUBSTRING(myColumn, CHARINDEX('/', myColumn) + 1, 1000) AS LastName
FROM MyTable
WHERE CHARINDEX('/', myColumn) > 0
または
SELECT SUBSTRING(myColumn, 1, CHARINDEX('/', myColumn)-1) AS FirstName,
SUBSTRING(myColumn, CHARINDEX('/', myColumn) + 1, 1000) AS LastName
FROM MyTable
WHERE myColumn LIKE '%/%'
2016年のSQL Serverバージョンを使用している場合に備えて、複数の区切り文字で文字列を分割する代替方法を提供したかっただけです。
一般的な考え方は、文字列内のすべての文字を分割し、区切り文字の位置を決定してから、区切り文字に関連する部分文字列を取得することです。サンプルを次に示します。
-- Sample data
DECLARE @testTable TABLE (
TestString VARCHAR(50)
)
INSERT INTO @testTable VALUES
('Teststring,1,2,3')
,('Test')
DECLARE @delimiter VARCHAR(1) = ','
-- Generate numbers with which we can enumerate
;WITH Numbers AS (
SELECT 1 AS N
UNION ALL
SELECT N + 1
FROM Numbers
WHERE N < 255
),
-- Enumerate letters in the string and select only the delimiters
Letters AS (
SELECT n.N
, SUBSTRING(t.TestString, n.N, 1) AS Letter
, t.TestString
, ROW_NUMBER() OVER ( PARTITION BY t.TestString
ORDER BY n.N
) AS Delimiter_Number
FROM Numbers n
INNER JOIN @testTable t
ON n <= LEN(t.TestString)
WHERE SUBSTRING(t.TestString, n, 1) = @delimiter
UNION
-- Include 0th position to "delimit" the start of the string
SELECT 0
, NULL
, t.TestString
, 0
FROM @testTable t
)
-- Obtain substrings based on delimiter positions
SELECT t.TestString
, ds.Delimiter_Number + 1 AS Position
, SUBSTRING(t.TestString, ds.N + 1, ISNULL(de.N, LEN(t.TestString) + 1) - ds.N - 1) AS Delimited_Substring
FROM @testTable t
LEFT JOIN Letters ds
ON t.TestString = ds.TestString
LEFT JOIN Letters de
ON t.TestString = de.TestString
AND ds.Delimiter_Number + 1 = de.Delimiter_Number
OPTION (MAXRECURSION 0)
ALTER FUNCTION [dbo].[split_string](
@delimited NVARCHAR(MAX),
@delimiter NVARCHAR(100)
) RETURNS @t TABLE (id INT IDENTITY(1,1), val NVARCHAR(MAX))
AS
BEGIN
DECLARE @xml XML
SET @xml = N'<t>' + REPLACE(@delimited,@delimiter,'</t><t>') + '</t>'
INSERT INTO @t(val)
SELECT r.value('.','varchar(MAX)') as item
FROM @xml.nodes('/t') as records(r)
RETURN
END
上記の例は、区切り文字が1つしかない場合は正常に機能しますが、複数の区切り文字に対してはうまく拡張できません。これは、SQL Server 2016以降でのみ機能することに注意してください。
/*Some Sample Data*/
DECLARE @mytable TABLE ([id] VARCHAR(10), [name] VARCHAR(1000));
INSERT INTO @mytable
VALUES ('1','John/Smith'),('2','Jane/Doe'), ('3','Steve'), ('4','Bob/Johnson')
/*Split based on delimeter*/
SELECT P.id, [1] 'FirstName', [2] 'LastName', [3] 'Col3', [4] 'Col4'
FROM(
SELECT A.id, X1.VALUE, ROW_NUMBER() OVER (PARTITION BY A.id ORDER BY A.id) RN
FROM @mytable A
CROSS APPLY STRING_SPLIT(A.name, '/') X1
) A
PIVOT (MAX(A.[VALUE]) FOR A.RN IN ([1],[2],[3],[4],[5])) P