web-dev-qa-db-ja.com

既知のサイズの文字列を分割するためのクエリの最適化

のある列があります。区切り文字としては、次のようになります。

_abc.efg.hij 
_

これをCol1、Col2、およびCol3の3つの列に変換するクエリが必要です。これを行う最速の方法は何だと思います。これまでのところ、データベースの経験が限られているため、うまくうまくいくことができませんでした。私は関数を持っています:

_CREATE FUNCTION [dbo].[split](
   @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
_

これは私が今それをやっている方法ですが、私はそれをはるかに速くすることができると私は信じています。また、文字列を分割するための大幅に優れた機能や枠外のアイデアにも開放的です。私はこれを実行していると信じていますdbo.split(Name, '.')を3回実行すると、1回しか実行できません。

_SELECT
      Col1 = (SELECT Val from dbo.split(Name, '.') WHERE Id = '1'),
      Col2 = (SELECT Val from dbo.split(Name, '.')  WHERE Id = '2'),
      Col3 = (SELECT Val from dbo.split(Name, '.')  WHERE Id = '3')
FROM Mains
_

どんな助けでも大歓迎です

5
jdmneon

の代わりに:

_SELECT
  Col1 = (SELECT Val from dbo.split(Name, '.') WHERE Id = '1'),
  Col2 = (SELECT Val from dbo.split(Name, '.')  WHERE Id = '2'),
  Col3 = (SELECT Val from dbo.split(Name, '.')  WHERE Id = '3')
FROM Mains
_

使用する:

_SELECT
  s.*
FROM Mains
CROSS APPLY (
    SELECT
        MAX(CASE WHEN Id = 1 THEN Val END) AS Col1,
        MAX(CASE WHEN Id = 2 THEN Val END) AS Col2,
        MAX(CASE WHEN Id = 3 THEN Val END) AS Col3
    FROM dbo.split(Name,'.') s
    ) s 
_

それでも、メインの行ごとに正確に1行が必要であるという考えです。そして、_CROSS APPLY_内で集約関数を使用すると、まさにそれが可能になります。 CASEを使用すると、行ごとにsplit()を呼び出すだけで済みます。

文字列の分割については、この辺りにたくさんの質問があり、SQL DBにはすでにstring_split()関数が組み込まれています。

7
Rob Farley

区切り文字はドットなので、これを行う非常に簡潔な方法は

SELECT Col1 =  PARSENAME(name,3), 
       Col2 =  PARSENAME(name,2), 
       Col3 =  PARSENAME(name,1)
FROM Mains

上記は、PARSENAMEがドット区切りのSQL Serverオブジェクト名を解析するためのものであり、NULLに収まらない場合にSYSNAMEを返すため、128文字を超えるコンポーネントパーツには依存しません。

パフォーマンステストはまだ行っていません。以下の方がパフォーマンスが良いと思います。

SELECT Col1 = LEFT(name, FirstDot - 1),
       Col2 = SUBSTRING(name, FirstDot + 1, SecondDot - FirstDot - 1),
       Col3 = SUBSTRING(name, 1 + SecondDot, 8000)
FROM   Mains
       CROSS APPLY (VALUES(CHARINDEX('.', name))) V1(FirstDot)
       CROSS APPLY (VALUES(CHARINDEX('.', name, 1 + FirstDot))) V2(SecondDot) 
7
Martin Smith