0

SQLサーバーに分割関数が必要です。このスレッドに出くわしました:列「dbo」またはユーザー定義関数または集約「dbo.Splitfn」のいずれかが見つからないか、名前があいまいです

インデックスなどを使用して計算が多すぎると感じます。この関数を作成しました。

ALTER FUNCTION [dbo].[Split]
(
    @Data   varchar(8000),
    @Delimter   char(1) = ','
)
RETURNS @RetVal TABLE 
(
    Data    varchar(8000)
)
AS
Begin
    Set @Data = RTrim(Ltrim(IsNull(@Data,'')))
    Set @Delimter = IsNull(@Delimter,',')

    If Substring(@Data,Len(@Data),1) <> @Delimter
    Begin
        Set @Data = @Data + @Delimter
    End

    Declare @Len int = Len(@Data)
    Declare @index int = 1
    Declare @Char char(1) = ''
    Declare @part varchar(8000) = ''

    While @index <= @Len
    Begin

        Set @Char = Substring(@Data,@index,1)       
        If @Char = @Delimter And @part <> ''
        Begin
            Insert into @RetVal Values (@part)      
            Set @part = ''
        End
        Else
        Begin
            Set @part = @part + @Char
        End

        Set @index = @index + 1
    End

    RETURN;
End

誰がどれが効率的かコメントできますか?廃棄アプリケーションの1つでデータを分割するためにこの関数を使いすぎますが、これを効率的にしたいと思います。また、その効率をどのように測定したかについても言及してください。

4

4 に答える 4

2

さまざまな文字列分割方法とその効率に関するいくつかの議論では、T-SQLでこれを実行しようとするのをやめさせようとする傾向があります。非効率的な関数との戦いに何時間も費やして、それらから数マイクロ秒余分に絞り出そうとすることができますが、それは無駄の練習です。T-SQLはこのタスクでは本質的に低速であり、CLR(2005)またはテーブル値パラメーター(TVP)(2008+)を使用して、T-SQLの外部に移動することをお勧めします。私は最近、これについて読む価値のある3部構成のシリーズを公開しましたが、私が行ったのと同じ結論に達すると思います(CLRは優れており、TVPは優れており、すべてのT-SQLメソッドは比較するとばかげているように見えます) )::

http://www.sqlperformance.com/2012/07/t-sql-queries/split-strings

http://www.sqlperformance.com/2012/08/t-sql-queries/splitting-strings-follow-up

http://www.sqlperformance.com/2012/08/t-sql-queries/splitting-strings-now-with-less-t-sql

また、その効率をどのように測定したかについても言及してください。

そうですね、これらの記事で私が行ったSYSDATETIME()ことを実行し、各テストを実行する前後を選択して、差を計算することができます。また、各テストの前後にテーブルにログインしたり、プロファイラーを使用してテストをキャプチャしたり、テストを次のように囲んだりすることもできます。

SET STATISTICS TIME ON;

PRINT 'Test 1';

-- do test 1 here

PRINT 'Test 2';

-- do test 2 here

SET STATISTICS TIME OFF;

次のようなメッセージペインに出力が表示されます。

Test 1

SQL Server execution times:
  CPU time: 247 ms, elapsed time: 345 ms

Test 2

SQL Server execution times:
  CPU time: 332 ms, elapsed time: 421 ms

最後に、無料のツールであるSQL SentryPlanExplorerを使用できます。(免責事項:私はSQL Sentryで働いています。)

クエリをプランエクスプローラーにフィードして実際のプランを生成できます。また、Management Studioのshowplanよりもはるかに読みやすいグラフィカルプランに加えて、期間、CPU、読み取りなどのランタイムメトリックも取得できます。したがって、上記のいずれも実行せずに、2つのクエリを実行し、それらを並べて比較できます。

ここに画像の説明を入力してください

于 2012-08-23T15:33:23.163 に答える
2

別の異なるアプローチ:

CREATE FUNCTION [dbo].[fGetTableFromList]
(
    @list VARCHAR(max), @delimiter VARCHAR(10)
)
RETURNS @table TABLE
(value VARCHAR(8000)) AS
BEGIN

DECLARE @list1 VARCHAR(8000), @pos INT, @rList VARCHAR(MAX);

SET @list = LTRIM(RTRIM(@list)) + @delimiter
SET @pos = CHARINDEX(@delimiter, @list, 1)

WHILE @pos > 0
    BEGIN
        SET @list1 = LTRIM(RTRIM(LEFT(@list, @pos - 1)))

        IF @list1 <> ''
            INSERT INTO @table(value) VALUES (@list1)

        SET @list = SUBSTRING(@list, @pos+1, LEN(@list))
        SET @pos = CHARINDEX(@delimiter, @list, 1)
    END
RETURN 
END

dbo.SplitStringCPU時間では、dbo.Splitと私の間にあまり違いはありませんdbo.fGetTableFromList。私はこれを実行することによってこれを知っています:

SET STATISTICS TIME ON;

SELECT * FROM [dbo].[Split]('Lorem ipsum dolor sit amet,...', ' ');
SELECT * FROM [dbo].[SplitString]('Lorem ipsum dolor sit amet,...', ' ');
SELECT * FROM [dbo].[fGetTableFromList]('Lorem ipsum dolor sit amet,...', ' ');

SET STATISTICS TIME OFF;

もちろん、さまざまな入力でテストして、より多くの時間実行レコードを取得できる限り、どの関数のパフォーマンスがより正確であるかをより正確に把握できます。

また、実行計画にも注意を払う必要があります。文を削除し、SET STATISTICS上記の3つのクエリを実行して、実行プランを表示するようにSMSSに指示します。

[実行計画]タブに表示される概要を確認するだけで、詳細を確認しなくても、最初の1つはSplit予測された作業の13%、2つ目SplitStringは60%の労力を費やしていることがわかります。そして3番目のものはfGetTableFromList再び13%です(残りの作業はSELECTsによって費やされます)。

これは「ダミー」の方法であり、DBA用ではありません。正確または正確なベンチマークが必要な場合は、いくつかのストレステストを作成して、簡潔な結果を抽出する必要があります(@AaronBertrandが提供するリンクのように)。

于 2012-08-23T15:38:10.600 に答える
0

@ AnandPhadke、

あなたがCTEで何をしているのかわかりません。これは完全に正常に機能します。

Create function dbo.SplitString(@inputStr varchar(1000),@del varchar(5))
RETURNS @table TABLE(col varchar(100))
As
BEGIN

DECLARE @t table(col1 varchar(100))
INSERT INTO @t
select @inputStr

if CHARINDEX(@del,@inputStr,1) > 0
BEGIN
    ;WITH CTE1 as (
    select ltrim(rtrim(LEFT(col1,CHARINDEX(@del,col1,1)-1))) as col,RIGHT(col1,LEN(col1)-CHARINDEX(@del,col1,1)) as rem from @t
    union all
    select ltrim(rtrim(LEFT(rem,CHARINDEX(@del,rem,1)-1))) as col,RIGHT(rem,LEN(rem)-CHARINDEX(@del,rem,1))
    from CTE1 c
    where CHARINDEX(@del,rem,1)>0
    )

        INSERT INTO @table 
        select col from CTE1
        union all
        select rem from CTE1 where CHARINDEX(@del,rem,1)=0
    END
ELSE
BEGIN
    INSERT INTO @table 
    select col1 from @t
END

RETURN

END
于 2012-08-23T14:04:02.667 に答える
0

これを試して:

CREATE function dbo.SplitString(@inputStr varchar(1000),@del varchar(5))
RETURNS @table TABLE(col varchar(100))
As
BEGIN

DECLARE @t table(col1 varchar(100))
INSERT INTO @t
select @inputStr

if CHARINDEX(@del,@inputStr,1) > 0
BEGIN
;WITH CTE as(select ROW_NUMBER() over (order by (select 0)) as id,* from @t)
,CTE1 as (
select id,ltrim(rtrim(LEFT(col1,CHARINDEX(@del,col1,1)-1))) as col,RIGHT(col1,LEN(col1)-CHARINDEX(@del,col1,1)) as rem from CTE
union all
select c.id,ltrim(rtrim(LEFT(rem,CHARINDEX(@del,rem,1)-1))) as col,RIGHT(rem,LEN(rem)-CHARINDEX(@del,rem,1))
from CTE1 c
where CHARINDEX(@del,rem,1)>0
)

INSERT INTO @table 
select col from CTE1
union all
select rem from CTE1 where CHARINDEX(@del,rem,1)=0
END
ELSE
BEGIN
INSERT INTO @table 
select col1 from @t
END


RETURN

END
于 2012-08-23T13:35:37.497 に答える